tic.c revision 1.7 1 1.7 pgoyette /* $NetBSD: tic.c,v 1.7 2010/02/20 06:08:01 pgoyette Exp $ */
2 1.1 roy
3 1.1 roy /*
4 1.5 roy * Copyright (c) 2009, 2010 The NetBSD Foundation, Inc.
5 1.1 roy *
6 1.1 roy * This code is derived from software contributed to The NetBSD Foundation
7 1.1 roy * by Roy Marples.
8 1.1 roy *
9 1.1 roy * Redistribution and use in source and binary forms, with or without
10 1.1 roy * modification, are permitted provided that the following conditions
11 1.1 roy * are met:
12 1.1 roy * 1. Redistributions of source code must retain the above copyright
13 1.1 roy * notice, this list of conditions and the following disclaimer.
14 1.1 roy * 2. Redistributions in binary form must reproduce the above copyright
15 1.1 roy * notice, this list of conditions and the following disclaimer in the
16 1.1 roy * documentation and/or other materials provided with the distribution.
17 1.1 roy *
18 1.1 roy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 1.1 roy * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 1.1 roy * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 1.1 roy * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 1.1 roy * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 1.1 roy * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 1.1 roy * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 1.1 roy * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 1.1 roy * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 1.1 roy * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 1.1 roy */
29 1.1 roy
30 1.1 roy #if HAVE_NBTOOL_CONFIG_H
31 1.1 roy #include "nbtool_config.h"
32 1.1 roy #endif
33 1.1 roy
34 1.1 roy #include <sys/cdefs.h>
35 1.7 pgoyette __RCSID("$NetBSD: tic.c,v 1.7 2010/02/20 06:08:01 pgoyette Exp $");
36 1.1 roy
37 1.1 roy #include <sys/types.h>
38 1.7 pgoyette #include <sys/endian.h>
39 1.1 roy
40 1.1 roy #include <ctype.h>
41 1.1 roy #include <err.h>
42 1.1 roy #include <errno.h>
43 1.1 roy #include <getopt.h>
44 1.1 roy #include <limits.h>
45 1.1 roy #include <fcntl.h>
46 1.1 roy #include <ndbm.h>
47 1.1 roy #include <stdarg.h>
48 1.1 roy #include <stdlib.h>
49 1.1 roy #include <stdio.h>
50 1.1 roy #include <string.h>
51 1.1 roy #include <term_private.h>
52 1.1 roy #include <term.h>
53 1.1 roy
54 1.1 roy #define UINT16_T_MAX 0xffff
55 1.1 roy
56 1.1 roy typedef struct tbuf {
57 1.1 roy char *buf;
58 1.1 roy size_t buflen;
59 1.1 roy size_t bufpos;
60 1.1 roy size_t entries;
61 1.1 roy } TBUF;
62 1.1 roy
63 1.1 roy typedef struct tic {
64 1.1 roy char *name;
65 1.2 roy char *alias;
66 1.1 roy char *desc;
67 1.1 roy TBUF flags;
68 1.1 roy TBUF nums;
69 1.1 roy TBUF strs;
70 1.1 roy TBUF extras;
71 1.1 roy } TIC;
72 1.1 roy
73 1.1 roy /* We store the full list of terminals we have instead of iterating
74 1.1 roy through the database as the sequential iterator doesn't work
75 1.1 roy the the data size stored changes N amount which ours will. */
76 1.1 roy typedef struct term {
77 1.1 roy struct term *next;
78 1.1 roy char *name;
79 1.1 roy char type;
80 1.1 roy TIC tic;
81 1.1 roy } TERM;
82 1.1 roy static TERM *terms;
83 1.1 roy
84 1.1 roy static int error_exit;
85 1.6 roy static int Sflag, aflag, xflag;
86 1.1 roy static char *dbname;
87 1.1 roy
88 1.1 roy static TBUF scratch;
89 1.1 roy
90 1.1 roy static void
91 1.1 roy do_unlink(void)
92 1.1 roy {
93 1.1 roy
94 1.1 roy if (dbname != NULL)
95 1.1 roy unlink(dbname);
96 1.1 roy }
97 1.1 roy
98 1.1 roy static void __attribute__((__format__(__printf__, 1, 2)))
99 1.1 roy dowarn(const char *fmt, ...)
100 1.1 roy {
101 1.1 roy va_list va;
102 1.1 roy
103 1.1 roy error_exit = 1;
104 1.1 roy va_start(va, fmt);
105 1.1 roy vwarnx(fmt, va);
106 1.1 roy va_end(va);
107 1.1 roy }
108 1.1 roy
109 1.1 roy static char *
110 1.1 roy grow_tbuf(TBUF *tbuf, size_t len)
111 1.1 roy {
112 1.1 roy char *buf;
113 1.1 roy size_t l;
114 1.1 roy
115 1.1 roy l = tbuf->bufpos + len;
116 1.1 roy if (l > tbuf->buflen) {
117 1.1 roy if (tbuf->bufpos == 0) {
118 1.1 roy buf = malloc(l);
119 1.1 roy if (buf == NULL)
120 1.1 roy err(1, "malloc (%zu bytes)", l);
121 1.1 roy } else {
122 1.1 roy buf = realloc(tbuf->buf, l);
123 1.1 roy if (buf == NULL)
124 1.1 roy err(1, "realloc (%zu bytes)", l);
125 1.1 roy }
126 1.1 roy tbuf->buf = buf;
127 1.1 roy tbuf->buflen = l;
128 1.1 roy }
129 1.1 roy return tbuf->buf;
130 1.1 roy }
131 1.1 roy
132 1.1 roy static char *
133 1.1 roy find_cap(TBUF *tbuf, char type, short ind)
134 1.1 roy {
135 1.1 roy size_t n;
136 1.1 roy short num;
137 1.1 roy char *cap;
138 1.1 roy
139 1.1 roy cap = tbuf->buf;
140 1.1 roy for (n = tbuf->entries; n > 0; n--) {
141 1.1 roy num = le16dec(cap);
142 1.1 roy cap += sizeof(uint16_t);
143 1.1 roy if (num == ind)
144 1.1 roy return cap;
145 1.1 roy switch (type) {
146 1.1 roy case 'f':
147 1.1 roy cap++;
148 1.1 roy break;
149 1.1 roy case 'n':
150 1.1 roy cap += sizeof(uint16_t);
151 1.1 roy break;
152 1.1 roy case 's':
153 1.1 roy num = le16dec(cap);
154 1.1 roy cap += sizeof(uint16_t);
155 1.1 roy cap += num;
156 1.1 roy break;
157 1.1 roy }
158 1.1 roy }
159 1.1 roy return NULL;
160 1.1 roy }
161 1.1 roy
162 1.1 roy static char *
163 1.1 roy find_extra(TBUF *tbuf, const char *code)
164 1.1 roy {
165 1.1 roy size_t n;
166 1.1 roy short num;
167 1.1 roy char *cap;
168 1.1 roy
169 1.1 roy cap = tbuf->buf;
170 1.1 roy for (n = tbuf->entries; n > 0; n--) {
171 1.1 roy num = le16dec(cap);
172 1.1 roy cap += sizeof(uint16_t);
173 1.1 roy if (strcmp(cap, code) == 0)
174 1.1 roy return cap + num;
175 1.1 roy cap += num;
176 1.1 roy switch (*cap++) {
177 1.1 roy case 'f':
178 1.1 roy cap++;
179 1.1 roy break;
180 1.1 roy case 'n':
181 1.1 roy cap += sizeof(uint16_t);
182 1.1 roy break;
183 1.1 roy case 's':
184 1.1 roy num = le16dec(cap);
185 1.1 roy cap += sizeof(uint16_t);
186 1.1 roy cap += num;
187 1.1 roy break;
188 1.1 roy }
189 1.1 roy }
190 1.1 roy return NULL;
191 1.1 roy }
192 1.1 roy
193 1.1 roy static size_t
194 1.1 roy store_extra(TIC *tic, int wrn, char *id, char type, char flag, short num,
195 1.1 roy char *str, size_t strl)
196 1.1 roy {
197 1.1 roy size_t l;
198 1.1 roy
199 1.1 roy if (strcmp(id, "use") != 0) {
200 1.1 roy if (find_extra(&tic->extras, id) != NULL)
201 1.1 roy return 0;
202 1.6 roy if (!xflag) {
203 1.1 roy if (wrn != 0)
204 1.1 roy dowarn("%s: %s: unknown capability",
205 1.1 roy tic->name, id);
206 1.1 roy return 0;
207 1.1 roy }
208 1.1 roy }
209 1.1 roy
210 1.1 roy l = strlen(id) + 1;
211 1.1 roy if (l > UINT16_T_MAX) {
212 1.1 roy dowarn("%s: %s: cap name is too long", tic->name, id);
213 1.1 roy return 0;
214 1.1 roy }
215 1.1 roy
216 1.1 roy grow_tbuf(&tic->extras, l + strl + (sizeof(uint16_t) * 2) + 1);
217 1.1 roy le16enc(tic->extras.buf + tic->extras.bufpos, l);
218 1.1 roy tic->extras.bufpos += sizeof(uint16_t);
219 1.1 roy memcpy(tic->extras.buf + tic->extras.bufpos, id, l);
220 1.1 roy tic->extras.bufpos += l;
221 1.1 roy tic->extras.buf[tic->extras.bufpos++] = type;
222 1.1 roy switch (type) {
223 1.1 roy case 'f':
224 1.1 roy tic->extras.buf[tic->extras.bufpos++] = flag;
225 1.1 roy break;
226 1.1 roy case 'n':
227 1.1 roy le16enc(tic->extras.buf + tic->extras.bufpos, num);
228 1.1 roy tic->extras.bufpos += sizeof(uint16_t);
229 1.1 roy break;
230 1.1 roy case 's':
231 1.1 roy le16enc(tic->extras.buf + tic->extras.bufpos, strl);
232 1.1 roy tic->extras.bufpos += sizeof(uint16_t);
233 1.1 roy memcpy(tic->extras.buf + tic->extras.bufpos, str, strl);
234 1.1 roy tic->extras.bufpos += strl;
235 1.1 roy break;
236 1.1 roy }
237 1.1 roy tic->extras.entries++;
238 1.1 roy return 1;
239 1.1 roy }
240 1.1 roy
241 1.5 roy static TBUF *
242 1.5 roy flatten_term(TERM *term)
243 1.1 roy {
244 1.2 roy size_t buflen, len, alen, dlen;
245 1.1 roy char *cap;
246 1.1 roy TIC *tic;
247 1.1 roy
248 1.1 roy scratch.bufpos = 0;
249 1.1 roy tic = &term->tic;
250 1.1 roy len = strlen(tic->name) + 1;
251 1.6 roy if (tic->alias == NULL || Sflag)
252 1.2 roy alen = 0;
253 1.2 roy else
254 1.2 roy alen = strlen(tic->alias) + 1;
255 1.6 roy if (tic->desc == NULL || Sflag)
256 1.1 roy dlen = 0;
257 1.1 roy else
258 1.1 roy dlen = strlen(tic->desc) + 1;
259 1.1 roy buflen = sizeof(char) +
260 1.1 roy sizeof(uint16_t) + len +
261 1.5 roy sizeof(uint16_t) + alen +
262 1.1 roy sizeof(uint16_t) + dlen +
263 1.1 roy (sizeof(uint16_t) * 2) + tic->flags.bufpos +
264 1.1 roy (sizeof(uint16_t) * 2) + tic->nums.bufpos +
265 1.1 roy (sizeof(uint16_t) * 2) + tic->strs.bufpos +
266 1.1 roy (sizeof(uint16_t) * 2) + tic->extras.bufpos;
267 1.1 roy grow_tbuf(&scratch, buflen);
268 1.1 roy cap = scratch.buf;
269 1.1 roy if (term->type == 'a')
270 1.1 roy *cap++ = 0;
271 1.1 roy else
272 1.2 roy *cap++ = 2; /* version */
273 1.1 roy le16enc(cap, len);
274 1.1 roy cap += sizeof(uint16_t);
275 1.1 roy memcpy(cap, tic->name, len);
276 1.1 roy cap += len;
277 1.1 roy if (term->type != 'a') {
278 1.2 roy le16enc(cap, alen);
279 1.2 roy cap += sizeof(uint16_t);
280 1.6 roy if (tic->alias != NULL && !Sflag) {
281 1.2 roy memcpy(cap, tic->alias, alen);
282 1.2 roy cap += alen;
283 1.2 roy }
284 1.1 roy le16enc(cap, dlen);
285 1.1 roy cap += sizeof(uint16_t);
286 1.6 roy if (tic->desc != NULL && !Sflag) {
287 1.1 roy memcpy(cap, tic->desc, dlen);
288 1.1 roy cap += dlen;
289 1.1 roy }
290 1.1 roy
291 1.1 roy if (tic->flags.entries == 0) {
292 1.1 roy le16enc(cap, 0);
293 1.1 roy cap += sizeof(uint16_t);
294 1.1 roy } else {
295 1.1 roy le16enc(cap, (tic->flags.bufpos + sizeof(uint16_t)));
296 1.1 roy cap += sizeof(uint16_t);
297 1.1 roy le16enc(cap, tic->flags.entries);
298 1.1 roy cap += sizeof(uint16_t);
299 1.1 roy memcpy(cap, tic->flags.buf, tic->flags.bufpos);
300 1.1 roy cap += tic->flags.bufpos;
301 1.1 roy }
302 1.1 roy
303 1.1 roy if (tic->nums.entries == 0) {
304 1.1 roy le16enc(cap, 0);
305 1.1 roy cap += sizeof(uint16_t);
306 1.1 roy } else {
307 1.1 roy le16enc(cap, (tic->nums.bufpos + sizeof(uint16_t)));
308 1.1 roy cap += sizeof(uint16_t);
309 1.1 roy le16enc(cap, tic->nums.entries);
310 1.1 roy cap += sizeof(uint16_t);
311 1.1 roy memcpy(cap, tic->nums.buf, tic->nums.bufpos);
312 1.1 roy cap += tic->nums.bufpos;
313 1.1 roy }
314 1.1 roy
315 1.1 roy if (tic->strs.entries == 0) {
316 1.1 roy le16enc(cap, 0);
317 1.1 roy cap += sizeof(uint16_t);
318 1.1 roy } else {
319 1.1 roy le16enc(cap, (tic->strs.bufpos + sizeof(uint16_t)));
320 1.1 roy cap += sizeof(uint16_t);
321 1.1 roy le16enc(cap, tic->strs.entries);
322 1.1 roy cap += sizeof(uint16_t);
323 1.1 roy memcpy(cap, tic->strs.buf, tic->strs.bufpos);
324 1.1 roy cap += tic->strs.bufpos;
325 1.1 roy }
326 1.1 roy
327 1.1 roy if (tic->extras.entries == 0) {
328 1.1 roy le16enc(cap, 0);
329 1.1 roy cap += sizeof(uint16_t);
330 1.1 roy } else {
331 1.1 roy le16enc(cap, (tic->extras.bufpos + sizeof(uint16_t)));
332 1.1 roy cap += sizeof(uint16_t);
333 1.1 roy le16enc(cap, tic->extras.entries);
334 1.1 roy cap += sizeof(uint16_t);
335 1.1 roy memcpy(cap, tic->extras.buf, tic->extras.bufpos);
336 1.1 roy cap += tic->extras.bufpos;
337 1.1 roy }
338 1.1 roy }
339 1.5 roy scratch.bufpos = cap - scratch.buf;
340 1.5 roy
341 1.5 roy return &scratch;
342 1.5 roy }
343 1.5 roy
344 1.5 roy static int
345 1.5 roy save_term(DBM *db, TERM *term)
346 1.5 roy {
347 1.5 roy TBUF *buf;
348 1.5 roy datum key, value;
349 1.5 roy
350 1.5 roy buf = flatten_term(term);
351 1.5 roy if (buf == NULL)
352 1.5 roy return -1;
353 1.1 roy
354 1.1 roy key.dptr = term->name;
355 1.1 roy key.dsize = strlen(term->name);
356 1.1 roy value.dptr = scratch.buf;
357 1.5 roy value.dsize = scratch.bufpos;
358 1.1 roy if (dbm_store(db, key, value, DBM_REPLACE) == -1)
359 1.1 roy err(1, "dbm_store");
360 1.1 roy return 0;
361 1.1 roy }
362 1.1 roy
363 1.1 roy static TERM *
364 1.1 roy find_term(const char *name)
365 1.1 roy {
366 1.1 roy TERM *term;
367 1.1 roy
368 1.1 roy for (term = terms; term != NULL; term = term->next)
369 1.1 roy if (strcmp(term->name, name) == 0)
370 1.1 roy return term;
371 1.1 roy return NULL;
372 1.1 roy }
373 1.1 roy
374 1.1 roy static TERM *
375 1.1 roy store_term(const char *name, char type)
376 1.1 roy {
377 1.1 roy TERM *term;
378 1.1 roy
379 1.1 roy term = calloc(1, sizeof(*term));
380 1.1 roy if (term == NULL)
381 1.1 roy errx(1, "malloc");
382 1.1 roy term->name = strdup(name);
383 1.1 roy term->type = type;
384 1.1 roy if (term->name == NULL)
385 1.1 roy errx(1, "malloc");
386 1.1 roy term->next = terms;
387 1.1 roy terms = term;
388 1.1 roy return term;
389 1.1 roy }
390 1.1 roy
391 1.1 roy static void
392 1.1 roy encode_string(const char *term, const char *cap, TBUF *tbuf, const char *str)
393 1.1 roy {
394 1.1 roy int slash, i, num;
395 1.1 roy char ch, *p, *s, last;
396 1.1 roy
397 1.1 roy grow_tbuf(tbuf, strlen(str) + 1);
398 1.1 roy p = s = tbuf->buf + tbuf->bufpos;
399 1.1 roy slash = 0;
400 1.1 roy last = '\0';
401 1.1 roy /* Convert escape codes */
402 1.1 roy while ((ch = *str++) != '\0') {
403 1.1 roy if (slash == 0 && ch == '\\') {
404 1.1 roy slash = 1;
405 1.1 roy continue;
406 1.1 roy }
407 1.1 roy if (slash == 0) {
408 1.1 roy if (last != '%' && ch == '^') {
409 1.1 roy ch = *str++;
410 1.1 roy if (((unsigned char)ch) >= 128)
411 1.1 roy dowarn("%s: %s: illegal ^ character",
412 1.1 roy term, cap);
413 1.1 roy if (ch == '\0')
414 1.1 roy break;
415 1.1 roy if (ch == '?')
416 1.1 roy ch = '\177';
417 1.1 roy else if ((ch &= 037) == 0)
418 1.1 roy ch = 128;
419 1.1 roy }
420 1.1 roy *p++ = ch;
421 1.1 roy last = ch;
422 1.1 roy continue;
423 1.1 roy }
424 1.1 roy slash = 0;
425 1.1 roy if (ch >= '0' && ch <= '7') {
426 1.1 roy num = ch - '0';
427 1.1 roy for (i = 0; i < 2; i++) {
428 1.1 roy if (*str < '0' || *str > '7') {
429 1.1 roy if (isdigit((unsigned char)*str))
430 1.1 roy dowarn("%s: %s: non octal"
431 1.1 roy " digit", term, cap);
432 1.1 roy else
433 1.1 roy break;
434 1.1 roy }
435 1.1 roy num = num * 8 + *str++ - '0';
436 1.1 roy }
437 1.1 roy if (num == 0)
438 1.1 roy num = 0200;
439 1.1 roy *p++ = (char)num;
440 1.1 roy continue;
441 1.1 roy }
442 1.1 roy switch (ch) {
443 1.1 roy case 'a':
444 1.1 roy *p++ = '\a';
445 1.1 roy break;
446 1.1 roy case 'b':
447 1.1 roy *p++ = '\b';
448 1.1 roy break;
449 1.1 roy case 'e': /* FALLTHROUGH */
450 1.1 roy case 'E':
451 1.1 roy *p++ = '\033';
452 1.1 roy break;
453 1.1 roy case 'f':
454 1.1 roy *p++ = '\014';
455 1.1 roy break;
456 1.1 roy case 'l': /* FALLTHROUGH */
457 1.1 roy case 'n':
458 1.1 roy *p++ = '\n';
459 1.1 roy break;
460 1.1 roy case 'r':
461 1.1 roy *p++ = '\r';
462 1.1 roy break;
463 1.1 roy case 's':
464 1.1 roy *p++ = ' ';
465 1.1 roy break;
466 1.1 roy case 't':
467 1.1 roy *p++ = '\t';
468 1.1 roy break;
469 1.1 roy default:
470 1.1 roy
471 1.1 roy /* We should warn here */
472 1.1 roy case '^':
473 1.1 roy case ',':
474 1.1 roy case ':':
475 1.1 roy case '|':
476 1.1 roy *p++ = ch;
477 1.1 roy break;
478 1.1 roy }
479 1.1 roy last = ch;
480 1.1 roy }
481 1.1 roy *p++ = '\0';
482 1.1 roy tbuf->bufpos += p - s;
483 1.1 roy }
484 1.1 roy
485 1.1 roy static int
486 1.1 roy process_entry(TBUF *buf)
487 1.1 roy {
488 1.3 he char *cap, *capstart, *p, *e, *name, *desc, *alias;
489 1.3 he signed char flag;
490 1.1 roy long num;
491 1.1 roy int slash;
492 1.1 roy ssize_t ind;
493 1.1 roy size_t len;
494 1.1 roy TERM *term;
495 1.1 roy TIC *tic;
496 1.1 roy
497 1.1 roy if (buf->bufpos == 0)
498 1.1 roy return 0;
499 1.1 roy /* Terminate the string */
500 1.1 roy buf->buf[buf->bufpos - 1] = '\0';
501 1.1 roy /* First rewind the buffer for new entries */
502 1.1 roy buf->bufpos = 0;
503 1.1 roy
504 1.1 roy if (isspace((unsigned char)*buf->buf))
505 1.1 roy return 0;
506 1.1 roy
507 1.1 roy cap = strchr(buf->buf, '\n');
508 1.1 roy if (cap == NULL)
509 1.1 roy return 0;
510 1.1 roy e = cap - 1;
511 1.1 roy if (*e == ',')
512 1.1 roy *e = '\0';
513 1.1 roy *cap++ = '\0';
514 1.1 roy
515 1.1 roy name = buf->buf;
516 1.1 roy desc = strrchr(buf->buf, '|');
517 1.1 roy if (desc != NULL)
518 1.1 roy *desc++ = '\0';
519 1.1 roy alias = strchr(buf->buf, '|');
520 1.1 roy if (alias != NULL)
521 1.1 roy *alias++ = '\0';
522 1.1 roy
523 1.1 roy if (*e != '\0')
524 1.1 roy dowarn("%s: description missing separator", buf->buf);
525 1.1 roy
526 1.1 roy /* If we already have this term, abort */
527 1.1 roy if (find_term(name) != NULL) {
528 1.1 roy dowarn("%s: duplicate entry", name);
529 1.1 roy return 0;
530 1.1 roy }
531 1.1 roy term = store_term(name, 't');
532 1.1 roy tic = &term->tic;
533 1.1 roy tic->name = strdup(name);
534 1.1 roy if (tic->name == NULL)
535 1.1 roy err(1, "malloc");
536 1.2 roy if (alias != NULL) {
537 1.2 roy tic->alias = strdup(alias);
538 1.2 roy if (tic->alias == NULL)
539 1.2 roy err(1, "malloc");
540 1.2 roy }
541 1.1 roy if (desc != NULL) {
542 1.1 roy tic->desc = strdup(desc);
543 1.1 roy if (tic->desc == NULL)
544 1.1 roy err(1, "malloc");
545 1.1 roy }
546 1.1 roy
547 1.1 roy do {
548 1.1 roy while (isspace((unsigned char)*cap))
549 1.1 roy cap++;
550 1.1 roy if (*cap == '\0')
551 1.1 roy break;
552 1.1 roy slash = 0;
553 1.1 roy for (capstart = cap;
554 1.1 roy *cap != '\0' && (slash == 1 || *cap != ',');
555 1.1 roy cap++)
556 1.1 roy {
557 1.1 roy if (slash == 0) {
558 1.1 roy if (*cap == '\\')
559 1.1 roy slash = 1;
560 1.1 roy } else
561 1.1 roy slash = 0;
562 1.1 roy continue;
563 1.1 roy }
564 1.1 roy *cap++ = '\0';
565 1.1 roy
566 1.1 roy /* Skip commented caps */
567 1.6 roy if (!aflag && capstart[0] == '.')
568 1.1 roy continue;
569 1.1 roy
570 1.1 roy /* Obsolete entries */
571 1.1 roy if (capstart[0] == 'O' && capstart[1] == 'T') {
572 1.6 roy if (!xflag)
573 1.1 roy continue;
574 1.1 roy capstart += 2;
575 1.1 roy }
576 1.1 roy
577 1.1 roy /* str cap */
578 1.1 roy p = strchr(capstart, '=');
579 1.1 roy if (p != NULL) {
580 1.1 roy *p++ = '\0';
581 1.1 roy /* Don't use the string if we already have it */
582 1.1 roy ind = _ti_strindex(capstart);
583 1.1 roy if (ind != -1 &&
584 1.1 roy find_cap(&tic->strs, 's', ind) != NULL)
585 1.1 roy continue;
586 1.1 roy
587 1.1 roy /* Encode the string to our scratch buffer */
588 1.1 roy scratch.bufpos = 0;
589 1.1 roy encode_string(tic->name, capstart, &scratch, p);
590 1.1 roy if (scratch.bufpos > UINT16_T_MAX) {
591 1.1 roy dowarn("%s: %s: string is too long",
592 1.1 roy tic->name, capstart);
593 1.1 roy continue;
594 1.1 roy }
595 1.1 roy if (!VALID_STRING(scratch.buf)) {
596 1.1 roy dowarn("%s: %s: invalid string",
597 1.1 roy tic->name, capstart);
598 1.1 roy continue;
599 1.1 roy }
600 1.1 roy
601 1.1 roy if (ind == -1)
602 1.1 roy store_extra(tic, 1, capstart, 's', -1, -2,
603 1.1 roy scratch.buf, scratch.bufpos);
604 1.1 roy else {
605 1.1 roy grow_tbuf(&tic->strs, (sizeof(uint16_t) * 2) +
606 1.1 roy scratch.bufpos);
607 1.1 roy le16enc(tic->strs.buf + tic->strs.bufpos, ind);
608 1.1 roy tic->strs.bufpos += sizeof(uint16_t);
609 1.1 roy le16enc(tic->strs.buf + tic->strs.bufpos,
610 1.1 roy scratch.bufpos);
611 1.1 roy tic->strs.bufpos += sizeof(uint16_t);
612 1.1 roy memcpy(tic->strs.buf + tic->strs.bufpos,
613 1.1 roy scratch.buf, scratch.bufpos);
614 1.1 roy tic->strs.bufpos += scratch.bufpos;
615 1.1 roy tic->strs.entries++;
616 1.1 roy }
617 1.1 roy continue;
618 1.1 roy }
619 1.1 roy
620 1.1 roy /* num cap */
621 1.1 roy p = strchr(capstart, '#');
622 1.1 roy if (p != NULL) {
623 1.1 roy *p++ = '\0';
624 1.1 roy /* Don't use the number if we already have it */
625 1.1 roy ind = _ti_numindex(capstart);
626 1.1 roy if (ind != -1 &&
627 1.1 roy find_cap(&tic->nums, 'n', ind) != NULL)
628 1.1 roy continue;
629 1.1 roy
630 1.1 roy num = strtol(p, &e, 0);
631 1.1 roy if (*e != '\0') {
632 1.1 roy dowarn("%s: %s: not a number",
633 1.1 roy tic->name, capstart);
634 1.1 roy continue;
635 1.1 roy }
636 1.1 roy if (!VALID_NUMERIC(num)) {
637 1.1 roy dowarn("%s: %s: number out of range",
638 1.1 roy tic->name, capstart);
639 1.1 roy continue;
640 1.1 roy }
641 1.1 roy if (ind == -1)
642 1.1 roy store_extra(tic, 1, capstart, 'n', -1,
643 1.1 roy num, NULL, 0);
644 1.1 roy else {
645 1.1 roy grow_tbuf(&tic->nums, sizeof(uint16_t) * 2);
646 1.1 roy le16enc(tic->nums.buf + tic->nums.bufpos, ind);
647 1.1 roy tic->nums.bufpos += sizeof(uint16_t);
648 1.1 roy le16enc(tic->nums.buf + tic->nums.bufpos, num);
649 1.1 roy tic->nums.bufpos += sizeof(uint16_t);
650 1.1 roy tic->nums.entries++;
651 1.1 roy }
652 1.1 roy continue;
653 1.1 roy }
654 1.1 roy
655 1.1 roy flag = 1;
656 1.1 roy len = strlen(capstart) - 1;
657 1.1 roy if (capstart[len] == '@') {
658 1.1 roy flag = CANCELLED_BOOLEAN;
659 1.1 roy capstart[len] = '\0';
660 1.1 roy }
661 1.1 roy ind = _ti_flagindex(capstart);
662 1.1 roy if (ind == -1 && flag == CANCELLED_BOOLEAN) {
663 1.1 roy if ((ind = _ti_numindex(capstart)) != -1) {
664 1.1 roy if (find_cap(&tic->nums, 'n', ind) != NULL)
665 1.1 roy continue;
666 1.1 roy grow_tbuf(&tic->nums, sizeof(uint16_t) * 2);
667 1.1 roy le16enc(tic->nums.buf + tic->nums.bufpos, ind);
668 1.1 roy tic->nums.bufpos += sizeof(uint16_t);
669 1.1 roy le16enc(tic->nums.buf + tic->nums.bufpos,
670 1.1 roy CANCELLED_NUMERIC);
671 1.1 roy tic->nums.bufpos += sizeof(uint16_t);
672 1.1 roy tic->nums.entries++;
673 1.1 roy continue;
674 1.1 roy } else if ((ind = _ti_strindex(capstart)) != -1) {
675 1.1 roy if (find_cap(&tic->strs, 's', ind) != NULL)
676 1.1 roy continue;
677 1.1 roy grow_tbuf(&tic->strs,
678 1.1 roy (sizeof(uint16_t) * 2) + 1);
679 1.1 roy le16enc(tic->strs.buf + tic->strs.bufpos, ind);
680 1.1 roy tic->strs.bufpos += sizeof(uint16_t);
681 1.1 roy le16enc(tic->strs.buf + tic->strs.bufpos, 0);
682 1.1 roy tic->strs.bufpos += sizeof(uint16_t);
683 1.1 roy tic->strs.entries++;
684 1.1 roy continue;
685 1.1 roy }
686 1.1 roy }
687 1.1 roy if (ind == -1)
688 1.1 roy store_extra(tic, 1, capstart, 'f', flag, 0, NULL, 0);
689 1.1 roy else if (find_cap(&tic->flags, 'f', ind) == NULL) {
690 1.1 roy grow_tbuf(&tic->flags, sizeof(uint16_t) + 1);
691 1.1 roy le16enc(tic->flags.buf + tic->flags.bufpos, ind);
692 1.1 roy tic->flags.bufpos += sizeof(uint16_t);
693 1.1 roy tic->flags.buf[tic->flags.bufpos++] = flag;
694 1.1 roy tic->flags.entries++;
695 1.1 roy }
696 1.1 roy } while (*cap == ',' || isspace((unsigned char)*cap));
697 1.1 roy
698 1.1 roy /* Create aliased terms */
699 1.1 roy if (alias != NULL) {
700 1.1 roy while (alias != NULL && *alias != '\0') {
701 1.1 roy desc = strchr(alias, '|');
702 1.1 roy if (desc != NULL)
703 1.1 roy *desc++ = '\0';
704 1.1 roy if (find_term(alias) != NULL) {
705 1.1 roy dowarn("%s: has alias for already assigned"
706 1.1 roy " term %s", tic->name, alias);
707 1.1 roy } else {
708 1.1 roy term = store_term(alias, 'a');
709 1.1 roy term->tic.name = strdup(tic->name);
710 1.1 roy if (term->tic.name == NULL)
711 1.1 roy err(1, "malloc");
712 1.1 roy }
713 1.1 roy alias = desc;
714 1.1 roy }
715 1.1 roy }
716 1.1 roy
717 1.1 roy return 0;
718 1.1 roy }
719 1.1 roy
720 1.1 roy static void
721 1.1 roy merge(TIC *rtic, TIC *utic)
722 1.1 roy {
723 1.1 roy char *cap, flag, *code, type, *str;
724 1.1 roy short ind, num;
725 1.1 roy size_t n;
726 1.1 roy
727 1.1 roy cap = utic->flags.buf;
728 1.1 roy for (n = utic->flags.entries; n > 0; n--) {
729 1.1 roy ind = le16dec(cap);
730 1.1 roy cap += sizeof(uint16_t);
731 1.1 roy flag = *cap++;
732 1.1 roy if (VALID_BOOLEAN(flag) &&
733 1.1 roy find_cap(&rtic->flags, 'f', ind) == NULL)
734 1.1 roy {
735 1.1 roy grow_tbuf(&rtic->flags, sizeof(uint16_t) + 1);
736 1.1 roy le16enc(rtic->flags.buf + rtic->flags.bufpos, ind);
737 1.1 roy rtic->flags.bufpos += sizeof(uint16_t);
738 1.1 roy rtic->flags.buf[rtic->flags.bufpos++] = flag;
739 1.1 roy rtic->flags.entries++;
740 1.1 roy }
741 1.1 roy }
742 1.1 roy
743 1.1 roy cap = utic->nums.buf;
744 1.1 roy for (n = utic->nums.entries; n > 0; n--) {
745 1.1 roy ind = le16dec(cap);
746 1.1 roy cap += sizeof(uint16_t);
747 1.1 roy num = le16dec(cap);
748 1.1 roy cap += sizeof(uint16_t);
749 1.1 roy if (VALID_NUMERIC(num) &&
750 1.1 roy find_cap(&rtic->nums, 'n', ind) == NULL)
751 1.1 roy {
752 1.1 roy grow_tbuf(&rtic->nums, sizeof(uint16_t) * 2);
753 1.1 roy le16enc(rtic->nums.buf + rtic->nums.bufpos, ind);
754 1.1 roy rtic->nums.bufpos += sizeof(uint16_t);
755 1.1 roy le16enc(rtic->nums.buf + rtic->nums.bufpos, num);
756 1.1 roy rtic->nums.bufpos += sizeof(uint16_t);
757 1.1 roy rtic->nums.entries++;
758 1.1 roy }
759 1.1 roy }
760 1.1 roy
761 1.1 roy cap = utic->strs.buf;
762 1.1 roy for (n = utic->strs.entries; n > 0; n--) {
763 1.1 roy ind = le16dec(cap);
764 1.1 roy cap += sizeof(uint16_t);
765 1.1 roy num = le16dec(cap);
766 1.1 roy cap += sizeof(uint16_t);
767 1.1 roy if (num > 0 &&
768 1.1 roy find_cap(&rtic->strs, 's', ind) == NULL)
769 1.1 roy {
770 1.1 roy grow_tbuf(&rtic->strs, (sizeof(uint16_t) * 2) + num);
771 1.1 roy le16enc(rtic->strs.buf + rtic->strs.bufpos, ind);
772 1.1 roy rtic->strs.bufpos += sizeof(uint16_t);
773 1.1 roy le16enc(rtic->strs.buf + rtic->strs.bufpos, num);
774 1.1 roy rtic->strs.bufpos += sizeof(uint16_t);
775 1.1 roy memcpy(rtic->strs.buf + rtic->strs.bufpos,
776 1.1 roy cap, num);
777 1.1 roy rtic->strs.bufpos += num;
778 1.1 roy rtic->strs.entries++;
779 1.1 roy }
780 1.1 roy cap += num;
781 1.1 roy }
782 1.1 roy
783 1.1 roy cap = utic->extras.buf;
784 1.1 roy for (n = utic->extras.entries; n > 0; n--) {
785 1.1 roy num = le16dec(cap);
786 1.1 roy cap += sizeof(uint16_t);
787 1.1 roy code = cap;
788 1.1 roy cap += num;
789 1.1 roy type = *cap++;
790 1.1 roy flag = 0;
791 1.1 roy str = NULL;
792 1.1 roy switch (type) {
793 1.1 roy case 'f':
794 1.1 roy flag = *cap++;
795 1.1 roy if (!VALID_BOOLEAN(flag))
796 1.1 roy continue;
797 1.1 roy break;
798 1.1 roy case 'n':
799 1.1 roy num = le16dec(cap);
800 1.1 roy cap += sizeof(uint16_t);
801 1.1 roy if (!VALID_NUMERIC(num))
802 1.1 roy continue;
803 1.1 roy break;
804 1.1 roy case 's':
805 1.1 roy num = le16dec(cap);
806 1.1 roy cap += sizeof(uint16_t);
807 1.1 roy str = cap;
808 1.1 roy cap += num;
809 1.1 roy if (num == 0)
810 1.1 roy continue;
811 1.1 roy break;
812 1.1 roy }
813 1.1 roy store_extra(rtic, 0, code, type, flag, num, str, num);
814 1.1 roy }
815 1.1 roy }
816 1.1 roy
817 1.1 roy static size_t
818 1.1 roy merge_use(void)
819 1.1 roy {
820 1.1 roy size_t skipped, merged, memn;
821 1.1 roy char *cap, *scap;
822 1.1 roy uint16_t num;
823 1.1 roy TIC *rtic, *utic;
824 1.1 roy TERM *term, *uterm;;
825 1.1 roy
826 1.1 roy skipped = merged = 0;
827 1.1 roy for (term = terms; term != NULL; term = term->next) {
828 1.1 roy if (term->type == 'a')
829 1.1 roy continue;
830 1.1 roy rtic = &term->tic;
831 1.1 roy while ((cap = find_extra(&rtic->extras, "use")) != NULL) {
832 1.1 roy if (*cap++ != 's') {
833 1.1 roy dowarn("%s: use is not string", rtic->name);
834 1.1 roy break;
835 1.1 roy }
836 1.1 roy cap += sizeof(uint16_t);
837 1.1 roy if (strcmp(rtic->name, cap) == 0) {
838 1.1 roy dowarn("%s: uses itself", rtic->name);
839 1.1 roy goto remove;
840 1.1 roy }
841 1.1 roy uterm = find_term(cap);
842 1.1 roy if (uterm != NULL && uterm->type == 'a')
843 1.1 roy uterm = find_term(uterm->tic.name);
844 1.1 roy if (uterm == NULL) {
845 1.1 roy dowarn("%s: no use record for %s",
846 1.1 roy rtic->name, cap);
847 1.1 roy goto remove;
848 1.1 roy }
849 1.1 roy utic = &uterm->tic;
850 1.1 roy if (strcmp(utic->name, rtic->name) == 0) {
851 1.1 roy dowarn("%s: uses itself", rtic->name);
852 1.1 roy goto remove;
853 1.1 roy }
854 1.1 roy if (find_extra(&utic->extras, "use") != NULL) {
855 1.1 roy skipped++;
856 1.1 roy break;
857 1.1 roy }
858 1.1 roy cap = find_extra(&rtic->extras, "use");
859 1.1 roy merge(rtic, utic);
860 1.1 roy remove:
861 1.1 roy /* The pointers may have changed, find the use again */
862 1.1 roy cap = find_extra(&rtic->extras, "use");
863 1.1 roy if (cap == NULL)
864 1.1 roy dowarn("%s: use no longer exists - impossible",
865 1.1 roy rtic->name);
866 1.1 roy else {
867 1.1 roy scap = cap - (4 + sizeof(uint16_t));
868 1.1 roy cap++;
869 1.1 roy num = le16dec(cap);
870 1.1 roy cap += sizeof(uint16_t) + num;
871 1.1 roy memn = rtic->extras.bufpos -
872 1.1 roy (cap - rtic->extras.buf);
873 1.1 roy memcpy(scap, cap, memn);
874 1.1 roy rtic->extras.bufpos -= cap - scap;
875 1.1 roy cap = scap;
876 1.1 roy rtic->extras.entries--;
877 1.1 roy merged++;
878 1.1 roy }
879 1.1 roy }
880 1.1 roy }
881 1.1 roy
882 1.1 roy if (merged == 0 && skipped != 0)
883 1.1 roy dowarn("circular use detected");
884 1.1 roy return merged;
885 1.1 roy }
886 1.1 roy
887 1.5 roy static int
888 1.5 roy print_dump(int argc, char **argv)
889 1.5 roy {
890 1.5 roy TERM *term;
891 1.5 roy TBUF *buf;
892 1.5 roy int i, n;
893 1.5 roy size_t j, col;
894 1.5 roy
895 1.6 roy printf("struct compiled_term {\n");
896 1.6 roy printf("\tconst char *name;\n");
897 1.6 roy printf("\tconst char *cap;\n");
898 1.6 roy printf("\tsize_t caplen;\n");
899 1.6 roy printf("};\n\n");
900 1.6 roy
901 1.6 roy printf("const struct compiled_term compiled_terms[] = {\n");
902 1.6 roy
903 1.5 roy n = 0;
904 1.5 roy for (i = 0; i < argc; i++) {
905 1.5 roy term = find_term(argv[i]);
906 1.5 roy if (term == NULL) {
907 1.5 roy warnx("%s: no description for terminal", argv[i]);
908 1.5 roy continue;
909 1.5 roy }
910 1.5 roy if (term->type == 'a') {
911 1.5 roy warnx("%s: cannot dump alias", argv[i]);
912 1.5 roy continue;
913 1.5 roy }
914 1.5 roy buf = flatten_term(term);
915 1.5 roy if (buf == NULL)
916 1.5 roy continue;
917 1.5 roy
918 1.6 roy printf("\t{\n");
919 1.6 roy printf("\t\t\"%s\",\n", argv[i]);
920 1.5 roy n++;
921 1.5 roy for (j = 0, col = 0; j < buf->bufpos; j++) {
922 1.5 roy if (col == 0) {
923 1.6 roy printf("\t\t\"");
924 1.6 roy col = 16;
925 1.5 roy }
926 1.5 roy
927 1.5 roy col += printf("\\%03o", (uint8_t)buf->buf[j]);
928 1.5 roy if (col > 75) {
929 1.5 roy printf("\"%s\n",
930 1.5 roy j + 1 == buf->bufpos ? "," : "");
931 1.5 roy col = 0;
932 1.5 roy }
933 1.5 roy }
934 1.5 roy if (col != 0)
935 1.6 roy printf("\",\n");
936 1.6 roy printf("\t\t%zu\n", buf->bufpos);
937 1.6 roy printf("\t}");
938 1.6 roy if (i + 1 < argc)
939 1.6 roy printf(",");
940 1.6 roy printf("\n");
941 1.5 roy }
942 1.6 roy printf("};\n");
943 1.5 roy
944 1.5 roy return n;
945 1.5 roy }
946 1.5 roy
947 1.1 roy int
948 1.1 roy main(int argc, char **argv)
949 1.1 roy {
950 1.6 roy int ch, cflag, sflag;
951 1.1 roy char *source, *p, *buf, *ofile;
952 1.1 roy FILE *f;
953 1.1 roy DBM *db;
954 1.1 roy size_t len, buflen, nterm, nalias;
955 1.1 roy TBUF tbuf;
956 1.1 roy TERM *term;
957 1.1 roy
958 1.1 roy cflag = sflag = 0;
959 1.1 roy ofile = NULL;
960 1.5 roy while ((ch = getopt(argc, argv, "Saco:sx")) != -1)
961 1.1 roy switch (ch) {
962 1.5 roy case 'S':
963 1.5 roy Sflag = 1;
964 1.5 roy break;
965 1.1 roy case 'a':
966 1.4 roy aflag = 1;
967 1.1 roy break;
968 1.1 roy case 'c':
969 1.4 roy cflag = 1;
970 1.1 roy break;
971 1.1 roy case 'o':
972 1.1 roy ofile = optarg;
973 1.1 roy break;
974 1.1 roy case 's':
975 1.4 roy sflag = 1;
976 1.1 roy break;
977 1.1 roy case 'x':
978 1.4 roy xflag = 1;
979 1.1 roy break;
980 1.1 roy case '?': /* FALLTHROUGH */
981 1.1 roy default:
982 1.6 roy fprintf(stderr, "usage: %s [-acSsx] [-o file] source\n",
983 1.1 roy getprogname());
984 1.1 roy return EXIT_FAILURE;
985 1.1 roy }
986 1.1 roy
987 1.1 roy if (optind == argc)
988 1.1 roy errx(1, "No source file given");
989 1.1 roy source = argv[optind++];
990 1.1 roy f = fopen(source, "r");
991 1.1 roy if (f == NULL)
992 1.1 roy err(1, "fopen: %s", source);
993 1.6 roy if (!cflag && !Sflag) {
994 1.1 roy if (ofile == NULL)
995 1.1 roy ofile = source;
996 1.1 roy len = strlen(ofile) + 9;
997 1.1 roy dbname = malloc(len + 4); /* For adding .db after open */
998 1.1 roy if (dbname == NULL)
999 1.1 roy err(1, "malloc");
1000 1.1 roy snprintf(dbname, len, "%s.tmp", ofile);
1001 1.1 roy db = dbm_open(dbname, O_CREAT | O_RDWR | O_TRUNC, DEFFILEMODE);
1002 1.1 roy if (db == NULL)
1003 1.1 roy err(1, "dbopen: %s", source);
1004 1.1 roy p = dbname + strlen(dbname);
1005 1.1 roy *p++ = '.';
1006 1.1 roy *p++ = 'd';
1007 1.1 roy *p++ = 'b';
1008 1.1 roy *p++ = '\0';
1009 1.1 roy atexit(do_unlink);
1010 1.1 roy } else
1011 1.1 roy db = NULL; /* satisfy gcc warning */
1012 1.1 roy
1013 1.1 roy tbuf.buflen = tbuf.bufpos = 0;
1014 1.1 roy while ((buf = fgetln(f, &buflen)) != NULL) {
1015 1.1 roy /* Skip comments */
1016 1.1 roy if (*buf == '#')
1017 1.1 roy continue;
1018 1.1 roy if (buf[buflen - 1] != '\n') {
1019 1.1 roy process_entry(&tbuf);
1020 1.1 roy dowarn("last line is not a comment"
1021 1.1 roy " and does not end with a newline");
1022 1.1 roy continue;
1023 1.1 roy }
1024 1.1 roy /*
1025 1.1 roy If the first char is space not a space then we have a
1026 1.1 roy new entry, so process it.
1027 1.1 roy */
1028 1.1 roy if (!isspace((unsigned char)*buf) && tbuf.bufpos != 0)
1029 1.1 roy process_entry(&tbuf);
1030 1.1 roy
1031 1.1 roy /* Grow the buffer if needed */
1032 1.1 roy grow_tbuf(&tbuf, buflen);
1033 1.1 roy /* Append the string */
1034 1.1 roy memcpy(tbuf.buf + tbuf.bufpos, buf, buflen);
1035 1.1 roy tbuf.bufpos += buflen;
1036 1.1 roy }
1037 1.1 roy /* Process the last entry if not done already */
1038 1.1 roy process_entry(&tbuf);
1039 1.1 roy
1040 1.1 roy /* Merge use entries until we have merged all we can */
1041 1.1 roy while (merge_use() != 0)
1042 1.1 roy ;
1043 1.1 roy
1044 1.6 roy if (Sflag) {
1045 1.5 roy print_dump(argc - optind, argv + optind);
1046 1.5 roy return error_exit;
1047 1.5 roy }
1048 1.5 roy
1049 1.6 roy if (cflag)
1050 1.1 roy return error_exit;
1051 1.1 roy
1052 1.1 roy /* Save the terms */
1053 1.1 roy nterm = nalias = 0;
1054 1.1 roy for (term = terms; term != NULL; term = term->next) {
1055 1.1 roy save_term(db, term);
1056 1.1 roy if (term->type == 'a')
1057 1.1 roy nalias++;
1058 1.1 roy else
1059 1.1 roy nterm++;
1060 1.1 roy }
1061 1.1 roy
1062 1.1 roy /* done! */
1063 1.1 roy dbm_close(db);
1064 1.1 roy
1065 1.1 roy /* Rename the tmp db to the real one now */
1066 1.1 roy len = strlen(ofile) + 4;
1067 1.1 roy p = malloc(len);
1068 1.1 roy if (p == NULL)
1069 1.1 roy err(1, "malloc");
1070 1.1 roy snprintf(p, len, "%s.db", ofile);
1071 1.1 roy if (rename(dbname, p) == -1)
1072 1.1 roy err(1, "rename");
1073 1.1 roy free(dbname);
1074 1.1 roy dbname = NULL;
1075 1.1 roy
1076 1.1 roy if (sflag != 0)
1077 1.1 roy fprintf(stderr, "%zu entries and %zu aliases written to %s\n",
1078 1.1 roy nterm, nalias, p);
1079 1.1 roy
1080 1.1 roy return EXIT_SUCCESS;
1081 1.1 roy }
1082