Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtappbuilder / src / libAButil / abio.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23
24 /*
25  * $TOG: abio.c /main/6 1998/04/06 13:12:48 mgreess $
26  * 
27  * @(#)abio.c   1.87 04 May 1995
28  * 
29  * RESTRICTED CONFIDENTIAL INFORMATION:
30  * 
31  * The information in this document is subject to special restrictions in a
32  * confidential disclosure agreement between HP, IBM, Sun, USL, SCO and
33  * Univel.  Do not distribute this document outside HP, IBM, Sun, USL, SCO,
34  * or Univel without Sun's specific written approval.  This document and all
35  * copies and derivative works thereof must be returned or destroyed at Sun's
36  * request.
37  * 
38  * Copyright 1993 Sun Microsystems, Inc.  All rights reserved.
39  * 
40  */
41
42
43 /*
44  * abio.c
45  * 
46  * Guide Intermediate Language (GIL) file input / output interface.
47  * 
48  * This file implements the functions required to read and write GIL files.  The
49  * GIL syntax is based on Lisp so that they may be read directly into a Lisp
50  * or Scheme interpreter.
51  * 
52  * Many of these functions return a string to indicate the begin or end of a
53  * special sequence in the GIL syntax, for example, a list. Internally it is
54  * common to only use the first character returned. I did it this way so the
55  * functions are consistent and to allow for flexibility in the future.
56  */
57
58 #ifndef _POSIX_SOURCE
59 #define _POSIX_SOURCE 1
60 #endif
61
62 #include <sys/types.h>
63 #include <unistd.h>
64 #include <stdarg.h>
65 #include <stdlib.h>
66 #include <stdio.h>
67 #include <ctype.h>
68 #include <sys/param.h>
69 #include <sys/stat.h>
70 #include <errno.h>
71 #include <string.h>
72 #include <malloc.h>
73 #include <assert.h>
74 #include <ab_private/util.h>
75 #include <ab_private/abio.h>
76 #include <ab/util_types.h>
77 #include <ab_private/istr.h>
78 #include "utilP.h"
79
80 /*
81  * Not defined in POSIX, but needed...
82  */
83 #ifdef __cplusplus
84 extern "C" {
85 #endif
86 extern int ftruncate(int fd, off_t size);       /* truncates file */
87 #ifdef __cplusplus
88 } // extern "C" {
89 #endif
90
91 #define util_dassert(_debugLevel, _boolExpr) \
92             {if (debug_level() >= (_debugLevel)) {assert((_boolExpr));}}
93
94
95 /*
96  * Delimiter for a full name.
97  */
98 #define DELIMITER               ':'
99 #define INDENT_ARRAY_SIZE       (132)
100 #define INDENT_MAX              ((INDENT_ARRAY_SIZE-1)/Indent_chars_per_level)
101
102 /*
103  * Internal state variables.
104  */
105 static char     Indent[INDENT_ARRAY_SIZE]= "";  /* indent string */
106 static int      Indent_chars_per_level = 4;
107 static char     Indent_char = ' ';
108 static int      Indent_level = 0;
109 static int      Newline = TRUE; /* TRUE if on new output line */
110
111 #ifndef MAXBSIZE
112 #  define MAXBSIZE 8192
113 #endif
114 static char     Buf[MAXBSIZE];  /* work buffer */
115
116 /*
117  * System error message definitions.
118  */
119 #if !defined(linux)
120 extern int          sys_nerr;
121 extern STRING       sys_errlist[];
122 #endif
123
124 /*
125  * Internal function declarations.
126  */
127 static int          debug_unbuffer_file(FILE *);        /*use on output files*/
128 static int          gil_version(FILE *);
129 static void         skip_comments(FILE *);
130 static STRING       get_token(FILE *);
131 static int      abioP_build_indent_string(
132                         FILE *out_file,
133                         int     indent_char,
134                         int     indent_chars_per_level,
135                         int     indent_level
136                 );
137
138 static UtilOutputHandler        output_handler = NULL;
139 static UtilOutputHandler        err_output_handler = NULL;
140 static STRING                   help_text = NULL;
141 static STRING                   help_volume = NULL;
142 static STRING                   help_locID = NULL;
143 static STRING   gilVersionPrefix = ";GIL-";
144 static int      gilMajorVersion = 4;
145 static int      gilMinorVersion = 0;
146
147 /*
148  * Return the string representation of a boolean.
149  */
150 STRING
151 abio_bil_boolean_string(BOOL value)
152 {
153     return value? abio_bil_true_string() : abio_bil_false_string();
154 }
155
156 /*
157  * Close the input file.
158  */
159 int
160 abio_close_input_impl(FILE ** pFile)
161 {
162 #define file (*pFile)
163     if ((file != NULL) && (file != stdin))
164     {
165         util_fclose(file);
166     }
167     return 0;
168 #undef file
169 }
170
171 /*
172  * Close the output file.
173  */
174 int
175 abio_close_output_impl(FILE ** pOutFile)
176 {
177 #define outFile (*pOutFile)
178     if ((outFile != NULL) && (outFile != stdout))
179     {
180         fflush(outFile);
181         ftruncate(fileno(outFile), (size_t)ftell(outFile));
182         util_fclose(outFile);
183     }
184     return 0;
185 #undef outFile
186 }
187
188 /*
189  * Return the comment string.
190  */
191 STRING
192 abio_comment_string(void)
193 {
194     return "//";
195 }
196
197 /*
198  * Return the boolean false string.
199  */
200 STRING
201 abio_bil_false_string(void)
202 {
203     return ":false";
204 }
205
206 /*
207  * Return the file begin string.
208  */
209 STRING
210 abio_file_begin_string(void)
211 {
212     return ":bil-version";
213 }
214
215 /*
216  * Return the file end string.
217  */
218 STRING
219 abio_file_end_string(void)
220 {
221     return ")";
222 }
223
224 /*
225  * Get a boolean from the input file.  Returns TRUE if successful.
226  */
227 int
228 abio_bil_get_boolean(FILE * inFile, BOOL *valueOut)
229 {
230     STRING              p = get_token(inFile);
231
232     if (strcmp(p, abio_bil_true_string()) == 0)
233     {
234         (*valueOut) = TRUE;
235         return TRUE;
236     }
237     if (strcmp(p, abio_bil_false_string()) == 0)
238     {
239         (*valueOut) = FALSE;
240         return TRUE;
241     }
242     return FALSE;
243 }
244
245 /*
246  * Return whether we are at end-of-file.
247  */
248 int
249 abio_get_eof(FILE * inFile)
250 {
251     int                 ch;
252
253     while (isspace(ch = fgetc(inFile)))
254         ;
255     if (ch == EOF)
256         return TRUE;
257     ungetc(ch, inFile);
258     return FALSE;
259 }
260
261 /*
262  * Get the start of the file.  Returns TRUE if successful.
263  */
264 int
265 abio_get_file_begin(FILE * inFile)
266 {
267     int                 ch;
268
269     while (isspace(ch = fgetc(inFile)))
270         ;
271     if (ch == *abio_file_begin_string())
272         return TRUE;
273     ungetc(ch, inFile);
274     return FALSE;
275 }
276
277 /*
278  * Get the end of the file.  Returns TRUE if successful.
279  */
280 int
281 abio_get_file_end(FILE * inFile)
282 {
283     int                 ch;
284
285     while (isspace(ch = fgetc(inFile)))
286         ;
287     if (ch == *abio_file_end_string())
288         return TRUE;
289     ungetc(ch, inFile);
290     return FALSE;
291 }
292
293 /*
294  * Get a handler from the input file.  Returns TRUE if successful. Sets the
295  * string pointer to a buffer allocated with malloc if the string is not
296  * empty, otherwise NULL.  Handler could be a quoted string for special
297  * language cases (PostScript, Lisp, etc...) or just a simple name for C.
298  */
299 int
300 abio_get_handler(FILE * inFile, ISTRING * stringOutPtr)
301 {
302 #define stringOut (*stringOutPtr)
303 #define INC             32
304     int                 ch;
305     int                 len;    /* string length */
306     int                 c = 0;  /* string count */
307     STRING              string = NULL;
308
309     while (isspace(ch = fgetc(inFile)))
310         ;
311     ungetc(ch, inFile);
312
313     if (ch != *abio_string_begin_string())
314     {
315         return abio_gil_get_name(inFile, &stringOut);
316     }
317     else
318     {
319         string = (STRING) malloc(len = INC);
320         string[c++] = fgetc(inFile);
321         while (((ch = fgetc(inFile)) != EOF) &&
322                (ch != *abio_string_end_string()))
323         {
324             if (c + 2 == len)
325                 string = (STRING) realloc(string, len += INC);
326             if (ch == '\\')
327                 ch = fgetc(inFile);
328             string[c++] = (char) ch;
329         }
330         string[c] = *abio_string_end_string();
331         string[c + 1] = '\0';
332         stringOut = istr_create(string);
333         util_free(string);
334         return TRUE;
335     }
336
337     return FALSE;
338 #undef INC
339 #undef stringOut
340 }
341
342
343 /*
344  * Get a boolean from the input file.  Returns TRUE if successful.
345  */
346 int
347 abio_gil_get_boolean(FILE * inFile, BOOL *valueOut)
348 {
349     STRING              p = get_token(inFile);
350
351     if (strcmp(p, abio_gil_true_string()) == 0)
352     {
353         (*valueOut) = TRUE;
354         return TRUE;
355     }
356     if (strcmp(p, abio_gil_false_string()) == 0)
357     {
358         (*valueOut) = FALSE;
359         return TRUE;
360     }
361     return FALSE;
362 }
363
364 /*
365  * Return the boolean false string.
366  */
367 STRING
368 abio_gil_false_string(void)
369 {
370     return "nil";
371 }
372
373
374 /*
375  * Return the boolean true string.
376  */
377 STRING
378 abio_gil_true_string(void)
379 {
380     return "t";
381 }
382
383
384 /*
385  * Get an integer from the input file.  Returns TRUE if successful.
386  */
387 int
388 abio_get_integer(FILE * inFile, int *i)
389 {
390     STRING              p = get_token(inFile);
391     STRING              q;
392     long                l = strtol(p, &q, 10);
393
394     if (p != q)
395     {
396         *i = (int) l;
397         return TRUE;
398     }
399     return FALSE;
400 }
401
402 /*
403  * Get a keyword from the input file.  Returns TRUE if successful. The
404  * keyword is returned in a static buffer.
405  */
406 int
407 abio_get_keyword(FILE * inFile, ISTRING *s)
408 {
409     *s = istr_create(get_token(inFile));
410     return TRUE;
411 }
412
413 /*
414  * Get a list as a string from the input file.  Returns TRUE if successful.
415  * Sets the string pointer to a buffer allocated with malloc if the string is
416  * not empty, otherwise NULL.
417  */
418 int
419 abio_get_list(FILE * inFile, ISTRING *stringOutPtr)
420 {
421 #define stringOut (*stringOutPtr)
422 #define INC             32
423     int                 ch;
424     int                 len;    /* string length */
425     int                 c;      /* string count */
426     STRING              string = NULL;
427
428     stringOut = NULL;
429     while (isspace(ch = fgetc(inFile)))
430         ;
431     if (ch != *abio_list_begin_string())
432     {
433         ungetc(ch, inFile);
434         return FALSE;
435     }
436
437     if (abio_get_list_end(inFile))
438     {
439         return TRUE;
440     }
441
442     string = (STRING) malloc(len = INC);
443     for (c = 0; (ch = fgetc(inFile)) != *abio_list_end_string(); c++)
444     {
445         if (c + 1 == len)
446             string = (STRING) realloc(string, len += INC);
447         if (ch == '\\')
448             ch = fgetc(inFile);
449         string[c] = (char) ch;
450     }
451     string[c] = '\0';
452     stringOut = istr_create(string);
453     return TRUE;
454 #undef INC
455 #undef stringOut
456 }
457
458 /*
459  * Get the start of a list from the input file.  Returns TRUE if successful.
460  */
461 int
462 abio_get_list_begin(FILE * inFile)
463 {
464     int                 ch;
465
466     while (isspace(ch = fgetc(inFile)))
467         ;
468     if (ch == *abio_list_begin_string())
469         return TRUE;
470     ungetc(ch, inFile);
471     return FALSE;
472 }
473
474 /*
475  * Get the end of a list from the input file.  Returns TRUE if successful.
476  */
477 int
478 abio_get_list_end(FILE * inFile)
479 {
480     int                 ch;
481
482     while (isspace(ch = fgetc(inFile)))
483         ;
484     if (ch == *abio_list_end_string())
485         return TRUE;
486     ungetc(ch, inFile);
487     return FALSE;
488 }
489
490 /*
491  * Get a symbol from the input file.  Returns TRUE if successful. Sets the
492  * string pointer to a buffer allocated with malloc if the string is not
493  * empty, otherwise NULL.
494  */
495 int
496 abio_gil_get_name(FILE * inFile, ISTRING *stringOutPtr)
497 {
498 #define stringOut (*stringOutPtr)
499     STRING              p = get_token(inFile);
500     stringOut = NULL;
501
502     if (p == NULL)
503     {
504         return FALSE;
505     }
506     if (strcmp(p, abio_gil_false_string()) == 0)
507     {
508         stringOut = NULL;
509     }
510     else
511     {
512         stringOut = istr_create(p);
513     }
514     return TRUE;
515 #undef stringOut
516 }
517
518
519 #ifdef BOGUS
520 /*
521  * Get the full name of an object in a bil file. The syntax of a full name
522  * is: (name)          when the object is top-level; (parent name)   when the
523  * object is not top-level; (parent name "item")    when the object is a
524  * setting or menu item.
525  */
526 int
527 abio_bil_get_full_name(
528                         FILE * inFile, 
529                         ISTRING *parentOutPtr, 
530                         ISTRING *nameOutPtr)
531 {
532 #define parentOut (*parentOutPtr)
533 #define nameOut (*nameOutPtr)
534     int                 iReturn = 0;
535
536     iReturn = ERR_NOT_IMPLEMENTED;
537 #ifdef BOGUS                    /* this should NOT call anything in libABil */
538     STRING              tmp1,
539                         tmp2;
540     STRING              ident = NULL;
541     char                ch[4];
542     ISTRING             string;
543     BOOL                tmp1_done = FALSE;
544     int                 type;
545     int                 i,
546                         y;
547
548     *parent = NULL;
549     *name = NULL;
550
551     /** REMIND: this shouldn't oughtta be here
552     if ((type = bil_load_get_value_type()) == AB_BIL_VALUE_IDENT)
553         ident = bil_load_get_value();
554     else
555         util_error(abo_loadmsg(ERR_WANT_STRING));
556     **/
557
558     string = istr_create(ident);
559     tmp1 = (STRING) malloc(strlen(istr_string(string)) + 1);
560     for (i = 0, y = 0; i < ((int) strlen(istr_string(string))); i++, y++)
561     {
562         ch[0] = ident[i];
563         ch[1] = '\0';
564         if ((strcmp(".", ch)) == 0)
565         {
566             tmp1[i] = '\0';
567             y = 0;
568             tmp1_done = TRUE;
569             tmp2 = (STRING) malloc(strlen(istr_string(string)) + 1);
570         }
571         else if (tmp1_done == FALSE)
572         {
573             tmp1[i] = ident[i];
574         }
575         else if (tmp1_done == TRUE)
576         {
577             tmp2[y - 1] = ident[i];
578         }
579
580     }
581     if (tmp1_done == FALSE)
582     {                           /* only one */
583         *name = (STRING) malloc(strlen(tmp1) + 1);
584         strcpy(*name, tmp1);
585         istr_destroy(string);
586         free(tmp1);
587         return TRUE;
588     }
589     else
590     {                           /* tmp1_done == TRUE */
591         tmp2[y] = '\0';
592         *parent = (STRING) malloc(strlen(tmp1) + 1);
593         *name = (STRING) malloc(strlen(tmp2) + 1);
594         strcpy(*parent, tmp1);
595         strcpy(*name, tmp2);
596         istr_destroy(string);
597         free(tmp1);
598         free(tmp2);
599         return TRUE;
600     }
601 #endif                          /* BOGUS */
602
603     return iReturn;
604 #undef parentOut
605 #undef nameOut
606 }
607 #endif /* BOGUS */
608
609
610 /*
611  * Get the full name of an object in a gil file. The syntax of a full name
612  * is one of: 
613  *      (obj-name)                      when the object is top-level
614  *      (toplevel-name obj-name)        when the object is not top-level
615  *      (obj-name "label")              when the object is an item
616  *      (file-name toplevel-name obj-name)      when obj is cross-interface
617  *      (file-name toplevel-name "label")       when obj is cross-interface
618  *
619  * Returns >= 0 if successful, <0 on failure
620  */
621 int
622 abio_gil_get_full_name(
623                         FILE    *inFile, 
624                         ISTRING *interfaceStringOut,
625                         ISTRING *parentStringOut,
626                         ISTRING *objStringOut,
627                         ISTRING *itemStringOut
628 )
629 {
630     int                 return_value = 0;
631     ISTRING             tmp1 = NULL;
632     ISTRING             tmp2 = NULL;
633     BOOL                tmp2IsString = FALSE;
634     ISTRING             tmp3 = NULL;
635     BOOL                tmp3IsString = FALSE;
636
637     *interfaceStringOut = NULL;
638     *parentStringOut = NULL;
639     *objStringOut = NULL;
640     *itemStringOut = NULL;
641
642     if (!abio_get_list_begin(inFile))
643     {
644         return_value = -1;
645         goto epilogue;
646     }
647
648     /*
649      * Get first value - always name
650      */
651     if (!abio_gil_get_name(inFile, &tmp1))
652     {
653         return_value = -1;
654         goto epilogue;
655     }
656     if (abio_get_list_end(inFile))
657     {
658         goto epilogue;
659     }
660
661     /*
662      * Get second value - string or name
663      */
664     if (abio_gil_get_name(inFile, &tmp2))
665     {
666         tmp2IsString = FALSE;
667     }
668     else if (abio_get_string(inFile, &tmp2))
669     {
670         tmp2IsString = TRUE;
671         /* we've seen: (name "string" */
672         if (!abio_get_list_end(inFile))
673         {
674             return_value = -1;
675             goto epilogue;
676         }
677         goto epilogue;
678     }
679     else
680     {
681         return_value = -1;
682         goto epilogue;
683     }
684     if (abio_get_list_end(inFile))
685     {
686         goto epilogue;
687     }
688
689     /*
690      * Get third value - string or name
691      */
692     if (abio_gil_get_name(inFile, &tmp3))
693     {
694         tmp3IsString = FALSE;
695     }
696     else if (abio_get_string(inFile, &tmp3))
697     {
698         tmp3IsString = TRUE;
699     }
700     else
701     {
702         return_value = -1;
703         goto epilogue;
704     }
705     if (!abio_get_list_end(inFile))
706     {
707         /* 3 values seen - must be at end of list */
708         return_value = -1;
709         goto epilogue;
710     }
711
712 epilogue:
713     if (return_value >= 0)
714     {
715         if (tmp3 != NULL)
716         {
717             /* 3 values */
718             if (tmp3IsString)
719             {
720                 (*parentStringOut) = istr_dup(tmp1);
721                 (*objStringOut) = istr_dup(tmp2);
722                 (*itemStringOut) = istr_dup(tmp3);
723             }
724             else
725             {
726                 (*interfaceStringOut) = istr_dup(tmp1);
727                 (*parentStringOut) = istr_dup(tmp2);
728                 (*objStringOut) = istr_dup(tmp3);
729             }
730         }
731         else if (tmp2 != NULL)
732         {
733             /* 2 strings */
734             if (tmp2IsString)
735             {
736                 (*objStringOut) = istr_dup(tmp1);
737                 (*itemStringOut) = istr_dup(tmp2);
738             }
739             else
740             {
741                 (*parentStringOut) = istr_dup(tmp1);
742                 (*objStringOut) = istr_dup(tmp2);
743             }
744         }
745         else if (tmp1 != NULL)
746         {
747             /* 1 string */
748             (*objStringOut) = istr_dup(tmp1);
749         }
750     }
751     istr_destroy(tmp1);
752     istr_destroy(tmp2);
753     istr_destroy(tmp3);
754
755 #ifdef DEBUG
756     /*util_dprintf(3, "abio_gil_get_full_name() -> %d(%s %s %s %s)\n",
757         return_value, 
758         istr_string_safe(*interfaceStringOut),
759         istr_string_safe(*parentStringOut),
760         istr_string_safe(*objStringOut),
761         istr_string_safe(*itemStringOut));*/
762 #endif
763     return return_value;
764 }
765
766
767 #ifdef BOGUS /* obsoleted by libABil */
768 /*
769  * Get the full name of an object in a project file. The syntax of a full
770  * name is: interface:name              when the object is top-level;
771  * interface:parent:name        when the object is not top-level;
772  * interface:parent:name:"item" when the object is a setting or menu item.
773  */
774 int
775 abio_get_proj_full_name(FILE * inFile, STRING * interface, STRING * parent,
776                         STRING * name, STRING * item)
777 {
778     STRING              tmp1,
779                         tmp2;
780
781     *interface = NULL;
782     *parent = NULL;
783     *name = NULL;
784     *item = NULL;
785
786     if (!abio_get_list_begin(inFile))
787         return FALSE;
788
789     if (!abio_gil_get_name(inFile, &tmp1))
790         return FALSE;
791     *interface = (STRING) malloc(strlen(tmp1) + 1);
792     strcpy(*interface, tmp1);
793     free(tmp1);
794
795     if (!abio_gil_get_name(inFile, &tmp1))
796         return FALSE;
797
798     if (abio_get_list_end(inFile))
799     {
800         *name = (STRING) malloc(strlen(tmp1) + 1);
801         strcpy(*name, tmp1);
802         return TRUE;
803     }
804
805     if (abio_get_string(inFile, &tmp2))
806     {
807         *name = (STRING) malloc(strlen(tmp1) + 1);
808         *item = (STRING) malloc(strlen(tmp2) + 1);
809         strcpy(*name, tmp1);
810         strcpy(*item, tmp2);
811         if (!abio_get_list_end(inFile))
812             return FALSE;
813         return TRUE;
814     }
815
816     *parent = (STRING) malloc(strlen(tmp1) + 1);
817     strcpy(*parent, tmp1);
818     free(tmp1);
819     if (abio_gil_get_name(inFile, &tmp1))
820     {
821         *name = (STRING) malloc(strlen(tmp1) + 1);
822         if (!abio_get_list_end(inFile))
823             if (abio_get_string(inFile, &tmp2))
824             {
825                 *item = (STRING) malloc(strlen(tmp2) + 1);
826                 strcpy(*name, tmp1);
827                 strcpy(*item, tmp2);
828                 if (abio_get_list_end(inFile))
829                     return TRUE;
830                 return FALSE;
831             }
832         strcpy(*name, tmp1);
833         return TRUE;
834     }
835     return FALSE;
836
837 }
838 #endif /* BOGUS */
839
840
841 /*
842  * Get the start of an object from the input file.  Returns TRUE if
843  * successful.
844  */
845 int
846 abio_gil_get_object_begin(FILE * inFile)
847 {
848     int                 ch = 0;
849     BOOL                objectFound = FALSE;
850
851     util_dassert(1, (strcmp(abio_gil_object_begin_string(), "(") == 0));
852                                                         /* ) vi hack */
853
854     while ((!objectFound) && ((ch = fgetc(inFile)) != EOF))
855     {
856         switch (ch)
857         {
858             case ';':           /* GIL comment */
859                 while (   ((ch = fgetc(inFile)) != EOF)
860                        && (ch != '\n') )
861                 {
862                 }
863             break;
864
865             case '(':           /* object begin string */
866                 objectFound = TRUE;
867             break;
868         }
869     }
870
871     if (!objectFound)
872     {
873         if (ch != EOF)
874         {
875             ungetc(ch, inFile);
876         }
877         return FALSE;
878     }
879     return TRUE;
880 }
881
882 /*
883  * Get the end of an object from the input file.  Returns TRUE if successful.
884  */
885 int
886 abio_gil_get_object_end(FILE * inFile)
887 {
888     int                 ch;
889
890     while (isspace(ch = fgetc(inFile)))
891         ;
892     if (ch == *abio_gil_object_end_string())
893         return TRUE;
894     ungetc(ch, inFile);
895     return FALSE;
896 }
897
898
899 /*
900  * Get a string from the input file.  Returns TRUE if successful. 
901  *
902  * The ISTRING returned must be destroyed by the caller.
903  */
904 int
905 abio_get_string(FILE * inFile, ISTRING *istringOutPtr)
906 {
907 #define istringOut (*istringOutPtr)
908 #define INC             256                     /* incremental allocation */
909     BOOL                return_value = TRUE;
910     int                 ch;
911     int                 len;    /* string length */
912     int                 c = 0;  /* string count */
913     STRING              string = NULL;          /* string */
914
915     istringOut = NULL;          /* necessary (see epilogue) */
916
917     while (isspace(ch = fgetc(inFile)))
918         ;
919     if (ch != *abio_string_begin_string())
920     {
921         ungetc(ch, inFile);
922         return_value = FALSE;
923         goto epilogue;
924     }
925
926     if (abio_get_string_end(inFile))
927     {
928         istringOut = istr_const("");
929         goto epilogue;
930     }
931
932     string = (STRING)malloc(len = INC);
933     while (((ch = fgetc(inFile)) != EOF) && (ch != *abio_string_end_string()))
934     {
935         if (c + 1 == len)
936             string = (STRING)realloc(string, len += INC);
937         if (ch == '\\')
938         {
939             ch = fgetc(inFile);
940             if (ch == 'n')      /* read in newline, '\n' */
941                 string[c++] = (char) '\n';
942             else
943                 string[c++] = (char) ch;
944         }
945         else
946         {
947             string[c++] = (char) ch;
948         }
949     }
950     string[c] = '\0';
951
952 epilogue:
953     if (istringOut == NULL)
954     {
955         istringOut = istr_create_alloced(string);
956     }
957     return return_value;
958 #undef INC
959 #undef istringOutPtr
960 }
961
962
963 /*
964  * Get the start of a string from the input file.  Returns TRUE if
965  * successful.
966  */
967 int
968 abio_get_string_begin(FILE * inFile)
969 {
970     int                 ch;
971
972     while (isspace(ch = fgetc(inFile)))
973         ;
974     if (ch == *abio_string_begin_string())
975         return TRUE;
976     ungetc(ch, inFile);
977     return FALSE;
978 }
979
980 /*
981  * Get the end of a string from the input file.  Returns TRUE if successful.
982  */
983 int
984 abio_get_string_end(FILE * inFile)
985 {
986     int                 ch;
987
988     if ((ch = fgetc(inFile)) == *abio_string_end_string())
989         return TRUE;
990     ungetc(ch, inFile);
991     return FALSE;
992 }
993
994 /*
995  * Write an integer to the output file.
996  */
997 STRING
998 abio_integer_string(int i)
999 {
1000     sprintf(Buf, "%d", i);
1001     return Buf;
1002 }
1003
1004 /*
1005  * Return the string representation of a keyword.
1006  */
1007 STRING
1008 abio_keyword_string(STRING s)
1009 {
1010     return s;
1011 }
1012
1013 /*
1014  * Return the list begin string.
1015  */
1016 STRING
1017 abio_list_begin_string(void)
1018 {
1019     return "(";
1020 }
1021
1022 /*
1023  * Return the list end string.
1024  */
1025 STRING
1026 abio_list_end_string(void)
1027 {
1028     return ")";
1029 }
1030
1031 /*
1032  * Return the string representation of a name.
1033  */
1034 STRING
1035 abio_name_string(STRING s)
1036 {
1037     return s ? s : abio_bil_false_string();
1038 }
1039
1040 /*
1041  * Return the object begin string.
1042  */
1043 STRING
1044 abio_gil_object_begin_string(void)
1045 {
1046     return "(";
1047 }
1048
1049 /*
1050  * Return the object end string.
1051  */
1052 STRING
1053 abio_gil_object_end_string(void)
1054 {
1055     return ")";
1056 }
1057
1058
1059 /*
1060  * Open an input file.  Returns NULL if successful, otherwise an error
1061  * message.
1062  * 
1063  * pLinesRead is set to the number of lines read from the file.
1064  */
1065 STRING
1066 abio_open_bil_input(STRING name, int *pLinesRead, FILE ** pInFile)
1067 {
1068 #define inFile (*pInFile)
1069     STRING      errmsg = NULL;
1070     float       v = 0.0;
1071
1072     inFile = NULL;
1073     /*
1074      * If the input file exists, can be read, and is
1075      * the correct version, then open it.
1076      */
1077     if ((inFile = util_fopen_locked(name, "r")) != NULL)
1078     {
1079         v = abio_get_bil_version(inFile, pLinesRead);
1080         errmsg = abio_check_bil_version(v, name);
1081     }
1082     else
1083     {
1084         errmsg = sys_errlist[errno];
1085         sprintf(Buf, "%s: %s", name, errmsg);
1086         errmsg = Buf;
1087     }
1088
1089     return errmsg;
1090
1091 #undef MAXVER
1092 #undef inFile
1093 }
1094
1095 STRING
1096 abio_check_bil_version(
1097     float ver,
1098     STRING name
1099 )
1100 {
1101 #define MAXVER 10
1102     STRING      errmsg = NULL;
1103     STRING      help_msg = NULL;
1104     STRING      help_buf = NULL;
1105     STRING      fileName = NULL;
1106
1107     fileName = name? name : catgets(UTIL_MESSAGE_CATD, UTIL_MESSAGE_SET, 35, "NoName");
1108     fileName = strdup(fileName);
1109
1110     if (ver > Util_major_version)
1111     {
1112         errmsg = strdup(catgets(UTIL_MESSAGE_CATD, UTIL_MESSAGE_SET, 32,
1113                 "Incompatible BIL version"));
1114
1115         help_msg = catgets(UTIL_MESSAGE_CATD,
1116                 UTIL_MESSAGE_SET, 33,
1117                 "The version of the BIL file %s (%3.2f)\nis not supported in this version of App Builder.");
1118
1119         help_buf = (STRING)util_malloc(strlen(help_msg) + strlen(fileName) + MAXVER +1);
1120         sprintf(help_buf, help_msg, fileName, ver);
1121         util_set_help_data(help_buf, NULL, NULL);
1122     }
1123     else if (ver == 0.0)
1124     {
1125         errmsg = strdup(catgets(UTIL_MESSAGE_CATD, UTIL_MESSAGE_SET, 30,
1126                         "Unrecognized file format"));
1127
1128         help_msg = catgets(UTIL_MESSAGE_CATD,
1129                 UTIL_MESSAGE_SET, 31,
1130                 "The file %s does not appear to be in BIL\nformat.  Either a BIL header (:bil-version) was\nnot found or the header is corrupt.");
1131
1132         help_buf = (STRING)util_malloc(strlen(help_msg) + strlen(fileName) + 1);
1133         sprintf(help_buf, help_msg, fileName);
1134         util_set_help_data(help_buf, NULL, NULL);
1135     }
1136     
1137     if (errmsg)
1138     {
1139         sprintf(Buf, "%s: %s", fileName, errmsg);
1140         free(errmsg);
1141         errmsg = Buf;
1142     }
1143
1144     if (help_buf)
1145         util_free(help_buf);
1146
1147     if (fileName)
1148         free(fileName);
1149
1150     return errmsg;
1151 }
1152
1153 /*
1154  * Open an output file.  Returns NULL if successful, otherwise an error
1155  * message.
1156  */
1157 int
1158 abio_open_bil_output(
1159     STRING      outfile,
1160     STRING      old_file, 
1161     FILE        **pOutFile
1162 )
1163 {
1164 #define outFile (*pOutFile)
1165     int                 version_written = FALSE;
1166     char                backup[MAXPATHLEN];
1167     FILE               *bakp = NULL;
1168     STRING              file_begin = abio_file_begin_string();
1169     int                 fblen = strlen(file_begin);
1170     int                 iRet = OK;
1171     BOOL                read_OK = FALSE,
1172                         write_OK = FALSE;
1173
1174     *backup = 0;
1175
1176     /* Look for the specified output file.
1177      */
1178     if (!util_file_exists(outfile))
1179     {
1180         /*
1181          * Output file does not exist.  Open a new one.
1182          */
1183         if ((outFile = util_fopen_locked(outfile, "w")) != NULL)
1184         {
1185             debug_unbuffer_file(outFile);
1186             abio_set_indent_char(outFile, '\t');
1187             abio_set_indent_chars_per_level(outFile, 1);
1188         }
1189         else 
1190         {
1191             iRet = ERR_WRITE;
1192             goto ERROR_EXIT;
1193         }
1194     }
1195     else
1196     {
1197         /* The output file exists.  Make sure it is
1198          * writable before backing it up.
1199          */
1200         abio_access_file(outfile, &read_OK, &write_OK);
1201         if (write_OK)
1202         {
1203             /* Rename existing file to a backup and
1204              * start a new output file.
1205              */
1206             sprintf(backup, "%s.BAK", outfile);
1207             if ((rename(outfile, backup) != OK) ||
1208                 !(bakp = util_fopen_locked(backup, "r")) ||
1209                 !(outFile = util_fopen_locked(outfile, "w")))
1210             {
1211                 iRet = ERR_BACKUP;
1212                 goto ERROR_EXIT;
1213             }
1214             debug_unbuffer_file(outFile);
1215             abio_set_indent_char(outFile, '\t');
1216             abio_set_indent_chars_per_level(outFile, 1);
1217         }
1218         else    /* The file is read-only */
1219         {
1220             iRet = ERR_READ_ONLY;
1221             goto ERROR_EXIT;    /* file access error */
1222         }
1223     }
1224
1225     /* If the file has been saved before, then read
1226      * in any beginning comments from the old file
1227      * and transfer them to the new file.
1228      */
1229     if (old_file || bakp)
1230     {
1231         debug_unbuffer_file(outFile);
1232         abio_set_indent_char(outFile, '\t');
1233         abio_set_indent_chars_per_level(outFile, 1);
1234
1235         if (bakp == NULL)
1236         {
1237             bakp = util_fopen_locked(old_file, "r");
1238             if (bakp == NULL)
1239             {
1240                 iRet = ERR_READ;
1241                 goto ERROR_EXIT;        /* file access error */
1242             }
1243         }
1244
1245         /*
1246          * Preserve leading text until :bil-version string.
1247          */
1248         while (fgets(Buf, sizeof(Buf), bakp) &&
1249                strncmp(Buf, file_begin, fblen) != 0)
1250         {
1251             fputs(Buf, outFile);
1252         }
1253         fclose(bakp);
1254     }
1255
1256     /*
1257      * Write the BIL version string
1258      */
1259     fprintf(outFile, "%s\t%d %d\n",
1260                 AB_VERSION_PREFIX,
1261                 Util_major_version,
1262                 Util_minor_version);
1263
1264 ERROR_EXIT:
1265     return iRet;
1266
1267 #undef outFile
1268 }
1269
1270 /*
1271  * Return the object begin string.
1272  */
1273 STRING
1274 abio_bil_object_begin_string(void)
1275 {
1276     return "(";
1277 }
1278
1279 /*
1280  * Return the object end string.
1281  */
1282 STRING
1283 abio_bil_object_end_string(void)
1284 {
1285     return ")";
1286 }
1287
1288 /*
1289  * Open an input file.  Returns NULL if successful, otherwise an error
1290  * message.
1291  */
1292 STRING
1293 abio_open_gil_input(STRING name, FILE ** pInFile)
1294 {
1295 #define inFile (*pInFile)
1296     int                 v = 0;
1297     STRING              errmsg = NULL;
1298     int                 ch = -1;
1299
1300     /*
1301      * If the input file exists and is the correct version, open it and skip
1302      * leading comments.
1303      */
1304     if (inFile = util_fopen_locked(name, "r"))
1305     {
1306         v = gil_version(inFile);
1307         if (v < 1)
1308         {
1309             /* No dgettext() wrapper on purpose */
1310             errmsg = "unrecognized file format";        
1311         }
1312         else if (v > gilMajorVersion)
1313         {
1314             /* REMIND: should be "Bad GIL version" */
1315             /* No dgettext() wrapper on purpose */
1316             errmsg = "unrecognized file format";        
1317         }
1318     }
1319     else
1320     {
1321         errmsg = sys_errlist[errno];
1322     }
1323
1324     /*
1325      * Otherwise, return an error message.
1326      */
1327     if (errmsg != NULL)
1328     {
1329         sprintf(Buf, "%s: %s", name, errmsg);
1330         return Buf;
1331     }
1332     return NULL;
1333 #undef inFile
1334 }
1335
1336
1337 /*
1338  * Open an output file.  Returns NULL if successful, otherwise an error
1339  * message.
1340  */
1341 STRING
1342 abio_open_gil_output(STRING outfile, FILE ** pOutFile)
1343 {
1344 #define outFile (*pOutFile)
1345     struct stat         statbuf;
1346     int                 version_written = FALSE;
1347
1348     /*
1349      * Look for the specified output file.
1350      */
1351     if (stat(outfile, &statbuf) != OK)
1352     {
1353
1354         /*
1355          * outFileut file does not exits.  Open a new one.
1356          */
1357         if (outFile = util_fopen_locked(outfile, "w"))
1358         {
1359             debug_unbuffer_file(outFile);
1360
1361             /*
1362              * First line is the version number.
1363              */
1364             fprintf(outFile, "%s%d\n", gilVersionPrefix, gilMajorVersion);
1365             return NULL;
1366         }
1367         else
1368             goto ERROR_EXIT;
1369     }
1370
1371     /*
1372      * The output file exists.  Make sure we can successfully open it before
1373      * backing it up.
1374      */
1375     if (outFile = util_fopen_locked(outfile, "a"))
1376     {
1377         char                backup[MAXPATHLEN];
1378         FILE               *bakp = NULL;
1379         STRING              comment = abio_comment_string();
1380         int                 len = strlen(comment);
1381
1382         debug_unbuffer_file(outFile);
1383
1384         /*
1385          * Rename existing file to a backup and start a new output file.
1386          */
1387         fclose(outFile);
1388         sprintf(backup, "%s.BAK", outfile);
1389         if ((rename(outfile, backup) != OK) ||
1390             !(bakp = util_fopen_locked(backup, "r")) ||
1391             !(outFile = util_fopen_locked(outfile, "w")))
1392             goto ERROR_EXIT;    /* file access error */
1393
1394         debug_unbuffer_file(outFile);
1395
1396         /*
1397          * Ignore any unrelated leading text until first comment (eg. garbage
1398          * from mailtool).
1399          */
1400         while (fgets(Buf, sizeof(Buf), bakp) &&
1401                strncmp(Buf, comment, len) != 0)
1402             ;
1403
1404         /*
1405          * Preserve any comments.
1406          */
1407         do
1408         {
1409             if (!version_written)
1410             {
1411
1412                 /*
1413                  * First comment is always version number.
1414                  */
1415                 fprintf(outFile, "%s%d\n",
1416                         gilVersionPrefix,
1417                         Util_major_version);
1418                 version_written = TRUE;
1419             }
1420             else
1421                 fputs(Buf, outFile);
1422         }
1423         while (fgets(Buf, sizeof(Buf), bakp) &&
1424                strncmp(Buf, comment, len) == 0);
1425
1426         fclose(bakp);
1427         return NULL;
1428     }
1429
1430 ERROR_EXIT:
1431
1432     /*
1433      * Return a message if unsuccessful.
1434      */
1435     sprintf(Buf, "%s: %s", outfile, sys_errlist[errno]);
1436     return Buf;
1437 #undef outFile
1438 }
1439
1440
1441 /*
1442  * Open an input proj file.  Returns NULL if successful, otherwise an error
1443  * message.
1444  */
1445 STRING
1446 abio_open_proj_input(STRING name, FILE ** pInFile)
1447 {
1448     return abio_open_gil_input(name, pInFile);
1449 }
1450
1451
1452 /*
1453  * Open an output proj file.  Returns NULL if successful, otherwise an error
1454  * message.
1455  */
1456 STRING
1457 abio_open_proj_output(STRING name, FILE ** pOutFile)
1458 {
1459     return abio_open_gil_output(name, pOutFile);
1460 }
1461
1462
1463 /*
1464  * Open an input resource file. Returns NULL is successful, otherwise an
1465  * error message.
1466  */
1467 STRING
1468 abio_open_resfile_input(STRING name, FILE ** pInFile)
1469 {
1470     return abio_open_gil_input(name, pInFile);
1471 }
1472
1473 /*
1474  * Open an output file.  Returns NULL if successful, otherwise an error
1475  * message.
1476  */
1477 STRING
1478 abio_open_output(STRING name, FILE ** pOutFile)
1479 {
1480 #define outFile (*pOutFile)
1481     if (name == NULL)
1482     {
1483         outFile = tmpfile();
1484     }
1485     else
1486     {
1487         outFile = util_fopen_locked(name, "w");
1488     }
1489     if (outFile != NULL)
1490     {
1491         debug_unbuffer_file(outFile);
1492         abio_set_indent_char(outFile, ' ');
1493         abio_set_indent_chars_per_level(outFile, 4);
1494         return NULL;
1495     }
1496
1497     sprintf(Buf, "%s: %s", name, sys_errlist[errno]);
1498     return Buf;
1499 #undef outFile
1500 }
1501
1502 /*
1503  * fprintf to the current output file.
1504  */
1505 int
1506 abio_printf(FILE *outFile, STRING fmt, ...)
1507 {
1508     va_list             args;
1509
1510     if (Newline)
1511         fputs(Indent, outFile);
1512
1513     va_start(args, fmt);
1514     vfprintf(outFile, fmt, args);
1515     va_end(args);
1516
1517     Newline = fmt[strlen(fmt) - 1] == '\n';
1518     return 0;
1519 }
1520
1521 int
1522 abio_print_line(FILE *outFile, STRING fmt,...)
1523 {
1524     int                 iReturn = 0;
1525     va_list             args;
1526
1527     va_start(args, fmt);
1528     if (fmt == NULL)
1529     {
1530         fputs("\n", outFile);
1531         Newline = TRUE;
1532     }
1533     else
1534     {
1535         if (Newline)
1536             fputs(Indent, outFile);
1537
1538         iReturn = vfprintf(outFile, fmt, args);
1539         Newline = (fmt[strlen(fmt)-1] == '\n');
1540     }
1541     va_end(args);
1542     return iReturn;
1543 }
1544
1545 int
1546 abio_open_block(FILE * outFile)
1547 {
1548     fprintf(outFile, "\n%s{", Indent);
1549     abio_indent(outFile);
1550     return 0;
1551 }
1552
1553 int
1554 abio_close_block(FILE * outFile)
1555 {
1556     abio_outdent(outFile);
1557     fprintf(outFile, "\n%s}", Indent);
1558     return 0;
1559 }
1560
1561 /*
1562  * Write a character to the output file, preceeded by the indent string if we
1563  * are on a new line.
1564  */
1565 int
1566 abio_putc(FILE * outFile, char c)
1567 {
1568     if (Newline)
1569         fputs(Indent, outFile);
1570     fputc(c, outFile);
1571     Newline = (c == '\n');
1572     return 0;
1573 }
1574
1575 /*
1576  * Write characters to the output file, preceeded by the indent string if we
1577  * are on a new line.
1578  */
1579 int
1580 abio_puts(FILE * outFile, STRING s)
1581 {
1582     if (Newline)
1583         fputs(Indent, outFile);
1584     fputs(s, outFile);
1585     Newline = s[strlen(s) - 1] == '\n';
1586     return 0;
1587 }
1588
1589 /*
1590  * Write a boolean to the output file.
1591  */
1592 int
1593 abio_bil_put_boolean(FILE * outFile, BOOL value)
1594 {
1595     return abio_puts(outFile, abio_bil_boolean_string(value));
1596 }
1597
1598 /*
1599  * Write an integer to the output file.
1600  */
1601 int
1602 abio_put_float(FILE * outFile, double d)
1603 {
1604     sprintf(Buf, "%f", d);
1605     return abio_puts(outFile, Buf);
1606 }
1607
1608 /*
1609  * Write an integer to the output file.
1610  */
1611 int
1612 abio_put_integer(FILE * outFile, int i)
1613 {
1614     sprintf(Buf, "%d", i);
1615     return abio_puts(outFile, Buf);
1616 }
1617
1618 int
1619 abio_put_keyword_name(FILE * outFile, STRING name)
1620 {
1621     return abio_puts(outFile, name);
1622 }
1623
1624 /*
1625  * Write a keyword to the output file.
1626  */
1627 int
1628 abio_put_keyword(FILE * outFile, AB_OBJECT_TYPE abo_type)
1629 {
1630     switch (abo_type)
1631     {
1632         case AB_TYPE_PROJECT:
1633         abio_puts(outFile, ":project");
1634         break;
1635
1636     case AB_TYPE_MODULE:
1637         abio_puts(outFile, ":module");
1638         break;
1639
1640     case AB_TYPE_ACTION:
1641         abio_puts(outFile, ":connection");
1642         break;
1643
1644     case AB_TYPE_BASE_WINDOW:
1645         abio_puts(outFile, ":element");
1646         break;
1647
1648     case AB_TYPE_BUTTON:
1649         abio_puts(outFile, ":element");
1650         break;
1651
1652     case AB_TYPE_CHOICE:
1653         abio_puts(outFile, ":element");
1654         break;
1655
1656     case AB_TYPE_COMBO_BOX:
1657         abio_puts(outFile, ":element");
1658         break;
1659
1660     case AB_TYPE_FILE_CHOOSER:
1661         abio_puts(outFile, ":element");
1662         break;
1663
1664     case AB_TYPE_MESSAGE:
1665         abio_puts(outFile, ":element");
1666         break;
1667
1668     case AB_TYPE_CONTAINER:
1669         abio_puts(outFile, ":element");
1670         break;
1671
1672     case AB_TYPE_DIALOG:
1673         abio_puts(outFile, ":element");
1674         break;
1675
1676     case AB_TYPE_DRAWING_AREA:
1677         abio_puts(outFile, ":element");
1678         break;
1679
1680     case AB_TYPE_ITEM:
1681         abio_puts(outFile, ":element");
1682         break;
1683
1684     case AB_TYPE_LABEL:
1685         abio_puts(outFile, ":element");
1686         break;
1687
1688     case AB_TYPE_LAYERS:
1689         abio_puts(outFile, ":element");
1690         break;
1691
1692     case AB_TYPE_LIST:
1693         abio_puts(outFile, ":element");
1694         break;
1695
1696     case AB_TYPE_MENU:
1697         abio_puts(outFile, ":element");
1698         break;
1699
1700     case AB_TYPE_SEPARATOR:
1701         abio_puts(outFile, ":element");
1702         break;
1703
1704     case AB_TYPE_SCALE:
1705         abio_puts(outFile, ":element");
1706         break;
1707
1708     case AB_TYPE_SPIN_BOX:
1709         abio_puts(outFile, ":element");
1710         break;
1711
1712     case AB_TYPE_TERM_PANE:
1713         abio_puts(outFile, ":element");
1714         break;
1715
1716     case AB_TYPE_TEXT_FIELD:
1717         abio_puts(outFile, ":element");
1718         break;
1719
1720     case AB_TYPE_TEXT_PANE:
1721         abio_puts(outFile, ":element");
1722         break;
1723
1724     case AB_TYPE_UNKNOWN:
1725         abio_puts(outFile, ":element");
1726         break;
1727
1728     case AB_OBJECT_TYPE_NUM_VALUES:
1729         abio_puts(outFile, ":element");
1730         break;
1731
1732
1733     default:
1734         break;
1735     }
1736     return 0;
1737 }
1738
1739 /*
1740  * Write a handler to the output file.
1741  */
1742 int
1743 abio_put_handler(FILE * outFile, STRING s)
1744 {
1745     STRING              newstr;
1746     STRING              p;
1747
1748     if (s && (s[0] == *abio_string_begin_string()) &&
1749         (s[strlen(s) - 1] == *abio_string_end_string()))
1750     {
1751         newstr = (STRING) malloc(strlen(s) + 1);
1752         strcpy(newstr, s);
1753         newstr[strlen(s) - 1] = '\0';
1754
1755         fputc(*abio_string_begin_string(), outFile);
1756         for (p = newstr + 1; *p; p++)
1757         {
1758             if ((*p == *abio_string_end_string()) || (*p == '\\'))
1759                 fputc('\\', outFile);
1760             fputc(*p, outFile);
1761         }
1762         fputc(*abio_string_end_string(), outFile);
1763         free(newstr);
1764     }
1765     else
1766     {
1767         abio_gil_put_name(outFile, s);
1768     }
1769     return 0;
1770 }
1771
1772 /*
1773  * Write a name list to the output file.
1774  */
1775 int
1776 abio_gil_put_name(FILE * outFile, STRING s)
1777 {
1778     return abio_puts(outFile, s ? s : abio_gil_false_string());
1779 }
1780
1781 /*
1782  * Write a full name to the output file.
1783  */
1784 int
1785 abio_put_full_name(FILE * outFile, STRING parent, STRING name, STRING item)
1786 {
1787     abio_puts(outFile, abio_list_begin_string());
1788     if (parent)
1789     {
1790         abio_puts(outFile, parent);
1791         abio_putc(outFile, ' ');
1792     }
1793     if (name)
1794     {
1795         abio_puts(outFile, name);
1796     }
1797     if (item)
1798     {
1799         abio_putc(outFile, ' ');
1800         abio_put_string(outFile, item);
1801     }
1802     abio_puts(outFile, abio_list_end_string());
1803     abio_putc(outFile, '\n');
1804     return 0;
1805 }
1806
1807 /*
1808  * Write a full name (the project form) to the output file.
1809  */
1810 int
1811 abio_put_proj_full_name(
1812                         FILE * outFile,
1813                         STRING interface,
1814                         STRING parent,
1815                         STRING name,
1816                         STRING item
1817 )
1818 {
1819     abio_puts(outFile, abio_list_begin_string());
1820
1821     if (interface)
1822     {
1823         abio_puts(outFile, interface);
1824         abio_putc(outFile, ' ');
1825     }
1826     if (parent)
1827     {
1828         abio_puts(outFile, parent);
1829         abio_putc(outFile, ' ');
1830     }
1831     if (name)
1832     {
1833         abio_puts(outFile, name);
1834     }
1835     if (item)
1836     {
1837         abio_putc(outFile, ' ');
1838         abio_put_string(outFile, item);
1839     }
1840
1841     abio_puts(outFile, abio_list_end_string());
1842     abio_putc(outFile, '\n');
1843     return 0;
1844 }
1845
1846
1847 /*
1848  * Write a string to the output file.
1849  *
1850  * Quotes special characters in C format 
1851  * (e.g., "\"hi\"", "newline:\n")
1852  */
1853 int
1854 abio_put_string(FILE * outFile, STRING s)
1855 {
1856     abio_puts(outFile, abio_string_begin_string());
1857     abio_put_string_to_file(outFile, s);
1858     abio_puts(outFile, abio_string_end_string());
1859     return 0;
1860 }
1861
1862 /*
1863  * Write a string to the output file.
1864  */
1865 int
1866 abio_put_string_to_file(FILE * outFile, STRING string)
1867 {
1868     register STRING     ptr;
1869     char                strEndChar = *(abio_string_end_string());
1870     char                curChar = 0;
1871
1872     if (string != NULL)
1873     {
1874         for (ptr = string; (*ptr != 0); ++ptr)
1875         {
1876             curChar = *ptr;
1877             if (   (curChar == strEndChar) 
1878                 || (curChar == '\\') )
1879             {
1880                 fputc('\\', outFile);
1881                 fputc(curChar, outFile);
1882             }
1883             else if (curChar == '\n')
1884             {
1885                 fputs("\\n", outFile);
1886             }
1887             else
1888             {
1889                 fputc(curChar, outFile);
1890             }
1891         }
1892     }
1893     return 0;
1894 }
1895
1896
1897 /*
1898  * Maintain indent state for output file.
1899  */
1900 int
1901 abio_indent(FILE *out_file)
1902 {
1903     return abioP_build_indent_string(out_file,
1904                 Indent_char, Indent_chars_per_level, Indent_level + 1);
1905
1906 }
1907
1908 int
1909 abio_outdent(FILE *out_file)
1910 {
1911     return abioP_build_indent_string(out_file,
1912                 Indent_char, Indent_chars_per_level, Indent_level - 1);
1913 }
1914
1915 /*
1916  * Get the current indent level of the output file.
1917  */
1918 int
1919 abio_get_indent(FILE *out_file)
1920 {
1921     out_file = out_file;        /* avoid warning */
1922     return Indent_level;
1923 }
1924
1925
1926 int
1927 abio_set_indent(FILE *out_file, int indent)
1928 {
1929     return abioP_build_indent_string(out_file, 
1930                 Indent_char, Indent_chars_per_level, indent);
1931 }
1932
1933
1934 /*
1935  * Set indent char
1936  */
1937 int
1938 abio_set_indent_char(FILE *out_file, int indent_char)
1939 {
1940     return abioP_build_indent_string(out_file, 
1941                 indent_char, Indent_chars_per_level, Indent_level);
1942 }
1943
1944
1945 int
1946 abio_get_indent_char(FILE *out_file)
1947 {
1948     out_file = out_file;        /* avoid warning */
1949     return Indent_char;
1950 }
1951
1952
1953 int
1954 abio_set_indent_chars_per_level(FILE *out_file, int chars_per_level)
1955 {
1956     return abioP_build_indent_string(out_file, 
1957                 Indent_char, chars_per_level, Indent_level);
1958 }
1959
1960
1961 int
1962 abio_get_indent_chars_per_level(FILE *out_file)
1963 {
1964     out_file = out_file;        /* avoid warning */
1965     return Indent_chars_per_level;
1966 }
1967
1968
1969 /*
1970  * Set the current indent level of the output file.
1971  */
1972 static int
1973 abioP_build_indent_string(
1974                         FILE    *out_file,
1975                         int     new_indent_char,
1976                         int     new_indent_chars_per_level,
1977                         int     new_indent_level
1978 )
1979 {
1980     register int        i;
1981     int                 indent_length = 0;
1982     out_file = out_file;        /* avoid warning */
1983
1984     if (   (new_indent_char == Indent_char)
1985         && (new_indent_chars_per_level == Indent_chars_per_level)
1986         && (new_indent_level == Indent_level) )
1987     {
1988         return Indent_level;
1989     }
1990
1991     Indent_char = new_indent_char;
1992     Indent_chars_per_level = new_indent_chars_per_level;
1993     Indent_level = new_indent_level;
1994     Indent_level = util_max(Indent_level, 0);
1995     Indent_level = util_min(Indent_level, INDENT_MAX);
1996     indent_length = Indent_level * Indent_chars_per_level;
1997
1998     for (i = 0; i < indent_length; ++i)
1999         Indent[i] = Indent_char;
2000     Indent[i] = 0;
2001
2002     return Indent_level;
2003 }
2004
2005
2006 /*
2007  * Return the string begin string.
2008  */
2009 STRING
2010 abio_string_begin_string(void)
2011 {
2012     return "\"";
2013 }
2014
2015 /*
2016  * Return the string end string.
2017  */
2018 STRING
2019 abio_string_end_string(void)
2020 {
2021     return "\"";
2022 }
2023
2024 /*
2025  * Return the string representation of a string.  Truncates the string if it
2026  * is too long.
2027  */
2028 STRING
2029 abio_string_string(STRING s)
2030 {
2031     char               *p;
2032     int                 i = 0;
2033
2034     Buf[i++] = *abio_string_begin_string();
2035     if (s)
2036         for (p = s; *p && i < MAXPATHLEN - 4; p++)
2037         {
2038             if (*p == *abio_string_end_string())
2039                 Buf[i++] = '\\';
2040             Buf[i++] = *p;
2041         }
2042     Buf[i++] = *abio_string_end_string();
2043     Buf[i] = '\0';
2044     return Buf;
2045 }
2046
2047 /*
2048  * Return the boolean true string.
2049  */
2050 STRING
2051 abio_bil_true_string(void)
2052 {
2053     return ":true";
2054 }
2055
2056 /*
2057  * Internal utility functions.
2058  */
2059
2060 /*
2061  * Get the next token from the input stream.  It is assumed that we are not
2062  * at end-of-file.
2063  */
2064 static              STRING
2065 get_token(FILE * inFile)
2066 {
2067     int                 ch = 0;
2068     int                 count = 0;      /* character count */
2069
2070     while (isspace(ch = fgetc(inFile)))
2071         ;
2072     if (ch != EOF)
2073     {
2074         ungetc(ch, inFile);
2075     }
2076
2077     /* vi hack ( */
2078     while (! (   ((ch = fgetc(inFile)) == EOF)
2079               || (isspace(ch))
2080               || (ch == ')')
2081               || (ch == '"') ))
2082     {
2083         if (count < (sizeof(Buf) - 1))
2084             Buf[count++] = (char)ch;
2085     }
2086
2087     if (ch != EOF)
2088     {
2089         ungetc(ch, inFile);
2090     }
2091     Buf[count] = '\0';
2092     return (count > 0? Buf:NULL);
2093 }
2094
2095
2096 /*
2097  * Return whether a file contains a GIL file.  Returns non-0 version number
2098  * if true, otherwise FALSE.  Leaves file positioned at the beginning of
2099  * first comment (should be version number).
2100  */
2101 static int
2102 gil_version(FILE * fp)
2103 {
2104     STRING              ascii_version_num;
2105     STRING              tmp;
2106     long                int_version_num;
2107     long                first_char;
2108     int                 version = 0;
2109     int                 len = strlen(gilVersionPrefix);
2110
2111     rewind(fp);
2112     while (fgets(Buf, sizeof(Buf), fp))
2113     {
2114
2115         /*
2116          * Ignore lines until a GIL prefix is found.
2117          */
2118         if (strncmp(Buf, gilVersionPrefix, len) == 0)
2119         {
2120
2121             /*
2122              * Prefix matched.  Point to the version number and convert it to
2123              * an integer.
2124              */
2125             first_char = ftell(fp);
2126             Buf[strlen(Buf) - 1] = '\0';
2127             ascii_version_num = Buf + len;
2128             int_version_num = strtol(ascii_version_num, &tmp, 10);
2129
2130             if (ascii_version_num != tmp)
2131                 version = (int) int_version_num;
2132             fseek(fp, first_char, 0);
2133             break;
2134         }
2135     }
2136     return version;
2137 }
2138
2139 /*
2140  * Skip leading comments in a GIL file.
2141  */
2142 static void
2143 skip_comments(FILE * inFile)
2144 {
2145     long                pos;
2146
2147     for (;;)
2148     {
2149         pos = ftell(inFile);
2150         if (!fgets(Buf, sizeof(Buf), inFile) ||
2151             Buf[0] != *abio_comment_string())
2152             break;
2153     }
2154     fseek(inFile, pos, 0);
2155 }
2156
2157 /*
2158  *  If the file is not NULL, and verbosity >= 3, then util_unbuffer_file
2159  *  is called on the file. This keeps files flushed during debugging, so
2160  *  it is easier to see what's going on while the file is being written.
2161  */
2162 static int
2163 debug_unbuffer_file(FILE *file)
2164 {
2165     if ((file == NULL) || (!debugging()))
2166     {
2167         return 0;
2168     }
2169
2170     if (   ((debug_level() >= 1) && ((file == stdin) || (file == stdout)) )
2171         || (debug_level() >= 5) )
2172     {
2173         util_unbuffer_file(file);
2174     }
2175     return 0;
2176 }
2177
2178
2179 /*************************************************************************
2180  **                                                                     **
2181  **     OUTPUT                                                          **
2182  **                                                                     **
2183  *************************************************************************/
2184
2185 int
2186 util_set_output_handler(UtilOutputHandler new_output_handler)
2187 {
2188     output_handler = new_output_handler;
2189     return 0;
2190 }
2191
2192
2193 int
2194 util_set_err_output_handler(UtilOutputHandler new_err_output_handler)
2195 {
2196     err_output_handler = new_err_output_handler;
2197     return 0;
2198 }
2199
2200
2201 int
2202 util_puts(STRING msg)
2203 {
2204     if (output_handler == NULL)
2205     {
2206         if (util_get_program_name() != NULL)
2207         {
2208             printf("%s: ", util_get_program_name());
2209         }
2210         printf("%s", msg);
2211     }
2212     else
2213     {
2214         output_handler(msg);
2215     }
2216     util_set_help_data(NULL, NULL, NULL);
2217     return 0;
2218 }
2219
2220
2221 int
2222 util_dputs(int debug_level, STRING msg)
2223 {
2224     if ((debugging()) && (debug_level() >= debug_level))
2225     {
2226         fprintf(stderr, "%s", msg);
2227     }
2228     return 0;
2229 }
2230
2231
2232 int
2233 util_puts_err(STRING msg)
2234 {
2235     if (err_output_handler == NULL)
2236     {
2237         if (util_get_program_name() != NULL)
2238         {
2239             fprintf(stderr, "%s: ", util_get_program_name());
2240         }
2241         fprintf(stderr, "%s", msg);
2242     }
2243     else
2244     {
2245         /* 
2246         ** Because the message passed in might be from catgets(), 
2247         ** we need to make a local copy before calling the output
2248         ** handler.  Then we'll free it.
2249         */
2250         int     msg_len = strlen(msg)+1;
2251         STRING  msg_buf = NULL;
2252
2253         if( (msg_buf = (STRING)util_malloc(msg_len)) == NULL) {
2254                 fprintf(stderr,"%s: could not allocate memory!\n",
2255                         util_get_program_name());
2256                 return 0;
2257         }
2258         strcpy(msg_buf,msg);
2259         err_output_handler(msg_buf);
2260         free(msg_buf);
2261     }
2262     util_set_help_data(NULL, NULL, NULL);
2263     return 0;
2264 }
2265
2266
2267 int
2268 util_printf(STRING fmt, ...)
2269 {
2270     va_list             args;
2271     va_start(args, fmt);
2272
2273     if (output_handler == NULL)
2274     {
2275         if (util_get_program_name() != NULL)
2276         {
2277             printf("%s: ", util_get_program_name());
2278         }
2279         vprintf(fmt, args);
2280     }
2281     else
2282     {
2283         char    msg[8192] = "";
2284         int     num_chars = 0;
2285
2286         num_chars = vsprintf(msg, fmt, args);
2287         util_dassert(1, (num_chars < 8192));
2288         output_handler(msg);
2289     }
2290
2291     va_end(args);
2292     util_set_help_data(NULL, NULL, NULL);
2293     return 0;
2294 }
2295
2296
2297 int
2298 util_dprintf(int debug_level, STRING fmt, ...)
2299 {
2300     va_list     args;
2301     BOOL        do_debug = debugging();
2302     int         the_level = debug_level();
2303
2304     va_start(args, fmt);
2305     if ((debugging()) && (debug_level() >= debug_level))
2306     {
2307         vfprintf(stderr, fmt, args);
2308     }
2309     va_end(args);
2310     return 0;
2311 }
2312
2313
2314 int
2315 util_printf_err(STRING fmt, ...)
2316 {
2317     va_list     args;
2318     va_start(args, fmt);
2319
2320     if (err_output_handler != NULL)
2321     {
2322         char    errmsg[8192] = "";
2323         int     num_chars = 0;
2324
2325         num_chars = vsprintf(errmsg, fmt, args);
2326         util_dassert(1, (num_chars < 8192));
2327         err_output_handler(errmsg);
2328     }
2329     else
2330     {
2331         if (util_get_program_name() != NULL)
2332         {
2333             fprintf(stderr, "%s: ", util_get_program_name());
2334         }
2335         vfprintf(stderr, fmt, args);
2336     }
2337
2338     va_end(args);
2339     util_set_help_data(NULL, NULL, NULL);
2340     return 0;
2341 }
2342
2343 /*
2344  * Check if we have read/write permission for the file.
2345  */
2346 int
2347 abio_access_file(
2348     STRING      name,
2349     BOOL        *read_OK,
2350     BOOL        *write_OK
2351 )
2352 {
2353     struct stat         statbuf;
2354     uid_t               userid;
2355     gid_t               groupid;
2356     int                 ret = 0;
2357
2358     *read_OK = FALSE;
2359     *write_OK = FALSE;
2360
2361     /*
2362      * Check if it can be read. If so, check that it really is
2363      * a bil file.
2364      */  
2365
2366     userid = geteuid();         /* Get the effective user id */
2367     groupid = getegid();        /* Get the effective group id */
2368
2369     if ((ret = stat(name, &statbuf)) == -1)
2370     {
2371         return ERR;
2372     }
2373
2374     /* If the user is the file owner then the file has to
2375      * have the owner read/write permission bits set.
2376      */
2377     if (statbuf.st_uid == userid)
2378     {   
2379         if ((statbuf.st_mode & S_IWUSR) != 0)
2380             *write_OK = TRUE;
2381         if ((statbuf.st_mode & S_IRUSR) != 0)
2382             *read_OK = TRUE;
2383     }   
2384     /* If the user is not the file owner, but is in the group, then
2385      * the file has to have the group read/write permission bits set.
2386      */ 
2387     else if (statbuf.st_gid == groupid)
2388     {   
2389         if ((statbuf.st_mode & S_IWGRP) != 0)
2390             *write_OK = TRUE;
2391         if ((statbuf.st_mode & S_IRGRP) != 0)
2392             *read_OK = TRUE;
2393     }
2394     /* If the user is not the file owner and is not in the group, then
2395      * the file has to have the other read/write permission bits set.
2396      */
2397     else
2398     {  
2399         if ((statbuf.st_mode & S_IWOTH) != 0)
2400             *write_OK = TRUE;
2401         if ((statbuf.st_mode & S_IROTH) != 0)
2402             *read_OK = TRUE;
2403     }
2404
2405     return 0;
2406 }
2407
2408 /*
2409  * Return the module keyword.
2410  */
2411 STRING
2412 abio_module_string(void)
2413 {
2414     return ":module";
2415 }
2416
2417 /*
2418  * Return the project keyword.
2419  */
2420 STRING
2421 abio_project_string(void)
2422 {
2423     return ":project";
2424 }
2425
2426 /*
2427  * Write an escaped string to a buffer.
2428  * Assumes space has been allocated for outBuf.
2429  * size is the size allocated for the buffer.
2430  */
2431 int
2432 abio_put_string_to_buffer(STRING string, STRING outBuf, int size)
2433 {
2434 #define INC             32
2435     register STRING     ptr;
2436     char                strEndChar = *(abio_string_end_string());
2437     char                curChar = 0;
2438     int                 i = 0;
2439
2440     *outBuf = 0;
2441     if (string != NULL)
2442     {
2443         for (ptr = string, i = 0; (*ptr != 0); ++ptr, ++i)
2444         {
2445             /* If we've run out of space, allocate more.
2446              */
2447             if (i == (size - 1))
2448             {
2449                 outBuf = (STRING) realloc(outBuf, size += INC);
2450             }
2451
2452             curChar = *ptr;
2453
2454             /* Escape backslashes, double-quotes, 
2455              * newlines, and tabs. 
2456              */
2457             if (   (curChar == strEndChar) 
2458                 || (curChar == '\\') )
2459             {
2460                 outBuf[i] = '\\'; i++;
2461                 outBuf[i] = curChar;
2462             }
2463             else if (curChar == '\n')
2464             {
2465                 outBuf[i] = '\\'; i++;
2466                 outBuf[i] = 'n';
2467             }
2468             else if (curChar == '\t')
2469             {
2470                 outBuf[i] = '\\'; i++;
2471                 outBuf[i] = 't';
2472             }
2473             else
2474             {
2475                 outBuf[i] = curChar;
2476             }
2477         }
2478         outBuf[i] = '\0';
2479     }
2480     return 0;
2481 }
2482
2483 /* This function is called to set the help text that will
2484  * be displayed for a message that is posted via a call
2485  * to any of the print utilities, such as util_printf_err,
2486  * util_puts_err, or util_print_error.
2487  */
2488 void
2489 util_set_help_data(
2490     STRING      msg,
2491     STRING      vol,
2492     STRING      locID
2493 )
2494 {
2495     if (help_text)
2496     {
2497         util_free(help_text); 
2498         help_text = NULL;
2499     }
2500     if (msg)
2501     {
2502         help_text = (STRING)util_malloc(strlen(msg)+1);
2503         strcpy(help_text, msg);
2504     }
2505
2506     if (help_volume)
2507     {
2508         util_free(help_volume); 
2509         help_volume = NULL;
2510     }
2511     if (vol)
2512     {
2513         help_volume = (STRING)util_malloc(strlen(vol)+1);
2514         strcpy(help_volume, vol);
2515     }
2516
2517     if (help_locID)
2518     {
2519         util_free(help_locID); 
2520         help_locID = NULL;
2521     }
2522     if (locID)
2523     {
2524         help_locID = (STRING)util_malloc(strlen(locID)+1);
2525         strcpy(help_locID, locID);
2526     } 
2527 }
2528
2529 /* This function is called by (err_)output_handler() in
2530  * dtbuilder.c. It retrieves the help text for the message
2531  * that is going to be displayed. 
2532  */
2533 int
2534 util_get_help_data(
2535     STRING      *msg,
2536     STRING      *vol,
2537     STRING      *locID
2538 )
2539 {
2540     if (msg)
2541         *msg = help_text;
2542     if (vol)
2543         *vol = help_volume;
2544     if (locID)
2545         *locID = help_locID;
2546     return 0;
2547 }