add manual pages; don't produce .o's unless necessary; add new labels;
[unix-history] / usr / src / usr.bin / man / man.c
CommitLineData
c2c2eae0
KB
1/*
2 * Copyright (c) 1987 Regents of the University of California.
ea9afd82
KB
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
b36fc510
KB
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
c2c2eae0
KB
16 */
17
18#ifndef lint
19char copyright[] =
20"@(#) Copyright (c) 1987 Regents of the University of California.\n\
21 All rights reserved.\n";
ea9afd82 22#endif /* not lint */
c2c2eae0
KB
23
24#ifndef lint
b36fc510 25static char sccsid[] = "@(#)man.c 5.17 (Berkeley) %G%";
ea9afd82 26#endif /* not lint */
c2c2eae0
KB
27
28#include <sys/param.h>
29#include <sys/file.h>
30#include <ctype.h>
31
81cd4ba7 32#define DEF_PAGER "/usr/ucb/more -s"
94d4c6aa 33#define DEF_PATH "/usr/man:/usr/new/man:/usr/local/man"
4610900a 34#define LOCAL_PATH "/usr/local/man"
94d4c6aa 35#define NEW_PATH "/usr/new/man"
94d4c6aa 36
c56621e7
KB
37#define NO 0
38#define YES 1
4610900a 39
c56621e7
KB
40static char *command, /* command buffer */
41 *defpath, /* default search path */
4610900a
KB
42 *locpath, /* local search path */
43 *machine, /* machine type */
44 *manpath, /* current search path */
94d4c6aa 45 *newpath, /* new search path */
07b262aa
KB
46 *pager, /* requested pager */
47 how; /* how to display */
48
49#define ALL 0x1 /* show all man pages */
50#define CAT 0x2 /* copy file to stdout */
51#define WHERE 0x4 /* just tell me where */
c2c2eae0
KB
52
53main(argc, argv)
bb1b89c7
KB
54 int argc;
55 register char **argv;
c2c2eae0 56{
07b262aa
KB
57 extern char *optarg;
58 extern int optind;
59 int ch;
60 char *getenv(), *malloc();
c2c2eae0 61
07b262aa
KB
62 while ((ch = getopt(argc, argv, "-M:P:afkw")) != EOF)
63 switch((char)ch) {
64 case '-':
65 how |= CAT;
c2c2eae0
KB
66 break;
67 case 'M':
68 case 'P': /* backward compatibility */
07b262aa
KB
69 defpath = optarg;
70 break;
71 case 'a':
72 how |= ALL;
c2c2eae0
KB
73 break;
74 /*
c56621e7
KB
75 * "man -f" and "man -k" are backward contemptible,
76 * undocumented ways of calling whatis(1) and apropos(1).
c2c2eae0
KB
77 */
78 case 'f':
07b262aa
KB
79 jump(argv, "-f", "whatis");
80 /*NOTREACHED*/
c2c2eae0 81 case 'k':
07b262aa
KB
82 jump(argv, "-k", "apropos");
83 /*NOTREACHED*/
bb1b89c7
KB
84 /*
85 * Deliberately undocumented; really only useful when
86 * you're moving man pages around. Not worth adding.
87 */
da0e94fc 88 case 'w':
07b262aa 89 how |= WHERE | ALL;
da0e94fc 90 break;
c2c2eae0
KB
91 case '?':
92 default:
07b262aa 93 usage();
c2c2eae0 94 }
07b262aa 95 argv += optind;
bb1b89c7 96
07b262aa
KB
97 if (!*argv)
98 usage();
c2c2eae0 99
07b262aa 100 if (!(how & CAT))
c2c2eae0 101 if (!isatty(1))
07b262aa 102 how |= CAT;
81cd4ba7 103 else if (pager = getenv("PAGER")) {
bb1b89c7 104 register char *p;
81cd4ba7
KB
105
106 /*
107 * if the user uses "more", we make it "more -s"
108 * watch out for PAGER = "mypager /usr/ucb/more"
109 */
bb1b89c7
KB
110 for (p = pager; *p && !isspace(*p); ++p);
111 for (; p > pager && *p != '/'; --p);
112 if (p != pager)
113 ++p;
81cd4ba7 114 /* make sure it's "more", not "morex" */
7c575297
MT
115 if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){
116 char *opager = pager;
81cd4ba7 117 /*
7c575297 118 * allocate space to add the "-s"
81cd4ba7 119 */
7c575297
MT
120 if (!(pager = malloc((u_int)(strlen(opager)
121 + sizeof("-s") + 1)))) {
81cd4ba7
KB
122 fputs("man: out of space.\n", stderr);
123 exit(1);
124 }
7c575297 125 (void)sprintf(pager, "%s %s", opager, "-s");
81cd4ba7
KB
126 }
127 }
128 else
c2c2eae0
KB
129 pager = DEF_PAGER;
130 if (!(machine = getenv("MACHINE")))
131 machine = MACHINE;
4610900a
KB
132 if (!defpath && !(defpath = getenv("MANPATH")))
133 defpath = DEF_PATH;
134 locpath = LOCAL_PATH;
94d4c6aa 135 newpath = NEW_PATH;
bb1b89c7 136 man(argv);
b5e4dc7e 137 /* use system(3) in case someone's pager is "pager arg1 arg2" */
c56621e7
KB
138 if (command)
139 (void)system(command);
bb1b89c7
KB
140 exit(0);
141}
142
c56621e7
KB
143typedef struct {
144 char *name, *msg;
145} DIR;
bb1b89c7
KB
146static DIR list1[] = { /* section one list */
147 "cat1", "1st", "cat8", "8th", "cat6", "6th",
148 "cat.old", "old", NULL, NULL,
149}, list2[] = { /* rest of the list */
150 "cat2", "2nd", "cat3", "3rd", "cat4", "4th",
151 "cat5", "5th", "cat7", "7th", "cat3f", "3rd (F)",
152 NULL, NULL,
153}, list3[2]; /* single section */
154
155static
156man(argv)
157 char **argv;
158{
159 register char *p;
160 DIR *section, *getsect();
161 int res;
c2c2eae0
KB
162
163 for (; *argv; ++argv) {
e13be9cf 164 manpath = defpath;
bb1b89c7 165 section = NULL;
c2c2eae0 166 switch(**argv) {
bb1b89c7 167 case 'l': /* local */
b5e4dc7e 168 /* support the "{l,local,n,new}###" syntax */
bb1b89c7
KB
169 for (p = *argv; isalpha(*p); ++p);
170 if (!strncmp(*argv, "l", p - *argv) ||
171 !strncmp(*argv, "local", p - *argv)) {
b5e4dc7e 172 ++argv;
bb1b89c7 173 manpath = locpath;
b5e4dc7e 174 section = getsect(p);
4610900a
KB
175 }
176 break;
bb1b89c7
KB
177 case 'n': /* new */
178 for (p = *argv; isalpha(*p); ++p);
179 if (!strncmp(*argv, "n", p - *argv) ||
180 !strncmp(*argv, "new", p - *argv)) {
b5e4dc7e 181 ++argv;
bb1b89c7 182 manpath = newpath;
b5e4dc7e 183 section = getsect(p);
c2c2eae0
KB
184 }
185 break;
8f946e8d 186 /*
bb1b89c7
KB
187 * old isn't really a separate section of the manual,
188 * and its entries are all in a single directory.
8f946e8d 189 */
bb1b89c7
KB
190 case 'o': /* old */
191 for (p = *argv; isalpha(*p); ++p);
192 if (!strncmp(*argv, "o", p - *argv) ||
193 !strncmp(*argv, "old", p - *argv)) {
b5e4dc7e 194 ++argv;
bb1b89c7
KB
195 list3[0] = list1[3];
196 section = list3;
4610900a 197 }
c2c2eae0 198 break;
bb1b89c7
KB
199 case '1': case '2': case '3': case '4':
200 case '5': case '6': case '7': case '8':
b5e4dc7e
KB
201 if (section = getsect(*argv))
202 ++argv;
203 }
204
205 if (*argv) {
206 if (section)
207 res = manual(section, *argv);
208 else {
209 res = manual(list1, *argv);
210 if (!res || (how & ALL))
211 res += manual(list2, *argv);
c2c2eae0 212 }
b5e4dc7e
KB
213 if (res || how&WHERE)
214 continue;
c2c2eae0 215 }
bb1b89c7 216
b5e4dc7e
KB
217 fputs("man: ", stderr);
218 if (*argv)
219 fprintf(stderr, "no entry for %s in the ", *argv);
220 else
221 fputs("what do you want from the ", stderr);
c56621e7 222 if (section)
b5e4dc7e
KB
223 fprintf(stderr, "%s section of the ", section->msg);
224 if (manpath == locpath)
225 fputs("local ", stderr);
226 else if (manpath == newpath)
227 fputs("new ", stderr);
228 if (*argv)
229 fputs("manual.\n", stderr);
230 else
231 fputs("manual?\n", stderr);
232 exit(1);
c2c2eae0 233 }
c2c2eae0
KB
234}
235
8f946e8d
KB
236/*
237 * manual --
c56621e7
KB
238 * given a directory list and a file name find a file that
239 * matches; check ${directory}/${dir}/{file name} and
240 * ${directory}/${dir}/${machine}/${file name}.
8f946e8d 241 */
c2c2eae0
KB
242static
243manual(section, name)
bb1b89c7
KB
244 DIR *section;
245 char *name;
c2c2eae0 246{
bb1b89c7
KB
247 register char *beg, *end;
248 register DIR *dp;
c56621e7
KB
249 register int res;
250 char fname[MAXPATHLEN + 1], *index();
c2c2eae0 251
c56621e7 252 for (beg = manpath, res = 0;; beg = end + 1) {
c2c2eae0
KB
253 if (end = index(beg, ':'))
254 *end = '\0';
c56621e7
KB
255 for (dp = section; dp->name; ++dp) {
256 (void)sprintf(fname, "%s/%s/%s.0", beg, dp->name, name);
257 if (access(fname, R_OK)) {
258 (void)sprintf(fname, "%s/%s/%s/%s.0", beg,
259 dp->name, machine, name);
260 if (access(fname, R_OK))
261 continue;
8f946e8d 262 }
07b262aa 263 if (how & WHERE)
c56621e7 264 printf("man: found in %s.\n", fname);
07b262aa 265 else if (how & CAT)
c56621e7
KB
266 cat(fname);
267 else
268 add(fname);
07b262aa
KB
269 if (!(how & ALL))
270 return(1);
c56621e7
KB
271 res = 1;
272 }
c2c2eae0 273 if (!end)
c56621e7 274 return(res);
fe684fa4 275 *end = ':';
c2c2eae0
KB
276 }
277 /*NOTREACHED*/
278}
279
8f946e8d 280/*
c56621e7
KB
281 * cat --
282 * cat out the file
8f946e8d 283 */
c2c2eae0 284static
c56621e7
KB
285cat(fname)
286 char *fname;
c2c2eae0 287{
c56621e7
KB
288 register int fd, n;
289 char buf[BUFSIZ];
c2c2eae0 290
c56621e7
KB
291 if (!(fd = open(fname, O_RDONLY, 0))) {
292 perror("man: open");
293 exit(1);
c2c2eae0 294 }
c56621e7
KB
295 while ((n = read(fd, buf, sizeof(buf))) > 0)
296 if (write(1, buf, n) != n) {
297 perror("man: write");
298 exit(1);
299 }
300 if (n == -1) {
301 perror("man: read");
302 exit(1);
303 }
304 (void)close(fd);
c2c2eae0
KB
305}
306
8f946e8d 307/*
c56621e7
KB
308 * add --
309 * add a file name to the list for future paging
8f946e8d 310 */
c2c2eae0 311static
c56621e7 312add(fname)
bb1b89c7 313 char *fname;
c2c2eae0 314{
c56621e7
KB
315 static u_int buflen;
316 static int len;
317 static char *cp;
318 int flen;
319 char *malloc(), *realloc(), *strcpy();
c2c2eae0 320
c56621e7
KB
321 if (!command) {
322 if (!(command = malloc(buflen = 1024))) {
323 fputs("man: out of space.\n", stderr);
c2c2eae0
KB
324 exit(1);
325 }
c56621e7
KB
326 len = strlen(strcpy(command, pager));
327 cp = command + len;
328 }
329 flen = strlen(fname);
330 if (len + flen + 2 > buflen) { /* +2 == space, EOS */
331 if (!(command = realloc(command, buflen += 1024))) {
332 fputs("man: out of space.\n", stderr);
c2c2eae0
KB
333 exit(1);
334 }
c56621e7 335 cp = command + len;
c2c2eae0 336 }
c56621e7
KB
337 *cp++ = ' ';
338 len += flen + 1; /* +1 = space */
339 (void)strcpy(cp, fname);
340 cp += flen;
c2c2eae0
KB
341}
342
8f946e8d 343/*
bb1b89c7
KB
344 * getsect --
345 * return a point to the section structure for a particular suffix
8f946e8d 346 */
bb1b89c7
KB
347static DIR *
348getsect(s)
349 char *s;
c2c2eae0 350{
bb1b89c7
KB
351 switch(*s++) {
352 case '1':
353 if (!*s)
354 return(list1);
355 break;
356 case '2':
357 if (!*s) {
358 list3[0] = list2[0];
359 return(list3);
360 }
361 break;
362 /* sect. 3 requests are for either section 3, or section 3[fF]. */
363 case '3':
364 if (!*s) {
365 list3[0] = list2[1];
366 return(list3);
367 }
368 else if ((*s == 'f' || *s == 'F') && !*++s) {
369 list3[0] = list2[5];
370 return(list3);
371 }
372 break;
373 case '4':
374 if (!*s) {
375 list3[0] = list2[2];
376 return(list3);
377 }
378 break;
379 case '5':
380 if (!*s) {
381 list3[0] = list2[3];
382 return(list3);
383 }
384 break;
385 case '6':
386 if (!*s) {
387 list3[0] = list1[2];
388 return(list3);
389 }
390 break;
391 case '7':
392 if (!*s) {
393 list3[0] = list2[4];
394 return(list3);
395 }
396 break;
397 case '8':
398 if (!*s) {
399 list3[0] = list1[1];
400 return(list3);
401 }
402 }
403 return((DIR *)NULL);
c2c2eae0 404}
07b262aa
KB
405
406/*
407 * jump --
408 * strip out flag argument and jump
409 */
410static
411jump(argv, flag, name)
412 char **argv, *name;
413 register char *flag;
414{
415 register char **arg;
416
417 argv[0] = name;
418 for (arg = argv + 1; *arg; ++arg)
419 if (!strcmp(*arg, flag))
420 break;
421 for (; *arg; ++arg)
422 arg[0] = arg[1];
423 execvp(name, argv);
424 fprintf(stderr, "%s: Command not found.\n", name);
425 exit(1);
426}
427
428/*
429 * usage --
430 * print usage and die
431 */
432static
433usage()
434{
435 fputs("usage: man [-] [-a] [-M path] [section] title ...\n", stderr);
436 exit(1);
437}