1 1.42 christos /* $NetBSD: tic.c,v 1.42 2024/05/20 14:41:37 christos Exp $ */ 2 1.1 roy 3 1.1 roy /* 4 1.32 roy * Copyright (c) 2009, 2010, 2020 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.42 christos __RCSID("$NetBSD: tic.c,v 1.42 2024/05/20 14:41:37 christos Exp $"); 36 1.1 roy 37 1.1 roy #include <sys/types.h> 38 1.14 joerg #include <sys/queue.h> 39 1.27 christos #include <sys/stat.h> 40 1.8 pgoyette 41 1.9 pgoyette #if !HAVE_NBTOOL_CONFIG_H || HAVE_SYS_ENDIAN_H 42 1.7 pgoyette #include <sys/endian.h> 43 1.8 pgoyette #endif 44 1.1 roy 45 1.20 joerg #include <cdbw.h> 46 1.1 roy #include <ctype.h> 47 1.1 roy #include <err.h> 48 1.1 roy #include <errno.h> 49 1.1 roy #include <getopt.h> 50 1.1 roy #include <limits.h> 51 1.1 roy #include <fcntl.h> 52 1.15 joerg #include <search.h> 53 1.1 roy #include <stdarg.h> 54 1.39 roy #include <stdbool.h> 55 1.1 roy #include <stdlib.h> 56 1.1 roy #include <stdio.h> 57 1.1 roy #include <string.h> 58 1.1 roy #include <term_private.h> 59 1.1 roy #include <term.h> 60 1.31 joerg #include <unistd.h> 61 1.23 joerg #include <util.h> 62 1.15 joerg 63 1.15 joerg #define HASH_SIZE 16384 /* 2012-06-01: 3600 entries */ 64 1.1 roy 65 1.1 roy typedef struct term { 66 1.20 joerg STAILQ_ENTRY(term) next; 67 1.1 roy char *name; 68 1.10 roy TIC *tic; 69 1.20 joerg uint32_t id; 70 1.20 joerg struct term *base_term; 71 1.1 roy } TERM; 72 1.20 joerg static STAILQ_HEAD(, term) terms = STAILQ_HEAD_INITIALIZER(terms); 73 1.1 roy 74 1.1 roy static int error_exit; 75 1.10 roy static int Sflag; 76 1.18 joerg static size_t nterm, nalias; 77 1.1 roy 78 1.13 joerg static void __printflike(1, 2) 79 1.1 roy dowarn(const char *fmt, ...) 80 1.1 roy { 81 1.1 roy va_list va; 82 1.1 roy 83 1.1 roy error_exit = 1; 84 1.1 roy va_start(va, fmt); 85 1.1 roy vwarnx(fmt, va); 86 1.1 roy va_end(va); 87 1.1 roy } 88 1.1 roy 89 1.1 roy static char * 90 1.1 roy grow_tbuf(TBUF *tbuf, size_t len) 91 1.1 roy { 92 1.1 roy char *buf; 93 1.1 roy 94 1.10 roy buf = _ti_grow_tbuf(tbuf, len); 95 1.10 roy if (buf == NULL) 96 1.37 roy err(EXIT_FAILURE, "_ti_grow_tbuf"); 97 1.10 roy return buf; 98 1.5 roy } 99 1.5 roy 100 1.5 roy static int 101 1.20 joerg save_term(struct cdbw *db, TERM *term) 102 1.5 roy { 103 1.10 roy uint8_t *buf; 104 1.10 roy ssize_t len; 105 1.20 joerg size_t slen = strlen(term->name) + 1; 106 1.20 joerg 107 1.20 joerg if (term->base_term != NULL) { 108 1.34 christos char *cap; 109 1.34 christos len = (ssize_t)(1 + sizeof(uint32_t) + sizeof(uint16_t) + slen); 110 1.20 joerg buf = emalloc(len); 111 1.34 christos cap = (char *)buf; 112 1.34 christos *cap++ = TERMINFO_ALIAS; 113 1.34 christos _ti_encode_32(&cap, term->base_term->id); 114 1.34 christos _ti_encode_count_str(&cap, term->name, slen); 115 1.20 joerg if (cdbw_put(db, term->name, slen, buf, len)) 116 1.37 roy err(EXIT_FAILURE, "cdbw_put"); 117 1.26 christos free(buf); 118 1.20 joerg return 0; 119 1.20 joerg } 120 1.5 roy 121 1.10 roy len = _ti_flatten(&buf, term->tic); 122 1.10 roy if (len == -1) 123 1.5 roy return -1; 124 1.1 roy 125 1.20 joerg if (cdbw_put_data(db, buf, len, &term->id)) 126 1.37 roy err(EXIT_FAILURE, "cdbw_put_data"); 127 1.20 joerg if (cdbw_put_key(db, term->name, slen, term->id)) 128 1.37 roy err(EXIT_FAILURE, "cdbw_put_key"); 129 1.10 roy free(buf); 130 1.1 roy return 0; 131 1.1 roy } 132 1.1 roy 133 1.1 roy static TERM * 134 1.1 roy find_term(const char *name) 135 1.1 roy { 136 1.15 joerg ENTRY elem, *elemp; 137 1.14 joerg 138 1.15 joerg elem.key = __UNCONST(name); 139 1.15 joerg elem.data = NULL; 140 1.15 joerg elemp = hsearch(elem, FIND); 141 1.15 joerg return elemp ? (TERM *)elemp->data : NULL; 142 1.1 roy } 143 1.1 roy 144 1.1 roy static TERM * 145 1.39 roy find_newest_term(const char *name) 146 1.39 roy { 147 1.39 roy char *lname; 148 1.39 roy TERM *term; 149 1.39 roy 150 1.39 roy lname = _ti_getname(TERMINFO_RTYPE, name); 151 1.39 roy if (lname == NULL) 152 1.39 roy return NULL; 153 1.39 roy term = find_term(lname); 154 1.39 roy free(lname); 155 1.39 roy if (term == NULL) 156 1.39 roy term = find_term(name); 157 1.39 roy return term; 158 1.39 roy } 159 1.39 roy 160 1.39 roy static TERM * 161 1.20 joerg store_term(const char *name, TERM *base_term) 162 1.1 roy { 163 1.1 roy TERM *term; 164 1.15 joerg ENTRY elem; 165 1.1 roy 166 1.16 joerg term = ecalloc(1, sizeof(*term)); 167 1.16 joerg term->name = estrdup(name); 168 1.20 joerg STAILQ_INSERT_TAIL(&terms, term, next); 169 1.15 joerg elem.key = estrdup(name); 170 1.15 joerg elem.data = term; 171 1.15 joerg hsearch(elem, ENTER); 172 1.18 joerg 173 1.20 joerg term->base_term = base_term; 174 1.20 joerg if (base_term != NULL) 175 1.18 joerg nalias++; 176 1.18 joerg else 177 1.18 joerg nterm++; 178 1.18 joerg 179 1.1 roy return term; 180 1.1 roy } 181 1.1 roy 182 1.40 roy static void 183 1.40 roy alias_terms(TERM *term) 184 1.40 roy { 185 1.40 roy char *p, *e, *alias; 186 1.40 roy 187 1.40 roy /* Create aliased terms */ 188 1.40 roy if (term->tic->alias == NULL) 189 1.40 roy return; 190 1.40 roy 191 1.40 roy alias = p = estrdup(term->tic->alias); 192 1.40 roy while (p != NULL && *p != '\0') { 193 1.40 roy e = strchr(p, '|'); 194 1.40 roy if (e != NULL) 195 1.40 roy *e++ = '\0'; 196 1.40 roy /* No need to lengthcheck the alias because the main 197 1.40 roy * terminfo description already stores all the aliases 198 1.40 roy * in the same length field as the alias. */ 199 1.40 roy if (find_term(p) != NULL) { 200 1.40 roy dowarn("%s: has alias for already assigned" 201 1.40 roy " term %s", term->tic->name, p); 202 1.40 roy } else { 203 1.40 roy store_term(p, term); 204 1.40 roy } 205 1.40 roy p = e; 206 1.40 roy } 207 1.40 roy free(alias); 208 1.40 roy } 209 1.40 roy 210 1.1 roy static int 211 1.10 roy process_entry(TBUF *buf, int flags) 212 1.1 roy { 213 1.1 roy TERM *term; 214 1.1 roy TIC *tic; 215 1.33 christos TBUF sbuf = *buf; 216 1.25 roy 217 1.1 roy if (buf->bufpos == 0) 218 1.1 roy return 0; 219 1.1 roy /* Terminate the string */ 220 1.1 roy buf->buf[buf->bufpos - 1] = '\0'; 221 1.1 roy /* First rewind the buffer for new entries */ 222 1.1 roy buf->bufpos = 0; 223 1.1 roy 224 1.1 roy if (isspace((unsigned char)*buf->buf)) 225 1.1 roy return 0; 226 1.1 roy 227 1.10 roy tic = _ti_compile(buf->buf, flags); 228 1.10 roy if (tic == NULL) 229 1.1 roy return 0; 230 1.10 roy 231 1.10 roy if (find_term(tic->name) != NULL) { 232 1.10 roy dowarn("%s: duplicate entry", tic->name); 233 1.10 roy _ti_freetic(tic); 234 1.1 roy return 0; 235 1.1 roy } 236 1.20 joerg term = store_term(tic->name, NULL); 237 1.10 roy term->tic = tic; 238 1.40 roy alias_terms(term); 239 1.25 roy 240 1.33 christos if (tic->rtype == TERMINFO_RTYPE) 241 1.33 christos return process_entry(&sbuf, flags | TIC_COMPAT_V1); 242 1.33 christos 243 1.1 roy return 0; 244 1.1 roy } 245 1.1 roy 246 1.1 roy static void 247 1.10 roy merge(TIC *rtic, TIC *utic, int flags) 248 1.1 roy { 249 1.33 christos char flag, type; 250 1.33 christos const char *cap, *code, *str; 251 1.32 roy short ind, len; 252 1.32 roy int num; 253 1.1 roy size_t n; 254 1.1 roy 255 1.39 roy if (rtic->rtype < utic->rtype) 256 1.39 roy errx(EXIT_FAILURE, "merge rtype diff (%s:%d into %s:%d)", 257 1.39 roy utic->name, utic->rtype, rtic->name, rtic->rtype); 258 1.35 roy 259 1.1 roy cap = utic->flags.buf; 260 1.1 roy for (n = utic->flags.entries; n > 0; n--) { 261 1.34 christos ind = _ti_decode_16(&cap); 262 1.1 roy flag = *cap++; 263 1.1 roy if (VALID_BOOLEAN(flag) && 264 1.33 christos _ti_find_cap(rtic, &rtic->flags, 'f', ind) == NULL) 265 1.1 roy { 266 1.34 christos if (!_ti_encode_buf_id_flags(&rtic->flags, ind, flag)) 267 1.37 roy err(EXIT_FAILURE, "encode flag"); 268 1.1 roy } 269 1.1 roy } 270 1.1 roy 271 1.1 roy cap = utic->nums.buf; 272 1.1 roy for (n = utic->nums.entries; n > 0; n--) { 273 1.34 christos ind = _ti_decode_16(&cap); 274 1.34 christos num = _ti_decode_num(&cap, utic->rtype); 275 1.1 roy if (VALID_NUMERIC(num) && 276 1.33 christos _ti_find_cap(rtic, &rtic->nums, 'n', ind) == NULL) 277 1.1 roy { 278 1.34 christos if (!_ti_encode_buf_id_num(&rtic->nums, ind, num, 279 1.34 christos _ti_numsize(rtic))) 280 1.37 roy err(EXIT_FAILURE, "encode num"); 281 1.1 roy } 282 1.1 roy } 283 1.1 roy 284 1.1 roy cap = utic->strs.buf; 285 1.1 roy for (n = utic->strs.entries; n > 0; n--) { 286 1.34 christos ind = _ti_decode_16(&cap); 287 1.34 christos len = _ti_decode_16(&cap); 288 1.32 roy if (len > 0 && 289 1.33 christos _ti_find_cap(rtic, &rtic->strs, 's', ind) == NULL) 290 1.1 roy { 291 1.34 christos if (!_ti_encode_buf_id_count_str(&rtic->strs, ind, cap, 292 1.34 christos len)) 293 1.37 roy err(EXIT_FAILURE, "encode str"); 294 1.1 roy } 295 1.32 roy cap += len; 296 1.1 roy } 297 1.1 roy 298 1.1 roy cap = utic->extras.buf; 299 1.1 roy for (n = utic->extras.entries; n > 0; n--) { 300 1.34 christos num = _ti_decode_16(&cap); 301 1.1 roy code = cap; 302 1.1 roy cap += num; 303 1.1 roy type = *cap++; 304 1.1 roy flag = 0; 305 1.1 roy str = NULL; 306 1.1 roy switch (type) { 307 1.1 roy case 'f': 308 1.1 roy flag = *cap++; 309 1.1 roy if (!VALID_BOOLEAN(flag)) 310 1.1 roy continue; 311 1.1 roy break; 312 1.1 roy case 'n': 313 1.34 christos num = _ti_decode_num(&cap, utic->rtype); 314 1.1 roy if (!VALID_NUMERIC(num)) 315 1.1 roy continue; 316 1.1 roy break; 317 1.1 roy case 's': 318 1.34 christos num = _ti_decode_16(&cap); 319 1.1 roy str = cap; 320 1.1 roy cap += num; 321 1.1 roy if (num == 0) 322 1.1 roy continue; 323 1.1 roy break; 324 1.1 roy } 325 1.10 roy _ti_store_extra(rtic, 0, code, type, flag, num, str, num, 326 1.10 roy flags); 327 1.1 roy } 328 1.1 roy } 329 1.1 roy 330 1.39 roy static int 331 1.39 roy dup_tbuf(TBUF *dst, const TBUF *src) 332 1.39 roy { 333 1.39 roy 334 1.39 roy if (src->buflen == 0) 335 1.39 roy return 0; 336 1.39 roy dst->buf = malloc(src->buflen); 337 1.39 roy if (dst->buf == NULL) 338 1.39 roy return -1; 339 1.39 roy dst->buflen = src->buflen; 340 1.39 roy memcpy(dst->buf, src->buf, dst->buflen); 341 1.39 roy dst->bufpos = src->bufpos; 342 1.39 roy dst->entries = src->entries; 343 1.39 roy return 0; 344 1.39 roy } 345 1.39 roy 346 1.39 roy static int 347 1.39 roy promote(TIC *rtic, TIC *utic) 348 1.39 roy { 349 1.39 roy TERM *nrterm = find_newest_term(rtic->name); 350 1.39 roy TERM *nuterm = find_newest_term(utic->name); 351 1.39 roy TERM *term; 352 1.39 roy TIC *tic; 353 1.39 roy 354 1.39 roy if (nrterm == NULL || nuterm == NULL) 355 1.39 roy return -1; 356 1.39 roy if (nrterm->tic->rtype >= nuterm->tic->rtype) 357 1.39 roy return 0; 358 1.39 roy 359 1.39 roy tic = calloc(1, sizeof(*tic)); 360 1.39 roy if (tic == NULL) 361 1.39 roy return -1; 362 1.39 roy 363 1.39 roy tic->name = _ti_getname(TERMINFO_RTYPE, rtic->name); 364 1.39 roy if (tic->name == NULL) 365 1.39 roy goto err; 366 1.40 roy if (rtic->alias != NULL) { 367 1.40 roy tic->alias = strdup(rtic->alias); 368 1.40 roy if (tic->alias == NULL) 369 1.40 roy goto err; 370 1.40 roy } 371 1.39 roy if (rtic->desc != NULL) { 372 1.39 roy tic->desc = strdup(rtic->desc); 373 1.39 roy if (tic->desc == NULL) 374 1.39 roy goto err; 375 1.39 roy } 376 1.39 roy 377 1.39 roy tic->rtype = rtic->rtype; 378 1.39 roy if (dup_tbuf(&tic->flags, &rtic->flags) == -1) 379 1.39 roy goto err; 380 1.39 roy if (dup_tbuf(&tic->nums, &rtic->nums) == -1) 381 1.39 roy goto err; 382 1.39 roy if (dup_tbuf(&tic->strs, &rtic->strs) == -1) 383 1.39 roy goto err; 384 1.39 roy if (dup_tbuf(&tic->extras, &rtic->extras) == -1) 385 1.39 roy goto err; 386 1.39 roy if (_ti_promote(tic) == -1) 387 1.39 roy goto err; 388 1.39 roy 389 1.39 roy term = store_term(tic->name, NULL); 390 1.40 roy if (term == NULL) 391 1.40 roy goto err; 392 1.40 roy 393 1.40 roy term->tic = tic; 394 1.40 roy alias_terms(term); 395 1.40 roy return 0; 396 1.39 roy 397 1.39 roy err: 398 1.39 roy free(tic->flags.buf); 399 1.39 roy free(tic->nums.buf); 400 1.39 roy free(tic->strs.buf); 401 1.39 roy free(tic->extras.buf); 402 1.39 roy free(tic->desc); 403 1.40 roy free(tic->alias); 404 1.39 roy free(tic->name); 405 1.39 roy free(tic); 406 1.39 roy return -1; 407 1.39 roy } 408 1.39 roy 409 1.1 roy static size_t 410 1.10 roy merge_use(int flags) 411 1.1 roy { 412 1.1 roy size_t skipped, merged, memn; 413 1.34 christos const char *cap; 414 1.39 roy char *name, *basename; 415 1.1 roy uint16_t num; 416 1.1 roy TIC *rtic, *utic; 417 1.39 roy TERM *term, *uterm; 418 1.39 roy bool promoted; 419 1.1 roy 420 1.1 roy skipped = merged = 0; 421 1.20 joerg STAILQ_FOREACH(term, &terms, next) { 422 1.20 joerg if (term->base_term != NULL) 423 1.1 roy continue; 424 1.10 roy rtic = term->tic; 425 1.39 roy basename = _ti_getname(TERMINFO_RTYPE_O1, rtic->name); 426 1.39 roy promoted = false; 427 1.33 christos while ((cap = _ti_find_extra(rtic, &rtic->extras, "use")) 428 1.33 christos != NULL) { 429 1.1 roy if (*cap++ != 's') { 430 1.1 roy dowarn("%s: use is not string", rtic->name); 431 1.1 roy break; 432 1.1 roy } 433 1.1 roy cap += sizeof(uint16_t); 434 1.39 roy if (strcmp(basename, cap) == 0) { 435 1.1 roy dowarn("%s: uses itself", rtic->name); 436 1.1 roy goto remove; 437 1.1 roy } 438 1.39 roy name = _ti_getname(rtic->rtype, cap); 439 1.39 roy if (name == NULL) { 440 1.39 roy dowarn("%s: ???: %s", rtic->name, cap); 441 1.39 roy goto remove; 442 1.39 roy } 443 1.39 roy uterm = find_term(name); 444 1.39 roy free(name); 445 1.39 roy if (uterm == NULL) 446 1.39 roy uterm = find_term(cap); 447 1.20 joerg if (uterm != NULL && uterm->base_term != NULL) 448 1.20 joerg uterm = uterm->base_term; 449 1.1 roy if (uterm == NULL) { 450 1.1 roy dowarn("%s: no use record for %s", 451 1.1 roy rtic->name, cap); 452 1.1 roy goto remove; 453 1.1 roy } 454 1.10 roy utic = uterm->tic; 455 1.1 roy if (strcmp(utic->name, rtic->name) == 0) { 456 1.1 roy dowarn("%s: uses itself", rtic->name); 457 1.1 roy goto remove; 458 1.1 roy } 459 1.33 christos if (_ti_find_extra(utic, &utic->extras, "use") 460 1.33 christos != NULL) { 461 1.1 roy skipped++; 462 1.1 roy break; 463 1.1 roy } 464 1.39 roy 465 1.39 roy /* If we need to merge in a term that requires 466 1.39 roy * this term to be promoted, we need to duplicate 467 1.39 roy * this term, promote it and append it to our list. */ 468 1.39 roy if (!promoted && rtic->rtype != TERMINFO_RTYPE) { 469 1.39 roy if (promote(rtic, utic) == -1) 470 1.39 roy err(EXIT_FAILURE, "promote"); 471 1.41 christos promoted = rtic->rtype == TERMINFO_RTYPE; 472 1.39 roy } 473 1.39 roy 474 1.10 roy merge(rtic, utic, flags); 475 1.1 roy remove: 476 1.1 roy /* The pointers may have changed, find the use again */ 477 1.33 christos cap = _ti_find_extra(rtic, &rtic->extras, "use"); 478 1.1 roy if (cap == NULL) 479 1.1 roy dowarn("%s: use no longer exists - impossible", 480 1.1 roy rtic->name); 481 1.1 roy else { 482 1.34 christos char *scap = __UNCONST( 483 1.34 christos cap - (4 + sizeof(uint16_t))); 484 1.1 roy cap++; 485 1.34 christos num = _ti_decode_16(&cap); 486 1.34 christos cap += num; 487 1.1 roy memn = rtic->extras.bufpos - 488 1.1 roy (cap - rtic->extras.buf); 489 1.11 roy memmove(scap, cap, memn); 490 1.1 roy rtic->extras.bufpos -= cap - scap; 491 1.1 roy cap = scap; 492 1.1 roy rtic->extras.entries--; 493 1.1 roy merged++; 494 1.1 roy } 495 1.1 roy } 496 1.39 roy free(basename); 497 1.1 roy } 498 1.1 roy 499 1.1 roy if (merged == 0 && skipped != 0) 500 1.1 roy dowarn("circular use detected"); 501 1.1 roy return merged; 502 1.1 roy } 503 1.1 roy 504 1.5 roy static int 505 1.5 roy print_dump(int argc, char **argv) 506 1.5 roy { 507 1.5 roy TERM *term; 508 1.10 roy uint8_t *buf; 509 1.5 roy int i, n; 510 1.5 roy size_t j, col; 511 1.10 roy ssize_t len; 512 1.5 roy 513 1.6 roy printf("struct compiled_term {\n"); 514 1.6 roy printf("\tconst char *name;\n"); 515 1.6 roy printf("\tconst char *cap;\n"); 516 1.6 roy printf("\tsize_t caplen;\n"); 517 1.6 roy printf("};\n\n"); 518 1.6 roy 519 1.6 roy printf("const struct compiled_term compiled_terms[] = {\n"); 520 1.6 roy 521 1.5 roy n = 0; 522 1.5 roy for (i = 0; i < argc; i++) { 523 1.39 roy term = find_newest_term(argv[i]); 524 1.5 roy if (term == NULL) { 525 1.5 roy warnx("%s: no description for terminal", argv[i]); 526 1.5 roy continue; 527 1.5 roy } 528 1.20 joerg if (term->base_term != NULL) { 529 1.5 roy warnx("%s: cannot dump alias", argv[i]); 530 1.5 roy continue; 531 1.5 roy } 532 1.10 roy /* Don't compile the aliases in, save space */ 533 1.10 roy free(term->tic->alias); 534 1.10 roy term->tic->alias = NULL; 535 1.10 roy len = _ti_flatten(&buf, term->tic); 536 1.10 roy if (len == 0 || len == -1) 537 1.5 roy continue; 538 1.5 roy 539 1.6 roy printf("\t{\n"); 540 1.6 roy printf("\t\t\"%s\",\n", argv[i]); 541 1.5 roy n++; 542 1.10 roy for (j = 0, col = 0; j < (size_t)len; j++) { 543 1.5 roy if (col == 0) { 544 1.6 roy printf("\t\t\""); 545 1.6 roy col = 16; 546 1.5 roy } 547 1.25 roy 548 1.10 roy col += printf("\\%03o", (uint8_t)buf[j]); 549 1.5 roy if (col > 75) { 550 1.5 roy printf("\"%s\n", 551 1.10 roy j + 1 == (size_t)len ? "," : ""); 552 1.5 roy col = 0; 553 1.5 roy } 554 1.5 roy } 555 1.5 roy if (col != 0) 556 1.6 roy printf("\",\n"); 557 1.10 roy printf("\t\t%zu\n", len); 558 1.6 roy printf("\t}"); 559 1.6 roy if (i + 1 < argc) 560 1.6 roy printf(","); 561 1.6 roy printf("\n"); 562 1.10 roy free(buf); 563 1.5 roy } 564 1.6 roy printf("};\n"); 565 1.5 roy 566 1.5 roy return n; 567 1.5 roy } 568 1.5 roy 569 1.20 joerg static void 570 1.20 joerg write_database(const char *dbname) 571 1.20 joerg { 572 1.20 joerg struct cdbw *db; 573 1.20 joerg char *tmp_dbname; 574 1.20 joerg TERM *term; 575 1.20 joerg int fd; 576 1.42 christos mode_t m; 577 1.20 joerg 578 1.20 joerg db = cdbw_open(); 579 1.20 joerg if (db == NULL) 580 1.37 roy err(EXIT_FAILURE, "cdbw_open failed"); 581 1.20 joerg /* Save the terms */ 582 1.20 joerg STAILQ_FOREACH(term, &terms, next) 583 1.20 joerg save_term(db, term); 584 1.20 joerg 585 1.20 joerg easprintf(&tmp_dbname, "%s.XXXXXX", dbname); 586 1.20 joerg fd = mkstemp(tmp_dbname); 587 1.20 joerg if (fd == -1) 588 1.37 roy err(EXIT_FAILURE, 589 1.37 roy "creating temporary database %s failed", tmp_dbname); 590 1.20 joerg if (cdbw_output(db, fd, "NetBSD terminfo", cdbw_stable_seeder)) 591 1.37 roy err(EXIT_FAILURE, 592 1.37 roy "writing temporary database %s failed", tmp_dbname); 593 1.42 christos m = umask(0); 594 1.42 christos (void)umask(m); 595 1.42 christos if (fchmod(fd, DEFFILEMODE & ~m)) 596 1.37 roy err(EXIT_FAILURE, "fchmod failed"); 597 1.20 joerg if (close(fd)) 598 1.37 roy err(EXIT_FAILURE, 599 1.37 roy "writing temporary database %s failed", tmp_dbname); 600 1.20 joerg if (rename(tmp_dbname, dbname)) 601 1.37 roy err(EXIT_FAILURE, "renaming %s to %s failed", tmp_dbname, dbname); 602 1.20 joerg free(tmp_dbname); 603 1.20 joerg cdbw_close(db); 604 1.20 joerg } 605 1.20 joerg 606 1.1 roy int 607 1.1 roy main(int argc, char **argv) 608 1.1 roy { 609 1.10 roy int ch, cflag, sflag, flags; 610 1.20 joerg char *source, *dbname, *buf, *ofile; 611 1.1 roy FILE *f; 612 1.18 joerg size_t buflen; 613 1.12 roy ssize_t len; 614 1.1 roy TBUF tbuf; 615 1.29 roy struct term *term; 616 1.1 roy 617 1.1 roy cflag = sflag = 0; 618 1.1 roy ofile = NULL; 619 1.10 roy flags = TIC_ALIAS | TIC_DESCRIPTION | TIC_WARNING; 620 1.5 roy while ((ch = getopt(argc, argv, "Saco:sx")) != -1) 621 1.1 roy switch (ch) { 622 1.5 roy case 'S': 623 1.5 roy Sflag = 1; 624 1.10 roy /* We still compile aliases so that use= works. 625 1.10 roy * However, it's removed before we flatten to save space. */ 626 1.10 roy flags &= ~TIC_DESCRIPTION; 627 1.5 roy break; 628 1.1 roy case 'a': 629 1.10 roy flags |= TIC_COMMENT; 630 1.1 roy break; 631 1.1 roy case 'c': 632 1.4 roy cflag = 1; 633 1.1 roy break; 634 1.1 roy case 'o': 635 1.1 roy ofile = optarg; 636 1.1 roy break; 637 1.1 roy case 's': 638 1.4 roy sflag = 1; 639 1.1 roy break; 640 1.1 roy case 'x': 641 1.10 roy flags |= TIC_EXTRA; 642 1.1 roy break; 643 1.1 roy case '?': /* FALLTHROUGH */ 644 1.1 roy default: 645 1.6 roy fprintf(stderr, "usage: %s [-acSsx] [-o file] source\n", 646 1.1 roy getprogname()); 647 1.1 roy return EXIT_FAILURE; 648 1.1 roy } 649 1.1 roy 650 1.1 roy if (optind == argc) 651 1.1 roy errx(1, "No source file given"); 652 1.1 roy source = argv[optind++]; 653 1.1 roy f = fopen(source, "r"); 654 1.1 roy if (f == NULL) 655 1.37 roy err(EXIT_FAILURE, "fopen: %s", source); 656 1.1 roy 657 1.15 joerg hcreate(HASH_SIZE); 658 1.15 joerg 659 1.19 joerg buf = tbuf.buf = NULL; 660 1.28 roy buflen = tbuf.buflen = tbuf.bufpos = 0; 661 1.12 roy while ((len = getline(&buf, &buflen, f)) != -1) { 662 1.1 roy /* Skip comments */ 663 1.1 roy if (*buf == '#') 664 1.1 roy continue; 665 1.12 roy if (buf[len - 1] != '\n') { 666 1.10 roy process_entry(&tbuf, flags); 667 1.1 roy dowarn("last line is not a comment" 668 1.1 roy " and does not end with a newline"); 669 1.1 roy continue; 670 1.1 roy } 671 1.1 roy /* 672 1.28 roy * If the first char is space not a space then we have a 673 1.28 roy * new entry, so process it. 674 1.28 roy */ 675 1.1 roy if (!isspace((unsigned char)*buf) && tbuf.bufpos != 0) 676 1.10 roy process_entry(&tbuf, flags); 677 1.25 roy 678 1.1 roy /* Grow the buffer if needed */ 679 1.12 roy grow_tbuf(&tbuf, len); 680 1.1 roy /* Append the string */ 681 1.12 roy memcpy(tbuf.buf + tbuf.bufpos, buf, len); 682 1.12 roy tbuf.bufpos += len; 683 1.1 roy } 684 1.19 joerg free(buf); 685 1.1 roy /* Process the last entry if not done already */ 686 1.10 roy process_entry(&tbuf, flags); 687 1.19 joerg free(tbuf.buf); 688 1.1 roy 689 1.1 roy /* Merge use entries until we have merged all we can */ 690 1.10 roy while (merge_use(flags) != 0) 691 1.1 roy ; 692 1.1 roy 693 1.6 roy if (Sflag) { 694 1.5 roy print_dump(argc - optind, argv + optind); 695 1.5 roy return error_exit; 696 1.5 roy } 697 1.5 roy 698 1.6 roy if (cflag) 699 1.1 roy return error_exit; 700 1.18 joerg 701 1.20 joerg if (ofile == NULL) 702 1.20 joerg easprintf(&dbname, "%s.cdb", source); 703 1.20 joerg else 704 1.20 joerg dbname = ofile; 705 1.20 joerg write_database(dbname); 706 1.1 roy 707 1.1 roy if (sflag != 0) 708 1.1 roy fprintf(stderr, "%zu entries and %zu aliases written to %s\n", 709 1.20 joerg nterm, nalias, dbname); 710 1.19 joerg 711 1.20 joerg if (ofile == NULL) 712 1.20 joerg free(dbname); 713 1.20 joerg while ((term = STAILQ_FIRST(&terms)) != NULL) { 714 1.20 joerg STAILQ_REMOVE_HEAD(&terms, next); 715 1.19 joerg _ti_freetic(term->tic); 716 1.19 joerg free(term->name); 717 1.19 joerg free(term); 718 1.19 joerg } 719 1.30 christos #ifndef HAVE_NBTOOL_CONFIG_H 720 1.30 christos /* 721 1.30 christos * hdestroy1 is not standard but we don't really care if we 722 1.30 christos * leak in the tools version 723 1.30 christos */ 724 1.24 christos hdestroy1(free, NULL); 725 1.30 christos #endif 726 1.1 roy 727 1.1 roy return EXIT_SUCCESS; 728 1.1 roy } 729