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