1*cf5a6c84SAndroid Build Coastguard Worker /* man.c - Read system documentation
2*cf5a6c84SAndroid Build Coastguard Worker *
3*cf5a6c84SAndroid Build Coastguard Worker * Copyright 2019 makepost <[email protected]>
4*cf5a6c84SAndroid Build Coastguard Worker *
5*cf5a6c84SAndroid Build Coastguard Worker * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/man.html
6*cf5a6c84SAndroid Build Coastguard Worker
7*cf5a6c84SAndroid Build Coastguard Worker USE_MAN(NEWTOY(man, "k:M:", TOYFLAG_USR|TOYFLAG_BIN))
8*cf5a6c84SAndroid Build Coastguard Worker
9*cf5a6c84SAndroid Build Coastguard Worker config MAN
10*cf5a6c84SAndroid Build Coastguard Worker bool "man"
11*cf5a6c84SAndroid Build Coastguard Worker default n
12*cf5a6c84SAndroid Build Coastguard Worker help
13*cf5a6c84SAndroid Build Coastguard Worker usage: man [-M PATH] [-k STRING] | [SECTION] COMMAND
14*cf5a6c84SAndroid Build Coastguard Worker
15*cf5a6c84SAndroid Build Coastguard Worker Read manual page for system command.
16*cf5a6c84SAndroid Build Coastguard Worker
17*cf5a6c84SAndroid Build Coastguard Worker -k List pages with STRING in their short description
18*cf5a6c84SAndroid Build Coastguard Worker -M Override $MANPATH
19*cf5a6c84SAndroid Build Coastguard Worker
20*cf5a6c84SAndroid Build Coastguard Worker Man pages are divided into 8 sections:
21*cf5a6c84SAndroid Build Coastguard Worker 1 commands 2 system calls 3 library functions 4 /dev files
22*cf5a6c84SAndroid Build Coastguard Worker 5 file formats 6 games 7 miscellaneous 8 system management
23*cf5a6c84SAndroid Build Coastguard Worker
24*cf5a6c84SAndroid Build Coastguard Worker Sections are searched in the order 1 8 3 2 5 4 6 7 unless you specify a
25*cf5a6c84SAndroid Build Coastguard Worker section. Each section has a page called "intro", and there's a global
26*cf5a6c84SAndroid Build Coastguard Worker introduction under "man-pages".
27*cf5a6c84SAndroid Build Coastguard Worker */
28*cf5a6c84SAndroid Build Coastguard Worker
29*cf5a6c84SAndroid Build Coastguard Worker #define FOR_man
30*cf5a6c84SAndroid Build Coastguard Worker #include <toys.h>
31*cf5a6c84SAndroid Build Coastguard Worker
32*cf5a6c84SAndroid Build Coastguard Worker GLOBALS(
33*cf5a6c84SAndroid Build Coastguard Worker char *M, *k;
34*cf5a6c84SAndroid Build Coastguard Worker
35*cf5a6c84SAndroid Build Coastguard Worker char any, cell, ex, *f, k_done, *line, *m, **sct, **scts, **sufs;
36*cf5a6c84SAndroid Build Coastguard Worker regex_t reg;
37*cf5a6c84SAndroid Build Coastguard Worker )
38*cf5a6c84SAndroid Build Coastguard Worker
newln()39*cf5a6c84SAndroid Build Coastguard Worker static void newln()
40*cf5a6c84SAndroid Build Coastguard Worker {
41*cf5a6c84SAndroid Build Coastguard Worker if (FLAG(k)) return;
42*cf5a6c84SAndroid Build Coastguard Worker if (TT.any) putchar('\n');
43*cf5a6c84SAndroid Build Coastguard Worker if (TT.any && TT.cell != 2) putchar('\n'); // gawk alias
44*cf5a6c84SAndroid Build Coastguard Worker TT.any = TT.cell = 0;
45*cf5a6c84SAndroid Build Coastguard Worker }
46*cf5a6c84SAndroid Build Coastguard Worker
put(char * x)47*cf5a6c84SAndroid Build Coastguard Worker static void put(char *x)
48*cf5a6c84SAndroid Build Coastguard Worker {
49*cf5a6c84SAndroid Build Coastguard Worker while (*x && (TT.ex || *x != '\n')) TT.any = putchar(*x++);
50*cf5a6c84SAndroid Build Coastguard Worker }
51*cf5a6c84SAndroid Build Coastguard Worker
52*cf5a6c84SAndroid Build Coastguard Worker // Substitute with same length or shorter.
s(char * x,char * y)53*cf5a6c84SAndroid Build Coastguard Worker static void s(char *x, char *y)
54*cf5a6c84SAndroid Build Coastguard Worker {
55*cf5a6c84SAndroid Build Coastguard Worker int i = strlen(x), j = strlen(y), k, l;
56*cf5a6c84SAndroid Build Coastguard Worker
57*cf5a6c84SAndroid Build Coastguard Worker for (k = 0; TT.line[k]; k++) if (!strncmp(x, &TT.line[k], i)) {
58*cf5a6c84SAndroid Build Coastguard Worker memmove(&TT.line[k], y, j);
59*cf5a6c84SAndroid Build Coastguard Worker for (l = k += j; TT.line[l]; l++) TT.line[l] = TT.line[l + i - j];
60*cf5a6c84SAndroid Build Coastguard Worker k--;
61*cf5a6c84SAndroid Build Coastguard Worker }
62*cf5a6c84SAndroid Build Coastguard Worker }
63*cf5a6c84SAndroid Build Coastguard Worker
start(char * x)64*cf5a6c84SAndroid Build Coastguard Worker static char start(char *x)
65*cf5a6c84SAndroid Build Coastguard Worker {
66*cf5a6c84SAndroid Build Coastguard Worker return !strncmp(x, TT.line, strlen(x));
67*cf5a6c84SAndroid Build Coastguard Worker }
68*cf5a6c84SAndroid Build Coastguard Worker
trim(char * x)69*cf5a6c84SAndroid Build Coastguard Worker static void trim(char *x)
70*cf5a6c84SAndroid Build Coastguard Worker {
71*cf5a6c84SAndroid Build Coastguard Worker if (start(x)) while (*x++) TT.line++;
72*cf5a6c84SAndroid Build Coastguard Worker }
73*cf5a6c84SAndroid Build Coastguard Worker
k(char * s)74*cf5a6c84SAndroid Build Coastguard Worker static char k(char *s) {
75*cf5a6c84SAndroid Build Coastguard Worker TT.k_done = 2;
76*cf5a6c84SAndroid Build Coastguard Worker if (s) TT.line = s;
77*cf5a6c84SAndroid Build Coastguard Worker return !regexec(&TT.reg, TT.k, 0, 0, 0)||!regexec(&TT.reg, TT.line, 0, 0, 0);
78*cf5a6c84SAndroid Build Coastguard Worker }
79*cf5a6c84SAndroid Build Coastguard Worker
do_man(char ** pline,long len)80*cf5a6c84SAndroid Build Coastguard Worker static void do_man(char **pline, long len)
81*cf5a6c84SAndroid Build Coastguard Worker {
82*cf5a6c84SAndroid Build Coastguard Worker if (!pline) return newln();
83*cf5a6c84SAndroid Build Coastguard Worker TT.line = *pline;
84*cf5a6c84SAndroid Build Coastguard Worker
85*cf5a6c84SAndroid Build Coastguard Worker if (FLAG(k)) {
86*cf5a6c84SAndroid Build Coastguard Worker if (!TT.k_done && !start(".") && !start("'") && k(strstr(*pline, "- ")))
87*cf5a6c84SAndroid Build Coastguard Worker printf("%-20s %s%s", TT.k, "- "+2*(TT.line!=*pline), TT.line);
88*cf5a6c84SAndroid Build Coastguard Worker else if (!TT.k_done && start(".so") && k(basename(*pline + 4)))
89*cf5a6c84SAndroid Build Coastguard Worker printf("%s - See %s", TT.k, TT.line);
90*cf5a6c84SAndroid Build Coastguard Worker } else {
91*cf5a6c84SAndroid Build Coastguard Worker s("\\fB", ""), s("\\fI", ""), s("\\fP", ""), s("\\fR", ""); // bash bold,ita
92*cf5a6c84SAndroid Build Coastguard Worker s("\\(aq", "'"), s("\\(cq", "'"), s("\\(dq", "\""); // bash,rsync quote
93*cf5a6c84SAndroid Build Coastguard Worker s("\\*(lq", "\""), s("\\*(rq", "\""); // gawk quote
94*cf5a6c84SAndroid Build Coastguard Worker s("\\(bu", "*"), s("\\(bv", "|"); // bash symbol
95*cf5a6c84SAndroid Build Coastguard Worker s("\\&", ""), s("\\f(CW", ""); // gawk,rsync fancy
96*cf5a6c84SAndroid Build Coastguard Worker s("\\-", "-"), s("\\(", ""), s("\\^", ""), s("\\e", "\\"); // bash escape
97*cf5a6c84SAndroid Build Coastguard Worker s("\\*(", "#"); // gawk var
98*cf5a6c84SAndroid Build Coastguard Worker
99*cf5a6c84SAndroid Build Coastguard Worker if (start(".BR")) trim(".BR "), s(" ", ""); // bash boldpunct
100*cf5a6c84SAndroid Build Coastguard Worker if (start(".IP")) newln(), trim(".IP "); // bash list
101*cf5a6c84SAndroid Build Coastguard Worker if (start(".IR")) trim(".IR "), s(" ", ""); // bash itapunct
102*cf5a6c84SAndroid Build Coastguard Worker
103*cf5a6c84SAndroid Build Coastguard Worker trim(".B "); // bash bold
104*cf5a6c84SAndroid Build Coastguard Worker trim(".BI "); // gawk boldita
105*cf5a6c84SAndroid Build Coastguard Worker trim(".FN "); // bash filename
106*cf5a6c84SAndroid Build Coastguard Worker trim(".I "); // bash ita
107*cf5a6c84SAndroid Build Coastguard Worker trim(".if n "); // bash nroff
108*cf5a6c84SAndroid Build Coastguard Worker if (start(".E")) TT.ex = TT.line[2] == 'X'; // stat example
109*cf5a6c84SAndroid Build Coastguard Worker else if (start(".PP")) newln(); // bash paragraph
110*cf5a6c84SAndroid Build Coastguard Worker else if (start(".SM")); // bash small
111*cf5a6c84SAndroid Build Coastguard Worker else if (start(".S")) newln(), put(TT.line + 4), newln(); // bash section
112*cf5a6c84SAndroid Build Coastguard Worker else if (start(".so")) put("See "), put(basename(TT.line + 4)); // lastb
113*cf5a6c84SAndroid Build Coastguard Worker else if (start(".TH")) s("\"", " "), put(TT.line + 4); // gawk,git head
114*cf5a6c84SAndroid Build Coastguard Worker else if (start(".TP")) newln(), TT.cell = 1; // bash table
115*cf5a6c84SAndroid Build Coastguard Worker else if (start(".") || start("\'")); // bash,git garbage
116*cf5a6c84SAndroid Build Coastguard Worker else if (!*TT.line); // emerge
117*cf5a6c84SAndroid Build Coastguard Worker else {
118*cf5a6c84SAndroid Build Coastguard Worker if (TT.cell) TT.cell++;
119*cf5a6c84SAndroid Build Coastguard Worker if (!TT.ex) put(" ");
120*cf5a6c84SAndroid Build Coastguard Worker put(TT.line);
121*cf5a6c84SAndroid Build Coastguard Worker }
122*cf5a6c84SAndroid Build Coastguard Worker }
123*cf5a6c84SAndroid Build Coastguard Worker }
124*cf5a6c84SAndroid Build Coastguard Worker
125*cf5a6c84SAndroid Build Coastguard Worker // Open file, decompressing if suffix known.
zopen(char * s)126*cf5a6c84SAndroid Build Coastguard Worker static int zopen(char *s)
127*cf5a6c84SAndroid Build Coastguard Worker {
128*cf5a6c84SAndroid Build Coastguard Worker int fds[] = {-1, -1};
129*cf5a6c84SAndroid Build Coastguard Worker char **known = TT.sufs, *suf = strrchr(s, '.');
130*cf5a6c84SAndroid Build Coastguard Worker
131*cf5a6c84SAndroid Build Coastguard Worker if ((*fds = open(s, O_RDONLY)) == -1) return -1;
132*cf5a6c84SAndroid Build Coastguard Worker while (suf && *known && strcmp(suf, *known++));
133*cf5a6c84SAndroid Build Coastguard Worker if (!suf || !*known) return *fds;
134*cf5a6c84SAndroid Build Coastguard Worker sprintf(toybuf, "%czcat"+2*(suf[1]=='g'), suf[1]);
135*cf5a6c84SAndroid Build Coastguard Worker xpopen_both((char *[]){toybuf, s, 0}, fds);
136*cf5a6c84SAndroid Build Coastguard Worker close(fds[0]);
137*cf5a6c84SAndroid Build Coastguard Worker return fds[1];
138*cf5a6c84SAndroid Build Coastguard Worker }
139*cf5a6c84SAndroid Build Coastguard Worker
manpath()140*cf5a6c84SAndroid Build Coastguard Worker static char manpath()
141*cf5a6c84SAndroid Build Coastguard Worker {
142*cf5a6c84SAndroid Build Coastguard Worker if (*++TT.sct) return 0;
143*cf5a6c84SAndroid Build Coastguard Worker if (!(TT.m = strsep(&TT.M, ":"))) return 1;
144*cf5a6c84SAndroid Build Coastguard Worker TT.sct = TT.scts;
145*cf5a6c84SAndroid Build Coastguard Worker return 0;
146*cf5a6c84SAndroid Build Coastguard Worker }
147*cf5a6c84SAndroid Build Coastguard Worker
148*cf5a6c84SAndroid Build Coastguard Worker // Try opening all the possible file extensions.
tryfile(char * name)149*cf5a6c84SAndroid Build Coastguard Worker static int tryfile(char *name)
150*cf5a6c84SAndroid Build Coastguard Worker {
151*cf5a6c84SAndroid Build Coastguard Worker int dotnum, fd = -1;
152*cf5a6c84SAndroid Build Coastguard Worker char *s = xmprintf("%s/man%s/%s.%s.bz2", TT.m, *TT.sct, name, *TT.sct), **suf;
153*cf5a6c84SAndroid Build Coastguard Worker size_t len = strlen(s) - 4;
154*cf5a6c84SAndroid Build Coastguard Worker
155*cf5a6c84SAndroid Build Coastguard Worker for (dotnum = 0; dotnum <= 2; dotnum += 2) {
156*cf5a6c84SAndroid Build Coastguard Worker suf = TT.sufs;
157*cf5a6c84SAndroid Build Coastguard Worker while ((fd == -1) && *suf) strcpy(s + len - dotnum, *suf++), fd = zopen(s);
158*cf5a6c84SAndroid Build Coastguard Worker // Recheck suf in zopen, because for x.1.gz name here it is "".
159*cf5a6c84SAndroid Build Coastguard Worker }
160*cf5a6c84SAndroid Build Coastguard Worker free(s);
161*cf5a6c84SAndroid Build Coastguard Worker return fd;
162*cf5a6c84SAndroid Build Coastguard Worker }
163*cf5a6c84SAndroid Build Coastguard Worker
man_main(void)164*cf5a6c84SAndroid Build Coastguard Worker void man_main(void)
165*cf5a6c84SAndroid Build Coastguard Worker {
166*cf5a6c84SAndroid Build Coastguard Worker int fd = -1;
167*cf5a6c84SAndroid Build Coastguard Worker TT.scts = (char *[]) {"1", "8", "3", "2", "5", "4", "6", "7", 0};
168*cf5a6c84SAndroid Build Coastguard Worker TT.sct = TT.scts - 1; // First manpath() read increments.
169*cf5a6c84SAndroid Build Coastguard Worker TT.sufs = (char *[]) {".bz2", ".gz", ".xz", "", 0};
170*cf5a6c84SAndroid Build Coastguard Worker
171*cf5a6c84SAndroid Build Coastguard Worker if (!TT.M) TT.M = getenv("MANPATH");
172*cf5a6c84SAndroid Build Coastguard Worker if (!TT.M) TT.M = "/usr/share/man";
173*cf5a6c84SAndroid Build Coastguard Worker
174*cf5a6c84SAndroid Build Coastguard Worker if (FLAG(k)) {
175*cf5a6c84SAndroid Build Coastguard Worker char *d, *f;
176*cf5a6c84SAndroid Build Coastguard Worker DIR *dp;
177*cf5a6c84SAndroid Build Coastguard Worker struct dirent *entry;
178*cf5a6c84SAndroid Build Coastguard Worker
179*cf5a6c84SAndroid Build Coastguard Worker xregcomp(&TT.reg, TT.k, REG_ICASE|REG_NOSUB);
180*cf5a6c84SAndroid Build Coastguard Worker while (!manpath()) {
181*cf5a6c84SAndroid Build Coastguard Worker d = xmprintf("%s/man%s", TT.m, *TT.sct);
182*cf5a6c84SAndroid Build Coastguard Worker if (!(dp = opendir(d))) continue;
183*cf5a6c84SAndroid Build Coastguard Worker while ((entry = readdir(dp))) {
184*cf5a6c84SAndroid Build Coastguard Worker if (entry->d_name[0] == '.') continue;
185*cf5a6c84SAndroid Build Coastguard Worker f = xmprintf("%s/%s", d, TT.k = entry->d_name);
186*cf5a6c84SAndroid Build Coastguard Worker if (-1 != (fd = zopen(f))) {
187*cf5a6c84SAndroid Build Coastguard Worker TT.k_done = 0;
188*cf5a6c84SAndroid Build Coastguard Worker do_lines(fd, '\n', do_man);
189*cf5a6c84SAndroid Build Coastguard Worker }
190*cf5a6c84SAndroid Build Coastguard Worker free(f);
191*cf5a6c84SAndroid Build Coastguard Worker }
192*cf5a6c84SAndroid Build Coastguard Worker closedir(dp);
193*cf5a6c84SAndroid Build Coastguard Worker free(d);
194*cf5a6c84SAndroid Build Coastguard Worker }
195*cf5a6c84SAndroid Build Coastguard Worker return regfree(&TT.reg);
196*cf5a6c84SAndroid Build Coastguard Worker }
197*cf5a6c84SAndroid Build Coastguard Worker
198*cf5a6c84SAndroid Build Coastguard Worker if (!toys.optc) help_exit("which page?");
199*cf5a6c84SAndroid Build Coastguard Worker
200*cf5a6c84SAndroid Build Coastguard Worker if (toys.optc == 1) {
201*cf5a6c84SAndroid Build Coastguard Worker if (strchr(*toys.optargs, '/')) fd = zopen(*toys.optargs);
202*cf5a6c84SAndroid Build Coastguard Worker else while ((fd == -1) && !manpath()) fd = tryfile(*toys.optargs);
203*cf5a6c84SAndroid Build Coastguard Worker if (fd == -1) error_exit("no %s", *toys.optargs);
204*cf5a6c84SAndroid Build Coastguard Worker
205*cf5a6c84SAndroid Build Coastguard Worker // If they specified a section, look for file in that section
206*cf5a6c84SAndroid Build Coastguard Worker } else {
207*cf5a6c84SAndroid Build Coastguard Worker TT.scts = (char *[]){*toys.optargs, 0}, TT.sct = TT.scts - 1;
208*cf5a6c84SAndroid Build Coastguard Worker while ((fd == -1) && !manpath()) fd = tryfile(toys.optargs[1]);
209*cf5a6c84SAndroid Build Coastguard Worker if (fd == -1) error_exit("section %s no %s", *--TT.sct, toys.optargs[1]);
210*cf5a6c84SAndroid Build Coastguard Worker }
211*cf5a6c84SAndroid Build Coastguard Worker
212*cf5a6c84SAndroid Build Coastguard Worker do_lines(fd, '\n', do_man);
213*cf5a6c84SAndroid Build Coastguard Worker }
214