1 /* $NetBSD: rcslex.c,v 1.5 1996/10/15 07:00:23 veego Exp $ */
3 /* lexical analysis of RCS files */
5 /******************************************************************************
7 * hashtable, Lexinit, nextlex, getlex, getkey,
8 * getid, getnum, readstring, printstring, savestring,
9 * checkid, fatserror, error, faterror, warn, diagnose
10 * Testprogram: define LEXDB
11 ******************************************************************************
14 /* Copyright 1982, 1988, 1989 Walter Tichy
15 Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
16 Distributed under license by the Free Software Foundation, Inc.
18 This file is part of RCS.
20 RCS is free software; you can redistribute it and/or modify
21 it under the terms of the GNU General Public License as published by
22 the Free Software Foundation; either version 2, or (at your option)
25 RCS is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
30 You should have received a copy of the GNU General Public License
31 along with RCS; see the file COPYING.
32 If not, write to the Free Software Foundation,
33 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
35 Report problems and direct all questions to:
37 rcs-bugs@cs.purdue.edu
45 * Revision 1.5 1996/10/15 07:00:23 veego
48 * Revision 5.19 1995/06/16 06:19:24 eggert
51 * Revision 5.18 1995/06/01 16:23:43 eggert
52 * (map_fd_deallocate,mmap_deallocate,read_deallocate,nothing_to_deallocate):
54 * (Iclose): If large_memory and maps_memory, use them to deallocate mapping.
55 * (fd2RILE): Use map_fd if available.
56 * If one mapping method fails, try the next instead of giving up;
57 * if they all fail, fall back on ordinary read.
58 * Work around bug: root mmap over NFS succeeds, but accessing dumps core.
59 * Use MAP_FAILED macro for mmap failure, and `char *' instead of caddr_t.
60 * (advise_access): Use madvise only if this instance used mmap.
61 * (Iopen): Use fdSafer to get safer file descriptor.
62 * (aflush): Moved here from rcsedit.c.
64 * Revision 5.17 1994/03/20 04:52:58 eggert
65 * Don't worry if madvise fails. Add Orewind. Remove lint.
67 * Revision 5.16 1993/11/09 17:55:29 eggert
68 * Fix `label: }' typo.
70 * Revision 5.15 1993/11/03 17:42:27 eggert
71 * Improve quality of diagnostics by putting file names in them more often.
72 * Don't discard ignored phrases.
74 * Revision 5.14 1992/07/28 16:12:44 eggert
75 * Identifiers may now start with a digit and (unless they are symbolic names)
76 * may contain `.'. Avoid `unsigned'. Statement macro names now end in _.
78 * Revision 5.13 1992/02/17 23:02:27 eggert
79 * Work around NFS mmap SIGBUS problem.
81 * Revision 5.12 1992/01/06 02:42:34 eggert
82 * Use OPEN_O_BINARY if mode contains 'b'.
84 * Revision 5.11 1991/11/03 03:30:44 eggert
85 * Fix porting bug to ancient hosts lacking vfprintf.
87 * Revision 5.10 1991/10/07 17:32:46 eggert
88 * Support piece tables even if !has_mmap.
90 * Revision 5.9 1991/09/24 00:28:42 eggert
91 * Don't export errsay().
93 * Revision 5.8 1991/08/19 03:13:55 eggert
94 * Add eoflex(), mmap support. Tune.
96 * Revision 5.7 1991/04/21 11:58:26 eggert
99 * Revision 5.6 1991/02/25 07:12:42 eggert
100 * Work around fputs bug. strsave -> str_save (DG/UX name clash)
102 * Revision 5.5 1990/12/04 05:18:47 eggert
103 * Use -I for prompts and -q for diagnostics.
105 * Revision 5.4 1990/11/19 20:05:28 hammer
106 * no longer gives warning about unknown keywords if -q is specified
108 * Revision 5.3 1990/11/01 05:03:48 eggert
109 * When ignoring unknown phrases, copy them to the output RCS file.
111 * Revision 5.2 1990/09/04 08:02:27 eggert
112 * Count RCS lines better.
114 * Revision 5.1 1990/08/29 07:14:03 eggert
115 * Work around buggy compilers with defective argument promotion.
117 * Revision 5.0 1990/08/22 08:12:55 eggert
118 * Remove compile-time limits; use malloc instead.
119 * Report errno-related errors with perror().
120 * Ansify and Posixate. Add support for ISO 8859.
121 * Use better hash function.
123 * Revision 4.6 89/05/01 15:13:07 narten
124 * changed copyright header to reflect current distribution rules
126 * Revision 4.5 88/08/28 15:01:12 eggert
127 * Don't loop when writing error messages to a full filesystem.
128 * Flush stderr/stdout when mixing output.
129 * Yield exit status compatible with diff(1).
130 * Shrink stdio code size; allow cc -R; remove lint.
132 * Revision 4.4 87/12/18 11:44:47 narten
133 * fixed to use "varargs" in "fprintf"; this is required if it is to
134 * work on a SPARC machine such as a Sun-4
136 * Revision 4.3 87/10/18 10:37:18 narten
137 * Updating version numbers. Changes relative to 1.1 actually relative
140 * Revision 1.3 87/09/24 14:00:17 narten
141 * Sources now pass through lint (if you ignore printf/sprintf/fprintf
144 * Revision 1.2 87/03/27 14:22:33 jenkins
147 * Revision 4.1 83/03/25 18:12:51 wft
148 * Only changed $Header to $Id.
150 * Revision 3.3 82/12/10 16:22:37 wft
151 * Improved error messages, changed exit status on error to 1.
153 * Revision 3.2 82/11/28 21:27:10 wft
154 * Renamed ctab to map and included EOFILE; ctab is now a macro in rcsbase.h.
155 * Added fflsbuf(), fputs(), and fprintf(), which abort the RCS operations
156 * properly in case there is an IO-error (e.g., file system full).
158 * Revision 3.1 82/10/11 19:43:56 wft
159 * removed unused label out:;
160 * made sure all calls to getc() return into an integer, not a char.
167 /* version LEXDB is for testing the lexical analyzer. The testprogram
168 * reads a stream of lexemes, enters the revision numbers into the
169 * hashtable, and prints the recognized tokens. Keywords are recognized
177 libId(lexId
, "Id: rcslex.c,v 5.19 1995/06/16 06:19:24 eggert Exp")
179 static char *checkidentifier
P((char*,int,int));
180 static void errsay
P((char const*));
181 static void fatsay
P((char const*));
182 static void lookup
P((char const*));
183 static void startsay
P((const char*,const char*));
184 static void warnsay
P((char const*));
186 static struct hshentry
*nexthsh
; /*pointer to next hash entry, set by lookup*/
188 enum tokens nexttok
; /*next token, set by nextlex */
190 int hshenter
; /*if true, next suitable lexeme will be entered */
191 /*into the symbol table. Handle with care. */
192 int nextc
; /*next input character, initialized by Lexinit */
194 long rcsline
; /*current line-number of input */
195 int nerror
; /*counter for errors */
196 int quietflag
; /*indicates quiet mode */
197 RILE
* finptr
; /*input file descriptor */
199 FILE * frewrite
; /*file descriptor for echoing input */
201 FILE * foutptr
; /* copy of frewrite, but 0 to suppress echo */
203 static struct buf tokbuf
; /* token buffer */
205 char const * NextString
; /* next token */
208 * Our hash algorithm is h[0] = 0, h[i+1] = 4*h[i] + c,
209 * so hshsize should be odd.
210 * See B J McKenzie, R Harries & T Bell, Selecting a hashing algorithm,
211 * Software--practice & experience 20, 2 (Feb 1990), 209-224.
217 static struct hshentry
*hshtab
[hshsize
]; /*hashtable */
219 static int ignored_phrases
; /* have we ignored phrases in this RCS file? */
224 if (!ignored_phrases
) {
225 ignored_phrases
= true;
226 rcswarn("Unknown phrases like `%s ...;' are present.", NextString
);
235 /* Function: Looks up the character string pointed to by str in the
236 * hashtable. If the string is not present, a new entry for it is created.
237 * In any case, the address of the corresponding hashtable entry is placed
241 register unsigned ihash
; /* index into hashtable */
242 register char const *sp
;
243 register struct hshentry
*n
, **p
;
245 /* calculate hash code */
249 ihash
= (ihash
<<2) + *sp
++;
252 for (p
= &hshtab
[ihash
]; ; p
= &n
->nexthsh
)
254 /* empty slot found */
255 *p
= n
= ftalloc(struct hshentry
);
256 n
->num
= fstr_save(str
);
259 VOID
printf("\nEntered: %s at %u ", str
, ihash
);
262 } else if (strcmp(str
, n
->num
) == 0)
276 /* Function: Initialization of lexical analyzer:
277 * initializes the hashtable,
278 * initializes nextc, nexttok if finptr != 0
282 for (c
= hshsize
; 0 <= --c
; ) {
290 ignored_phrases
= false;
292 bufrealloc(&tokbuf
, 2);
294 nextlex(); /*initial token*/
307 /* Function: Reads the next token and sets nexttok to the next token code.
308 * Only if hshenter is set, a revision number is entered into the
309 * hashtable and a pointer to it is placed into nexthsh.
310 * This is useful for avoiding that dates are placed into the hashtable.
311 * For ID's and NUM's, NextString is set to the character string.
312 * Assumption: nextc contains the next character.
319 register enum tokens d
;
322 fin
=finptr
; frew
=foutptr
;
323 setupcache(fin
); cache(fin
);
326 for (;;) { switch ((d
= ctab
[c
])) {
329 fatserror("unknown character `%c'", c
);
337 /* Note: falls into next case */
351 limit
= sp
+ tokbuf
.size
;
365 sp
= bufenlarge(&tokbuf
, &limit
);
374 if (d
== DIGIT
|| d
== PERIOD
) {
377 lookup(tokbuf
.string
);
381 NextString
= fstr_save(tokbuf
.string
);
384 case SBEGIN
: /* long string */
386 /* note: only the initial SBEGIN has been read*/
387 /* read the string, and reset nextc afterwards*/
403 * Yield true if we look ahead to the end of the input, false otherwise.
404 * nextc becomes undefined at end of file.
415 setupcache(fin
); cache(fin
);
428 cachegeteof_(c
, {uncache(fin
);return true;})
439 /* Function: Checks if nexttok is the same as token. If so,
440 * advances the input by calling nextlex and returns true.
441 * otherwise returns false.
442 * Doesn't work for strings and keywords; loses the character string for ids.
445 if (nexttok
==token
) {
448 } else return(false);
454 /* Function: If the current token is a keyword identical to key,
455 * advances the input by calling nextlex and returns true;
456 * otherwise returns false.
459 if (nexttok
==ID
&& strcmp(key
,NextString
) == 0) {
471 /* Check that the current input token is a keyword identical to key,
472 * and advance the input by calling nextlex.
476 fatserror("missing '%s' keyword", key
);
482 /* Check that the current input token is a keyword identical to key,
483 * and advance the input by calling nextlex; then look ahead for a string.
487 if (nexttok
!= STRING
)
488 fatserror("missing string after '%s' keyword", key
);
494 /* Function: Checks if nexttok is an identifier. If so,
495 * advances the input by calling nextlex and returns a pointer
496 * to the identifier; otherwise returns 0.
497 * Treats keywords as identifiers.
500 register char const *name
;
510 struct hshentry
* getnum()
511 /* Function: Checks if nexttok is a number. If so,
512 * advances the input by calling nextlex and returns a pointer
513 * to the hashtable entry. Otherwise returns 0.
514 * Doesn't work if hshenter is false.
517 register struct hshentry
* num
;
530 * Get a series of phrases that do not start with KEY. Yield resulting buffer.
531 * Stop when the next phrase starts with a token that is not an identifier,
532 * or is KEY. Copy input to foutptr if it is set. Unlike ignorephrases(),
533 * this routine assumes nextlex() has already been invoked before we start.
538 register char const *kn
;
543 # define savech_(c) ;
548 # define savech_(c) {if (limit<=p)p=bufenlarge(&b,&limit); *p++ =(c);}
551 if (nexttok
!=ID
|| strcmp(NextString
,key
) == 0)
557 setupcache(fin
); cache(fin
);
559 r
.string
= (char const*)cacheptr() - strlen(NextString
) - 1;
562 bufscpy(&b
, NextString
);
563 p
= b
.string
+ strlen(b
.string
);
564 limit
= b
.string
+ b
.size
;
573 fatserror("unknown character `%c'", c
);
578 case COLON
: case DIGIT
: case LETTER
: case Letter
:
579 case PERIOD
: case SPACE
:
582 case SBEGIN
: /* long string */
607 if (ctab
[c
] == NEWLN
) {
615 r
.size
= (char const*)cacheptr() - 1 - r
.string
;
636 if (ctab
[c
] == Letter
) {
637 for (kn
= key
; c
&& *kn
==c
; kn
++)
641 case DIGIT
: case LETTER
: case Letter
:
642 case IDCHAR
: case PERIOD
:
646 NextString
= fstr_save(key
);
653 register char const *ki
;
654 for (ki
=key
; ki
<kn
; )
667 return bufremember(&b
, (size_t)(p
- b
.string
));
676 /* skip over characters until terminating single SDELIM */
677 /* If foutptr is set, copy every character read to foutptr. */
678 /* Does not advance nextlex at the end. */
683 fin
=finptr
; frew
=foutptr
;
684 setupcache(fin
); cache(fin
);
708 /* Function: copy a string to stdout, until terminated with a single SDELIM.
709 * Does not advance nextlex at the end.
718 setupcache(fin
); cache(fin
);
743 /* Copies a string terminated with SDELIM from file finptr to buffer target.
744 * Double SDELIM is replaced with SDELIM.
745 * If foutptr is set, the string is also copied unchanged to foutptr.
746 * Does not advance nextlex at the end.
747 * Yield a copy of *TARGET, except with exact length.
758 fin
=finptr
; frew
=foutptr
;
759 setupcache(fin
); cache(fin
);
760 tp
= target
->string
; limit
= tp
+ target
->size
;
772 r
.string
= target
->string
;
773 r
.size
= tp
- r
.string
;
780 tp
= bufenlarge(target
, &limit
);
787 checkidentifier(id
, delimiter
, dotok
)
791 /* Function: check whether the string starting at id is an */
792 /* identifier and return a pointer to the delimiter*/
793 /* after the identifier. White space, delim and 0 */
794 /* are legal delimiters. Aborts the program if not*/
795 /* a legal identifier. Useful for checking commands*/
796 /* If !delim, the only delimiter is 0. */
797 /* Allow '.' in identifier only if DOTOK is set. */
801 register char delim
= delimiter
;
806 switch (ctab
[(unsigned char)(c
= *id
)]) {
827 || (c
&& (!delim
|| (c
!=delim
&& c
!=' ' && c
!='\t' && c
!='\n')))
829 /* append \0 to end of id before error message */
830 while ((c
= *id
) && c
!=' ' && c
!='\t' && c
!='\n' && c
!=delim
)
833 faterror("invalid %s `%s'",
834 dotok
? "identifier" : "symbol", temp
841 checkid(id
, delimiter
)
845 return checkidentifier(id
, delimiter
, true);
849 checksym(sym
, delimiter
)
853 return checkidentifier(sym
, delimiter
, false);
859 /* Check whether the string ID is an identifier. */
868 VOID
checksym(sym
, 0);
873 # define Iclose(f) fclose(f)
876 static int Iclose
P((RILE
*));
883 return fclose(f
->stream
);
886 static int Iclose
P((RILE
*));
891 (* f
->deallocate
) (f
);
897 static void map_fd_deallocate
P((RILE
*));
904 (vm_address_t
) f
->base
,
905 (vm_size_t
) (f
->lim
- f
->base
)
907 efaterror("vm_deallocate");
911 static void mmap_deallocate
P((RILE
*));
916 if (munmap((char *) f
->base
, (size_t) (f
->lim
- f
->base
)) != 0)
920 static void read_deallocate
P((RILE
*));
928 static void nothing_to_deallocate
P((RILE
*));
930 nothing_to_deallocate(f
)
938 #if large_memory && maps_memory
939 static RILE
*fd2_RILE
P((int,char const*,struct stat
*));
941 fd2_RILE(fd
, name
, status
)
943 static RILE
*fd2RILE
P((int,char const*,char const*,struct stat
*));
945 fd2RILE(fd
, name
, type
, status
)
950 register struct stat
*status
;
956 if (fstat(fd
, status
) != 0)
958 if (!S_ISREG(status
->st_mode
)) {
959 error("`%s' is not a regular file", name
);
965 # if !(large_memory && maps_memory)
967 if (!(stream
= fdopen(fd
, type
)))
976 static RILE rilebuf
[RILES
];
979 size_t s
= status
->st_size
;
981 if (s
!= status
->st_size
)
982 faterror("%s: too large", name
);
983 for (f
= rilebuf
; f
->base
; f
++)
984 if (f
== rilebuf
+RILES
)
985 faterror("too many RILEs");
987 f
->deallocate
= nothing_to_deallocate
;
990 static unsigned char nothing
;
991 f
->base
= ¬hing
; /* Any nonzero address will do. */
996 fd
, (vm_offset_t
)0, (vm_address_t
*) &f
->base
,
999 f
->deallocate
= map_fd_deallocate
;
1004 f
->base
= (unsigned char *) mmap(
1005 (char *)0, s
, PROT_READ
, MAP_FILE
|MAP_SHARED
,
1009 # define MAP_FAILED (-1)
1011 if (f
->base
== (unsigned char *) MAP_FAILED
)
1014 # if has_NFS && mmap_signal
1016 * On many hosts, the superuser
1017 * can mmap an NFS file it can't read.
1018 * So access the first page now, and print
1019 * a nice message if a bus error occurs.
1021 readAccessFilenameBuffer(name
, f
->base
);
1024 f
->deallocate
= mmap_deallocate
;
1028 f
->base
= tnalloc(unsigned char, s
);
1032 * We can't map the file into memory for some reason.
1033 * Read it into main memory all at once; this is
1034 * the simplest substitute for memory mapping.
1036 char *bufptr
= (char *) f
->base
;
1039 ssize_t r
= read(fd
, bufptr
, bufsiz
);
1045 /* The file must have shrunk! */
1046 status
->st_size
= s
-= bufsiz
;
1056 if (lseek(fd
, (off_t
)0, SEEK_SET
) == -1)
1058 f
->deallocate
= read_deallocate
;
1064 f
->lim
= f
->base
+ s
;
1067 f
->readlim
= f
->base
;
1070 if_advise_access(s
, f
, MADV_SEQUENTIAL
);
1077 #if !maps_memory && large_memory
1082 register fread_type r
;
1083 register size_t s
= f
->lim
- f
->readlim
;
1087 if (!(r
= Fread(f
->readlim
, sizeof(*f
->readlim
), s
, f
->stream
))) {
1088 testIerror(f
->stream
);
1089 f
->lim
= f
->readlim
; /* The file might have shrunk! */
1097 #if has_madvise && has_mmap && large_memory
1099 advise_access(f
, advice
)
1103 if (f
->deallocate
== mmap_deallocate
)
1104 VOID
madvise((char *)f
->base
, (size_t)(f
->lim
- f
->base
), advice
);
1105 /* Don't worry if madvise fails; it's only advisory. */
1110 #if large_memory && maps_memory
1111 I_open(name
, status
)
1113 Iopen(name
, type
, status
)
1117 struct stat
*status
;
1118 /* Open NAME for reading, yield its descriptor, and set *STATUS. */
1120 int fd
= fdSafer(open(name
, O_RDONLY
1122 | (strchr(type
,'b') ? OPEN_O_BINARY
: 0)
1128 # if large_memory && maps_memory
1129 return fd2_RILE(fd
, name
, status
);
1131 return fd2RILE(fd
, name
, type
, status
);
1136 static int Oerrloop
;
1144 efaterror("output error");
1147 void Ieof() { fatserror("unexpected end of file"); }
1148 void Ierror() { efaterror("input error"); }
1149 void testIerror(f
) FILE *f
; { if (ferror(f
)) Ierror(); }
1150 void testOerror(o
) FILE *o
; { if (ferror(o
)) Oerror(); }
1152 void Ifclose(f
) RILE
*f
; { if (f
&& Iclose(f
)!=0) Ierror(); }
1153 void Ofclose(f
) FILE *f
; { if (f
&& fclose(f
)!=0) Oerror(); }
1154 void Izclose(p
) RILE
**p
; { Ifclose(*p
); *p
= 0; }
1155 void Ozclose(p
) FILE **p
; { Ofclose(*p
); *p
= 0; }
1166 void Irewind(f
) FILE *f
; { if (fseek(f
,0L,SEEK_SET
) != 0) Ierror(); }
1169 void Orewind(f
) FILE *f
; { if (fseek(f
,0L,SEEK_SET
) != 0) Oerror(); }
1171 void aflush(f
) FILE *f
; { if (fflush(f
) != 0) Oerror(); }
1172 void eflush() { if (fflush(stderr
)!=0 && !Oerrloop
) Oerror(); }
1175 if (fflush(workstdout
? workstdout
: stdout
) != 0 && !Oerrloop
)
1180 fatcleanup(already_newline
)
1181 int already_newline
;
1183 VOID
fprintf(stderr
, already_newline
+"\n%s aborted\n", cmdid
);
1193 aprintf(stderr
, "%s: %s: %s", cmdid
, s
, t
);
1195 aprintf(stderr
, "%s: %s", cmdid
, t
);
1217 startsay(s
, "warning: ");
1220 void eerror(s
) char const *s
; { enerror(errno
,s
); }
1227 errsay((char const*)0);
1233 void efaterror(s
) char const *s
; { enfaterror(errno
,s
); }
1240 fatsay((char const*)0);
1248 error(char const *format
,...)
1250 /*VARARGS1*/ void error(format
, va_alist
) char const *format
; va_dcl
1252 /* non-fatal error */
1255 errsay((char const*)0);
1256 vararg_start(args
, format
);
1257 fvfprintf(stderr
, format
, args
);
1259 afputc('\n',stderr
);
1265 rcserror(char const *format
,...)
1267 /*VARARGS1*/ void rcserror(format
, va_alist
) char const *format
; va_dcl
1269 /* non-fatal RCS file error */
1273 vararg_start(args
, format
);
1274 fvfprintf(stderr
, format
, args
);
1276 afputc('\n',stderr
);
1282 workerror(char const *format
,...)
1284 /*VARARGS1*/ void workerror(format
, va_alist
) char const *format
; va_dcl
1286 /* non-fatal working file error */
1290 vararg_start(args
, format
);
1291 fvfprintf(stderr
, format
, args
);
1293 afputc('\n',stderr
);
1299 fatserror(char const *format
,...)
1302 fatserror(format
, va_alist
) char const *format
; va_dcl
1304 /* fatal RCS file syntax error */
1308 VOID
fprintf(stderr
, "%s: %s:%ld: ", cmdid
, RCSname
, rcsline
);
1309 vararg_start(args
, format
);
1310 fvfprintf(stderr
, format
, args
);
1317 faterror(char const *format
,...)
1319 /*VARARGS1*/ void faterror(format
, va_alist
)
1320 char const *format
; va_dcl
1322 /* fatal error, terminates program after cleanup */
1325 fatsay((char const*)0);
1326 vararg_start(args
, format
);
1327 fvfprintf(stderr
, format
, args
);
1334 rcsfaterror(char const *format
,...)
1336 /*VARARGS1*/ void rcsfaterror(format
, va_alist
)
1337 char const *format
; va_dcl
1339 /* fatal RCS file error, terminates program after cleanup */
1343 vararg_start(args
, format
);
1344 fvfprintf(stderr
, format
, args
);
1351 warn(char const *format
,...)
1353 /*VARARGS1*/ void warn(format
, va_alist
) char const *format
; va_dcl
1360 vararg_start(args
, format
);
1361 fvfprintf(stderr
, format
, args
);
1363 afputc('\n', stderr
);
1370 rcswarn(char const *format
,...)
1372 /*VARARGS1*/ void rcswarn(format
, va_alist
) char const *format
; va_dcl
1374 /* RCS file warning */
1379 vararg_start(args
, format
);
1380 fvfprintf(stderr
, format
, args
);
1382 afputc('\n', stderr
);
1389 workwarn(char const *format
,...)
1391 /*VARARGS1*/ void workwarn(format
, va_alist
) char const *format
; va_dcl
1393 /* working file warning */
1398 vararg_start(args
, format
);
1399 fvfprintf(stderr
, format
, args
);
1401 afputc('\n', stderr
);
1410 warn("redefinition of -%c option", c
);
1415 diagnose(char const *format
,...)
1417 /*VARARGS1*/ void diagnose(format
, va_alist
) char const *format
; va_dcl
1419 /* prints a diagnostic message */
1420 /* Unlike the other routines, it does not append a newline. */
1421 /* This lets some callers suppress the newline, and is faster */
1422 /* in implementations that flush stderr just at the end of each printf. */
1427 vararg_start(args
, format
);
1428 fvfprintf(stderr
, format
, args
);
1438 /* afputc(c,f); acts like aputc_(c,f) but is smaller and slower. */
1450 /* Function: Put string s on file iop, abort on error.
1454 if (fputs(s
, iop
) < 0)
1457 awrite(s
, strlen(s
), iop
);
1465 fvfprintf(FILE *stream
, char const *format
, va_list args
)
1467 fvfprintf(stream
,format
,args
) FILE *stream
; char *format
; va_list args
;
1469 /* like vfprintf, except abort program on error */
1472 if (vfprintf(stream
, format
, args
) < 0)
1476 _doprintf(stream
, format
, args
);
1479 _doprnt(format
, args
, stream
);
1481 int *a
= (int *)args
;
1482 VOID
fprintf(stream
, format
,
1483 a
[0], a
[1], a
[2], a
[3], a
[4],
1484 a
[5], a
[6], a
[7], a
[8], a
[9]
1495 aprintf(FILE *iop
, char const *fmt
, ...)
1498 aprintf(iop
, fmt
, va_alist
)
1503 /* Function: formatted output. Same as fprintf in stdio,
1504 * but aborts program on error
1508 vararg_start(ap
, fmt
);
1509 fvfprintf(iop
, fmt
, ap
);
1516 /* test program reading a stream of lexemes and printing the tokens.
1523 int argc
; char * argv
[];
1527 aputs("No input file\n",stderr
);
1528 exitmain(EXIT_FAILURE
);
1530 if (!(finptr
=Iopen(argv
[1], FOPEN_R
, (struct stat
*)0))) {
1531 faterror("can't open input file %s",argv
[1]);
1538 VOID
printf("ID: %s",NextString
);
1543 VOID
printf("NUM: %s, index: %d",nexthsh
->num
, nexthsh
-hshtab
);
1545 VOID
printf("NUM, unentered: %s",NextString
);
1546 hshenter
= !hshenter
; /*alternate between dates and numbers*/
1550 VOID
printf("COLON"); break;
1553 VOID
printf("SEMI"); break;
1557 VOID
printf("STRING"); break;
1560 VOID
printf("UNKN"); break;
1563 VOID
printf("DEFAULT"); break;
1568 exitmain(EXIT_SUCCESS
);
1571 void exiterr() { _exit(EXIT_FAILURE
); }