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