lexi.c revision 1.143 1 /* $NetBSD: lexi.c,v 1.143 2021/11/19 17:30:10 rillig Exp $ */
2
3 /*-
4 * SPDX-License-Identifier: BSD-4-Clause
5 *
6 * Copyright (c) 1985 Sun Microsystems, Inc.
7 * Copyright (c) 1980, 1993
8 * The Regents of the University of California. All rights reserved.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 */
39
40 #if 0
41 static char sccsid[] = "@(#)lexi.c 8.1 (Berkeley) 6/6/93";
42 #endif
43
44 #include <sys/cdefs.h>
45 #if defined(__NetBSD__)
46 __RCSID("$NetBSD: lexi.c,v 1.143 2021/11/19 17:30:10 rillig Exp $");
47 #elif defined(__FreeBSD__)
48 __FBSDID("$FreeBSD: head/usr.bin/indent/lexi.c 337862 2018-08-15 18:19:45Z pstef $");
49 #endif
50
51 #include <assert.h>
52 #include <ctype.h>
53 #include <stdlib.h>
54 #include <string.h>
55
56 #include "indent.h"
57
58 /*
59 * While inside lexi_alnum, this constant just marks a type, independently of
60 * the parentheses level.
61 */
62 #define lsym_type lsym_type_outside_parentheses
63
64 /* must be sorted alphabetically, is used in binary search */
65 static const struct keyword {
66 const char *name;
67 lexer_symbol lsym;
68 } keywords[] = {
69 {"_Bool", lsym_type},
70 {"_Complex", lsym_type},
71 {"_Imaginary", lsym_type},
72 {"auto", lsym_storage_class},
73 {"bool", lsym_type},
74 {"break", lsym_word},
75 {"case", lsym_case_label},
76 {"char", lsym_type},
77 {"complex", lsym_type},
78 {"const", lsym_type},
79 {"continue", lsym_word},
80 {"default", lsym_case_label},
81 {"do", lsym_do},
82 {"double", lsym_type},
83 {"else", lsym_else},
84 {"enum", lsym_tag},
85 {"extern", lsym_storage_class},
86 {"float", lsym_type},
87 {"for", lsym_for},
88 {"goto", lsym_word},
89 {"if", lsym_if},
90 {"imaginary", lsym_type},
91 {"inline", lsym_word},
92 {"int", lsym_type},
93 {"long", lsym_type},
94 {"offsetof", lsym_offsetof},
95 {"register", lsym_storage_class},
96 {"restrict", lsym_word},
97 {"return", lsym_return},
98 {"short", lsym_type},
99 {"signed", lsym_type},
100 {"sizeof", lsym_sizeof},
101 {"static", lsym_storage_class},
102 {"struct", lsym_tag},
103 {"switch", lsym_switch},
104 {"typedef", lsym_typedef},
105 {"union", lsym_tag},
106 {"unsigned", lsym_type},
107 {"void", lsym_type},
108 {"volatile", lsym_type},
109 {"while", lsym_while}
110 };
111
112 static struct {
113 const char **items;
114 unsigned int len;
115 unsigned int cap;
116 } typenames;
117
118 /*
119 * The transition table below was rewritten by hand from lx's output, given
120 * the following definitions. lx is Katherine Flavel's lexer generator.
121 *
122 * O = /[0-7]/; D = /[0-9]/; NZ = /[1-9]/;
123 * H = /[a-f0-9]/i; B = /[0-1]/; HP = /0x/i;
124 * BP = /0b/i; E = /e[+\-]?/i D+; P = /p[+\-]?/i D+;
125 * FS = /[fl]/i; IS = /u/i /(l|L|ll|LL)/? | /(l|L|ll|LL)/ /u/i?;
126 *
127 * D+ E FS? -> $float;
128 * D* "." D+ E? FS? -> $float;
129 * D+ "." E? FS? -> $float; HP H+ IS? -> $int;
130 * HP H+ P FS? -> $float; NZ D* IS? -> $int;
131 * HP H* "." H+ P FS? -> $float; "0" O* IS? -> $int;
132 * HP H+ "." P FS -> $float; BP B+ IS? -> $int;
133 */
134 /* INDENT OFF */
135 static const unsigned char lex_number_state[][26] = {
136 /* examples:
137 00
138 s 0xx
139 t 00xaa
140 a 11 101100xxa..
141 r 11ee0001101lbuuxx.a.pp
142 t.01.e+008bLuxll0Ll.aa.p+0
143 states: ABCDEFGHIJKLMNOPQRSTUVWXYZ */
144 [0] = "uuiifuufiuuiiuiiiiiuiuuuuu", /* (other) */
145 [1] = "CEIDEHHHIJQ U Q VUVVZZZ", /* 0 */
146 [2] = "DEIDEHHHIJQ U Q VUVVZZZ", /* 1 */
147 [3] = "DEIDEHHHIJ U VUVVZZZ", /* 2 3 4 5 6 7 */
148 [4] = "DEJDEHHHJJ U VUVVZZZ", /* 8 9 */
149 [5] = " U VUVV ", /* A a C c D d */
150 [6] = " K U VUVV ", /* B b */
151 [7] = " FFF FF U VUVV ", /* E e */
152 [8] = " f f U VUVV f", /* F f */
153 [9] = " LLf fL PR Li L f", /* L */
154 [10] = " OOf fO S P O i O f", /* l */
155 [11] = " FFX ", /* P p */
156 [12] = " MM M i iiM M ", /* U u */
157 [13] = " N ", /* X x */
158 [14] = " G Y ", /* + - */
159 [15] = "B EE EE T W ", /* . */
160 /* ABCDEFGHIJKLMNOPQRSTUVWXYZ */
161 };
162 /* INDENT ON */
163
164 static const unsigned char lex_number_row[] = {
165 ['0'] = 1,
166 ['1'] = 2,
167 ['2'] = 3, ['3'] = 3, ['4'] = 3, ['5'] = 3, ['6'] = 3, ['7'] = 3,
168 ['8'] = 4, ['9'] = 4,
169 ['A'] = 5, ['a'] = 5, ['C'] = 5, ['c'] = 5, ['D'] = 5, ['d'] = 5,
170 ['B'] = 6, ['b'] = 6,
171 ['E'] = 7, ['e'] = 7,
172 ['F'] = 8, ['f'] = 8,
173 ['L'] = 9,
174 ['l'] = 10,
175 ['P'] = 11, ['p'] = 11,
176 ['U'] = 12, ['u'] = 12,
177 ['X'] = 13, ['x'] = 13,
178 ['+'] = 14, ['-'] = 14,
179 ['.'] = 15,
180 };
181
182 static void
183 check_size_token(size_t desired_size)
184 {
185 if (token.e + desired_size >= token.l)
186 buf_expand(&token, desired_size);
187 }
188
189 static void
190 token_add_char(char ch)
191 {
192 check_size_token(1);
193 *token.e++ = ch;
194 }
195
196 #ifdef debug
197 static const char *
198 lsym_name(lexer_symbol sym)
199 {
200 static const char *const name[] = {
201 "eof",
202 "preprocessing",
203 "newline",
204 "form_feed",
205 "comment",
206 "lparen_or_lbracket",
207 "rparen_or_rbracket",
208 "lbrace",
209 "rbrace",
210 "period",
211 "unary_op",
212 "binary_op",
213 "postfix_op",
214 "question",
215 "colon",
216 "comma",
217 "semicolon",
218 "typedef",
219 "storage_class",
220 "type_outside_parentheses",
221 "type_in_parentheses",
222 "tag",
223 "case_label",
224 "string_prefix",
225 "sizeof",
226 "offsetof",
227 "word",
228 "funcname",
229 "do",
230 "else",
231 "for",
232 "if",
233 "switch",
234 "while",
235 "return",
236 };
237
238 return name[sym];
239 }
240
241 static void
242 debug_print_buf(const char *name, const struct buffer *buf)
243 {
244 if (buf->s < buf->e) {
245 debug_printf("%s ", name);
246 debug_vis_range("\"", buf->s, buf->e, "\"\n");
247 }
248 }
249
250 #define debug_ps_bool(name) \
251 if (ps.name != prev_ps.name) \
252 debug_println("[%c] ps." #name, ps.name ? 'x' : ' ')
253 #define debug_ps_int(name) \
254 if (ps.name != prev_ps.name) \
255 debug_println("%3d ps." #name, ps.name)
256
257 static void
258 debug_lexi(lexer_symbol lsym)
259 {
260 /*
261 * Watch out for 'rolled back parser state' in the debug output; the
262 * differences around these are unreliable.
263 */
264 static struct parser_state prev_ps;
265
266 debug_println("");
267 debug_printf("line %d: %s", line_no, lsym_name(lsym));
268 debug_vis_range(" \"", token.s, token.e, "\"\n");
269
270 debug_print_buf("label", &lab);
271 debug_print_buf("code", &code);
272 debug_print_buf("comment", &com);
273
274 debug_println(" ps.prev_token = %s", lsym_name(ps.prev_token));
275 debug_ps_bool(next_col_1);
276 debug_ps_bool(curr_col_1);
277 debug_ps_bool(next_unary);
278 if (strcmp(ps.procname, prev_ps.procname) != 0)
279 debug_println(" ps.procname = '%s'", ps.procname);
280 debug_ps_bool(want_blank);
281 debug_ps_int(paren_level);
282 debug_ps_int(p_l_follow);
283 if (ps.paren_level != prev_ps.paren_level) {
284 debug_printf(" ps.paren_indents:");
285 for (int i = 0; i < ps.paren_level; i++)
286 debug_printf(" %d", ps.paren_indents[i]);
287 debug_println("");
288 }
289 debug_ps_int(cast_mask);
290 debug_ps_int(not_cast_mask);
291
292 debug_ps_int(comment_delta);
293 debug_ps_int(n_comment_delta);
294 debug_ps_int(com_ind);
295
296 debug_ps_bool(block_init);
297 debug_ps_int(block_init_level);
298 debug_ps_bool(init_or_struct);
299
300 debug_ps_int(ind_level);
301 debug_ps_int(ind_level_follow);
302
303 debug_ps_int(decl_level);
304 debug_ps_bool(decl_on_line);
305 debug_ps_bool(in_decl);
306 debug_ps_int(just_saw_decl);
307 debug_ps_bool(in_parameter_declaration);
308 debug_ps_bool(decl_indent_done);
309
310 debug_ps_bool(in_stmt);
311 debug_ps_bool(ind_stmt);
312 debug_ps_bool(is_case_label);
313
314 debug_ps_bool(search_stmt);
315
316 prev_ps = ps;
317 }
318 #endif
319
320 /* ARGSUSED */
321 static lexer_symbol
322 lexi_end(lexer_symbol lsym)
323 {
324 #ifdef debug
325 debug_lexi(lsym);
326 #endif
327 return lsym;
328 }
329
330 static void
331 lex_number(void)
332 {
333 for (unsigned char s = 'A'; s != 'f' && s != 'i' && s != 'u';) {
334 unsigned char ch = (unsigned char)inp_peek();
335 if (ch >= array_length(lex_number_row) || lex_number_row[ch] == 0)
336 break;
337
338 unsigned char row = lex_number_row[ch];
339 if (lex_number_state[row][s - 'A'] == ' ') {
340 /*-
341 * lex_number_state[0][s - 'A'] now indicates the type:
342 * f = floating, i = integer, u = unknown
343 */
344 return;
345 }
346
347 s = lex_number_state[row][s - 'A'];
348 token_add_char(inp_next());
349 }
350 }
351
352 static void
353 lex_word(void)
354 {
355 while (isalnum((unsigned char)inp_peek()) ||
356 inp_peek() == '\\' ||
357 inp_peek() == '_' || inp_peek() == '$') {
358
359 if (inp_peek() == '\\') {
360 if (inp_lookahead(1) == '\n') {
361 inp_skip();
362 inp_skip();
363 } else
364 break;
365 }
366
367 token_add_char(inp_next());
368 }
369 }
370
371 static void
372 lex_char_or_string(void)
373 {
374 for (char delim = token.e[-1];;) {
375 if (inp_peek() == '\n') {
376 diag(1, "Unterminated literal");
377 return;
378 }
379
380 token_add_char(inp_next());
381 if (token.e[-1] == delim)
382 return;
383
384 if (token.e[-1] == '\\') {
385 if (inp_peek() == '\n')
386 ++line_no;
387 token_add_char(inp_next());
388 }
389 }
390 }
391
392 /* Guess whether the current token is a declared type. */
393 static bool
394 probably_typename(void)
395 {
396 if (ps.block_init || ps.in_stmt)
397 return false;
398 if (inp_peek() == '*' && inp_lookahead(1) != '=')
399 goto maybe;
400 if (isalpha((unsigned char)inp_peek()))
401 goto maybe;
402 return false;
403 maybe:
404 return ps.prev_token == lsym_semicolon ||
405 ps.prev_token == lsym_lbrace ||
406 ps.prev_token == lsym_rbrace;
407 }
408
409 static int
410 bsearch_typenames(const char *key)
411 {
412 const char **arr = typenames.items;
413 int lo = 0;
414 int hi = (int)typenames.len - 1;
415
416 while (lo <= hi) {
417 int mid = (int)((unsigned)(lo + hi) >> 1);
418 int cmp = strcmp(arr[mid], key);
419 if (cmp < 0)
420 lo = mid + 1;
421 else if (cmp > 0)
422 hi = mid - 1;
423 else
424 return mid;
425 }
426 return -(lo + 1);
427 }
428
429 static bool
430 is_typename(void)
431 {
432 if (opt.auto_typedefs &&
433 token.e - token.s >= 2 && memcmp(token.e - 2, "_t", 2) == 0)
434 return true;
435
436 return bsearch_typenames(token.s) >= 0;
437 }
438
439 static int
440 cmp_keyword_by_name(const void *key, const void *elem)
441 {
442 return strcmp(key, ((const struct keyword *)elem)->name);
443 }
444
445 /* Read an alphanumeric token into 'token', or return lsym_eof. */
446 static lexer_symbol
447 lexi_alnum(void)
448 {
449 if (isdigit((unsigned char)inp_peek()) ||
450 (inp_peek() == '.' && isdigit((unsigned char)inp_lookahead(1)))) {
451 lex_number();
452 } else if (isalnum((unsigned char)inp_peek()) ||
453 inp_peek() == '_' || inp_peek() == '$') {
454 lex_word();
455 } else
456 return lsym_eof; /* just as a placeholder */
457
458 *token.e = '\0';
459
460 if (token.s[0] == 'L' && token.s[1] == '\0' &&
461 (inp_peek() == '"' || inp_peek() == '\''))
462 return lsym_string_prefix;
463
464 while (ch_isblank(inp_peek()))
465 inp_skip();
466
467 if (ps.prev_token == lsym_tag && ps.p_l_follow == 0) {
468 ps.next_unary = true;
469 return lsym_type_outside_parentheses;
470 }
471
472 /* Operator after identifier is binary unless last token was 'struct'. */
473 ps.next_unary = ps.prev_token == lsym_tag;
474
475 const struct keyword *kw = bsearch(token.s, keywords,
476 array_length(keywords), sizeof(keywords[0]), cmp_keyword_by_name);
477 bool is_type = false;
478 if (kw == NULL) {
479 if (is_typename()) {
480 is_type = true;
481 ps.next_unary = true;
482 goto found_typename;
483 }
484
485 } else { /* we have a keyword */
486 is_type = kw->lsym == lsym_type;
487 ps.next_unary = true;
488 if (kw->lsym != lsym_tag && kw->lsym != lsym_type)
489 return kw->lsym;
490
491 found_typename:
492 if (ps.p_l_follow > 0) {
493 /* inside parentheses: cast, param list, offsetof or sizeof */
494 ps.cast_mask |= (1 << ps.p_l_follow) & ~ps.not_cast_mask;
495 }
496 if (ps.prev_token != lsym_period && ps.prev_token != lsym_unary_op) {
497 if (kw != NULL && kw->lsym == lsym_tag)
498 return lsym_tag;
499 if (ps.p_l_follow == 0)
500 return lsym_type_outside_parentheses;
501 }
502 }
503
504 if (inp_peek() == '(' && ps.tos <= 1 && ps.ind_level == 0 &&
505 !ps.in_parameter_declaration && !ps.block_init) {
506
507 for (const char *p = inp_p(), *e = inp_line_end(); p < e;)
508 if (*p++ == ')' && (*p == ';' || *p == ','))
509 goto no_function_definition;
510
511 strncpy(ps.procname, token.s, sizeof ps.procname - 1);
512 if (ps.in_decl)
513 ps.in_parameter_declaration = true;
514 return lsym_funcname;
515 no_function_definition:;
516
517 } else if (ps.p_l_follow == 0 && probably_typename()) {
518 ps.next_unary = true;
519 return lsym_type_outside_parentheses;
520 }
521
522 return is_type ? lsym_type_in_parentheses : lsym_word;
523 }
524
525 /* Reads the next token, placing it in the global variable "token". */
526 lexer_symbol
527 lexi(void)
528 {
529 token.e = token.s;
530 ps.curr_col_1 = ps.next_col_1;
531 ps.next_col_1 = false;
532
533 while (ch_isblank(inp_peek())) {
534 ps.curr_col_1 = false;
535 inp_skip();
536 }
537
538 lexer_symbol alnum_lsym = lexi_alnum();
539 if (alnum_lsym != lsym_eof)
540 return lexi_end(alnum_lsym);
541
542 /* Scan a non-alphanumeric token */
543
544 check_size_token(3); /* for things like "<<=" */
545 *token.e++ = inp_next();
546 *token.e = '\0';
547
548 lexer_symbol lsym;
549 bool unary_delim = false; /* whether the current token forces a
550 * following operator to be unary */
551
552 switch (token.e[-1]) {
553 case '\n':
554 unary_delim = ps.next_unary;
555 ps.next_col_1 = true;
556 /* if data has been exhausted, the newline is a dummy. */
557 lsym = had_eof ? lsym_eof : lsym_newline;
558 break;
559
560 case '\'':
561 case '"':
562 lex_char_or_string();
563 lsym = lsym_word;
564 break;
565
566 case '(':
567 case '[':
568 unary_delim = true;
569 lsym = lsym_lparen_or_lbracket;
570 break;
571
572 case ')':
573 case ']':
574 lsym = lsym_rparen_or_rbracket;
575 break;
576
577 case '#':
578 unary_delim = ps.next_unary;
579 lsym = lsym_preprocessing;
580 break;
581
582 case '?':
583 unary_delim = true;
584 lsym = lsym_question;
585 break;
586
587 case ':':
588 lsym = lsym_colon;
589 unary_delim = true;
590 break;
591
592 case ';':
593 unary_delim = true;
594 lsym = lsym_semicolon;
595 break;
596
597 case '{':
598 unary_delim = true;
599 lsym = lsym_lbrace;
600 break;
601
602 case '}':
603 unary_delim = true;
604 lsym = lsym_rbrace;
605 break;
606
607 case '\f':
608 unary_delim = ps.next_unary;
609 ps.next_col_1 = true;
610 lsym = lsym_form_feed;
611 break;
612
613 case ',':
614 unary_delim = true;
615 lsym = lsym_comma;
616 break;
617
618 case '.':
619 unary_delim = false;
620 lsym = lsym_period;
621 break;
622
623 case '-':
624 case '+':
625 lsym = ps.next_unary ? lsym_unary_op : lsym_binary_op;
626 unary_delim = true;
627
628 if (inp_peek() == token.e[-1]) { /* ++, -- */
629 *token.e++ = inp_next();
630 if (ps.prev_token == lsym_word ||
631 ps.prev_token == lsym_rparen_or_rbracket) {
632 lsym = ps.next_unary ? lsym_unary_op : lsym_postfix_op;
633 unary_delim = false;
634 }
635
636 } else if (inp_peek() == '=') { /* += */
637 *token.e++ = inp_next();
638
639 } else if (inp_peek() == '>') { /* -> */
640 *token.e++ = inp_next();
641 unary_delim = false;
642 lsym = lsym_unary_op;
643 ps.want_blank = false;
644 }
645 break;
646
647 case '=':
648 if (ps.init_or_struct)
649 ps.block_init = true;
650 if (inp_peek() == '=') { /* == */
651 *token.e++ = inp_next();
652 *token.e = '\0';
653 }
654 lsym = lsym_binary_op;
655 unary_delim = true;
656 break;
657
658 case '>':
659 case '<':
660 case '!': /* ops like <, <<, <=, !=, etc */
661 if (inp_peek() == '>' || inp_peek() == '<' || inp_peek() == '=')
662 *token.e++ = inp_next();
663 if (inp_peek() == '=')
664 *token.e++ = inp_next();
665 lsym = ps.next_unary ? lsym_unary_op : lsym_binary_op;
666 unary_delim = true;
667 break;
668
669 case '*':
670 unary_delim = true;
671 if (!ps.next_unary) {
672 if (inp_peek() == '=')
673 *token.e++ = inp_next();
674 lsym = lsym_binary_op;
675 break;
676 }
677
678 while (inp_peek() == '*' || isspace((unsigned char)inp_peek())) {
679 if (inp_peek() == '*')
680 token_add_char('*');
681 inp_skip();
682 }
683
684 if (ps.in_decl) {
685 const char *tp = inp_p();
686
687 while (isalpha((unsigned char)*tp) ||
688 isspace((unsigned char)*tp)) {
689 if (++tp >= inp_line_end()) {
690 const char *p_before = inp_p();
691 inp_read_line();
692 if (inp_p() != p_before)
693 abort();
694 }
695 }
696 if (*tp == '(')
697 ps.procname[0] = ' ';
698 }
699
700 lsym = lsym_unary_op;
701 break;
702
703 default:
704 if (token.e[-1] == '/' && (inp_peek() == '*' || inp_peek() == '/')) {
705 *token.e++ = inp_next();
706 lsym = lsym_comment;
707 unary_delim = ps.next_unary;
708 break;
709 }
710
711 /* handle '||', '&&', etc., and also things as in 'int *****i' */
712 while (token.e[-1] == inp_peek() || inp_peek() == '=')
713 token_add_char(inp_next());
714
715 lsym = ps.next_unary ? lsym_unary_op : lsym_binary_op;
716 unary_delim = true;
717 }
718
719 ps.next_unary = unary_delim;
720
721 check_size_token(1);
722 *token.e = '\0';
723
724 return lexi_end(lsym);
725 }
726
727 void
728 register_typename(const char *name)
729 {
730 if (typenames.len >= typenames.cap) {
731 typenames.cap = 16 + 2 * typenames.cap;
732 typenames.items = xrealloc(typenames.items,
733 sizeof(typenames.items[0]) * typenames.cap);
734 }
735
736 int pos = bsearch_typenames(name);
737 if (pos >= 0)
738 return; /* already in the list */
739
740 pos = -(pos + 1);
741 memmove(typenames.items + pos + 1, typenames.items + pos,
742 sizeof(typenames.items[0]) * (typenames.len++ - (unsigned)pos));
743 typenames.items[pos] = xstrdup(name);
744 }
745