1 /* $Id: main.c,v 1.301 2017/07/26 10:21:55 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010-2012, 2014-2017 Ingo Schwarze <schwarze@openbsd.org>
5 * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.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 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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 */
19 #include "config.h"
20
21 #include <sys/types.h>
22 #include <sys/param.h> /* MACHINE */
23 #include <sys/wait.h>
24
25 #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 #if HAVE_SANDBOX_INIT
34 #include <sandbox.h>
35 #endif
36 #include <signal.h>
37 #include <stdio.h>
38 #include <stdint.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <time.h>
42 #include <unistd.h>
103 static void parse(struct curparse *, int, const char *);
104 static void passthrough(const char *, int, int);
105 static pid_t spawn_pager(struct tag_files *);
106 static int toptions(struct curparse *, char *);
107 static void usage(enum argmode) __attribute__((__noreturn__));
108 static int woptions(struct curparse *, char *);
109
110 static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
111 static char help_arg[] = "help";
112 static char *help_argv[] = {help_arg, NULL};
113 static enum mandoclevel rc;
114 static FILE *mmsg_stream;
115
116
117 int
118 main(int argc, char *argv[])
119 {
120 struct manconf conf;
121 struct mansearch search;
122 struct curparse curp;
123 struct tag_files *tag_files;
124 struct manpage *res, *resp;
125 const char *progname, *sec, *thisarg;
126 char *conf_file, *defpaths, *auxpaths;
127 char *oarg;
128 unsigned char *uc;
129 size_t i, sz;
130 int prio, best_prio;
131 enum outmode outmode;
132 int fd;
133 int show_usage;
134 int options;
135 int use_pager;
136 int status, signum;
137 int c;
138 pid_t pager_pid, tc_pgid, man_pgid, pid;
139
140 #if HAVE_PROGNAME
141 progname = getprogname();
142 #else
143 if (argc < 1)
144 progname = mandoc_strdup("mandoc");
145 else if ((progname = strrchr(argv[0], '/')) == NULL)
146 progname = argv[0];
147 else
148 ++progname;
149 setprogname(progname);
150 #endif
151
152 if (strncmp(progname, "mandocdb", 8) == 0 ||
299 if (oarg != NULL) {
300 if (outmode == OUTMODE_LST)
301 search.outkey = oarg;
302 else {
303 while (oarg != NULL) {
304 thisarg = oarg;
305 if (manconf_output(&conf.output,
306 strsep(&oarg, ","), 0) == 0)
307 continue;
308 warnx("-O %s: Bad argument", thisarg);
309 return (int)MANDOCLEVEL_BADARG;
310 }
311 }
312 }
313
314 if (outmode == OUTMODE_FLN ||
315 outmode == OUTMODE_LST ||
316 !isatty(STDOUT_FILENO))
317 use_pager = 0;
318
319 #if HAVE_PLEDGE
320 if (!use_pager)
321 if (pledge("stdio rpath", NULL) == -1)
322 err((int)MANDOCLEVEL_SYSERR, "pledge");
323 #endif
324
325 /* Parse arguments. */
326
327 if (argc > 0) {
328 argc -= optind;
329 argv += optind;
330 }
331 resp = NULL;
332
333 /*
334 * Quirks for help(1)
335 * and for a man(1) section argument without -s.
336 */
337
338 if (search.argmode == ARG_NAME) {
357 search.arch = MACHINE;
358 #endif
359 }
360
361 rc = MANDOCLEVEL_OK;
362
363 /* man(1), whatis(1), apropos(1) */
364
365 if (search.argmode != ARG_FILE) {
366 if (search.argmode == ARG_NAME &&
367 outmode == OUTMODE_ONE)
368 search.firstmatch = 1;
369
370 /* Access the mandoc database. */
371
372 manconf_parse(&conf, conf_file, defpaths, auxpaths);
373 if ( ! mansearch(&search, &conf.manpath,
374 argc, argv, &res, &sz))
375 usage(search.argmode);
376
377 if (sz == 0) {
378 if (search.argmode == ARG_NAME)
379 fs_search(&search, &conf.manpath,
380 argc, argv, &res, &sz);
381 else
382 warnx("nothing appropriate");
383 }
384
385 if (sz == 0) {
386 rc = MANDOCLEVEL_BADARG;
387 goto out;
388 }
389
390 /*
391 * For standard man(1) and -a output mode,
392 * prepare for copying filename pointers
393 * into the program parameter array.
394 */
395
396 if (outmode == OUTMODE_ONE) {
397 argc = 1;
398 best_prio = 20;
399 } else if (outmode == OUTMODE_ALL)
400 argc = (int)sz;
401
402 /* Iterate all matching manuals. */
403
404 resp = res;
405 for (i = 0; i < sz; i++) {
449
450 if (search.argmode == ARG_FILE)
451 moptions(&options, auxpaths);
452
453 mchars_alloc();
454 curp.mp = mparse_alloc(options, curp.mmin, mmsg,
455 curp.os_e, curp.os_s);
456
457 /*
458 * Conditionally start up the lookaside buffer before parsing.
459 */
460 if (OUTT_MAN == curp.outtype)
461 mparse_keep(curp.mp);
462
463 if (argc < 1) {
464 if (use_pager)
465 tag_files = tag_init();
466 parse(&curp, STDIN_FILENO, "<stdin>");
467 }
468
469 while (argc > 0) {
470 fd = mparse_open(curp.mp, resp != NULL ? resp->file : *argv);
471 if (fd != -1) {
472 if (use_pager) {
473 tag_files = tag_init();
474 use_pager = 0;
475 }
476
477 if (resp == NULL)
478 parse(&curp, fd, *argv);
479 else if (resp->form == FORM_SRC) {
480 /* For .so only; ignore failure. */
481 (void)chdir(conf.manpath.paths[resp->ipath]);
482 parse(&curp, fd, resp->file);
483 } else
484 passthrough(resp->file, fd,
485 conf.output.synopsisonly);
486
487 if (argc > 1 && curp.outtype <= OUTT_UTF8) {
488 if (curp.outdata == NULL)
489 outdata_alloc(&curp);
490 terminal_sepline(curp.outdata);
491 }
492 } else if (rc < MANDOCLEVEL_ERROR)
493 rc = MANDOCLEVEL_ERROR;
494
495 if (MANDOCLEVEL_OK != rc && curp.wstop)
496 break;
497
498 if (resp != NULL)
499 resp++;
500 else
501 argv++;
502 if (--argc)
503 mparse_reset(curp.mp);
504 }
505
506 if (curp.outdata != NULL) {
507 switch (curp.outtype) {
508 case OUTT_HTML:
509 html_free(curp.outdata);
510 break;
511 case OUTT_UTF8:
512 case OUTT_LOCALE:
513 case OUTT_ASCII:
514 ascii_free(curp.outdata);
515 break;
516 case OUTT_PDF:
517 case OUTT_PS:
518 pspdf_free(curp.outdata);
519 break;
520 default:
521 break;
522 }
523 }
524 mandoc_xr_free();
705 size_t ipath, isec, lastsz;
706
707 assert(cfg->argmode == ARG_NAME);
708
709 if (res != NULL)
710 *res = NULL;
711 *ressz = lastsz = 0;
712 while (argc) {
713 for (ipath = 0; ipath < paths->sz; ipath++) {
714 if (cfg->sec != NULL) {
715 if (fs_lookup(paths, ipath, cfg->sec,
716 cfg->arch, *argv, res, ressz) &&
717 cfg->firstmatch)
718 return 1;
719 } else for (isec = 0; isec < nsec; isec++)
720 if (fs_lookup(paths, ipath, sections[isec],
721 cfg->arch, *argv, res, ressz) &&
722 cfg->firstmatch)
723 return 1;
724 }
725 if (res != NULL && *ressz == lastsz)
726 warnx("No entry for %s in the manual.", *argv);
727 lastsz = *ressz;
728 argv++;
729 argc--;
730 }
731 return 0;
732 }
733
734 static void
735 parse(struct curparse *curp, int fd, const char *file)
736 {
737 enum mandoclevel rctmp;
738 struct roff_man *man;
739
740 /* Begin by parsing the file itself. */
741
742 assert(file);
743 assert(fd >= 0);
744
745 rctmp = mparse_readfd(curp->mp, fd, file);
1156 case -1:
1157 err((int)MANDOCLEVEL_SYSERR, "fork");
1158 case 0:
1159 break;
1160 default:
1161 (void)setpgid(pager_pid, 0);
1162 (void)tcsetpgrp(tag_files->ofd, pager_pid);
1163 #if HAVE_PLEDGE
1164 if (pledge("stdio rpath tmppath tty proc", NULL) == -1)
1165 err((int)MANDOCLEVEL_SYSERR, "pledge");
1166 #endif
1167 tag_files->pager_pid = pager_pid;
1168 return pager_pid;
1169 }
1170
1171 /* The child process becomes the pager. */
1172
1173 if (dup2(tag_files->ofd, STDOUT_FILENO) == -1)
1174 err((int)MANDOCLEVEL_SYSERR, "pager stdout");
1175 close(tag_files->ofd);
1176 close(tag_files->tfd);
1177
1178 /* Do not start the pager before controlling the terminal. */
1179
1180 while (tcgetpgrp(STDOUT_FILENO) != getpid())
1181 nanosleep(&timeout, NULL);
1182
1183 execvp(argv[0], argv);
1184 err((int)MANDOCLEVEL_SYSERR, "exec %s", argv[0]);
1185 }
|
1 /* $Id: main.c,v 1.306 2018/05/14 14:10:23 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010-2012, 2014-2018 Ingo Schwarze <schwarze@openbsd.org>
5 * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.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 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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 */
19 #include "config.h"
20
21 #include <sys/types.h>
22 #include <sys/ioctl.h>
23 #include <sys/param.h> /* MACHINE */
24 #include <sys/termios.h>
25 #include <sys/wait.h>
26
27 #include <assert.h>
28 #include <ctype.h>
29 #if HAVE_ERR
30 #include <err.h>
31 #endif
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <glob.h>
35 #if HAVE_SANDBOX_INIT
36 #include <sandbox.h>
37 #endif
38 #include <signal.h>
39 #include <stdio.h>
40 #include <stdint.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <time.h>
44 #include <unistd.h>
105 static void parse(struct curparse *, int, const char *);
106 static void passthrough(const char *, int, int);
107 static pid_t spawn_pager(struct tag_files *);
108 static int toptions(struct curparse *, char *);
109 static void usage(enum argmode) __attribute__((__noreturn__));
110 static int woptions(struct curparse *, char *);
111
112 static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
113 static char help_arg[] = "help";
114 static char *help_argv[] = {help_arg, NULL};
115 static enum mandoclevel rc;
116 static FILE *mmsg_stream;
117
118
119 int
120 main(int argc, char *argv[])
121 {
122 struct manconf conf;
123 struct mansearch search;
124 struct curparse curp;
125 struct winsize ws;
126 struct tag_files *tag_files;
127 struct manpage *res, *resp;
128 const char *progname, *sec, *thisarg;
129 char *conf_file, *defpaths, *auxpaths;
130 char *oarg;
131 unsigned char *uc;
132 size_t i, sz;
133 int prio, best_prio;
134 enum outmode outmode;
135 int fd, startdir;
136 int show_usage;
137 int options;
138 int use_pager;
139 int status, signum;
140 int c;
141 pid_t pager_pid, tc_pgid, man_pgid, pid;
142
143 #if HAVE_PROGNAME
144 progname = getprogname();
145 #else
146 if (argc < 1)
147 progname = mandoc_strdup("mandoc");
148 else if ((progname = strrchr(argv[0], '/')) == NULL)
149 progname = argv[0];
150 else
151 ++progname;
152 setprogname(progname);
153 #endif
154
155 if (strncmp(progname, "mandocdb", 8) == 0 ||
302 if (oarg != NULL) {
303 if (outmode == OUTMODE_LST)
304 search.outkey = oarg;
305 else {
306 while (oarg != NULL) {
307 thisarg = oarg;
308 if (manconf_output(&conf.output,
309 strsep(&oarg, ","), 0) == 0)
310 continue;
311 warnx("-O %s: Bad argument", thisarg);
312 return (int)MANDOCLEVEL_BADARG;
313 }
314 }
315 }
316
317 if (outmode == OUTMODE_FLN ||
318 outmode == OUTMODE_LST ||
319 !isatty(STDOUT_FILENO))
320 use_pager = 0;
321
322 if (use_pager &&
323 (conf.output.width == 0 || conf.output.indent == 0) &&
324 ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 &&
325 ws.ws_col > 1) {
326 if (conf.output.width == 0 && ws.ws_col < 79)
327 conf.output.width = ws.ws_col - 1;
328 if (conf.output.indent == 0 && ws.ws_col < 66)
329 conf.output.indent = 3;
330 }
331
332 #if HAVE_PLEDGE
333 if (!use_pager)
334 if (pledge("stdio rpath", NULL) == -1)
335 err((int)MANDOCLEVEL_SYSERR, "pledge");
336 #endif
337
338 /* Parse arguments. */
339
340 if (argc > 0) {
341 argc -= optind;
342 argv += optind;
343 }
344 resp = NULL;
345
346 /*
347 * Quirks for help(1)
348 * and for a man(1) section argument without -s.
349 */
350
351 if (search.argmode == ARG_NAME) {
370 search.arch = MACHINE;
371 #endif
372 }
373
374 rc = MANDOCLEVEL_OK;
375
376 /* man(1), whatis(1), apropos(1) */
377
378 if (search.argmode != ARG_FILE) {
379 if (search.argmode == ARG_NAME &&
380 outmode == OUTMODE_ONE)
381 search.firstmatch = 1;
382
383 /* Access the mandoc database. */
384
385 manconf_parse(&conf, conf_file, defpaths, auxpaths);
386 if ( ! mansearch(&search, &conf.manpath,
387 argc, argv, &res, &sz))
388 usage(search.argmode);
389
390 if (sz == 0 && search.argmode == ARG_NAME)
391 fs_search(&search, &conf.manpath,
392 argc, argv, &res, &sz);
393
394 if (search.argmode == ARG_NAME) {
395 for (c = 0; c < argc; c++) {
396 if (strchr(argv[c], '/') == NULL)
397 continue;
398 if (access(argv[c], R_OK) == -1) {
399 warn("%s", argv[c]);
400 continue;
401 }
402 res = mandoc_reallocarray(res,
403 sz + 1, sizeof(*res));
404 res[sz].file = mandoc_strdup(argv[c]);
405 res[sz].names = NULL;
406 res[sz].output = NULL;
407 res[sz].ipath = SIZE_MAX;
408 res[sz].bits = 0;
409 res[sz].sec = 10;
410 res[sz].form = FORM_SRC;
411 sz++;
412 }
413 }
414
415 if (sz == 0) {
416 if (search.argmode != ARG_NAME)
417 warnx("nothing appropriate");
418 rc = MANDOCLEVEL_BADARG;
419 goto out;
420 }
421
422 /*
423 * For standard man(1) and -a output mode,
424 * prepare for copying filename pointers
425 * into the program parameter array.
426 */
427
428 if (outmode == OUTMODE_ONE) {
429 argc = 1;
430 best_prio = 20;
431 } else if (outmode == OUTMODE_ALL)
432 argc = (int)sz;
433
434 /* Iterate all matching manuals. */
435
436 resp = res;
437 for (i = 0; i < sz; i++) {
481
482 if (search.argmode == ARG_FILE)
483 moptions(&options, auxpaths);
484
485 mchars_alloc();
486 curp.mp = mparse_alloc(options, curp.mmin, mmsg,
487 curp.os_e, curp.os_s);
488
489 /*
490 * Conditionally start up the lookaside buffer before parsing.
491 */
492 if (OUTT_MAN == curp.outtype)
493 mparse_keep(curp.mp);
494
495 if (argc < 1) {
496 if (use_pager)
497 tag_files = tag_init();
498 parse(&curp, STDIN_FILENO, "<stdin>");
499 }
500
501 /*
502 * Remember the original working directory, if possible.
503 * This will be needed if some names on the command line
504 * are page names and some are relative file names.
505 * Do not error out if the current directory is not
506 * readable: Maybe it won't be needed after all.
507 */
508 startdir = open(".", O_RDONLY | O_DIRECTORY);
509
510 while (argc > 0) {
511
512 /*
513 * Changing directories is not needed in ARG_FILE mode.
514 * Do it on a best-effort basis. Even in case of
515 * failure, some functionality may still work.
516 */
517 if (resp != NULL) {
518 if (resp->ipath != SIZE_MAX)
519 (void)chdir(conf.manpath.paths[resp->ipath]);
520 else if (startdir != -1)
521 (void)fchdir(startdir);
522 }
523
524 fd = mparse_open(curp.mp, resp != NULL ? resp->file : *argv);
525 if (fd != -1) {
526 if (use_pager) {
527 tag_files = tag_init();
528 use_pager = 0;
529 }
530
531 if (resp == NULL)
532 parse(&curp, fd, *argv);
533 else if (resp->form == FORM_SRC)
534 parse(&curp, fd, resp->file);
535 else
536 passthrough(resp->file, fd,
537 conf.output.synopsisonly);
538
539 if (ferror(stdout)) {
540 if (tag_files != NULL) {
541 warn("%s", tag_files->ofn);
542 tag_unlink();
543 tag_files = NULL;
544 } else
545 warn("stdout");
546 rc = MANDOCLEVEL_SYSERR;
547 break;
548 }
549
550 if (argc > 1 && curp.outtype <= OUTT_UTF8) {
551 if (curp.outdata == NULL)
552 outdata_alloc(&curp);
553 terminal_sepline(curp.outdata);
554 }
555 } else if (rc < MANDOCLEVEL_ERROR)
556 rc = MANDOCLEVEL_ERROR;
557
558 if (MANDOCLEVEL_OK != rc && curp.wstop)
559 break;
560
561 if (resp != NULL)
562 resp++;
563 else
564 argv++;
565 if (--argc)
566 mparse_reset(curp.mp);
567 }
568 if (startdir != -1) {
569 (void)fchdir(startdir);
570 close(startdir);
571 }
572
573 if (curp.outdata != NULL) {
574 switch (curp.outtype) {
575 case OUTT_HTML:
576 html_free(curp.outdata);
577 break;
578 case OUTT_UTF8:
579 case OUTT_LOCALE:
580 case OUTT_ASCII:
581 ascii_free(curp.outdata);
582 break;
583 case OUTT_PDF:
584 case OUTT_PS:
585 pspdf_free(curp.outdata);
586 break;
587 default:
588 break;
589 }
590 }
591 mandoc_xr_free();
772 size_t ipath, isec, lastsz;
773
774 assert(cfg->argmode == ARG_NAME);
775
776 if (res != NULL)
777 *res = NULL;
778 *ressz = lastsz = 0;
779 while (argc) {
780 for (ipath = 0; ipath < paths->sz; ipath++) {
781 if (cfg->sec != NULL) {
782 if (fs_lookup(paths, ipath, cfg->sec,
783 cfg->arch, *argv, res, ressz) &&
784 cfg->firstmatch)
785 return 1;
786 } else for (isec = 0; isec < nsec; isec++)
787 if (fs_lookup(paths, ipath, sections[isec],
788 cfg->arch, *argv, res, ressz) &&
789 cfg->firstmatch)
790 return 1;
791 }
792 if (res != NULL && *ressz == lastsz &&
793 strchr(*argv, '/') == NULL)
794 warnx("No entry for %s in the manual.", *argv);
795 lastsz = *ressz;
796 argv++;
797 argc--;
798 }
799 return 0;
800 }
801
802 static void
803 parse(struct curparse *curp, int fd, const char *file)
804 {
805 enum mandoclevel rctmp;
806 struct roff_man *man;
807
808 /* Begin by parsing the file itself. */
809
810 assert(file);
811 assert(fd >= 0);
812
813 rctmp = mparse_readfd(curp->mp, fd, file);
1224 case -1:
1225 err((int)MANDOCLEVEL_SYSERR, "fork");
1226 case 0:
1227 break;
1228 default:
1229 (void)setpgid(pager_pid, 0);
1230 (void)tcsetpgrp(tag_files->ofd, pager_pid);
1231 #if HAVE_PLEDGE
1232 if (pledge("stdio rpath tmppath tty proc", NULL) == -1)
1233 err((int)MANDOCLEVEL_SYSERR, "pledge");
1234 #endif
1235 tag_files->pager_pid = pager_pid;
1236 return pager_pid;
1237 }
1238
1239 /* The child process becomes the pager. */
1240
1241 if (dup2(tag_files->ofd, STDOUT_FILENO) == -1)
1242 err((int)MANDOCLEVEL_SYSERR, "pager stdout");
1243 close(tag_files->ofd);
1244 assert(tag_files->tfd == -1);
1245
1246 /* Do not start the pager before controlling the terminal. */
1247
1248 while (tcgetpgrp(STDOUT_FILENO) != getpid())
1249 nanosleep(&timeout, NULL);
1250
1251 execvp(argv[0], argv);
1252 err((int)MANDOCLEVEL_SYSERR, "exec %s", argv[0]);
1253 }
|