xref: /freebsd/usr.bin/indent/lexi.c (revision 9522d0b0d2c9418330de71422a6b4688cb000de6)
1 /*-
2  * SPDX-License-Identifier: BSD-4-Clause
3  *
4  * Copyright (c) 1985 Sun Microsystems, Inc.
5  * Copyright (c) 1980, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by the University of
20  *	California, Berkeley and its contributors.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 
38 #if 0
39 #ifndef lint
40 static char sccsid[] = "@(#)lexi.c	8.1 (Berkeley) 6/6/93";
41 #endif /* not lint */
42 #endif
43 #include <sys/cdefs.h>
44 __FBSDID("$FreeBSD$");
45 
46 /*
47  * Here we have the token scanner for indent.  It scans off one token and puts
48  * it in the global variable "token".  It returns a code, indicating the type
49  * of token scanned.
50  */
51 
52 #include <err.h>
53 #include <stdio.h>
54 #include <ctype.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include "indent_globs.h"
58 #include "indent_codes.h"
59 #include "indent.h"
60 
61 #define alphanum 1
62 #ifdef undef
63 #define opchar 3
64 #endif
65 
66 struct templ {
67     const char *rwd;
68     int         rwcode;
69 };
70 
71 /*
72  * This table has to be sorted alphabetically, because it'll be used in binary
73  * search. For the same reason, string must be the first thing in struct templ.
74  */
75 struct templ specials[] =
76 {
77     {"auto", 10},
78     {"break", 9},
79     {"case", 8},
80     {"char", 4},
81     {"const", 4},
82     {"default", 8},
83     {"do", 6},
84     {"double", 4},
85     {"else", 6},
86     {"enum", 3},
87     {"extern", 10},
88     {"float", 4},
89     {"for", 5},
90     {"global", 4},
91     {"goto", 9},
92     {"if", 5},
93     {"int", 4},
94     {"long", 4},
95     {"offsetof", 1},
96     {"register", 10},
97     {"return", 9},
98     {"short", 4},
99     {"sizeof", 2},
100     {"static", 10},
101     {"struct", 3},
102     {"switch", 7},
103     {"typedef", 11},
104     {"union", 3},
105     {"unsigned", 4},
106     {"void", 4},
107     {"volatile", 4},
108     {"while", 5}
109 };
110 
111 const char **typenames;
112 int         typename_count;
113 int         typename_top = -1;
114 
115 char        chartype[128] =
116 {				/* this is used to facilitate the decision of
117 				 * what type (alphanumeric, operator) each
118 				 * character is */
119     0, 0, 0, 0, 0, 0, 0, 0,
120     0, 0, 0, 0, 0, 0, 0, 0,
121     0, 0, 0, 0, 0, 0, 0, 0,
122     0, 0, 0, 0, 0, 0, 0, 0,
123     0, 3, 0, 0, 1, 3, 3, 0,
124     0, 0, 3, 3, 0, 3, 0, 3,
125     1, 1, 1, 1, 1, 1, 1, 1,
126     1, 1, 0, 0, 3, 3, 3, 3,
127     0, 1, 1, 1, 1, 1, 1, 1,
128     1, 1, 1, 1, 1, 1, 1, 1,
129     1, 1, 1, 1, 1, 1, 1, 1,
130     1, 1, 1, 0, 0, 0, 3, 1,
131     0, 1, 1, 1, 1, 1, 1, 1,
132     1, 1, 1, 1, 1, 1, 1, 1,
133     1, 1, 1, 1, 1, 1, 1, 1,
134     1, 1, 1, 0, 3, 0, 3, 0
135 };
136 
137 static int
138 strcmp_type(const void *e1, const void *e2)
139 {
140     return (strcmp(e1, *(const char * const *)e2));
141 }
142 
143 int
144 lexi(struct parser_state *state)
145 {
146     int         unary_delim;	/* this is set to 1 if the current token
147 				 * forces a following operator to be unary */
148     int         code;		/* internal code to be returned */
149     char        qchar;		/* the delimiter character for a string */
150 
151     e_token = s_token;		/* point to start of place to save token */
152     unary_delim = false;
153     state->col_1 = state->last_nl;	/* tell world that this token started
154 					 * in column 1 iff the last thing
155 					 * scanned was a newline */
156     state->last_nl = false;
157 
158     while (*buf_ptr == ' ' || *buf_ptr == '\t') {	/* get rid of blanks */
159 	state->col_1 = false;	/* leading blanks imply token is not in column
160 				 * 1 */
161 	if (++buf_ptr >= buf_end)
162 	    fill_buffer();
163     }
164 
165     /* Scan an alphanumeric token */
166     if (chartype[(int)*buf_ptr] == alphanum || (buf_ptr[0] == '.' && isdigit(buf_ptr[1]))) {
167 	/*
168 	 * we have a character or number
169 	 */
170 	struct templ *p;
171 
172 	if (isdigit(*buf_ptr) || (buf_ptr[0] == '.' && isdigit(buf_ptr[1]))) {
173 	    enum base {
174 		BASE_2, BASE_8, BASE_10, BASE_16
175 	    };
176 	    int         seendot = 0,
177 	                seenexp = 0,
178 			seensfx = 0;
179 	    enum base	in_base = BASE_10;
180 
181 	    if (*buf_ptr == '0') {
182 		if (buf_ptr[1] == 'b' || buf_ptr[1] == 'B')
183 		    in_base = BASE_2;
184 		else if (buf_ptr[1] == 'x' || buf_ptr[1] == 'X')
185 		    in_base = BASE_16;
186 		else if (isdigit(buf_ptr[1]))
187 		    in_base = BASE_8;
188 	    }
189 	    switch (in_base) {
190 	    case BASE_2:
191 		*e_token++ = *buf_ptr++;
192 		*e_token++ = *buf_ptr++;
193 		while (*buf_ptr == '0' || *buf_ptr == '1') {
194 		    CHECK_SIZE_TOKEN;
195 		    *e_token++ = *buf_ptr++;
196 		}
197 		break;
198 	    case BASE_8:
199 		*e_token++ = *buf_ptr++;
200 		while (*buf_ptr >= '0' && *buf_ptr <= '8') {
201 		    CHECK_SIZE_TOKEN;
202 		    *e_token++ = *buf_ptr++;
203 		}
204 		break;
205 	    case BASE_16:
206 		*e_token++ = *buf_ptr++;
207 		*e_token++ = *buf_ptr++;
208 		while (isxdigit(*buf_ptr)) {
209 		    CHECK_SIZE_TOKEN;
210 		    *e_token++ = *buf_ptr++;
211 		}
212 		break;
213 	    case BASE_10:
214 		while (1) {
215 		    if (*buf_ptr == '.') {
216 			if (seendot)
217 			    break;
218 			else
219 			    seendot++;
220 		    }
221 		    CHECK_SIZE_TOKEN;
222 		    *e_token++ = *buf_ptr++;
223 		    if (!isdigit(*buf_ptr) && *buf_ptr != '.') {
224 			if ((*buf_ptr != 'E' && *buf_ptr != 'e') || seenexp)
225 			    break;
226 			else {
227 			    seenexp++;
228 			    seendot++;
229 			    CHECK_SIZE_TOKEN;
230 			    *e_token++ = *buf_ptr++;
231 			    if (*buf_ptr == '+' || *buf_ptr == '-')
232 				*e_token++ = *buf_ptr++;
233 			}
234 		    }
235 		}
236 		break;
237 	    }
238 	    while (1) {
239 		if (!(seensfx & 1) && (*buf_ptr == 'U' || *buf_ptr == 'u')) {
240 		    CHECK_SIZE_TOKEN;
241 		    *e_token++ = *buf_ptr++;
242 		    seensfx |= 1;
243 		    continue;
244 		}
245 		if (!(seensfx & 2) && (strchr("fFlL", *buf_ptr) != NULL)) {
246 		    CHECK_SIZE_TOKEN;
247 		    if (buf_ptr[1] == buf_ptr[0])
248 		        *e_token++ = *buf_ptr++;
249 		    *e_token++ = *buf_ptr++;
250 		    seensfx |= 2;
251 		    continue;
252 		}
253 		break;
254 	    }
255 	}
256 	else
257 	    while (chartype[(int)*buf_ptr] == alphanum || *buf_ptr == BACKSLASH) {
258 		/* fill_buffer() terminates buffer with newline */
259 		if (*buf_ptr == BACKSLASH) {
260 		    if (*(buf_ptr + 1) == '\n') {
261 			buf_ptr += 2;
262 			if (buf_ptr >= buf_end)
263 			    fill_buffer();
264 			} else
265 			    break;
266 		}
267 		CHECK_SIZE_TOKEN;
268 		/* copy it over */
269 		*e_token++ = *buf_ptr++;
270 		if (buf_ptr >= buf_end)
271 		    fill_buffer();
272 	    }
273 	*e_token++ = '\0';
274 
275 	if (s_token[0] == 'L' && s_token[1] == '\0' &&
276 	      (*buf_ptr == '"' || *buf_ptr == '\''))
277 	    return (strpfx);
278 
279 	while (*buf_ptr == ' ' || *buf_ptr == '\t') {	/* get rid of blanks */
280 	    if (++buf_ptr >= buf_end)
281 		fill_buffer();
282 	}
283 	state->keyword = 0;
284 	if (state->last_token == structure && !state->p_l_follow) {
285 				/* if last token was 'struct' and we're not
286 				 * in parentheses, then this token
287 				 * should be treated as a declaration */
288 	    state->last_u_d = true;
289 	    return (decl);
290 	}
291 	/*
292 	 * Operator after identifier is binary unless last token was 'struct'
293 	 */
294 	state->last_u_d = (state->last_token == structure);
295 
296 	p = bsearch(s_token,
297 	    specials,
298 	    sizeof(specials) / sizeof(specials[0]),
299 	    sizeof(specials[0]),
300 	    strcmp_type);
301 	if (p == NULL) {	/* not a special keyword... */
302 	    char *u;
303 
304 	    /* ... so maybe a type_t or a typedef */
305 	    if ((auto_typedefs && ((u = strrchr(s_token, '_')) != NULL) &&
306 	        strcmp(u, "_t") == 0) || (typename_top >= 0 &&
307 		  bsearch(s_token, typenames, typename_top + 1,
308 		    sizeof(typenames[0]), strcmp_type))) {
309 		state->keyword = 4;	/* a type name */
310 		state->last_u_d = true;
311 	        goto found_typename;
312 	    }
313 	} else {			/* we have a keyword */
314 	    state->keyword = p->rwcode;
315 	    state->last_u_d = true;
316 	    switch (p->rwcode) {
317 	    case 7:		/* it is a switch */
318 		return (swstmt);
319 	    case 8:		/* a case or default */
320 		return (casestmt);
321 
322 	    case 3:		/* a "struct" */
323 		/* FALLTHROUGH */
324 	    case 4:		/* one of the declaration keywords */
325 	    found_typename:
326 		if (state->p_l_follow) {
327 		    /* inside parens: cast, param list, offsetof or sizeof */
328 		    state->cast_mask |= (1 << state->p_l_follow) & ~state->not_cast_mask;
329 		}
330 		if (p != NULL && p->rwcode == 3)
331 		    return (structure);
332 		if (state->p_l_follow)
333 		    break;
334 		return (decl);
335 
336 	    case 5:		/* if, while, for */
337 		return (sp_paren);
338 
339 	    case 6:		/* do, else */
340 		return (sp_nparen);
341 
342 	    case 10:		/* storage class specifier */
343 		return (storage);
344 
345 	    case 11:		/* typedef */
346 		return (type_def);
347 
348 	    default:		/* all others are treated like any other
349 				 * identifier */
350 		return (ident);
351 	    }			/* end of switch */
352 	}			/* end of if (found_it) */
353 	if (*buf_ptr == '(' && state->tos <= 1 && state->ind_level == 0 &&
354 	    state->in_parameter_declaration == 0 && state->block_init == 0) {
355 	    char *tp = buf_ptr;
356 	    while (tp < buf_end)
357 		if (*tp++ == ')' && (*tp == ';' || *tp == ','))
358 		    goto not_proc;
359 	    strncpy(state->procname, token, sizeof state->procname - 1);
360 	    if (state->in_decl)
361 		state->in_parameter_declaration = 1;
362 	    return (funcname);
363     not_proc:;
364 	}
365 	/*
366 	 * The following hack attempts to guess whether or not the current
367 	 * token is in fact a declaration keyword -- one that has been
368 	 * typedefd
369 	 */
370 	else if (!state->p_l_follow && !state->block_init &&
371 	    !state->in_stmt &&
372 	    ((*buf_ptr == '*' && buf_ptr[1] != '=') ||
373 		isalpha((unsigned char)*buf_ptr)) &&
374 	    (state->last_token == semicolon || state->last_token == lbrace ||
375 		state->last_token == rbrace)) {
376 	    state->keyword = 4;	/* a type name */
377 	    state->last_u_d = true;
378 	    return decl;
379 	}
380 	if (state->last_token == decl)	/* if this is a declared variable,
381 					 * then following sign is unary */
382 	    state->last_u_d = true;	/* will make "int a -1" work */
383 	return (ident);		/* the ident is not in the list */
384     }				/* end of procesing for alpanum character */
385 
386     /* Scan a non-alphanumeric token */
387 
388     *e_token++ = *buf_ptr;		/* if it is only a one-character token, it is
389 				 * moved here */
390     *e_token = '\0';
391     if (++buf_ptr >= buf_end)
392 	fill_buffer();
393 
394     switch (*token) {
395     case '\n':
396 	unary_delim = state->last_u_d;
397 	state->last_nl = true;	/* remember that we just had a newline */
398 	code = (had_eof ? 0 : newline);
399 
400 	/*
401 	 * if data has been exhausted, the newline is a dummy, and we should
402 	 * return code to stop
403 	 */
404 	break;
405 
406     case '\'':			/* start of quoted character */
407     case '"':			/* start of string */
408 	qchar = *token;
409 	if (troff) {
410 	    e_token[-1] = '`';
411 	    if (qchar == '"')
412 		*e_token++ = '`';
413 	    e_token = chfont(&bodyf, &stringf, e_token);
414 	}
415 	do {			/* copy the string */
416 	    while (1) {		/* move one character or [/<char>]<char> */
417 		if (*buf_ptr == '\n') {
418 		    diag2(1, "Unterminated literal");
419 		    goto stop_lit;
420 		}
421 		CHECK_SIZE_TOKEN;	/* Only have to do this once in this loop,
422 					 * since CHECK_SIZE guarantees that there
423 					 * are at least 5 entries left */
424 		*e_token = *buf_ptr++;
425 		if (buf_ptr >= buf_end)
426 		    fill_buffer();
427 		if (*e_token == BACKSLASH) {	/* if escape, copy extra char */
428 		    if (*buf_ptr == '\n')	/* check for escaped newline */
429 			++line_no;
430 		    if (troff) {
431 			*++e_token = BACKSLASH;
432 			if (*buf_ptr == BACKSLASH)
433 			    *++e_token = BACKSLASH;
434 		    }
435 		    *++e_token = *buf_ptr++;
436 		    ++e_token;	/* we must increment this again because we
437 				 * copied two chars */
438 		    if (buf_ptr >= buf_end)
439 			fill_buffer();
440 		}
441 		else
442 		    break;	/* we copied one character */
443 	    }			/* end of while (1) */
444 	} while (*e_token++ != qchar);
445 	if (troff) {
446 	    e_token = chfont(&stringf, &bodyf, e_token - 1);
447 	    if (qchar == '"')
448 		*e_token++ = '\'';
449 	}
450 stop_lit:
451 	code = ident;
452 	break;
453 
454     case ('('):
455     case ('['):
456 	unary_delim = true;
457 	code = lparen;
458 	break;
459 
460     case (')'):
461     case (']'):
462 	code = rparen;
463 	break;
464 
465     case '#':
466 	unary_delim = state->last_u_d;
467 	code = preesc;
468 	break;
469 
470     case '?':
471 	unary_delim = true;
472 	code = question;
473 	break;
474 
475     case (':'):
476 	code = colon;
477 	unary_delim = true;
478 	break;
479 
480     case (';'):
481 	unary_delim = true;
482 	code = semicolon;
483 	break;
484 
485     case ('{'):
486 	unary_delim = true;
487 
488 	/*
489 	 * if (state->in_or_st) state->block_init = 1;
490 	 */
491 	/* ?	code = state->block_init ? lparen : lbrace; */
492 	code = lbrace;
493 	break;
494 
495     case ('}'):
496 	unary_delim = true;
497 	/* ?	code = state->block_init ? rparen : rbrace; */
498 	code = rbrace;
499 	break;
500 
501     case 014:			/* a form feed */
502 	unary_delim = state->last_u_d;
503 	state->last_nl = true;	/* remember this so we can set 'state->col_1'
504 				 * right */
505 	code = form_feed;
506 	break;
507 
508     case (','):
509 	unary_delim = true;
510 	code = comma;
511 	break;
512 
513     case '.':
514 	unary_delim = false;
515 	code = period;
516 	break;
517 
518     case '-':
519     case '+':			/* check for -, +, --, ++ */
520 	code = (state->last_u_d ? unary_op : binary_op);
521 	unary_delim = true;
522 
523 	if (*buf_ptr == token[0]) {
524 	    /* check for doubled character */
525 	    *e_token++ = *buf_ptr++;
526 	    /* buffer overflow will be checked at end of loop */
527 	    if (state->last_token == ident || state->last_token == rparen) {
528 		code = (state->last_u_d ? unary_op : postop);
529 		/* check for following ++ or -- */
530 		unary_delim = false;
531 	    }
532 	}
533 	else if (*buf_ptr == '=')
534 	    /* check for operator += */
535 	    *e_token++ = *buf_ptr++;
536 	else if (*buf_ptr == '>') {
537 	    /* check for operator -> */
538 	    *e_token++ = *buf_ptr++;
539 	    unary_delim = false;
540 	    code = unary_op;
541 	    state->want_blank = false;
542 	}
543 	break;			/* buffer overflow will be checked at end of
544 				 * switch */
545 
546     case '=':
547 	if (state->in_or_st)
548 	    state->block_init = 1;
549 #ifdef undef
550 	if (chartype[*buf_ptr] == opchar) {	/* we have two char assignment */
551 	    e_token[-1] = *buf_ptr++;
552 	    if ((e_token[-1] == '<' || e_token[-1] == '>') && e_token[-1] == *buf_ptr)
553 		*e_token++ = *buf_ptr++;
554 	    *e_token++ = '=';	/* Flip =+ to += */
555 	    *e_token = 0;
556 	}
557 #else
558 	if (*buf_ptr == '=') {/* == */
559 	    *e_token++ = '=';	/* Flip =+ to += */
560 	    buf_ptr++;
561 	    *e_token = 0;
562 	}
563 #endif
564 	code = binary_op;
565 	unary_delim = true;
566 	break;
567 	/* can drop thru!!! */
568 
569     case '>':
570     case '<':
571     case '!':			/* ops like <, <<, <=, !=, etc */
572 	if (*buf_ptr == '>' || *buf_ptr == '<' || *buf_ptr == '=') {
573 	    *e_token++ = *buf_ptr;
574 	    if (++buf_ptr >= buf_end)
575 		fill_buffer();
576 	}
577 	if (*buf_ptr == '=')
578 	    *e_token++ = *buf_ptr++;
579 	code = (state->last_u_d ? unary_op : binary_op);
580 	unary_delim = true;
581 	break;
582 
583     case '*':
584 	unary_delim = true;
585 	if (!state->last_u_d) {
586 	    if (*buf_ptr == '=')
587 		*e_token++ = *buf_ptr++;
588 	    code = binary_op;
589 	    break;
590 	}
591 	while (*buf_ptr == '*' || isspace((unsigned char)*buf_ptr)) {
592 	    if (*buf_ptr == '*')
593 		*e_token++ = *buf_ptr;
594 	    if (++buf_ptr >= buf_end)
595 		fill_buffer();
596 	}
597 	if (ps.in_decl) {
598 	    char *tp = buf_ptr;
599 
600 	    while (isalpha((unsigned char)*tp) ||
601 		   isspace((unsigned char)*tp)) {
602 		if (++tp >= buf_end)
603 		    fill_buffer();
604 	    }
605 	    if (*tp == '(')
606 		ps.procname[0] = ' ';
607 	}
608 	code = unary_op;
609 	break;
610 
611     default:
612 	if (token[0] == '/' && *buf_ptr == '*') {
613 	    /* it is start of comment */
614 	    *e_token++ = '*';
615 
616 	    if (++buf_ptr >= buf_end)
617 		fill_buffer();
618 
619 	    code = comment;
620 	    unary_delim = state->last_u_d;
621 	    break;
622 	}
623 	while (*(e_token - 1) == *buf_ptr || *buf_ptr == '=') {
624 	    /*
625 	     * handle ||, &&, etc, and also things as in int *****i
626 	     */
627 	    *e_token++ = *buf_ptr;
628 	    if (++buf_ptr >= buf_end)
629 		fill_buffer();
630 	}
631 	code = (state->last_u_d ? unary_op : binary_op);
632 	unary_delim = true;
633 
634 
635     }				/* end of switch */
636     if (buf_ptr >= buf_end)	/* check for input buffer empty */
637 	fill_buffer();
638     state->last_u_d = unary_delim;
639     *e_token = '\0';		/* null terminate the token */
640     return (code);
641 }
642 
643 void
644 alloc_typenames(void)
645 {
646 
647     typenames = (const char **)malloc(sizeof(typenames[0]) *
648         (typename_count = 16));
649     if (typenames == NULL)
650 	err(1, NULL);
651 }
652 
653 void
654 add_typename(const char *key)
655 {
656     int comparison;
657     const char *copy;
658 
659     if (typename_top + 1 >= typename_count) {
660 	typenames = realloc((void *)typenames,
661 	    sizeof(typenames[0]) * (typename_count *= 2));
662 	if (typenames == NULL)
663 	    err(1, NULL);
664     }
665     if (typename_top == -1)
666 	typenames[++typename_top] = copy = strdup(key);
667     else if ((comparison = strcmp(key, typenames[typename_top])) >= 0) {
668 	/* take advantage of sorted input */
669 	if (comparison == 0)	/* remove duplicates */
670 	    return;
671 	typenames[++typename_top] = copy = strdup(key);
672     }
673     else {
674 	int p;
675 
676 	for (p = 0; (comparison = strcmp(key, typenames[p])) > 0; p++)
677 	    /* find place for the new key */;
678 	if (comparison == 0)	/* remove duplicates */
679 	    return;
680 	memmove(&typenames[p + 1], &typenames[p],
681 	    sizeof(typenames[0]) * (++typename_top - p));
682 	typenames[p] = copy = strdup(key);
683     }
684 
685     if (copy == NULL)
686 	err(1, NULL);
687 }
688