1 #if 0
2 \documentclass{article}
3 \usepackage{axiom}
4 \begin{document}
5 \title{\$SPAD/etc asq.c}
6 \author{Timothy Daly and Waldek Hebisch}
7 \maketitle
8 \begin{abstract}
9 [[asq]] is a mini-browser for FriCAS databases. It understands
10 structure of databases and can retrieve information about
11 constructors. To do this it implements (limited) Lisp-like
12 S-expressions.
13 \end{abstract}
14 \eject
15 \tableofcontents
16 \eject
17 \section{S-expressions}
18
19 Data in FriCAS databases is stored as S-expressions. One could
20 try to handle them with purely string based methods. But such
21 approach would be inflexible and awkward. Namely, FriCAS perform
22 various transformations before writing S-expressions to files.
23 Such transformations are natural and easy on S-expressions,
24 but complicated (require parsing and multiple string substitutions)
25 on string level. So [[asq]]
26 needs S-expression support. FriCAS uses only very special forms
27 of S-expression: pairs, lists, strings, symbols and integers.
28 We implement lists, strings, symbols and small integers. Actually,
29 since in C it is easier to work with arrays than with genuine lists,
30 we implement adjustable vectors and treat them as lists (so our
31 terminology is closer to Perl than to Lisp). We cheat reading
32 pairs: we read them as a three element list, the middle element
33 being the symbol ``.'' (dot).
34
35
36 \section{Database format}
37
38 Below we try to explain essential properties of FriCAS databases,
39 for longer description (including many low level details) look
40 at [[daase.lisp]].
41 General format of FriCAS databases is as follows
42 \begin{verbatim}
43 stamp
44 indirect hunks
45 main data list
46 \end{verbatim}
47 [[stamp]] above is a Lisp pair, its first element is an integer
48 giving byte offset to [[main data list]]. Elements of main
49 data list are themselves lists, which for given database have
50 fixed number (and order) of fields. For [[interp.daase]] the
51 fields are:
52 \begin{verbatim}
53 constructor name (symbol)
54 operation list
55 constructor modemap
56 modemaps (of added operations)
57 object file (name of the file containing compiled code
58 of the constructor)
59 constructorcategory
60 niladic (boolean field telling if the constructor takes arguments)
61 abbreviation
62 cosig
63 constructorkind
64 defaultdomain
65 ancestors
66 \end{verbatim}
67 For [[browse.daase]] the fields are:
68 \begin{verbatim}
69 constructor name
70 sourcefile
71 constructorform
72 documentation
73 attributes
74 predicates
75 \end{verbatim}
76 Only simple fields are stored directly, most values are stored in indirect
77 way: given value is replace by an integer giving position (byte offset
78 with respect to the start of the file) of actual value. Additionally,
79 fields may be compressed. Compression uses extra file ([[compress.daase]])
80 which contains a single list of values. Values from other files may
81 be put in the [[compress.daase]] and replaced by minus their index
82 on the [[compress.daase]] list.
83
84 For example, expanded form of S-expression describing FriCAS [[Type]]
85 (one of the simplest categories) is:
86 \begin{verbatim}
87 (|Type| NIL (((|Type|) (|Category|)) (T |Type|)) NIL "TYPE"
88 (|Join| (CATEGORY |package| (ATTRIBUTE |nil|)))
89 T TYPE (NIL) |category| NIL NIL)
90 \end{verbatim}
91 After replacing composite values by file offsets we get something
92 like:
93 \begin{verbatim}
94 (|Type| 3013278 3013283 3013314 "TYPE" 3013319 T TYPE (NIL)
95 |category| NIL NIL)
96 \end{verbatim}
97 Compression turns this into the following (assuming that position
98 9 in [[compress.daase]] contains symbol [[|category|]] and position
99 1106 in [[compress.daase]] contains symbol [[|Type|]]):
100 \begin{verbatim}
101 (-1106 3013278 3013283 3013314 "TYPE" 3013319 T TYPE (NIL) -9 NIL NIL)
102 \end{verbatim}
103 Remark: When compression is active indirect values are also compressed,
104 so the list at offset 3013283:
105 \begin{verbatim}
106 (((|Type|) (|Category|)) (T |Type|))
107 \end{verbatim}
108 is stored as (assuming that position 1182 in [[compress.daase]] contains
109 symbol [[|Category|]]):
110 \begin{verbatim}
111 (((-1106) (-1182)) (T -1106))
112 \end{verbatim}
113 #endif
114
115 #include <stdio.h>
116 #include <stdlib.h>
117 #include <limits.h>
118 #include <stdarg.h>
119 #include <string.h>
120 #include <ctype.h>
121
122 void
fatal(const char * message,...)123 fatal(const char * message, ...)
124 {
125 va_list ap;
126 va_start(ap, message);
127 vfprintf(stderr, message, ap);
128 va_end(ap);
129 exit(1);
130 }
131
132 void *
my_malloc(size_t n)133 my_malloc(size_t n)
134 {
135 void * p = malloc(n);
136 if (!n) {
137 fatal("Out of memory\n");
138 }
139 return p;
140 }
141
142 /* Dynamic data structures, Perl/Lisp like. */
143
144 typedef struct item {
145 int tag;
146 } item;
147
148 #define TAG_int 1
149 #define TAG_string 2
150 #define TAG_symbol 3
151 #define TAG_list 4
152
153 int
get_tag(const item * it)154 get_tag(const item * it)
155 {
156 return *((int *)it);
157 }
158
159 void
assert_tag(const void * it,int tag)160 assert_tag(const void * it, int tag)
161 {
162 if (get_tag(it) != tag) {
163 fatal("Unexpected tag %d (expected %d)\n", get_tag(it), tag);
164 }
165 }
166
167 typedef struct int_item {
168 int tag;
169 long val;
170 } int_item;
171
172 /*
173 Counted strings with fill pointer. Not necessarily null terminated.
174 */
175
176 typedef struct counted_string {
177 int tag;
178 char * buff;
179 int size;
180 int pos;
181 } counted_string;
182
183 void
add_char(counted_string * s,char c)184 add_char(counted_string * s, char c)
185 {
186 if (s->pos >= s->size) {
187 if (s->size < INT_MAX/2) {
188 s->size *= 2;
189 s->buff = realloc(s->buff, s->size);
190 if (!s->buff) {
191 fatal("Out of memory\n");
192 }
193 } else {
194 fatal("Item too long\n");
195 }
196 }
197 s->buff[s->pos] = c;
198 s->pos++;
199 }
200
201 #define COUNTED_STRING_INITIAL_SIZE 80
202 counted_string *
empty_string(void)203 empty_string(void)
204 {
205 counted_string * s = my_malloc(sizeof(*s));
206 s->buff = my_malloc(COUNTED_STRING_INITIAL_SIZE);
207 s->tag = TAG_string;
208 s->buff[0] = 0;
209 s->size = COUNTED_STRING_INITIAL_SIZE;
210 s->pos = 0;
211 return s;
212 }
213
214 void
free_counted_string(counted_string * s)215 free_counted_string(counted_string * s)
216 {
217 free(s->buff);
218 free(s);
219 }
220
221
222 /*
223 Item lists.
224 */
225
226 typedef struct item_list {
227 int tag;
228 item * * buff;
229 int size;
230 int pos;
231 } item_list;
232
233 void
add_item(item_list * sl,item * s)234 add_item(item_list * sl, item * s)
235 {
236 if (sl->pos >= sl->size) {
237 if (sl->size < INT_MAX/(2*sizeof(item *))) {
238 sl->size *= 2;
239 sl->buff = realloc(sl->buff,
240 sizeof(item *)*sl->size);
241 if (!sl->buff) {
242 fatal("Out of memory\n");
243 }
244 } else {
245 fatal("List too long\n");
246 }
247 }
248 sl->buff[sl->pos] = s;
249 sl->pos++;
250 }
251
252 /* 12 allows reading interp.daase entries without reallocation */
253 #define ITEM_LIST_INITIAL_SIZE 12
254
255 item_list *
empty_list(void)256 empty_list(void)
257 {
258 item_list * sl = my_malloc(sizeof(*sl));
259 sl->buff = my_malloc(ITEM_LIST_INITIAL_SIZE*sizeof(item *));
260 sl->buff[0] = 0;
261 sl->tag = TAG_list;
262 sl->size = ITEM_LIST_INITIAL_SIZE;
263 sl->pos = 0;
264 return sl;
265 }
266
267 void
268 free_item_list(item_list * sl);
269
270 void
free_item(item * it)271 free_item(item * it)
272 {
273 switch (get_tag(it)) {
274 case TAG_int:
275 free(it);
276 break;
277 case TAG_string:
278 case TAG_symbol:
279 free_counted_string((counted_string *) it);
280 break;
281 case TAG_list:
282 free_item_list((item_list *) it);
283 break;
284 default:
285 fatal("Unknown tag %d in free_item\n", get_tag(it));
286 break;
287 }
288 }
289
290 void
free_item_list(item_list * sl)291 free_item_list(item_list * sl)
292 {
293 int i;
294 for (i = 0; i < sl->pos; i++) {
295 free_item(sl->buff[i]);
296 }
297 free(sl->buff);
298 free(sl);
299 }
300
301
302 char *
string_val(item * it)303 string_val(item * it)
304 {
305 int tag = get_tag(it);
306 if (tag != TAG_string && tag != TAG_symbol) {
307 fatal("Accessing string value of something not a string or symbol");
308 }
309 return ((counted_string *)it)->buff;
310 }
311
312 /*
313 Below we implement a simple pretty-printer. We want nicely
314 indented lists, but to conserve space we try to print
315 multiple items on a single line. More precisely, when printing
316 list we try first to print the whole list in a single line (starting
317 at given indentation level). If list does not fit (is too large) we
318 then switch to multiline mode, where we try to fit as many items as
319 possible on a single line. To support this we fist print to a line
320 sized buffer, and if the result fits we print the buffered content.
321 Items which can not be printed on single line are printed "directly".
322 In direct mode printing routine has to print spaces to indent output,
323 while when printing to the buffer printing routine prints just content,
324 checking that it fits into the buffer. To handle indentation in
325 we just subtract indentation offset form the buffer size, passing
326 lower limit to printing routines. Later upper routine prints
327 indentating spaces followed by buffer content.
328
329 Unfortunately, this approach means that there is significant duplication
330 in printing code, since various modes and cases are quite similar,
331 (but different enough to inhibit sharing).
332 */
333
334 #define LINE_LENGTH 76
335 int
print_item1(item * it,char * buff,int lim)336 print_item1(item * it, char * buff, int lim)
337 {
338 int tag = get_tag(it);
339 int len;
340 if (tag == TAG_int) {
341 long val = ((int_item *)it)->val;
342 if (buff) {
343 len = snprintf(buff, lim, "%ld", val);
344 if (len > 0 && len < lim) {
345 return len;
346 } else {
347 return -1;
348 }
349 }
350 printf("%ld", ((int_item *)it)->val);
351 return -1;
352 } else {
353 char * str = string_val(it);
354 char * s1 = "";
355 int l1 = 0;
356 if (tag == TAG_string) {
357 l1 = 1;
358 s1 = "\"";
359 }
360 if (buff) {
361 int l2 = strlen(str);
362 if (l2 + 2*l1 < lim) {
363 sprintf(buff, "%s%s%s", s1, str, s1);
364 return l2 + 2*l1;
365 } else {
366 return -1;
367 }
368 } else {
369 printf("%s%s%s", s1, str, s1);
370 return -1;
371 }
372 }
373 }
374
375 void
indent(int offset)376 indent(int offset)
377 {
378 int i;
379 for(i = 0; i < offset; i++) {
380 putchar(' ');
381 }
382 }
383
384 int
print_items1(item * it,int offset,char * buff,int lim)385 print_items1(item * it, int offset, char * buff, int lim)
386 {
387 int tag = get_tag(it);
388 int i;
389 if (buff) {
390 if (tag == TAG_list) {
391 item_list * il = (item_list *)it;
392 int pos = 1;
393 int i = 0;
394 if (pos >= lim - 1) {
395 return -1;
396 }
397 buff[0] = '(';
398 for (i = 0 ; i < il->pos; i++) {
399 int l1;
400 if (pos >= lim - 1) {
401 return -1;
402 }
403 l1 = print_items1(il->buff[i], offset+3,
404 buff+pos, lim - pos - 1);
405 if (l1 < 0) {
406 return -1;
407 }
408 pos += l1;
409 buff[pos] = ' ';
410 pos++;
411 }
412 if (pos >= lim) {
413 return -1;
414 }
415 buff[pos - 1] = ')';
416 buff[pos] = 0;
417 return pos;
418 } else {
419 return print_item1(it, buff, lim);
420 }
421 }
422 indent(offset);
423 if (tag == TAG_list) {
424 char buff1[LINE_LENGTH];
425 int pos = 0;
426 int lim1 = LINE_LENGTH - offset - 2;
427 item_list * il = (item_list *)it;
428 buff1[0] = 0;
429 for(i = 0; i < il->pos; i++) {
430 int l1 = print_items1(il->buff[i], offset+3,
431 buff1 + pos, lim1 - pos - 1);
432 if (l1 < 0) {
433 goto multiline;
434 }
435 pos += l1;
436 buff1[pos] = ' ';
437 pos++;
438 }
439 if (pos>0) {
440 pos--;
441 buff1[pos] = 0;
442 }
443 printf("(%s)", buff1);
444 return -1;
445 multiline:
446 printf("(\n");
447 pos = 0;
448 lim1 = LINE_LENGTH - offset - 3;
449 for(i = 0; i < il->pos; i++) {
450 int l1 = print_items1(il->buff[i], offset + 3,
451 buff1+pos, lim1 - pos - 1);
452 if (l1 < 0) {
453 if (pos > 0) {
454 indent(offset+3);
455 pos--;
456 buff1[pos] = 0;
457 printf("%s\n", buff1);
458 pos = 0;
459 l1 = print_items1(il->buff[i], offset + 3, buff1, lim1-1);
460 if (l1 < 0) {
461 print_items1(il->buff[i], offset + 3, 0, lim1);
462 putchar('\n');
463 } else {
464 pos = l1;
465 buff1[pos] = ' ';
466 pos++;
467 }
468 } else {
469 print_items1(il->buff[i], offset+3, 0, lim1);
470 putchar('\n');
471 }
472 } else {
473 pos += l1;
474 buff1[pos] = ' ';
475 pos++;
476 }
477 }
478 if (pos > 0) {
479 pos--;
480 buff1[pos] = 0;
481 indent(offset+3);
482 printf("%s\n", buff1);
483 }
484 indent(offset);
485 putchar(')');
486 return -1;
487 } else {
488 return print_item1(it, 0, lim);
489 }
490 }
491
492 void
print_item(item * it)493 print_item(item * it)
494 {
495 print_items1(it, 0, 0, LINE_LENGTH);
496 }
497
498 void
print_cars(item * it)499 print_cars(item * it)
500 {
501 const item_list * il = (item_list *)it;
502 int i;
503 int tag = get_tag(it);
504 if (tag == TAG_symbol) {
505 counted_string * sy = (counted_string *) it;
506 if (!strcmp(sy->buff, "NIL")) {
507 printf("NIL");
508 return;
509 }
510 }
511 assert_tag(il, TAG_list);
512 for (i = 0; i< il->pos; i++) {
513 item_list * el = (item_list *)(il->buff[i]);
514 assert_tag(el, TAG_list);
515 if (el->pos < 1) {
516 fatal("print_cars trying to print car of empty list\n");
517 }
518 print_items1(el->buff[0], 3, 0, LINE_LENGTH);
519 putchar('\n');
520 }
521 }
522
523 /*
524 Utilities to read S-expression from files. Support only things
525 which appear in FriCAS databases.
526 */
527
528 /* Precondition: File is positioned before item.
529 Postcondition: File is positioned just after the item.
530 */
531
532 counted_string *
read_string(FILE * file)533 read_string(FILE * file)
534 {
535 int c;
536 counted_string * s = empty_string();
537 while(isspace(c = fgetc(file)))
538 ;
539 if (c != '"') {
540 fatal("String must begin with '\"'\n");
541 }
542 while (1) {
543 c = fgetc(file);
544 if (c == EOF) {
545 fatal("Unexpected EOF\n");
546 }
547 if (c == '"') {
548 add_char(s, 0);
549 return s;
550 } else if (c == '\\') {
551 c = fgetc(file);
552 }
553 add_char(s, c);
554 }
555 }
556
557 counted_string *
read_symbol(FILE * file)558 read_symbol(FILE * file)
559 {
560 int c;
561 enum {normal, in_ticks} state = normal;
562 counted_string * s = empty_string();
563 s->tag = TAG_symbol;
564 while(isspace(c = fgetc(file)))
565 ;
566 ungetc(c, file);
567 while (1) {
568 c = fgetc(file);
569 if (c == EOF) {
570 fatal("Unexpected EOF\n");
571 }
572 if (state == normal) {
573 if (c == ')' || c == '(' || isspace(c)) {
574 ungetc(c, file);
575 add_char(s, 0);
576 return s;
577 } else if (c == '|') {
578 state = in_ticks;
579 } else {
580 add_char(s, c);
581 }
582 } else if (state == in_ticks) {
583 if (c == '|') {
584 state = normal;
585 } else if (c == '\\') {
586 c = fgetc(file);
587 add_char(s, c);
588 } else {
589 add_char(s, c);
590 }
591 }
592 }
593 }
594
595 item *
read_item(FILE * file)596 read_item(FILE * file)
597 {
598 int c;
599 while(isspace(c = fgetc(file)))
600 ;
601 if (c == ')') {
602 fatal("Unexpected ')'\n");
603 }
604 if (c == '(') {
605 item_list * res = empty_list();
606 while (1) {
607 while(isspace(c = fgetc(file)))
608 ;
609 if (c == ')') {
610 return (item *) res;
611 }
612 ungetc(c, file);
613 add_item(res, read_item(file));
614 }
615 }
616 if (c == '\'') {
617 item_list * res = empty_list();
618 counted_string * s = empty_string();
619 s->tag = TAG_symbol;
620 char * ss = "QUOTE";
621 while (*ss) {
622 add_char(s, *ss);
623 ss++;
624 }
625 add_item(res, (item *)s);
626 add_item(res, read_item(file));
627 return (item *) res;
628 }
629 if (c == '"') {
630 counted_string * res = (ungetc(c, file), read_string(file));
631 return (item *) res;
632 }
633 if (c == '-' || isdigit(c)) {
634 int_item * res = my_malloc(sizeof(*res));
635 char nbuff[25];
636 int i = 0;
637 nbuff[0] = c;
638 i++;
639 c = getc(file);
640 while(isdigit(c)) {
641 if (i + 1 >= sizeof(nbuff)) {
642 fatal("Integer value too large\n");
643 }
644 nbuff[i] = c;
645 i++;
646 c = getc(file);
647 }
648 nbuff[i] = 0;
649 ungetc(c, file);
650 res->val = atol(nbuff);
651 res->tag = TAG_int;
652 return (item *) res;
653 }
654 {
655 counted_string * res = (ungetc(c, file), read_symbol(file));
656 return (item *) res;
657 }
658 }
659
660 /* Main program */
661
662 char * FRICAS;
663 FILE *
open_file(const char * name)664 open_file(const char * name)
665 {
666 FILE * file;
667 long offset;
668 item_list * stamp;
669 char * file_path;
670 if (FRICAS != NULL) {
671 file_path = my_malloc(strlen(FRICAS)+strlen("%s/algebra/%s.daase")
672 +strlen(name));
673 sprintf(file_path, "%s/algebra/%s.daase", FRICAS, name);
674 } else {
675 file_path = my_malloc(strlen("%s.daase")+strlen(name));
676 sprintf(file_path, "%s.daase", name);
677 }
678 file = fopen(file_path, "rb");
679 if (!file) {
680 fatal("unable to find the file %s\n", file_path);
681 }
682 /* Read main offset */
683 stamp = (item_list *)read_item(file);
684 assert_tag(stamp, TAG_list);
685 if (stamp->pos != 3) {
686 fatal("Bad stamp, have %d entries (should have 3)\n", stamp->pos);
687 }
688 assert_tag(stamp->buff[0], TAG_int);
689 offset = ((int_item *)stamp->buff[0])->val;
690 if (offset <= 0) {
691 fatal("Bad main offset %ld\n", offset);
692 }
693 free_item((item *)stamp);
694 fseek(file, offset, SEEK_SET);
695 return file;
696 }
697
698 item *
decode(item * s,item_list * ct)699 decode(item * s, item_list * ct)
700 {
701 int tag = get_tag(s);
702 if (tag == TAG_int) {
703 long n = ((int_item *)s)->val;
704 if (n > 0 || 1 - n >= ct->pos) {
705 return s;
706 } else {
707 return ct->buff[1 - n];
708 }
709 }
710 if (tag == TAG_list) {
711 item_list * res = empty_list();
712 item_list * il = (item_list *)s;
713 int i;
714 for (i = 0; i < il->pos; i++) {
715 add_item(res, decode(il->buff[i], ct));
716 }
717 return (item *) res;
718 }
719 return s;
720 }
721
722 item_list *
scan_file(FILE * file,char * fname,char * key,item_list * ct,int alt_pos)723 scan_file(FILE * file, char * fname, char * key,
724 item_list * ct, int alt_pos)
725 {
726 int c;
727 while(isspace(c = fgetc(file)))
728 ;
729 if (c != '(') {
730 fatal("Main %s.daase list begins with %c\n", fname, c);
731 }
732 while (1) {
733 while(isspace(c = fgetc(file)))
734 ;
735 if (c == '(') {
736 item_list * iml;
737 item * it;
738 ungetc(c, file);
739 it = read_item(file);
740 assert_tag(it, TAG_list);
741 iml = (item_list *)it;
742 #if 0
743 /* Debugging printout */
744 {
745 int i;
746 for (i = 0; i < iml->pos; i++) {
747 /* printf("%s\n", decode (iml->buff[i], ct)); */
748 printf("%s\n", iml->buff[i]->buff);
749 }
750 putchar('\n');
751 }
752 #endif
753 if (!strcmp(key,
754 string_val(decode(iml->buff[0], ct)))) {
755 return iml;
756 } else if (alt_pos && !strcmp(key,
757 string_val(decode(iml->buff[alt_pos], ct)))) {
758 return iml;
759 }
760 free_item(it);
761 } else if (c != ')') {
762 fatal("Unexpected char %c in interp.daase\n", c);
763 } else {
764 return 0;
765 }
766 }
767 }
768
769 struct info_slot {char * name; char *abbr; int slot; void (*handler)(item *);};
770 #define BROWSE_SHIFT 100
771 struct info_slot ot[] = {
772 {"constructor", "con", 0, 0},
773 {"sourcefile", "so", 101, 0},
774 {"constructorkind", "ckind", 9, 0},
775 {"niladic", "nilad", 6, 0},
776 {"abbreviation", "abbr", 7, 0},
777 {"defaultdomain", "ddom", 10, 0},
778 {"ancestors", "anc", 11, 0},
779 {"operations", "op", 1, &print_cars},
780 {"cosig", "cos", 8, 0},
781 {"constructorform", "cform", 102, 0},
782 {"constructormodemap", "cmod", 2, 0},
783 {"modemaps", "mod", 3, 0},
784 {"constructorcategory", "ccat", 5, 0},
785 {"documentation", "doc", 103, 0},
786 {"predicates", "pre", 104, 0},
787 };
788
789 void
printinfo(item_list * iml,item_list * bml,const char * propname,item_list * ct,FILE * interp_file,FILE * browse_file)790 printinfo(item_list * iml, item_list * bml, const char * propname,
791 item_list * ct, FILE * interp_file, FILE * browse_file)
792 {
793 int i;
794 int all = !strcmp(propname, "all");
795 const int nopts = sizeof(ot)/sizeof(struct info_slot);
796 for (i=0; i < nopts; i++) {
797 int slot = ot[i].slot;
798 item * slot_data;
799 FILE * file;
800 if (!all && strcmp(propname, ot[i].name)) {
801 continue;
802 }
803 printf("%s:\n", ot[i].name);
804 if (slot >= BROWSE_SHIFT) {
805 slot_data = bml->buff[slot - BROWSE_SHIFT];
806 file = browse_file;
807 } else {
808 slot_data = iml->buff[slot];
809 file = interp_file;
810 }
811 slot_data = decode(slot_data, ct);
812 if (get_tag(slot_data) == TAG_int) {
813 fseek(file, ((int_item *)slot_data)->val, SEEK_SET);
814 slot_data = decode(read_item (file), ct);
815 }
816 if (ot[i].handler) {
817 (*(ot[i].handler))(slot_data);
818 } else {
819 print_item(slot_data);
820 }
821 putchar('\n');
822 }
823 }
824
825 void
usage(char * prog)826 usage(char * prog)
827 {
828 int i;
829 const int nopts = sizeof(ot)/sizeof(struct info_slot);
830 printf("Usage:\n"
831 " %s key\n", prog);
832 printf("or\n"
833 " %s -property key\n", prog);
834 printf("where key is the name (or abbreviation) and property\n"
835 "is one of the following:\n"
836 " (al) all\n");
837 for (i=0; i < nopts; i++) {
838 printf(" (%s) %s\n", ot[i].abbr, ot[i].name);
839 }
840 printf("You can give full property name or use abbreviated form\n"
841 "given in parenthesis above.\n");
842 printf("The first form prints all properties.\n");
843 exit(1);
844 }
845
846 char *
unabbreviate_property(char * propname)847 unabbreviate_property(char * propname)
848 {
849 int i;
850 int j = strlen(propname);
851 const int nopts = sizeof(ot)/sizeof(struct info_slot);
852 if (j < 2) {
853 j = 2;
854 }
855 if (!strncmp(propname, "all", j)) {
856 return "all";
857 }
858 for (i=0; i < nopts; i++) {
859 char * s = ot[i].name;
860 if (!strcmp(propname, ot[i].abbr)
861 || !strcmp(propname, s)) {
862 return s;
863 }
864 }
865 return 0;
866 }
867 int
main(int argc,char ** argv)868 main(int argc, char * * argv)
869 {
870 FILE * compress_file;
871 FILE * interp_file;
872 FILE * browse_file;
873 item_list * ct;
874 item_list * iml;
875 item_list * bml;
876 char * cname;
877 char * propname = "all";
878 long ct_n;
879 FRICAS=(char *)getenv("FRICAS");
880
881 if (FRICAS == 0)
882 fprintf(stderr, "FRICAS shell variable has no value. "
883 "using current directory\n");
884 if (argc < 2 || argc > 3) {
885 usage(argv[0]);
886 }
887 if (argc == 3 && argv[1][0] == '-') {
888 cname = argv[2];
889 propname = unabbreviate_property(argv[1]+1);
890 if (!propname) {
891 usage(argv[0]);
892 }
893 } else {
894 cname = argv[1];
895 }
896 compress_file = open_file("compress");
897 ct = (item_list *)read_item(compress_file);
898 assert_tag(ct, TAG_list);
899 if (ct->pos < 1) {
900 fatal("Bad compress_file, no count\n");
901 }
902 {
903 item * ct_n_item = ct->buff[0];
904 assert_tag(ct_n_item, TAG_int);
905 ct_n = ((int_item *)ct->buff[0])->val;
906 if (ct->pos - 1 != ct_n) {
907 fatal("Bad compress_file, count %d, (stored %ld)\n",
908 ct->pos - 1, ct_n);
909 }
910 }
911 interp_file = open_file("interp");
912 iml = scan_file(interp_file, "interp", cname, ct, 7);
913 if (!iml) {
914 printf("Not found!\n");
915 exit(1);
916 }
917 browse_file = open_file("browse");
918 bml = scan_file(browse_file, "browse",
919 string_val(decode(iml->buff[0], ct)), ct, 0);
920 if (!bml) {
921 printf("Not found!\n");
922 exit(1);
923 }
924 printinfo(iml, bml, propname, ct, interp_file, browse_file);
925 return 0;
926 }
927