1 /* File miscio.c - 11. 10. 94.
2  * 6/11/99 added add_expanded_word_to_buffer for dbae.
3  * 9/10/98 moved set_separator to this file. More convenient here.
4  * 9.1.98. change of `gen' to `char' for generator type - requires
5  *         functions genstrlen, genstrcmp, genstrcat to be written
6  *         with this type.
7  * 21. 9. 95. - return type of read_word changed from void to boolean,
8  *              to distinguish between the cases when no word is read, and
9  *              when the empty word is read.
10  *              do same for read_int, read_string (although not needed yet)
11  * 13. 8. 95. - added function read_word_list to read a list of words.
12  * 13. 3. 95. - skip_gap_expression written
13  * 27. 2. 95. - allowed generators of form string^-1 - read_ident gets
14  *              an extra parameter.
15  * 12. 12. 94. transferred the function process_names to this file.
16  * This file contains some miscellaneous input and output functions that
17  * are used by both automata and string-rewriting programs.
18  * Also we recode the standard function strlen as stringlen to avoid
19  * some type incompatabilities resulting in warning messages on solaris!
20  */
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <ctype.h>
25 #include "defs.h"
26 #include "fsa.h"
27 #include "rws.h"
28 #include "externals.h"
29 
30 /* Note: '.' is not included in list of delimiters, since identifiers may
31  * contain dots.
32  * Integers are exceptional, however, in that they may be terminated by a
33  * '.' as well as by as delimiter.
34  * identifiers, strings and words are terminated by a delimiter.
35  */
isdelim(int c)36 boolean isdelim(int c)
37 {
38   return c == '^' || c == '*' || c == '[' || c == ']' || c == ',' || c == '(' ||
39          c == '{' || c == '}' || c == ')' || c == ';' || c == '\"' ||
40          c == ':' || c == EOF;
41 }
42 
43 /* Checks if symbol is valid in an identifier */
isvalid(int c)44 boolean isvalid(int c)
45 {
46   return isalpha(c) || isdigit(c) || c == '_' || c == '.';
47 }
48 
49 /* This is the basic character-reading function, which all other routines
50  * call.
51  * It skips over a backslash '\' followed by a newline '\n'.
52  * It replaces tabs, newlines and comments (starting with # ending with
53  * carriage return) with a space.
54  * Otherwise it returns the next character read.
55  * Use getc diretcly when readin literal strings.
56  */
read_char(FILE * rfile)57 static int read_char(FILE *rfile)
58 {
59   static int last; /* used to remember a character from the last call */
60   int n;
61   n = last ? last : getc(rfile);
62   /* The input may contain a '\' character which is printed before '\n'
63    * -- this is particularly true if the input file is generated by GAP
64    */
65   while (n == '\\') {
66     n = getc(rfile);
67     if (n == '\n')
68       n = getc(rfile);
69     else {
70       last = n;
71       return (int)'\\';
72     }
73   }
74   if (n == '#')
75     while ((n = getc(rfile)) != '\n' && n != EOF)
76       ;
77   if (n == '\n' || n == '\t')
78     return (' ');
79   return (n);
80 }
81 
82 /* The next non-white character is expected to be 'c'.
83    If not exit with error message.
84 */
check_next_char(FILE * rfile,int c)85 void check_next_char(FILE *rfile, int c)
86 {
87   int n;
88   while ((n = read_char(rfile)) == ' ')
89     ;
90   if (n == '{' && c == '[')
91     return;
92   if (n == '}' && c == ']')
93     return;
94   if (n != c) {
95     fprintf(stderr, "#Input error: \'%c\' expected (not %c).\n", c, n);
96     exit(1);
97   }
98 }
99 
100 /* Read next delimiter. */
read_delim(FILE * rfile,int * delim)101 void read_delim(FILE *rfile, int *delim)
102 {
103   int n;
104   while ((n = read_char(rfile)) == ' ')
105     ;
106   if (!isdelim(n)) {
107     fprintf(stderr, "#Input error: delimiter expected (not %c).\n", n);
108     exit(1);
109   }
110   *delim = n;
111   if (*delim == '{')
112     *delim = '[';
113   if (*delim == '}')
114     *delim = ']';
115 }
116 
117 /* Read stuff into token up to next delimiter or space  -
118  * leading spaces are removed from stuff.
119  */
read_token(FILE * rfile,char * token,int * delim)120 static void read_token(FILE *rfile, char *token, int *delim)
121 {
122   int n;
123   *token = '\0';
124   while ((n = read_char(rfile)) == ' ')
125     ;
126   while (!isdelim(n) && !(n == ' ')) {
127     *(token++) = n;
128     n = read_char(rfile);
129   }
130   *token = '\0';
131   *delim = n;
132   if (*delim == '{')
133     *delim = '[';
134   if (*delim == '}')
135     *delim = ']';
136 }
137 
138 /* Skip the next GAP expression -
139  * probably not perfect, but should be adequate for the use intended.
140  */
skip_gap_expression(FILE * rfile,int * delim)141 void skip_gap_expression(FILE *rfile, int *delim)
142 {
143   int n, br_ct = 0;
144   char br_rec[1024];
145 
146   while (1) {
147     read_token(rfile, kbm_buffer, delim);
148     if (strcmp(kbm_buffer, "function") == 0)
149       br_rec[br_ct++] = 'f'; /*marks that we are in a function definition */
150     else if (strcmp(kbm_buffer, "end") == 0) {
151       if (br_ct > 0 && br_rec[br_ct - 1] == 'f')
152         br_ct--;
153       else {
154         fprintf(stderr, "#Input error: \"end\" without \"function\".\n");
155         exit(1);
156       }
157     }
158     if (*delim == '\'') {
159       n = getc(rfile);
160       if (n == '\\')
161         n = getc(rfile);
162       n = getc(rfile);
163       if (n != '\'') {
164         fprintf(stderr, "#Input error: ' expected.\n");
165         exit(1);
166       }
167     }
168     else if (*delim == '\"') {
169       do {
170         n = getc(rfile);
171         while (n == '\\') {
172           n = getc(rfile);
173           n = getc(rfile);
174         }
175         if (n == '\n' || n == EOF) {
176           fprintf(stderr, "#Input error: newline or EOF in string.\n");
177           exit(1);
178         }
179       } while (n != '\"');
180     }
181     else if (*delim == '(' || *delim == '[' || *delim == '{') /*}*/
182       br_rec[br_ct++] = *delim;
183     else if (*delim == ')' && br_ct > 0) {
184       if (br_rec[br_ct - 1] == '(')
185         br_ct--;
186       else {
187         fprintf(stderr, "#Input error: ')' without '('.\n");
188         exit(1);
189       }
190     }
191     else if (*delim == ']') {
192       if (br_ct > 0 && br_rec[br_ct - 1] == '[')
193         br_ct--;
194       else {
195         fprintf(stderr, "#Input error: ']' without '['.\n");
196         exit(1);
197       }
198     }
199     /*{*/
200     else if (*delim == '}') {
201       if (br_ct > 0 && br_rec[br_ct - 1] == '{') /*}*/
202         br_ct--;
203       else {
204         fprintf(stderr,
205                 /*{*/ "#Input error: '}' without '{'.\n"); /*}*/
206         exit(1);
207       }
208     }
209     else if (br_ct == 0 && (*delim == ',' || *delim == ')' || *delim == ';'))
210       break;
211     if (br_ct >= 1024) {
212       fprintf(stderr, "#Input error: Bracket level too deep.\n");
213       exit(1);
214     }
215   }
216 }
217 
218 /* Read next identifier, field name, etc.
219  * if inv is true, it is allowed to end ^-1.
220  */
read_ident(FILE * rfile,char * ident,int * delim,boolean inv)221 void read_ident(FILE *rfile, char *ident, int *delim, boolean inv)
222 {
223   int n;
224   char *ptr = ident;
225   while ((n = read_char(rfile)) == ' ')
226     ;
227   while (n != ' ' && !isdelim(n)) {
228     *(ptr++) = n;
229     n = read_char(rfile);
230   }
231   if (n == ' ')
232     while ((n = read_char(rfile)) == ' ')
233       ;
234   if (!isdelim(n)) {
235     fprintf(stderr,
236             "#Input error: delimiter expected to terminate ident (not %c).\n",
237             n);
238     exit(1);
239   }
240   if (inv && n == '^') {
241     /* With some reluctance, I allow a generator to be called {ident}^-1 */
242     *(ptr++) = '^';
243     check_next_char(rfile, '-');
244     *(ptr++) = '-';
245     check_next_char(rfile, '1');
246     *(ptr++) = '1';
247     read_delim(rfile, &n);
248   }
249   *ptr = '\0';
250   *delim = n;
251   if (*delim == '{')
252     *delim = '[';
253   if (*delim == '}')
254     *delim = ']';
255 }
256 
257 /* Read next integer - may be empty, when 0 is returned as *integ.
258  * NOTE integer can be terminated by a '.' as well as a delimiter
259  * FALSE returned if no int read, otherwise TRUE.
260  */
read_int(FILE * rfile,int * integ,int * delim)261 boolean read_int(FILE *rfile, int *integ, int *delim)
262 {
263   int n, value = 0;
264   boolean neg = FALSE, ans = FALSE;
265   while ((n = read_char(rfile)) == ' ')
266     ;
267   do {
268     if (n == '-' && value == 0)
269       neg = TRUE;
270     else if (isdigit(n)) {
271       value = 10 * value + n - '0';
272       ans = TRUE;
273     }
274     else if (n == ' ' || n == '.' || isdelim(n))
275       break;
276     else {
277       fprintf(stderr, "#Input error: integer expected.\n");
278       exit(1);
279     }
280     n = read_char(rfile);
281   } while (1);
282   if (n == ' ')
283     while ((n = read_char(rfile)) == ' ')
284       ;
285   if (n != '.' && !isdelim(n)) {
286     fprintf(stderr,
287             "#Input error: delimiter expected to terminate integer (not %c).\n",
288             n);
289     exit(1);
290   }
291   if (neg)
292     value = -value;
293   *integ = value;
294   *delim = n;
295   if (*delim == '{')
296     *delim = '[';
297   if (*delim == '}')
298     *delim = ']';
299   return ans;
300 }
301 
302 
303 /* Read next string (enclosed in quotes) - if delim comes first, return
304  * empty string as string.
305  * FALSE returned if no string read, otherwise TRUE.
306  */
read_string(FILE * rfile,char * string,int * delim)307 boolean read_string(FILE *rfile, char *string, int *delim)
308 {
309   int n;
310   char *ptr = string;
311   boolean ans = FALSE;
312   while ((n = read_char(rfile)) == ' ')
313     ;
314   if (n == '\"') {
315     ans = TRUE;
316     do {
317       n = getc(rfile);
318       while (n == '\\') {
319         n = getc(rfile);
320         *(ptr++) = n;
321         n = getc(rfile);
322       }
323       if (n == '\n' || n == EOF) {
324         fprintf(stderr, "#Input error: newline or EOF in string.\n");
325         exit(1);
326       }
327       if (n != '\"')
328         *(ptr++) = n;
329       else
330         break;
331     } while (1);
332     read_delim(rfile, delim);
333     /* We want the next delimiter after the terminating quotes */
334   }
335   else if (!isdelim(n)) {
336     fprintf(stderr,
337             "#Input error: delimiter expected in read_string (not %c).\n", n);
338     exit(1);
339   }
340   else
341     *delim = n;
342   if (*delim == '{')
343     *delim = '[';
344   if (*delim == '}')
345     *delim = ']';
346   *ptr = '\0';
347   return ans;
348 }
349 
350 /* name is a list of num_names names (probably names of monoid generators).
351  * First they are checked for validity - they must start with a letter or
352  * underscore, and all characters must be alphanumeric '_' or '.'.
353  * (or they can end in ^-1).
354  * They are then checked to see if they comply with one of the two
355  * special formats a) and b) described at the top of the file rwsio.h.
356  * If so, then the external variables kbm_algno and kbm_gen_no are set
357  * accordingly. These are used by the next function read_next_word for
358  * fast reading of words in the generators.
359  * Otherwise, kbm_algno is set to -1, and words are located in the list
360  * by a simple search.
361  */
process_names(char ** name,int num_names)362 void process_names(char **name, int num_names)
363 {
364   int i, j, l;
365   char *genname, *genname1, *ptr;
366 
367   for (i = 0; i <= MAXGEN; i++)
368     kbm_gen_no[i] = 0;
369   kbm_algno = 0;
370   if (num_names == 0)
371     return;
372   for (i = 1; i <= num_names; i++) {
373     genname = name[i];
374     if (stringlen(genname) == 0) {
375       fprintf(stderr, "#Input error: a generator name has length 0.\n");
376       exit(1);
377     }
378     if (stringlen(genname) > 1)
379       kbm_algno = -1;
380     if (!isalpha(genname[0]) && genname[0] != '_') {
381       fprintf(stderr,
382               "#Input error: generators should start with a letter or `_'.\n");
383       exit(1);
384     }
385     if (kbm_algno == 0) {
386       if (kbm_gen_no[genname[0]] != 0) {
387         fprintf(stderr, "#Input error: repeated generator name.\n");
388         exit(1);
389       }
390       kbm_gen_no[genname[0]] = i;
391     }
392   }
393   if (kbm_algno == 0)
394     return; /* all generators are single letters or underscore. */
395 
396   for (i = 1; i <= num_names; i++) {
397     genname = name[i];
398     l = stringlen(genname);
399     for (j = 0; j < l; j++)
400       if (!isvalid(genname[j])) {
401         if (j != l - 3 || genname[j] != '^' || genname[j + 1] != '-' ||
402             genname[j + 2] != '1') {
403           fprintf(stderr,
404                   "#Input error: generator %s: names must be alphanumeric, "
405                   "'_', or '.'.\n",
406                   genname);
407           exit(1);
408         }
409         else
410           break;
411       }
412     for (j = 1; j < i; j++)
413       if (strcmp(genname, name[j]) == 0) {
414         fprintf(stderr, "#Input error: repeated generator name.\n");
415         exit(1);
416       }
417   }
418 
419   /* Try to locate prefix  for Case b). */
420   genname1 = name[1];
421   ptr = genname1 + stringlen(genname1) - 1;
422   if (!isdigit(*ptr)) { /* not Case b) */
423     kbm_algno = -1;
424     return;
425   }
426   while (isdigit(*ptr))
427     ptr--;
428   kbm_algno = ptr - genname1 + 1;
429   if (atoi(genname1 + kbm_algno) > MAXGEN) { /* numerical subscript too large */
430     kbm_algno = -1;
431     return;
432   }
433 
434   kbm_gen_no[atoi(genname1 + kbm_algno)] = 1;
435   for (i = 2; i <= num_names; i++) {
436     genname = name[i];
437     if (stringlen(genname) < kbm_algno + 1) { /* not Case b) */
438       kbm_algno = -1;
439       return;
440     }
441     for (j = 0; j < kbm_algno; j++)
442       if (genname[j] != genname1[j]) { /* not Case b) */
443         kbm_algno = -1;
444         return;
445       }
446     for (j = kbm_algno; j < stringlen(genname); j++)
447       if (!isdigit(genname[j])) {
448         /* not Case b) */
449         kbm_algno = -1;
450         return;
451       }
452     if (atoi(genname + kbm_algno) >
453         MAXGEN) { /* numerical subscript too large */
454       kbm_algno = -1;
455       return;
456     }
457     kbm_gen_no[atoi(genname + kbm_algno)] = i;
458   }
459 }
460 
461 /* A word is read into gen_word.
462    It is assumed that space is already allocated for the word beginning at
463    gen_word and ending at end_word.
464    If the word is too long, the process aborts.
465    The word must be empty or equal to "IdWord" (equivalent) or have the form
466    t1*t2*...tr (for some r>=1), where each ti is either the name of
467    a generator, or of form <gen-name>^<integer>.
468    2/6/95 - brackets are now allowed up to level 8, so
469             expressions like (x*(y*z)^3)^9  are allowed.
470    If <integer> is negative, then there must be a generator called
471    <gen-name>^-1.
472    The word is translated and expanded into a string of generator
473    numbers and stored as a string of `gen's in gen_word.
474    The external array kbm_gen_no, and the external integer kbm_algno are used
475    for the translation - see comment in rwsio.h for explanation
476    If check is set true, then the validity of the word is checked -
477    using the list "name" of generator names.
478    Otherwise it is processed as fast as possible.
479    FALSE returned if no word read (delim comes first), otherwise TRUE.
480 */
read_word(FILE * rfile,gen * gen_word,gen * end_word,int * delim,char ** name,int num_names,boolean check)481 boolean read_word(FILE *rfile, gen *gen_word, gen *end_word, int *delim,
482                   char **name, int num_names, boolean check)
483 {
484   int i, g, n, delimchar, bracketlevel;
485   gen *ptr, *ptrr, *ptre, *bracketptr[9];
486   ptr = gen_word;
487   bracketlevel = 0;
488   do {
489     read_ident(rfile, kbm_buffer, &delimchar, FALSE);
490     if (delimchar == '(') {
491       if (stringlen(kbm_buffer) != 0) {
492         fprintf(stderr, "Input error: '(' in wrong position in word.\n");
493         exit(1);
494       }
495       bracketlevel++;
496       if (bracketlevel > 8) {
497         fprintf(stderr, "Input error: at most 8 levels of brackets allowed.\n");
498         exit(1);
499       }
500       bracketptr[bracketlevel] = ptr;
501       continue;
502     }
503     if (ptr == gen_word &&
504         (strcmp(kbm_buffer, "") == 0 || strcmp(kbm_buffer, "IdWord") == 0))
505       break;
506     if (kbm_algno == -1) {
507       g = 0;
508       for (i = 1; i <= num_names; i++)
509         if (strcmp(kbm_buffer, name[i]) == 0) {
510           g = i;
511           break;
512         }
513       if (g == 0) {
514         fprintf(stderr, "#Input error: unknown generator in word.\n");
515         exit(1);
516       }
517     }
518     else {
519       g = (kbm_algno == 0) ? kbm_gen_no[*kbm_buffer]
520                            : kbm_gen_no[atoi(kbm_buffer + kbm_algno)];
521       if (check && (g == 0 || strcmp(kbm_buffer, name[g])) != 0) {
522         fprintf(stderr, "#Input error: invalid entry in word.\n");
523         exit(1);
524       }
525     }
526     if (delimchar == '^') {
527       read_int(rfile, &n, &delimchar);
528       if (delimchar != '*' && delimchar != ']' && delimchar != ',' &&
529           delimchar != ';' && delimchar != ')') {
530         fprintf(stderr, "#Input error: ',' '*', ')'  or ']' expected.\n");
531         exit(1);
532       }
533       if (n < 0) {
534         /* This is only allowed if we have a generator ending in ^-1 */
535         g = 0;
536         if (kbm_algno == -1) {
537           sprintf(kbm_buffer + stringlen(kbm_buffer), "^-1");
538           for (i = 1; i <= num_names; i++)
539             if (strcmp(kbm_buffer, name[i]) == 0) {
540               g = i;
541               break;
542             }
543         }
544         if (g == 0) {
545           fprintf(stderr, "#Input error: unknown generator or illegal negative "
546                           "power in word.\n");
547           exit(1);
548         }
549         n = -n;
550       }
551       for (i = 1; i <= n; i++) {
552         *(ptr++) = g;
553         if (ptr > end_word) {
554           fprintf(stderr,
555                   "The word being read is too long. Increase maxreducelen.\n");
556           exit(1);
557         }
558       }
559     }
560     else if (delimchar == '*' || delimchar == ']' || delimchar == ',' ||
561              delimchar == ';' || delimchar == ')') {
562       *(ptr++) = g;
563       if (ptr > end_word) {
564         fprintf(stderr,
565                 "The word being read is too long. Increase maxreducelen.\n");
566         exit(1);
567       }
568     }
569     else {
570       fprintf(stderr, "#Input error: ',' '^' '*' ';' or ']' expected.\n");
571       exit(1);
572     }
573     while (delimchar == ')') {
574       read_delim(rfile, &delimchar);
575       while (delimchar == ')') {
576         bracketlevel--;
577         read_delim(rfile, &delimchar);
578       }
579       if (bracketlevel <= 0) {
580         fprintf(stderr, "Input error: ')' with no matching '('.\n");
581         exit(1);
582       }
583       if (delimchar == '^') {
584         read_int(rfile, &n, &delimchar);
585         if (delimchar != '*' && delimchar != ']' && delimchar != ',' &&
586             delimchar != ';' && delimchar != ')') {
587           fprintf(stderr, "#Input error: ',' '*' ')' or ']' expected.\n");
588           exit(1);
589         }
590         if (n <= 0) {
591           fprintf(stderr,
592                   "#Error: Non-positive powers not allowed in monoid words.\n");
593           exit(1);
594         }
595         ptre = ptr - 1;
596         for (i = 1; i < n; i++) {
597           ptrr = bracketptr[bracketlevel];
598           while (ptrr <= ptre) {
599             *(ptr++) = *(ptrr++);
600             if (ptr > end_word) {
601               fprintf(
602                   stderr,
603                   "The word being read is too long. Increase maxreducelen.\n");
604               exit(1);
605             }
606           }
607         }
608       }
609       else if (delimchar != '*' && delimchar != ']' && delimchar != ',' &&
610                delimchar != ';') {
611         fprintf(stderr, "#Input error: ',' '^' '*' ';' or ']' expected.\n");
612         exit(1);
613       }
614       bracketlevel--;
615     }
616   } while (delimchar != ',' && delimchar != ']' && delimchar != ';');
617 
618   if (bracketlevel != 0) {
619     fprintf(stderr, "#Input error: brackets do not balance in word.\n");
620     exit(1);
621   }
622   *delim = delimchar;
623   if (*delim == '{')
624     *delim = '[';
625   if (*delim == '}')
626     *delim = ']';
627   *ptr = 0;
628   return (ptr != gen_word || strcmp(kbm_buffer, "IdWord") == 0);
629 }
630 
631 /* Read a list of words separated by commas into wordlist, and return the
632  * number read. space is the total space available - the program aborts
633  * if this is exceeded.
634  * The individual words are terminated by the string terminator 0 in
635  * wordlist, so that they can be conveniently copied.
636  * name, num_names and check are as in read_word.
637  */
read_word_list(FILE * rfile,gen * wordlist,int space,int * delim,char ** name,int num_names,int check)638 int read_word_list(FILE *rfile, gen *wordlist, int space, int *delim,
639                    char **name, int num_names, int check)
640 {
641   int ct;
642   gen *ptr, *ptre;
643   ptr = wordlist;
644   ptre = wordlist + space - 1;
645   ct = 0;
646   do {
647     read_word(rfile, ptr, ptre, delim, name, num_names, check);
648     ct++;
649     ptr += (genstrlen(ptr) + 1);
650   } while (*delim == ',');
651   if (*delim == '{')
652     *delim = '[';
653   if (*delim == '}')
654     *delim = ']';
655   return ct;
656 }
657 
658 /* prints contents of buffer, followed by new-line to wfile
659  * and then clears buffer.
660  */
printbuffer(FILE * wfile)661 void printbuffer(FILE *wfile)
662 {
663   fprintf(wfile, "%s\n", kbm_buffer);
664   *kbm_buffer = '\0';
665 }
666 
667 /* w should be a string.
668  * Add at least n characters to the end of the string buffer, with w at the end,
669  * padding with spaces at beginning
670  */
add_to_buffer(int n,char * w)671 void add_to_buffer(int n, char *w)
672 {
673   int i, nsp;
674   nsp = n - stringlen(w);
675   for (i = 1; i <= nsp; i++)
676     strcat(kbm_buffer, " ");
677   strcat(kbm_buffer, w);
678 }
679 
680 /* word is a string of `gen's. to be printed as a word in GAP syntax.
681  * If gen number i appears in the string, then
682  * the gen is to be printed as the string symbols[i].
683  * The buffer contains the line to be printed, and the word is
684  * appended to the end of the current contents of the buffer.
685  * New lines are printed in mid-word if necessary, but not at the
686  * end, since the calling function may need to append more.
687  * The returned value is the number of newlines inserted.
688  */
add_word_to_buffer(FILE * wfile,gen * word,char ** symbols)689 int add_word_to_buffer(FILE *wfile, gen *word, char **symbols)
690 {
691   int offset, pow, g, len, lg, nln = 0;
692   gen *w;
693   char sg[256];
694   /* 256 here is the maximum length of a generator name in `symbols'. */
695   boolean first;
696   offset = stringlen(kbm_buffer) + 2; /* remember where word begins on the line,
697                                        * in case we need to add new lines. */
698   if (offset > 32)
699     offset = 32; /* to avoid silly situations */
700   w = word;
701   if (*w == 0)
702     add_to_buffer(0, "IdWord");
703   first = TRUE;
704   while (*w) {
705     g = *(w++);
706     pow = 1;
707     while (*w == g) {
708       pow++;
709       w++;
710     }
711     len = first ? 0 : 1;
712     strcpy(sg, symbols[g]);
713     len += stringlen(sg);
714     if (pow > 1) {
715       len += (1 + int_len(pow));
716     }
717     if (stringlen(kbm_buffer) + len >= 76) { /* new line needed */
718       printbuffer(wfile);
719       add_to_buffer(offset, "");
720       nln++;
721     }
722     if (!first)
723       add_to_buffer(0, "*");
724     /* Check if we have a power of a generator ending with ^-1 */
725     lg = stringlen(sg);
726     if (pow > 1 && lg > 3 && sg[lg - 3] == '^' && sg[lg - 2] == '-' &&
727         sg[lg - 1] == '1') {
728       sg[lg - 3] = '\0';
729       pow = -pow;
730     }
731     add_to_buffer(0, sg);
732     if (pow > 1 || pow < -1)
733       sprintf(kbm_buffer + stringlen(kbm_buffer), "^%d", pow);
734     first = FALSE;
735   }
736   return nln;
737 }
738 
739 /* The same as add_word_to_buffer, but powers of generators are
740  * printed out in full rather than as powers.
741  */
add_expanded_word_to_buffer(FILE * wfile,gen * word,char ** symbols)742 int add_expanded_word_to_buffer(FILE *wfile, gen *word, char **symbols)
743 {
744   int offset, g, len, nln = 0;
745   gen *w;
746   char sg[256];
747   /* 256 here is the maximum length of a generator name in `symbols'. */
748   boolean first;
749   offset = stringlen(kbm_buffer) + 2; /* remember where word begins on the line,
750                                        * in case we need to add new lines. */
751   if (offset > 32)
752     offset = 32; /* to avoid silly situations */
753   w = word;
754   if (*w == 0)
755     add_to_buffer(0, "IdWord");
756   first = TRUE;
757   while (*w) {
758     g = *(w++);
759     len = first ? 0 : 1;
760     strcpy(sg, symbols[g]);
761     len += stringlen(sg);
762     if (stringlen(kbm_buffer) + len >= 76) { /* new line needed */
763       printbuffer(wfile);
764       add_to_buffer(offset, "");
765       nln++;
766     }
767     if (!first)
768       add_to_buffer(0, "*");
769     add_to_buffer(0, sg);
770     first = FALSE;
771   }
772   return nln;
773 }
774 
775 /* This is the same as add_word_to_buffer, except that "word" is a string of
776  * integers instead of gens.
777  */
add_iword_to_buffer(FILE * wfile,int * word,char ** symbols)778 int add_iword_to_buffer(FILE *wfile, int *word, char **symbols)
779 {
780   int offset, pow, g, lg, len, nln = 0;
781   int *w;
782   char sg[128];
783   boolean first;
784   offset = stringlen(kbm_buffer) + 2; /* remember where word begins on the line,
785                                        * in case we need to add new lines. */
786   w = word;
787   if (*w == 0)
788     add_to_buffer(0, "IdWord");
789   first = TRUE;
790   while (*w) {
791     g = *(w++);
792     pow = 1;
793     while (*w == g) {
794       pow++;
795       w++;
796     }
797     len = first ? 0 : 1;
798     strcpy(sg, symbols[g]);
799     len += stringlen(sg);
800     if (pow > 1) {
801       len += (1 + int_len(pow));
802     }
803     if (stringlen(kbm_buffer) + len >= 76) { /* new line needed */
804       printbuffer(wfile);
805       add_to_buffer(offset, "");
806       nln++;
807     }
808     if (!first)
809       add_to_buffer(0, "*");
810     /* Check if we have a power of a generator ending with ^-1 */
811     lg = stringlen(sg);
812     if (pow > 1 && lg > 3 && sg[lg - 3] == '^' && sg[lg - 2] == '-' &&
813         sg[lg - 1] == '1') {
814       sg[lg - 3] = '\0';
815       pow = -pow;
816     }
817     add_to_buffer(0, sg);
818     if (pow > 1 || pow < -1)
819       sprintf(kbm_buffer + stringlen(kbm_buffer), "^%d", pow);
820     first = FALSE;
821   }
822   return nln;
823 }
824 
825 int
826 /* The length of the  integer  n */
int_len(int n)827 int_len(int n)
828 {
829   int l = 0;
830   if (n < 0) {
831     n = -n;
832     l = 1;
833   }
834   while (n != 0) {
835     l++;
836     n = n / 10;
837   }
838   return l;
839 }
840 
841 /* returns true if the string x is an integer */
is_int(char * x)842 boolean is_int(char *x)
843 {
844   int i, l;
845   l = stringlen(x);
846   if (l == 0 || (l == 1 && x[0] == '-'))
847     return FALSE;
848   for (i = 0; i < l; i++)
849     if (!isdigit(x[i]) && (i != 0 || x[i] != '-'))
850       return FALSE;
851   return TRUE;
852 }
853 
854 /* We recode this standard function, since the solaris version returns a type
855  * other than int, which causes irritating warning messages!
856  */
stringlen(char * c)857 int stringlen(char *c)
858 {
859   int l = 0;
860   while (*(c++))
861     l++;
862   return l;
863 }
864 
genstrlen(gen * c)865 int genstrlen(gen *c)
866 {
867   int l = 0;
868   while (*(c++))
869     l++;
870   return l;
871 }
872 
genstrcmp(gen * c,gen * d)873 int genstrcmp(gen *c, gen *d)
874 {
875   while (*c) {
876     if (*(c++) != *(d++))
877       return 1;
878   }
879   if (*c != *d)
880     return 1;
881   return 0;
882 }
883 
genstrcat(gen * c,gen * d)884 void genstrcat(gen *c, gen *d)
885 {
886   while (*c)
887     c++;
888   while (*d)
889     *(c++) = *(d++);
890   *c = 0;
891 }
892 
genstrcpy(gen * c,gen * d)893 void genstrcpy(gen *c, gen *d)
894 {
895   while (*d)
896     *(c++) = *(d++);
897   *c = 0;
898 }
899 /* Locates separator, and checks the level conditions on G-generators and
900  * H-generators.
901  */
set_separator(rewriting_system * rwsptr)902 void set_separator(rewriting_system *rwsptr)
903 {
904   int i, lev;
905   /* Locate separator */
906   rwsptr->separator = 0;
907   for (i = 1; i <= rwsptr->num_gens; i++)
908     if (strcmp(rwsptr->gen_name[i], "_H") == 0) {
909       rwsptr->separator = i;
910       break;
911     }
912   if (rwsptr->separator == 0) {
913     fprintf(stderr, "Error - there is no generator named '_H'.\n");
914     exit(1);
915   }
916   if (rwsptr->inv_of[rwsptr->separator]) {
917     fprintf(stderr, "Error - generator '_H' should have no inverse!.\n");
918     exit(1);
919   }
920 
921   if (rwsptr->level == 0) {
922     fprintf(stderr, "Error - ordering must be WREATHPROD in cosets program.\n");
923     exit(1);
924   }
925   lev = rwsptr->level[rwsptr->separator];
926   for (i = 1; i < rwsptr->separator; i++)
927     if (rwsptr->level[i] <= lev) {
928       fprintf(stderr, "Error: G-generators must precede separator and have "
929                       "higher level.\n");
930       exit(1);
931     }
932   for (i = rwsptr->separator + 1; i <= rwsptr->num_gens; i++)
933     if (rwsptr->level[i] > lev) {
934       fprintf(stderr, "Error: G-generators must follow separator and not have "
935                       "higher level.\n");
936       exit(1);
937     }
938 
939   /* Now determine Gislevel, Hislevel, Hhasinverses */
940   rwsptr->Gislevel = TRUE;
941   lev = 0;
942   for (i = 1; i < rwsptr->separator; i++)
943     if (lev == 0)
944       lev = rwsptr->level[i];
945     else if (rwsptr->level[i] != lev) {
946       rwsptr->Gislevel = FALSE;
947       break;
948     }
949   rwsptr->Hislevel = TRUE;
950   lev = 0;
951   for (i = rwsptr->separator + 1; i <= rwsptr->num_gens; i++)
952     if (lev == 0)
953       lev = rwsptr->level[i];
954     else if (rwsptr->level[i] != lev) {
955       rwsptr->Hislevel = FALSE;
956       break;
957     }
958 
959   if (rwsptr->finitestop && !rwsptr->Gislevel) {
960     printf(
961         " #Warning: finiteness test only works when all G-gens have the same weight.\n\
962  #Finiteness test swithched off.\n");
963     rwsptr->finitestop = FALSE;
964   }
965 
966 
967   rwsptr->Hhasinverses = TRUE;
968   for (i = rwsptr->separator + 1; i <= rwsptr->num_gens; i++)
969     if (!rwsptr->inv_of[i]) {
970       rwsptr->Hhasinverses = FALSE;
971       break;
972     }
973 }
974