main.c (302408) | main.c (241675) |
---|---|
1/* $Id: main.c,v 1.262 2016/01/08 02:53:13 schwarze Exp $ */ | 1/* $Id: main.c,v 1.165 2011/10/06 22:29:12 kristaps Exp $ */ |
2/* | 2/* |
3 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2010-2012, 2014-2016 Ingo Schwarze <schwarze@openbsd.org> 5 * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org> | 3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org> |
6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * | 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * |
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES | 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR | 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ | 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ |
18#ifdef HAVE_CONFIG_H |
|
19#include "config.h" | 19#include "config.h" |
20#endif |
|
20 | 21 |
21#include <sys/types.h> 22#include <sys/param.h> /* MACHINE */ 23#include <sys/wait.h> 24 | |
25#include <assert.h> | 22#include <assert.h> |
26#include <ctype.h> 27#if HAVE_ERR 28#include <err.h> 29#endif 30#include <errno.h> 31#include <fcntl.h> 32#include <glob.h> 33#include <signal.h> | |
34#include <stdio.h> 35#include <stdint.h> 36#include <stdlib.h> 37#include <string.h> 38#include <unistd.h> 39 | 23#include <stdio.h> 24#include <stdint.h> 25#include <stdlib.h> 26#include <string.h> 27#include <unistd.h> 28 |
40#include "mandoc_aux.h" | |
41#include "mandoc.h" | 29#include "mandoc.h" |
42#include "roff.h" | 30#include "main.h" |
43#include "mdoc.h" 44#include "man.h" | 31#include "mdoc.h" 32#include "man.h" |
45#include "tag.h" 46#include "main.h" 47#include "manconf.h" 48#include "mansearch.h" | |
49 50#if !defined(__GNUC__) || (__GNUC__ < 2) 51# if !defined(lint) 52# define __attribute__(x) 53# endif 54#endif /* !defined(__GNUC__) || (__GNUC__ < 2) */ 55 | 33 34#if !defined(__GNUC__) || (__GNUC__ < 2) 35# if !defined(lint) 36# define __attribute__(x) 37# endif 38#endif /* !defined(__GNUC__) || (__GNUC__ < 2) */ 39 |
56enum outmode { 57 OUTMODE_DEF = 0, 58 OUTMODE_FLN, 59 OUTMODE_LST, 60 OUTMODE_ALL, 61 OUTMODE_INT, 62 OUTMODE_ONE 63}; | 40typedef void (*out_mdoc)(void *, const struct mdoc *); 41typedef void (*out_man)(void *, const struct man *); 42typedef void (*out_free)(void *); |
64 65enum outt { 66 OUTT_ASCII = 0, /* -Tascii */ 67 OUTT_LOCALE, /* -Tlocale */ 68 OUTT_UTF8, /* -Tutf8 */ 69 OUTT_TREE, /* -Ttree */ 70 OUTT_MAN, /* -Tman */ 71 OUTT_HTML, /* -Thtml */ | 43 44enum outt { 45 OUTT_ASCII = 0, /* -Tascii */ 46 OUTT_LOCALE, /* -Tlocale */ 47 OUTT_UTF8, /* -Tutf8 */ 48 OUTT_TREE, /* -Ttree */ 49 OUTT_MAN, /* -Tman */ 50 OUTT_HTML, /* -Thtml */ |
51 OUTT_XHTML, /* -Txhtml */ |
|
72 OUTT_LINT, /* -Tlint */ 73 OUTT_PS, /* -Tps */ 74 OUTT_PDF /* -Tpdf */ 75}; 76 77struct curparse { 78 struct mparse *mp; 79 enum mandoclevel wlevel; /* ignore messages below this */ 80 int wstop; /* stop after a file with a warning */ | 52 OUTT_LINT, /* -Tlint */ 53 OUTT_PS, /* -Tps */ 54 OUTT_PDF /* -Tpdf */ 55}; 56 57struct curparse { 58 struct mparse *mp; 59 enum mandoclevel wlevel; /* ignore messages below this */ 60 int wstop; /* stop after a file with a warning */ |
81 enum outt outtype; /* which output to use */ | 61 enum outt outtype; /* which output to use */ 62 out_mdoc outmdoc; /* mdoc output ptr */ 63 out_man outman; /* man output ptr */ 64 out_free outfree; /* free output ptr */ |
82 void *outdata; /* data for output */ | 65 void *outdata; /* data for output */ |
83 struct manoutput *outopts; /* output options */ | 66 char outopts[BUFSIZ]; /* buf of output opts */ |
84}; 85 | 67}; 68 |
86static int fs_lookup(const struct manpaths *, 87 size_t ipath, const char *, 88 const char *, const char *, 89 struct manpage **, size_t *); 90static void fs_search(const struct mansearch *, 91 const struct manpaths *, int, char**, 92 struct manpage **, size_t *); 93static int koptions(int *, char *); 94#if HAVE_SQLITE3 95int mandocdb(int, char**); 96#endif 97static int moptions(int *, char *); | 69static int moptions(enum mparset *, char *); |
98static void mmsg(enum mandocerr, enum mandoclevel, 99 const char *, int, int, const char *); | 70static void mmsg(enum mandocerr, enum mandoclevel, 71 const char *, int, int, const char *); |
100static void parse(struct curparse *, int, const char *); 101static void passthrough(const char *, int, int); 102static pid_t spawn_pager(struct tag_files *); | 72static void parse(struct curparse *, int, 73 const char *, enum mandoclevel *); |
103static int toptions(struct curparse *, char *); | 74static int toptions(struct curparse *, char *); |
104static void usage(enum argmode) __attribute__((noreturn)); | 75static void usage(void) __attribute__((noreturn)); 76static void version(void) __attribute__((noreturn)); |
105static int woptions(struct curparse *, char *); 106 | 77static int woptions(struct curparse *, char *); 78 |
107static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9}; 108static char help_arg[] = "help"; 109static char *help_argv[] = {help_arg, NULL}; 110static enum mandoclevel rc; | 79static const char *progname; |
111 | 80 |
112 | |
113int 114main(int argc, char *argv[]) 115{ | 81int 82main(int argc, char *argv[]) 83{ |
116 struct manconf conf; 117 struct curparse curp; 118 struct mansearch search; 119 struct tag_files *tag_files; 120 const char *progname; 121 char *auxpaths; 122 char *defos; 123 unsigned char *uc; 124 struct manpage *res, *resp; 125 char *conf_file, *defpaths; 126 size_t isec, i, sz; 127 int prio, best_prio; 128 char sec; 129 enum outmode outmode; 130 int fd; 131 int show_usage; 132 int options; 133 int use_pager; 134 int status, signum; | |
135 int c; | 84 int c; |
136 pid_t pager_pid, tc_pgid, man_pgid, pid; | 85 struct curparse curp; 86 enum mparset type; 87 enum mandoclevel rc; |
137 | 88 |
138#if HAVE_PROGNAME 139 progname = getprogname(); 140#else 141 if (argc < 1) 142 progname = mandoc_strdup("mandoc"); 143 else if ((progname = strrchr(argv[0], '/')) == NULL) | 89 progname = strrchr(argv[0], '/'); 90 if (progname == NULL) |
144 progname = argv[0]; 145 else 146 ++progname; | 91 progname = argv[0]; 92 else 93 ++progname; |
147 setprogname(progname); 148#endif | |
149 | 94 |
150#if HAVE_SQLITE3 151 if (strncmp(progname, "mandocdb", 8) == 0 || 152 strcmp(progname, BINM_MAKEWHATIS) == 0) 153 return mandocdb(argc, argv); 154#endif 155 156#if HAVE_PLEDGE 157 if (pledge("stdio rpath tmppath tty proc exec flock", NULL) == -1) 158 err((int)MANDOCLEVEL_SYSERR, "pledge"); 159#endif 160 161 /* Search options. */ 162 163 memset(&conf, 0, sizeof(conf)); 164 conf_file = defpaths = NULL; 165 auxpaths = NULL; 166 167 memset(&search, 0, sizeof(struct mansearch)); 168 search.outkey = "Nd"; 169 170 if (strcmp(progname, BINM_MAN) == 0) 171 search.argmode = ARG_NAME; 172 else if (strcmp(progname, BINM_APROPOS) == 0) 173 search.argmode = ARG_EXPR; 174 else if (strcmp(progname, BINM_WHATIS) == 0) 175 search.argmode = ARG_WORD; 176 else if (strncmp(progname, "help", 4) == 0) 177 search.argmode = ARG_NAME; 178 else 179 search.argmode = ARG_FILE; 180 181 /* Parser and formatter options. */ 182 | |
183 memset(&curp, 0, sizeof(struct curparse)); | 95 memset(&curp, 0, sizeof(struct curparse)); |
184 curp.outtype = OUTT_LOCALE; 185 curp.wlevel = MANDOCLEVEL_BADARG; 186 curp.outopts = &conf.output; 187 options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1; 188 defos = NULL; | |
189 | 96 |
190 use_pager = 1; 191 tag_files = NULL; 192 show_usage = 0; 193 outmode = OUTMODE_DEF; | 97 type = MPARSE_AUTO; 98 curp.outtype = OUTT_ASCII; 99 curp.wlevel = MANDOCLEVEL_FATAL; |
194 | 100 |
195 while (-1 != (c = getopt(argc, argv, 196 "aC:cfhI:iK:klM:m:O:S:s:T:VW:w"))) { | 101 /* LINTED */ 102 while (-1 != (c = getopt(argc, argv, "m:O:T:VW:"))) |
197 switch (c) { | 103 switch (c) { |
198 case 'a': 199 outmode = OUTMODE_ALL; | 104 case ('m'): 105 if ( ! moptions(&type, optarg)) 106 return((int)MANDOCLEVEL_BADARG); |
200 break; | 107 break; |
201 case 'C': 202 conf_file = optarg; | 108 case ('O'): 109 (void)strlcat(curp.outopts, optarg, BUFSIZ); 110 (void)strlcat(curp.outopts, ",", BUFSIZ); |
203 break; | 111 break; |
204 case 'c': 205 use_pager = 0; 206 break; 207 case 'f': 208 search.argmode = ARG_WORD; 209 break; 210 case 'h': 211 conf.output.synopsisonly = 1; 212 use_pager = 0; 213 outmode = OUTMODE_ALL; 214 break; 215 case 'I': 216 if (strncmp(optarg, "os=", 3)) { 217 warnx("-I %s: Bad argument", optarg); 218 return (int)MANDOCLEVEL_BADARG; 219 } 220 if (defos) { 221 warnx("-I %s: Duplicate argument", optarg); 222 return (int)MANDOCLEVEL_BADARG; 223 } 224 defos = mandoc_strdup(optarg + 3); 225 break; 226 case 'i': 227 outmode = OUTMODE_INT; 228 break; 229 case 'K': 230 if ( ! koptions(&options, optarg)) 231 return (int)MANDOCLEVEL_BADARG; 232 break; 233 case 'k': 234 search.argmode = ARG_EXPR; 235 break; 236 case 'l': 237 search.argmode = ARG_FILE; 238 outmode = OUTMODE_ALL; 239 break; 240 case 'M': 241 defpaths = optarg; 242 break; 243 case 'm': 244 auxpaths = optarg; 245 break; 246 case 'O': 247 search.outkey = optarg; 248 while (optarg != NULL) 249 manconf_output(&conf.output, 250 strsep(&optarg, ",")); 251 break; 252 case 'S': 253 search.arch = optarg; 254 break; 255 case 's': 256 search.sec = optarg; 257 break; 258 case 'T': | 112 case ('T'): |
259 if ( ! toptions(&curp, optarg)) | 113 if ( ! toptions(&curp, optarg)) |
260 return (int)MANDOCLEVEL_BADARG; | 114 return((int)MANDOCLEVEL_BADARG); |
261 break; | 115 break; |
262 case 'W': | 116 case ('W'): |
263 if ( ! woptions(&curp, optarg)) | 117 if ( ! woptions(&curp, optarg)) |
264 return (int)MANDOCLEVEL_BADARG; | 118 return((int)MANDOCLEVEL_BADARG); |
265 break; | 119 break; |
266 case 'w': 267 outmode = OUTMODE_FLN; 268 break; | 120 case ('V'): 121 version(); 122 /* NOTREACHED */ |
269 default: | 123 default: |
270 show_usage = 1; 271 break; | 124 usage(); 125 /* NOTREACHED */ |
272 } | 126 } |
273 } | |
274 | 127 |
275 if (show_usage) 276 usage(search.argmode); | 128 curp.mp = mparse_alloc(type, curp.wlevel, mmsg, &curp); |
277 | 129 |
278 /* Postprocess options. */ 279 280 if (outmode == OUTMODE_DEF) { 281 switch (search.argmode) { 282 case ARG_FILE: 283 outmode = OUTMODE_ALL; 284 use_pager = 0; 285 break; 286 case ARG_NAME: 287 outmode = OUTMODE_ONE; 288 break; 289 default: 290 outmode = OUTMODE_LST; 291 break; 292 } 293 } 294 295 if (outmode == OUTMODE_FLN || 296 outmode == OUTMODE_LST || 297 !isatty(STDOUT_FILENO)) 298 use_pager = 0; 299 300#if HAVE_PLEDGE 301 if (!use_pager) 302 if (pledge("stdio rpath flock", NULL) == -1) 303 err((int)MANDOCLEVEL_SYSERR, "pledge"); 304#endif 305 306 /* Parse arguments. */ 307 308 if (argc > 0) { 309 argc -= optind; 310 argv += optind; 311 } 312 resp = NULL; 313 | |
314 /* | 130 /* |
315 * Quirks for help(1) 316 * and for a man(1) section argument without -s. 317 */ 318 319 if (search.argmode == ARG_NAME) { 320 if (*progname == 'h') { 321 if (argc == 0) { 322 argv = help_argv; 323 argc = 1; 324 } 325 } else if (argc > 1 && 326 ((uc = (unsigned char *)argv[0]) != NULL) && 327 ((isdigit(uc[0]) && (uc[1] == '\0' || 328 (isalpha(uc[1]) && uc[2] == '\0'))) || 329 (uc[0] == 'n' && uc[1] == '\0'))) { 330 search.sec = (char *)uc; 331 argv++; 332 argc--; 333 } 334 if (search.arch == NULL) 335 search.arch = getenv("MACHINE"); 336#ifdef MACHINE 337 if (search.arch == NULL) 338 search.arch = MACHINE; 339#endif 340 } 341 342 rc = MANDOCLEVEL_OK; 343 344 /* man(1), whatis(1), apropos(1) */ 345 346 if (search.argmode != ARG_FILE) { 347 if (argc == 0) 348 usage(search.argmode); 349 350 if (search.argmode == ARG_NAME && 351 outmode == OUTMODE_ONE) 352 search.firstmatch = 1; 353 354 /* Access the mandoc database. */ 355 356 manconf_parse(&conf, conf_file, defpaths, auxpaths); 357#if HAVE_SQLITE3 358 mansearch_setup(1); 359 if ( ! mansearch(&search, &conf.manpath, 360 argc, argv, &res, &sz)) 361 usage(search.argmode); 362#else 363 if (search.argmode != ARG_NAME) { 364 fputs("mandoc: database support not compiled in\n", 365 stderr); 366 return (int)MANDOCLEVEL_BADARG; 367 } 368 sz = 0; 369#endif 370 371 if (sz == 0) { 372 if (search.argmode == ARG_NAME) 373 fs_search(&search, &conf.manpath, 374 argc, argv, &res, &sz); 375 else 376 warnx("nothing appropriate"); 377 } 378 379 if (sz == 0) { 380 rc = MANDOCLEVEL_BADARG; 381 goto out; 382 } 383 384 /* 385 * For standard man(1) and -a output mode, 386 * prepare for copying filename pointers 387 * into the program parameter array. 388 */ 389 390 if (outmode == OUTMODE_ONE) { 391 argc = 1; 392 best_prio = 10; 393 } else if (outmode == OUTMODE_ALL) 394 argc = (int)sz; 395 396 /* Iterate all matching manuals. */ 397 398 resp = res; 399 for (i = 0; i < sz; i++) { 400 if (outmode == OUTMODE_FLN) 401 puts(res[i].file); 402 else if (outmode == OUTMODE_LST) 403 printf("%s - %s\n", res[i].names, 404 res[i].output == NULL ? "" : 405 res[i].output); 406 else if (outmode == OUTMODE_ONE) { 407 /* Search for the best section. */ 408 isec = strcspn(res[i].file, "123456789"); 409 sec = res[i].file[isec]; 410 if ('\0' == sec) 411 continue; 412 prio = sec_prios[sec - '1']; 413 if (prio >= best_prio) 414 continue; 415 best_prio = prio; 416 resp = res + i; 417 } 418 } 419 420 /* 421 * For man(1), -a and -i output mode, fall through 422 * to the main mandoc(1) code iterating files 423 * and running the parsers on each of them. 424 */ 425 426 if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST) 427 goto out; 428 } 429 430 /* mandoc(1) */ 431 432#if HAVE_PLEDGE 433 if (use_pager) { 434 if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) 435 err((int)MANDOCLEVEL_SYSERR, "pledge"); 436 } else { 437 if (pledge("stdio rpath", NULL) == -1) 438 err((int)MANDOCLEVEL_SYSERR, "pledge"); 439 } 440#endif 441 442 if (search.argmode == ARG_FILE && ! moptions(&options, auxpaths)) 443 return (int)MANDOCLEVEL_BADARG; 444 445 mchars_alloc(); 446 curp.mp = mparse_alloc(options, curp.wlevel, mmsg, defos); 447 448 /* | |
449 * Conditionally start up the lookaside buffer before parsing. 450 */ 451 if (OUTT_MAN == curp.outtype) 452 mparse_keep(curp.mp); 453 | 131 * Conditionally start up the lookaside buffer before parsing. 132 */ 133 if (OUTT_MAN == curp.outtype) 134 mparse_keep(curp.mp); 135 |
454 if (argc < 1) { 455 if (use_pager) 456 tag_files = tag_init(); 457 parse(&curp, STDIN_FILENO, "<stdin>"); 458 } | 136 argc -= optind; 137 argv += optind; |
459 | 138 |
460 while (argc > 0) { 461 fd = mparse_open(curp.mp, resp != NULL ? resp->file : *argv); 462 if (fd != -1) { 463 if (use_pager) { 464 tag_files = tag_init(); 465 use_pager = 0; 466 } | 139 rc = MANDOCLEVEL_OK; |
467 | 140 |
468 if (resp == NULL) 469 parse(&curp, fd, *argv); 470 else if (resp->form & FORM_SRC) { 471 /* For .so only; ignore failure. */ 472 chdir(conf.manpath.paths[resp->ipath]); 473 parse(&curp, fd, resp->file); 474 } else 475 passthrough(resp->file, fd, 476 conf.output.synopsisonly); | 141 if (NULL == *argv) 142 parse(&curp, STDIN_FILENO, "<stdin>", &rc); |
477 | 143 |
478 if (argc > 1 && curp.outtype <= OUTT_UTF8) 479 ascii_sepline(curp.outdata); 480 } else if (rc < MANDOCLEVEL_ERROR) 481 rc = MANDOCLEVEL_ERROR; 482 | 144 while (*argv) { 145 parse(&curp, -1, *argv, &rc); |
483 if (MANDOCLEVEL_OK != rc && curp.wstop) 484 break; | 146 if (MANDOCLEVEL_OK != rc && curp.wstop) 147 break; |
485 486 if (resp != NULL) 487 resp++; 488 else 489 argv++; 490 if (--argc) 491 mparse_reset(curp.mp); | 148 ++argv; |
492 } 493 | 149 } 150 |
494 if (curp.outdata != NULL) { 495 switch (curp.outtype) { 496 case OUTT_HTML: 497 html_free(curp.outdata); 498 break; 499 case OUTT_UTF8: 500 case OUTT_LOCALE: 501 case OUTT_ASCII: 502 ascii_free(curp.outdata); 503 break; 504 case OUTT_PDF: 505 case OUTT_PS: 506 pspdf_free(curp.outdata); 507 break; 508 default: 509 break; 510 } 511 } 512 mparse_free(curp.mp); 513 mchars_free(); | 151 if (curp.outfree) 152 (*curp.outfree)(curp.outdata); 153 if (curp.mp) 154 mparse_free(curp.mp); |
514 | 155 |
515out: 516 if (search.argmode != ARG_FILE) { 517 manconf_free(&conf); 518#if HAVE_SQLITE3 519 mansearch_free(res, sz); 520 mansearch_setup(0); 521#endif 522 } 523 524 free(defos); 525 526 /* 527 * When using a pager, finish writing both temporary files, 528 * fork it, wait for the user to close it, and clean up. 529 */ 530 531 if (tag_files != NULL) { 532 fclose(stdout); 533 tag_write(); 534 man_pgid = getpgid(0); 535 tag_files->tcpgid = man_pgid == getpid() ? 536 getpgid(getppid()) : man_pgid; 537 pager_pid = 0; 538 signum = SIGSTOP; 539 for (;;) { 540 541 /* Stop here until moved to the foreground. */ 542 543 tc_pgid = tcgetpgrp(STDIN_FILENO); 544 if (tc_pgid != man_pgid) { 545 if (tc_pgid == pager_pid) { 546 (void)tcsetpgrp(STDIN_FILENO, 547 man_pgid); 548 if (signum == SIGTTIN) 549 continue; 550 } else 551 tag_files->tcpgid = tc_pgid; 552 kill(0, signum); 553 continue; 554 } 555 556 /* Once in the foreground, activate the pager. */ 557 558 if (pager_pid) { 559 (void)tcsetpgrp(STDIN_FILENO, pager_pid); 560 kill(pager_pid, SIGCONT); 561 } else 562 pager_pid = spawn_pager(tag_files); 563 564 /* Wait for the pager to stop or exit. */ 565 566 while ((pid = waitpid(pager_pid, &status, 567 WUNTRACED)) == -1 && errno == EINTR) 568 continue; 569 570 if (pid == -1) { 571 warn("wait"); 572 rc = MANDOCLEVEL_SYSERR; 573 break; 574 } 575 if (!WIFSTOPPED(status)) 576 break; 577 578 signum = WSTOPSIG(status); 579 } 580 tag_unlink(); 581 } 582 583 return (int)rc; | 156 return((int)rc); |
584} 585 586static void | 157} 158 159static void |
587usage(enum argmode argmode) | 160version(void) |
588{ 589 | 161{ 162 |
590 switch (argmode) { 591 case ARG_FILE: 592 fputs("usage: mandoc [-acfhkl] [-I os=name] " 593 "[-K encoding] [-mformat] [-O option]\n" 594 "\t [-T output] [-W level] [file ...]\n", stderr); 595 break; 596 case ARG_NAME: 597 fputs("usage: man [-acfhklw] [-C file] [-I os=name] " 598 "[-K encoding] [-M path] [-m path]\n" 599 "\t [-O option=value] [-S subsection] [-s section] " 600 "[-T output] [-W level]\n" 601 "\t [section] name ...\n", stderr); 602 break; 603 case ARG_WORD: 604 fputs("usage: whatis [-acfhklw] [-C file] " 605 "[-M path] [-m path] [-O outkey] [-S arch]\n" 606 "\t [-s section] name ...\n", stderr); 607 break; 608 case ARG_EXPR: 609 fputs("usage: apropos [-acfhklw] [-C file] " 610 "[-M path] [-m path] [-O outkey] [-S arch]\n" 611 "\t [-s section] expression ...\n", stderr); 612 break; 613 } 614 exit((int)MANDOCLEVEL_BADARG); | 163 printf("%s %s\n", progname, VERSION); 164 exit((int)MANDOCLEVEL_OK); |
615} 616 | 165} 166 |
617static int 618fs_lookup(const struct manpaths *paths, size_t ipath, 619 const char *sec, const char *arch, const char *name, 620 struct manpage **res, size_t *ressz) 621{ 622 glob_t globinfo; 623 struct manpage *page; 624 char *file; 625 int form, globres; 626 627 form = FORM_SRC; 628 mandoc_asprintf(&file, "%s/man%s/%s.%s", 629 paths->paths[ipath], sec, name, sec); 630 if (access(file, R_OK) != -1) 631 goto found; 632 free(file); 633 634 mandoc_asprintf(&file, "%s/cat%s/%s.0", 635 paths->paths[ipath], sec, name); 636 if (access(file, R_OK) != -1) { 637 form = FORM_CAT; 638 goto found; 639 } 640 free(file); 641 642 if (arch != NULL) { 643 mandoc_asprintf(&file, "%s/man%s/%s/%s.%s", 644 paths->paths[ipath], sec, arch, name, sec); 645 if (access(file, R_OK) != -1) 646 goto found; 647 free(file); 648 } 649 650 mandoc_asprintf(&file, "%s/man%s/%s.[01-9]*", 651 paths->paths[ipath], sec, name); 652 globres = glob(file, 0, NULL, &globinfo); 653 if (globres != 0 && globres != GLOB_NOMATCH) 654 warn("%s: glob", file); 655 free(file); 656 if (globres == 0) 657 file = mandoc_strdup(*globinfo.gl_pathv); 658 globfree(&globinfo); 659 if (globres != 0) 660 return 0; 661 662found: 663#if HAVE_SQLITE3 664 warnx("outdated mandoc.db lacks %s(%s) entry, run makewhatis %s", 665 name, sec, paths->paths[ipath]); 666#endif 667 *res = mandoc_reallocarray(*res, ++*ressz, sizeof(struct manpage)); 668 page = *res + (*ressz - 1); 669 page->file = file; 670 page->names = NULL; 671 page->output = NULL; 672 page->ipath = ipath; 673 page->bits = NAME_FILE & NAME_MASK; 674 page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10; 675 page->form = form; 676 return 1; 677} 678 | |
679static void | 167static void |
680fs_search(const struct mansearch *cfg, const struct manpaths *paths, 681 int argc, char **argv, struct manpage **res, size_t *ressz) | 168usage(void) |
682{ | 169{ |
683 const char *const sections[] = 684 {"1", "8", "6", "2", "3", "3p", "5", "7", "4", "9"}; 685 const size_t nsec = sizeof(sections)/sizeof(sections[0]); | |
686 | 170 |
687 size_t ipath, isec, lastsz; | 171 fprintf(stderr, "usage: %s " 172 "[-V] " 173 "[-foption] " 174 "[-mformat] " 175 "[-Ooption] " 176 "[-Toutput] " 177 "[-Wlevel] " 178 "[file...]\n", 179 progname); |
688 | 180 |
689 assert(cfg->argmode == ARG_NAME); 690 691 *res = NULL; 692 *ressz = lastsz = 0; 693 while (argc) { 694 for (ipath = 0; ipath < paths->sz; ipath++) { 695 if (cfg->sec != NULL) { 696 if (fs_lookup(paths, ipath, cfg->sec, 697 cfg->arch, *argv, res, ressz) && 698 cfg->firstmatch) 699 return; 700 } else for (isec = 0; isec < nsec; isec++) 701 if (fs_lookup(paths, ipath, sections[isec], 702 cfg->arch, *argv, res, ressz) && 703 cfg->firstmatch) 704 return; 705 } 706 if (*ressz == lastsz) 707 warnx("No entry for %s in the manual.", *argv); 708 lastsz = *ressz; 709 argv++; 710 argc--; 711 } | 181 exit((int)MANDOCLEVEL_BADARG); |
712} 713 714static void | 182} 183 184static void |
715parse(struct curparse *curp, int fd, const char *file) | 185parse(struct curparse *curp, int fd, 186 const char *file, enum mandoclevel *level) |
716{ | 187{ |
717 enum mandoclevel rctmp; 718 struct roff_man *man; | 188 enum mandoclevel rc; 189 struct mdoc *mdoc; 190 struct man *man; |
719 720 /* Begin by parsing the file itself. */ 721 722 assert(file); | 191 192 /* Begin by parsing the file itself. */ 193 194 assert(file); |
723 assert(fd >= 0); | 195 assert(fd >= -1); |
724 | 196 |
725 rctmp = mparse_readfd(curp->mp, fd, file); 726 if (fd != STDIN_FILENO) 727 close(fd); 728 if (rc < rctmp) 729 rc = rctmp; | 197 rc = mparse_readfd(curp->mp, fd, file); |
730 | 198 |
199 /* Stop immediately if the parse has failed. */ 200 201 if (MANDOCLEVEL_FATAL <= rc) 202 goto cleanup; 203 |
|
731 /* 732 * With -Wstop and warnings or errors of at least the requested 733 * level, do not produce output. 734 */ 735 | 204 /* 205 * With -Wstop and warnings or errors of at least the requested 206 * level, do not produce output. 207 */ 208 |
736 if (rctmp != MANDOCLEVEL_OK && curp->wstop) 737 return; | 209 if (MANDOCLEVEL_OK != rc && curp->wstop) 210 goto cleanup; |
738 739 /* If unset, allocate output dev now (if applicable). */ 740 | 211 212 /* If unset, allocate output dev now (if applicable). */ 213 |
741 if (curp->outdata == NULL) { | 214 if ( ! (curp->outman && curp->outmdoc)) { |
742 switch (curp->outtype) { | 215 switch (curp->outtype) { |
743 case OUTT_HTML: | 216 case (OUTT_XHTML): 217 curp->outdata = xhtml_alloc(curp->outopts); 218 curp->outfree = html_free; 219 break; 220 case (OUTT_HTML): |
744 curp->outdata = html_alloc(curp->outopts); | 221 curp->outdata = html_alloc(curp->outopts); |
222 curp->outfree = html_free; |
|
745 break; | 223 break; |
746 case OUTT_UTF8: | 224 case (OUTT_UTF8): |
747 curp->outdata = utf8_alloc(curp->outopts); | 225 curp->outdata = utf8_alloc(curp->outopts); |
226 curp->outfree = ascii_free; |
|
748 break; | 227 break; |
749 case OUTT_LOCALE: | 228 case (OUTT_LOCALE): |
750 curp->outdata = locale_alloc(curp->outopts); | 229 curp->outdata = locale_alloc(curp->outopts); |
230 curp->outfree = ascii_free; |
|
751 break; | 231 break; |
752 case OUTT_ASCII: | 232 case (OUTT_ASCII): |
753 curp->outdata = ascii_alloc(curp->outopts); | 233 curp->outdata = ascii_alloc(curp->outopts); |
234 curp->outfree = ascii_free; |
|
754 break; | 235 break; |
755 case OUTT_PDF: | 236 case (OUTT_PDF): |
756 curp->outdata = pdf_alloc(curp->outopts); | 237 curp->outdata = pdf_alloc(curp->outopts); |
238 curp->outfree = pspdf_free; |
|
757 break; | 239 break; |
758 case OUTT_PS: | 240 case (OUTT_PS): |
759 curp->outdata = ps_alloc(curp->outopts); | 241 curp->outdata = ps_alloc(curp->outopts); |
242 curp->outfree = pspdf_free; |
|
760 break; 761 default: 762 break; 763 } | 243 break; 244 default: 245 break; 246 } |
764 } | |
765 | 247 |
766 mparse_result(curp->mp, &man, NULL); 767 768 /* Execute the out device, if it exists. */ 769 770 if (man == NULL) 771 return; 772 if (man->macroset == MACROSET_MDOC) { 773 mdoc_validate(man); | |
774 switch (curp->outtype) { | 248 switch (curp->outtype) { |
775 case OUTT_HTML: 776 html_mdoc(curp->outdata, man); | 249 case (OUTT_HTML): 250 /* FALLTHROUGH */ 251 case (OUTT_XHTML): 252 curp->outman = html_man; 253 curp->outmdoc = html_mdoc; |
777 break; | 254 break; |
778 case OUTT_TREE: 779 tree_mdoc(curp->outdata, man); | 255 case (OUTT_TREE): 256 curp->outman = tree_man; 257 curp->outmdoc = tree_mdoc; |
780 break; | 258 break; |
781 case OUTT_MAN: 782 man_mdoc(curp->outdata, man); | 259 case (OUTT_MAN): 260 curp->outmdoc = man_mdoc; 261 curp->outman = man_man; |
783 break; | 262 break; |
784 case OUTT_PDF: 785 case OUTT_ASCII: 786 case OUTT_UTF8: 787 case OUTT_LOCALE: 788 case OUTT_PS: 789 terminal_mdoc(curp->outdata, man); | 263 case (OUTT_PDF): 264 /* FALLTHROUGH */ 265 case (OUTT_ASCII): 266 /* FALLTHROUGH */ 267 case (OUTT_UTF8): 268 /* FALLTHROUGH */ 269 case (OUTT_LOCALE): 270 /* FALLTHROUGH */ 271 case (OUTT_PS): 272 curp->outman = terminal_man; 273 curp->outmdoc = terminal_mdoc; |
790 break; 791 default: 792 break; 793 } 794 } | 274 break; 275 default: 276 break; 277 } 278 } |
795 if (man->macroset == MACROSET_MAN) { 796 man_validate(man); 797 switch (curp->outtype) { 798 case OUTT_HTML: 799 html_man(curp->outdata, man); 800 break; 801 case OUTT_TREE: 802 tree_man(curp->outdata, man); 803 break; 804 case OUTT_MAN: 805 man_man(curp->outdata, man); 806 break; 807 case OUTT_PDF: 808 case OUTT_ASCII: 809 case OUTT_UTF8: 810 case OUTT_LOCALE: 811 case OUTT_PS: 812 terminal_man(curp->outdata, man); 813 break; 814 default: 815 break; 816 } 817 } 818} | |
819 | 279 |
820static void 821passthrough(const char *file, int fd, int synopsis_only) 822{ 823 const char synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"; 824 const char synr[] = "SYNOPSIS"; | 280 mparse_result(curp->mp, &mdoc, &man); |
825 | 281 |
826 FILE *stream; 827 const char *syscall; 828 char *line, *cp; 829 size_t linesz; 830 int print; | 282 /* Execute the out device, if it exists. */ |
831 | 283 |
832 line = NULL; 833 linesz = 0; | 284 if (man && curp->outman) 285 (*curp->outman)(curp->outdata, man); 286 if (mdoc && curp->outmdoc) 287 (*curp->outmdoc)(curp->outdata, mdoc); |
834 | 288 |
835 if ((stream = fdopen(fd, "r")) == NULL) { 836 close(fd); 837 syscall = "fdopen"; 838 goto fail; 839 } | 289 cleanup: |
840 | 290 |
841 print = 0; 842 while (getline(&line, &linesz, stream) != -1) { 843 cp = line; 844 if (synopsis_only) { 845 if (print) { 846 if ( ! isspace((unsigned char)*cp)) 847 goto done; 848 while (isspace((unsigned char)*cp)) 849 cp++; 850 } else { 851 if (strcmp(cp, synb) == 0 || 852 strcmp(cp, synr) == 0) 853 print = 1; 854 continue; 855 } 856 } 857 if (fputs(cp, stdout)) { 858 fclose(stream); 859 syscall = "fputs"; 860 goto fail; 861 } 862 } | 291 mparse_reset(curp->mp); |
863 | 292 |
864 if (ferror(stream)) { 865 fclose(stream); 866 syscall = "getline"; 867 goto fail; 868 } 869 870done: 871 free(line); 872 fclose(stream); 873 return; 874 875fail: 876 free(line); 877 warn("%s: SYSERR: %s", file, syscall); 878 if (rc < MANDOCLEVEL_SYSERR) 879 rc = MANDOCLEVEL_SYSERR; | 293 if (*level < rc) 294 *level = rc; |
880} 881 882static int | 295} 296 297static int |
883koptions(int *options, char *arg) | 298moptions(enum mparset *tflags, char *arg) |
884{ 885 | 299{ 300 |
886 if ( ! strcmp(arg, "utf-8")) { 887 *options |= MPARSE_UTF8; 888 *options &= ~MPARSE_LATIN1; 889 } else if ( ! strcmp(arg, "iso-8859-1")) { 890 *options |= MPARSE_LATIN1; 891 *options &= ~MPARSE_UTF8; 892 } else if ( ! strcmp(arg, "us-ascii")) { 893 *options &= ~(MPARSE_UTF8 | MPARSE_LATIN1); 894 } else { 895 warnx("-K %s: Bad argument", arg); 896 return 0; 897 } 898 return 1; 899} 900 901static int 902moptions(int *options, char *arg) 903{ 904 905 if (arg == NULL) 906 /* nothing to do */; 907 else if (0 == strcmp(arg, "doc")) 908 *options |= MPARSE_MDOC; | 301 if (0 == strcmp(arg, "doc")) 302 *tflags = MPARSE_MDOC; |
909 else if (0 == strcmp(arg, "andoc")) | 303 else if (0 == strcmp(arg, "andoc")) |
910 /* nothing to do */; | 304 *tflags = MPARSE_AUTO; |
911 else if (0 == strcmp(arg, "an")) | 305 else if (0 == strcmp(arg, "an")) |
912 *options |= MPARSE_MAN; | 306 *tflags = MPARSE_MAN; |
913 else { | 307 else { |
914 warnx("-m %s: Bad argument", arg); 915 return 0; | 308 fprintf(stderr, "%s: Bad argument\n", arg); 309 return(0); |
916 } 917 | 310 } 311 |
918 return 1; | 312 return(1); |
919} 920 921static int 922toptions(struct curparse *curp, char *arg) 923{ 924 925 if (0 == strcmp(arg, "ascii")) 926 curp->outtype = OUTT_ASCII; --- 6 unchanged lines hidden (view full) --- 933 curp->outtype = OUTT_MAN; 934 else if (0 == strcmp(arg, "html")) 935 curp->outtype = OUTT_HTML; 936 else if (0 == strcmp(arg, "utf8")) 937 curp->outtype = OUTT_UTF8; 938 else if (0 == strcmp(arg, "locale")) 939 curp->outtype = OUTT_LOCALE; 940 else if (0 == strcmp(arg, "xhtml")) | 313} 314 315static int 316toptions(struct curparse *curp, char *arg) 317{ 318 319 if (0 == strcmp(arg, "ascii")) 320 curp->outtype = OUTT_ASCII; --- 6 unchanged lines hidden (view full) --- 327 curp->outtype = OUTT_MAN; 328 else if (0 == strcmp(arg, "html")) 329 curp->outtype = OUTT_HTML; 330 else if (0 == strcmp(arg, "utf8")) 331 curp->outtype = OUTT_UTF8; 332 else if (0 == strcmp(arg, "locale")) 333 curp->outtype = OUTT_LOCALE; 334 else if (0 == strcmp(arg, "xhtml")) |
941 curp->outtype = OUTT_HTML; | 335 curp->outtype = OUTT_XHTML; |
942 else if (0 == strcmp(arg, "ps")) 943 curp->outtype = OUTT_PS; 944 else if (0 == strcmp(arg, "pdf")) 945 curp->outtype = OUTT_PDF; 946 else { | 336 else if (0 == strcmp(arg, "ps")) 337 curp->outtype = OUTT_PS; 338 else if (0 == strcmp(arg, "pdf")) 339 curp->outtype = OUTT_PDF; 340 else { |
947 warnx("-T %s: Bad argument", arg); 948 return 0; | 341 fprintf(stderr, "%s: Bad argument\n", arg); 342 return(0); |
949 } 950 | 343 } 344 |
951 return 1; | 345 return(1); |
952} 953 954static int 955woptions(struct curparse *curp, char *arg) 956{ 957 char *v, *o; | 346} 347 348static int 349woptions(struct curparse *curp, char *arg) 350{ 351 char *v, *o; |
958 const char *toks[7]; | 352 const char *toks[6]; |
959 960 toks[0] = "stop"; 961 toks[1] = "all"; 962 toks[2] = "warning"; 963 toks[3] = "error"; | 353 354 toks[0] = "stop"; 355 toks[1] = "all"; 356 toks[2] = "warning"; 357 toks[3] = "error"; |
964 toks[4] = "unsupp"; 965 toks[5] = "fatal"; 966 toks[6] = NULL; | 358 toks[4] = "fatal"; 359 toks[5] = NULL; |
967 968 while (*arg) { 969 o = arg; 970 switch (getsubopt(&arg, UNCONST(toks), &v)) { | 360 361 while (*arg) { 362 o = arg; 363 switch (getsubopt(&arg, UNCONST(toks), &v)) { |
971 case 0: | 364 case (0): |
972 curp->wstop = 1; 973 break; | 365 curp->wstop = 1; 366 break; |
974 case 1: 975 case 2: | 367 case (1): 368 /* FALLTHROUGH */ 369 case (2): |
976 curp->wlevel = MANDOCLEVEL_WARNING; 977 break; | 370 curp->wlevel = MANDOCLEVEL_WARNING; 371 break; |
978 case 3: | 372 case (3): |
979 curp->wlevel = MANDOCLEVEL_ERROR; 980 break; | 373 curp->wlevel = MANDOCLEVEL_ERROR; 374 break; |
981 case 4: 982 curp->wlevel = MANDOCLEVEL_UNSUPP; | 375 case (4): 376 curp->wlevel = MANDOCLEVEL_FATAL; |
983 break; | 377 break; |
984 case 5: 985 curp->wlevel = MANDOCLEVEL_BADARG; 986 break; | |
987 default: | 378 default: |
988 warnx("-W %s: Bad argument", o); 989 return 0; | 379 fprintf(stderr, "-W%s: Bad argument\n", o); 380 return(0); |
990 } 991 } 992 | 381 } 382 } 383 |
993 return 1; | 384 return(1); |
994} 995 996static void | 385} 386 387static void |
997mmsg(enum mandocerr t, enum mandoclevel lvl, | 388mmsg(enum mandocerr t, enum mandoclevel lvl, |
998 const char *file, int line, int col, const char *msg) 999{ | 389 const char *file, int line, int col, const char *msg) 390{ |
1000 const char *mparse_msg; | |
1001 | 391 |
1002 fprintf(stderr, "%s: %s:", getprogname(), file); | 392 fprintf(stderr, "%s:%d:%d: %s: %s", 393 file, line, col + 1, 394 mparse_strlevel(lvl), 395 mparse_strerror(t)); |
1003 | 396 |
1004 if (line) 1005 fprintf(stderr, "%d:%d:", line, col + 1); 1006 1007 fprintf(stderr, " %s", mparse_strlevel(lvl)); 1008 1009 if (NULL != (mparse_msg = mparse_strerror(t))) 1010 fprintf(stderr, ": %s", mparse_msg); 1011 | |
1012 if (msg) 1013 fprintf(stderr, ": %s", msg); 1014 1015 fputc('\n', stderr); 1016} | 397 if (msg) 398 fprintf(stderr, ": %s", msg); 399 400 fputc('\n', stderr); 401} |
1017 1018static pid_t 1019spawn_pager(struct tag_files *tag_files) 1020{ 1021#define MAX_PAGER_ARGS 16 1022 char *argv[MAX_PAGER_ARGS]; 1023 const char *pager; 1024 char *cp; 1025 size_t cmdlen; 1026 int argc; 1027 pid_t pager_pid; 1028 1029 pager = getenv("MANPAGER"); 1030 if (pager == NULL || *pager == '\0') 1031 pager = getenv("PAGER"); 1032 if (pager == NULL || *pager == '\0') 1033 pager = "more -s"; 1034 cp = mandoc_strdup(pager); 1035 1036 /* 1037 * Parse the pager command into words. 1038 * Intentionally do not do anything fancy here. 1039 */ 1040 1041 argc = 0; 1042 while (argc + 4 < MAX_PAGER_ARGS) { 1043 argv[argc++] = cp; 1044 cp = strchr(cp, ' '); 1045 if (cp == NULL) 1046 break; 1047 *cp++ = '\0'; 1048 while (*cp == ' ') 1049 cp++; 1050 if (*cp == '\0') 1051 break; 1052 } 1053 1054 /* For more(1) and less(1), use the tag file. */ 1055 1056 if ((cmdlen = strlen(argv[0])) >= 4) { 1057 cp = argv[0] + cmdlen - 4; 1058 if (strcmp(cp, "less") == 0 || strcmp(cp, "more") == 0) { 1059 argv[argc++] = mandoc_strdup("-T"); 1060 argv[argc++] = tag_files->tfn; 1061 } 1062 } 1063 argv[argc++] = tag_files->ofn; 1064 argv[argc] = NULL; 1065 1066 switch (pager_pid = fork()) { 1067 case -1: 1068 err((int)MANDOCLEVEL_SYSERR, "fork"); 1069 case 0: 1070 /* Set pgrp in both parent and child to avoid racing exec. */ 1071 (void)setpgid(0, 0); 1072 break; 1073 default: 1074 (void)setpgid(pager_pid, 0); 1075 (void)tcsetpgrp(STDIN_FILENO, pager_pid); 1076#if HAVE_PLEDGE 1077 if (pledge("stdio rpath tmppath tty proc", NULL) == -1) 1078 err((int)MANDOCLEVEL_SYSERR, "pledge"); 1079#endif 1080 tag_files->pager_pid = pager_pid; 1081 return pager_pid; 1082 } 1083 1084 /* The child process becomes the pager. */ 1085 1086 if (dup2(tag_files->ofd, STDOUT_FILENO) == -1) 1087 err((int)MANDOCLEVEL_SYSERR, "pager stdout"); 1088 close(tag_files->ofd); 1089 close(tag_files->tfd); 1090 execvp(argv[0], argv); 1091 err((int)MANDOCLEVEL_SYSERR, "exec %s", argv[0]); 1092} | |