vm, kernel, top: report memory usage of vm, kernel
[minix.git] / usr.bin / tic / tic.c
blobb9209037674554f4b43230704ed8539ee02d27f7
1 /* $NetBSD: tic.c,v 1.19 2012/06/01 12:08:40 joerg Exp $ */
3 /*
4 * Copyright (c) 2009, 2010 The NetBSD Foundation, Inc.
6 * This code is derived from software contributed to The NetBSD Foundation
7 * by Roy Marples.
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.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #if HAVE_NBTOOL_CONFIG_H
31 #include "nbtool_config.h"
32 #endif
34 #include <sys/cdefs.h>
35 __RCSID("$NetBSD: tic.c,v 1.19 2012/06/01 12:08:40 joerg Exp $");
37 #include <sys/types.h>
38 #include <sys/queue.h>
40 #if !HAVE_NBTOOL_CONFIG_H || HAVE_SYS_ENDIAN_H
41 #include <sys/endian.h>
42 #endif
44 #include <ctype.h>
45 #include <err.h>
46 #include <errno.h>
47 #include <getopt.h>
48 #include <limits.h>
49 #include <fcntl.h>
50 #include <ndbm.h>
51 #include <search.h>
52 #include <stdarg.h>
53 #include <stdlib.h>
54 #include <stdio.h>
55 #include <string.h>
56 #include <term_private.h>
57 #include <term.h>
58 #include <util.h>
60 #define HASH_SIZE 16384 /* 2012-06-01: 3600 entries */
62 /* We store the full list of terminals we have instead of iterating
63 through the database as the sequential iterator doesn't work
64 the the data size stored changes N amount which ours will. */
65 typedef struct term {
66 SLIST_ENTRY(term) next;
67 char *name;
68 char type;
69 TIC *tic;
70 } TERM;
71 static SLIST_HEAD(, term) terms = SLIST_HEAD_INITIALIZER(terms);
73 static int error_exit;
74 static int Sflag;
75 static char *dbname;
76 static size_t nterm, nalias;
78 static void
79 do_unlink(void)
82 if (dbname != NULL)
83 unlink(dbname);
86 static void __printflike(1, 2)
87 dowarn(const char *fmt, ...)
89 va_list va;
91 error_exit = 1;
92 va_start(va, fmt);
93 vwarnx(fmt, va);
94 va_end(va);
97 static char *
98 grow_tbuf(TBUF *tbuf, size_t len)
100 char *buf;
102 buf = _ti_grow_tbuf(tbuf, len);
103 if (buf == NULL)
104 err(1, "_ti_grow_tbuf");
105 return buf;
108 static int
109 save_term(DBM *db, TERM *term)
111 uint8_t *buf;
112 ssize_t len;
113 datum key, value;
115 len = _ti_flatten(&buf, term->tic);
116 if (len == -1)
117 return -1;
119 key.dptr = term->name;
120 key.dsize = strlen(term->name);
121 value.dptr = buf;
122 value.dsize = len;
123 if (dbm_store(db, key, value, DBM_REPLACE) == -1)
124 err(1, "dbm_store");
125 free(buf);
126 return 0;
129 static TERM *
130 find_term(const char *name)
132 ENTRY elem, *elemp;
134 elem.key = __UNCONST(name);
135 elem.data = NULL;
136 elemp = hsearch(elem, FIND);
137 return elemp ? (TERM *)elemp->data : NULL;
140 static TERM *
141 store_term(const char *name, char type)
143 TERM *term;
144 ENTRY elem;
146 term = ecalloc(1, sizeof(*term));
147 term->name = estrdup(name);
148 term->type = type;
149 SLIST_INSERT_HEAD(&terms, term, next);
150 elem.key = estrdup(name);
151 elem.data = term;
152 hsearch(elem, ENTER);
154 if (type == 'a')
155 nalias++;
156 else
157 nterm++;
159 return term;
162 static int
163 process_entry(TBUF *buf, int flags)
165 char *p, *e, *alias;
166 TERM *term;
167 TIC *tic;
169 if (buf->bufpos == 0)
170 return 0;
171 /* Terminate the string */
172 buf->buf[buf->bufpos - 1] = '\0';
173 /* First rewind the buffer for new entries */
174 buf->bufpos = 0;
176 if (isspace((unsigned char)*buf->buf))
177 return 0;
179 tic = _ti_compile(buf->buf, flags);
180 if (tic == NULL)
181 return 0;
183 if (find_term(tic->name) != NULL) {
184 dowarn("%s: duplicate entry", tic->name);
185 _ti_freetic(tic);
186 return 0;
188 term = store_term(tic->name, 't');
189 term->tic = tic;
191 /* Create aliased terms */
192 if (tic->alias != NULL) {
193 alias = p = estrdup(tic->alias);
194 while (p != NULL && *p != '\0') {
195 e = strchr(p, '|');
196 if (e != NULL)
197 *e++ = '\0';
198 if (find_term(p) != NULL) {
199 dowarn("%s: has alias for already assigned"
200 " term %s", tic->name, p);
201 } else {
202 term = store_term(p, 'a');
203 term->tic = ecalloc(sizeof(*term->tic), 1);
204 term->tic->name = estrdup(tic->name);
206 p = e;
208 free(alias);
211 return 0;
214 static void
215 merge(TIC *rtic, TIC *utic, int flags)
217 char *cap, flag, *code, type, *str;
218 short ind, num;
219 size_t n;
221 cap = utic->flags.buf;
222 for (n = utic->flags.entries; n > 0; n--) {
223 ind = le16dec(cap);
224 cap += sizeof(uint16_t);
225 flag = *cap++;
226 if (VALID_BOOLEAN(flag) &&
227 _ti_find_cap(&rtic->flags, 'f', ind) == NULL)
229 _ti_grow_tbuf(&rtic->flags, sizeof(uint16_t) + 1);
230 le16enc(rtic->flags.buf + rtic->flags.bufpos, ind);
231 rtic->flags.bufpos += sizeof(uint16_t);
232 rtic->flags.buf[rtic->flags.bufpos++] = flag;
233 rtic->flags.entries++;
237 cap = utic->nums.buf;
238 for (n = utic->nums.entries; n > 0; n--) {
239 ind = le16dec(cap);
240 cap += sizeof(uint16_t);
241 num = le16dec(cap);
242 cap += sizeof(uint16_t);
243 if (VALID_NUMERIC(num) &&
244 _ti_find_cap(&rtic->nums, 'n', ind) == NULL)
246 grow_tbuf(&rtic->nums, sizeof(uint16_t) * 2);
247 le16enc(rtic->nums.buf + rtic->nums.bufpos, ind);
248 rtic->nums.bufpos += sizeof(uint16_t);
249 le16enc(rtic->nums.buf + rtic->nums.bufpos, num);
250 rtic->nums.bufpos += sizeof(uint16_t);
251 rtic->nums.entries++;
255 cap = utic->strs.buf;
256 for (n = utic->strs.entries; n > 0; n--) {
257 ind = le16dec(cap);
258 cap += sizeof(uint16_t);
259 num = le16dec(cap);
260 cap += sizeof(uint16_t);
261 if (num > 0 &&
262 _ti_find_cap(&rtic->strs, 's', ind) == NULL)
264 grow_tbuf(&rtic->strs, (sizeof(uint16_t) * 2) + num);
265 le16enc(rtic->strs.buf + rtic->strs.bufpos, ind);
266 rtic->strs.bufpos += sizeof(uint16_t);
267 le16enc(rtic->strs.buf + rtic->strs.bufpos, num);
268 rtic->strs.bufpos += sizeof(uint16_t);
269 memcpy(rtic->strs.buf + rtic->strs.bufpos,
270 cap, num);
271 rtic->strs.bufpos += num;
272 rtic->strs.entries++;
274 cap += num;
277 cap = utic->extras.buf;
278 for (n = utic->extras.entries; n > 0; n--) {
279 num = le16dec(cap);
280 cap += sizeof(uint16_t);
281 code = cap;
282 cap += num;
283 type = *cap++;
284 flag = 0;
285 str = NULL;
286 switch (type) {
287 case 'f':
288 flag = *cap++;
289 if (!VALID_BOOLEAN(flag))
290 continue;
291 break;
292 case 'n':
293 num = le16dec(cap);
294 cap += sizeof(uint16_t);
295 if (!VALID_NUMERIC(num))
296 continue;
297 break;
298 case 's':
299 num = le16dec(cap);
300 cap += sizeof(uint16_t);
301 str = cap;
302 cap += num;
303 if (num == 0)
304 continue;
305 break;
307 _ti_store_extra(rtic, 0, code, type, flag, num, str, num,
308 flags);
312 static size_t
313 merge_use(int flags)
315 size_t skipped, merged, memn;
316 char *cap, *scap;
317 uint16_t num;
318 TIC *rtic, *utic;
319 TERM *term, *uterm;;
321 skipped = merged = 0;
322 SLIST_FOREACH(term, &terms, next) {
323 if (term->type == 'a')
324 continue;
325 rtic = term->tic;
326 while ((cap = _ti_find_extra(&rtic->extras, "use")) != NULL) {
327 if (*cap++ != 's') {
328 dowarn("%s: use is not string", rtic->name);
329 break;
331 cap += sizeof(uint16_t);
332 if (strcmp(rtic->name, cap) == 0) {
333 dowarn("%s: uses itself", rtic->name);
334 goto remove;
336 uterm = find_term(cap);
337 if (uterm != NULL && uterm->type == 'a')
338 uterm = find_term(uterm->tic->name);
339 if (uterm == NULL) {
340 dowarn("%s: no use record for %s",
341 rtic->name, cap);
342 goto remove;
344 utic = uterm->tic;
345 if (strcmp(utic->name, rtic->name) == 0) {
346 dowarn("%s: uses itself", rtic->name);
347 goto remove;
349 if (_ti_find_extra(&utic->extras, "use") != NULL) {
350 skipped++;
351 break;
353 cap = _ti_find_extra(&rtic->extras, "use");
354 merge(rtic, utic, flags);
355 remove:
356 /* The pointers may have changed, find the use again */
357 cap = _ti_find_extra(&rtic->extras, "use");
358 if (cap == NULL)
359 dowarn("%s: use no longer exists - impossible",
360 rtic->name);
361 else {
362 scap = cap - (4 + sizeof(uint16_t));
363 cap++;
364 num = le16dec(cap);
365 cap += sizeof(uint16_t) + num;
366 memn = rtic->extras.bufpos -
367 (cap - rtic->extras.buf);
368 memmove(scap, cap, memn);
369 rtic->extras.bufpos -= cap - scap;
370 cap = scap;
371 rtic->extras.entries--;
372 merged++;
377 if (merged == 0 && skipped != 0)
378 dowarn("circular use detected");
379 return merged;
382 static int
383 print_dump(int argc, char **argv)
385 TERM *term;
386 uint8_t *buf;
387 int i, n;
388 size_t j, col;
389 ssize_t len;
391 printf("struct compiled_term {\n");
392 printf("\tconst char *name;\n");
393 printf("\tconst char *cap;\n");
394 printf("\tsize_t caplen;\n");
395 printf("};\n\n");
397 printf("const struct compiled_term compiled_terms[] = {\n");
399 n = 0;
400 for (i = 0; i < argc; i++) {
401 term = find_term(argv[i]);
402 if (term == NULL) {
403 warnx("%s: no description for terminal", argv[i]);
404 continue;
406 if (term->type == 'a') {
407 warnx("%s: cannot dump alias", argv[i]);
408 continue;
410 /* Don't compile the aliases in, save space */
411 free(term->tic->alias);
412 term->tic->alias = NULL;
413 len = _ti_flatten(&buf, term->tic);
414 if (len == 0 || len == -1)
415 continue;
417 printf("\t{\n");
418 printf("\t\t\"%s\",\n", argv[i]);
419 n++;
420 for (j = 0, col = 0; j < (size_t)len; j++) {
421 if (col == 0) {
422 printf("\t\t\"");
423 col = 16;
426 col += printf("\\%03o", (uint8_t)buf[j]);
427 if (col > 75) {
428 printf("\"%s\n",
429 j + 1 == (size_t)len ? "," : "");
430 col = 0;
433 if (col != 0)
434 printf("\",\n");
435 printf("\t\t%zu\n", len);
436 printf("\t}");
437 if (i + 1 < argc)
438 printf(",");
439 printf("\n");
440 free(buf);
442 printf("};\n");
444 return n;
448 main(int argc, char **argv)
450 int ch, cflag, sflag, flags;
451 char *source, *p, *buf, *ofile;
452 FILE *f;
453 DBM *db;
454 size_t buflen;
455 ssize_t len;
456 TBUF tbuf;
457 TERM *term;
459 cflag = sflag = 0;
460 ofile = NULL;
461 flags = TIC_ALIAS | TIC_DESCRIPTION | TIC_WARNING;
462 while ((ch = getopt(argc, argv, "Saco:sx")) != -1)
463 switch (ch) {
464 case 'S':
465 Sflag = 1;
466 /* We still compile aliases so that use= works.
467 * However, it's removed before we flatten to save space. */
468 flags &= ~TIC_DESCRIPTION;
469 break;
470 case 'a':
471 flags |= TIC_COMMENT;
472 break;
473 case 'c':
474 cflag = 1;
475 break;
476 case 'o':
477 ofile = optarg;
478 break;
479 case 's':
480 sflag = 1;
481 break;
482 case 'x':
483 flags |= TIC_EXTRA;
484 break;
485 case '?': /* FALLTHROUGH */
486 default:
487 fprintf(stderr, "usage: %s [-acSsx] [-o file] source\n",
488 getprogname());
489 return EXIT_FAILURE;
492 if (optind == argc)
493 errx(1, "No source file given");
494 source = argv[optind++];
495 f = fopen(source, "r");
496 if (f == NULL)
497 err(1, "fopen: %s", source);
498 if (!cflag && !Sflag) {
499 if (ofile == NULL)
500 ofile = source;
501 len = strlen(ofile) + 9;
502 dbname = emalloc(len + 4); /* For adding .db after open */
503 snprintf(dbname, len, "%s.tmp", ofile);
504 db = dbm_open(dbname, O_CREAT | O_RDWR | O_TRUNC, DEFFILEMODE);
505 if (db == NULL)
506 err(1, "dbopen: %s", source);
507 p = dbname + strlen(dbname);
508 *p++ = '.';
509 *p++ = 'd';
510 *p++ = 'b';
511 *p++ = '\0';
512 atexit(do_unlink);
513 } else
514 db = NULL; /* satisfy gcc warning */
516 hcreate(HASH_SIZE);
518 buf = tbuf.buf = NULL;
519 buflen = tbuf.buflen = tbuf.bufpos = 0;
520 while ((len = getline(&buf, &buflen, f)) != -1) {
521 /* Skip comments */
522 if (*buf == '#')
523 continue;
524 if (buf[len - 1] != '\n') {
525 process_entry(&tbuf, flags);
526 dowarn("last line is not a comment"
527 " and does not end with a newline");
528 continue;
531 If the first char is space not a space then we have a
532 new entry, so process it.
534 if (!isspace((unsigned char)*buf) && tbuf.bufpos != 0)
535 process_entry(&tbuf, flags);
537 /* Grow the buffer if needed */
538 grow_tbuf(&tbuf, len);
539 /* Append the string */
540 memcpy(tbuf.buf + tbuf.bufpos, buf, len);
541 tbuf.bufpos += len;
543 free(buf);
544 /* Process the last entry if not done already */
545 process_entry(&tbuf, flags);
546 free(tbuf.buf);
548 /* Merge use entries until we have merged all we can */
549 while (merge_use(flags) != 0)
552 if (Sflag) {
553 print_dump(argc - optind, argv + optind);
554 return error_exit;
557 if (cflag)
558 return error_exit;
560 /* Save the terms */
561 SLIST_FOREACH(term, &terms, next)
562 save_term(db, term);
564 /* done! */
565 dbm_close(db);
567 /* Rename the tmp db to the real one now */
568 easprintf(&p, "%s.db", ofile);
569 if (rename(dbname, p) == -1)
570 err(1, "rename");
571 free(dbname);
572 dbname = NULL;
574 if (sflag != 0)
575 fprintf(stderr, "%zu entries and %zu aliases written to %s\n",
576 nterm, nalias, p);
578 #ifdef __VALGRIND__
579 free(p);
580 while ((term = SLIST_FIRST(&terms)) != NULL) {
581 SLIST_REMOVE_HEAD(&terms, next);
582 _ti_freetic(term->tic);
583 free(term->name);
584 free(term);
586 hdestroy();
587 #endif
590 return EXIT_SUCCESS;