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.17 (Berkeley) 06/29/88"; 26 #endif /* not lint */ 27 28 #include <sys/param.h> 29 #include <sys/file.h> 30 #include <ctype.h> 31 32 #define DEF_PAGER "/usr/ucb/more -s" 33 #define DEF_PATH "/usr/man:/usr/new/man:/usr/local/man" 34 #define LOCAL_PATH "/usr/local/man" 35 #define NEW_PATH "/usr/new/man" 36 37 #define NO 0 38 #define YES 1 39 40 static char *command, /* command buffer */ 41 *defpath, /* default search path */ 42 *locpath, /* local search path */ 43 *machine, /* machine type */ 44 *manpath, /* current search path */ 45 *newpath, /* new search path */ 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 */ 52 53 main(argc, argv) 54 int argc; 55 register char **argv; 56 { 57 extern char *optarg; 58 extern int optind; 59 int ch; 60 char *getenv(), *malloc(); 61 62 while ((ch = getopt(argc, argv, "-M:P:afkw")) != EOF) 63 switch((char)ch) { 64 case '-': 65 how |= CAT; 66 break; 67 case 'M': 68 case 'P': /* backward compatibility */ 69 defpath = optarg; 70 break; 71 case 'a': 72 how |= ALL; 73 break; 74 /* 75 * "man -f" and "man -k" are backward contemptible, 76 * undocumented ways of calling whatis(1) and apropos(1). 77 */ 78 case 'f': 79 jump(argv, "-f", "whatis"); 80 /*NOTREACHED*/ 81 case 'k': 82 jump(argv, "-k", "apropos"); 83 /*NOTREACHED*/ 84 /* 85 * Deliberately undocumented; really only useful when 86 * you're moving man pages around. Not worth adding. 87 */ 88 case 'w': 89 how |= WHERE | ALL; 90 break; 91 case '?': 92 default: 93 usage(); 94 } 95 argv += optind; 96 97 if (!*argv) 98 usage(); 99 100 if (!(how & CAT)) 101 if (!isatty(1)) 102 how |= CAT; 103 else if (pager = getenv("PAGER")) { 104 register char *p; 105 106 /* 107 * if the user uses "more", we make it "more -s" 108 * watch out for PAGER = "mypager /usr/ucb/more" 109 */ 110 for (p = pager; *p && !isspace(*p); ++p); 111 for (; p > pager && *p != '/'; --p); 112 if (p != pager) 113 ++p; 114 /* make sure it's "more", not "morex" */ 115 if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){ 116 char *opager = pager; 117 /* 118 * allocate space to add the "-s" 119 */ 120 if (!(pager = malloc((u_int)(strlen(opager) 121 + sizeof("-s") + 1)))) { 122 fputs("man: out of space.\n", stderr); 123 exit(1); 124 } 125 (void)sprintf(pager, "%s %s", opager, "-s"); 126 } 127 } 128 else 129 pager = DEF_PAGER; 130 if (!(machine = getenv("MACHINE"))) 131 machine = MACHINE; 132 if (!defpath && !(defpath = getenv("MANPATH"))) 133 defpath = DEF_PATH; 134 locpath = LOCAL_PATH; 135 newpath = NEW_PATH; 136 man(argv); 137 /* use system(3) in case someone's pager is "pager arg1 arg2" */ 138 if (command) 139 (void)system(command); 140 exit(0); 141 } 142 143 typedef struct { 144 char *name, *msg; 145 } DIR; 146 static 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 155 static 156 man(argv) 157 char **argv; 158 { 159 register char *p; 160 DIR *section, *getsect(); 161 int res; 162 163 for (; *argv; ++argv) { 164 manpath = defpath; 165 section = NULL; 166 switch(**argv) { 167 case 'l': /* local */ 168 /* support the "{l,local,n,new}###" syntax */ 169 for (p = *argv; isalpha(*p); ++p); 170 if (!strncmp(*argv, "l", p - *argv) || 171 !strncmp(*argv, "local", p - *argv)) { 172 ++argv; 173 manpath = locpath; 174 section = getsect(p); 175 } 176 break; 177 case 'n': /* new */ 178 for (p = *argv; isalpha(*p); ++p); 179 if (!strncmp(*argv, "n", p - *argv) || 180 !strncmp(*argv, "new", p - *argv)) { 181 ++argv; 182 manpath = newpath; 183 section = getsect(p); 184 } 185 break; 186 /* 187 * old isn't really a separate section of the manual, 188 * and its entries are all in a single directory. 189 */ 190 case 'o': /* old */ 191 for (p = *argv; isalpha(*p); ++p); 192 if (!strncmp(*argv, "o", p - *argv) || 193 !strncmp(*argv, "old", p - *argv)) { 194 ++argv; 195 list3[0] = list1[3]; 196 section = list3; 197 } 198 break; 199 case '1': case '2': case '3': case '4': 200 case '5': case '6': case '7': case '8': 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); 212 } 213 if (res || how&WHERE) 214 continue; 215 } 216 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); 222 if (section) 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); 233 } 234 } 235 236 /* 237 * manual -- 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}. 241 */ 242 static 243 manual(section, name) 244 DIR *section; 245 char *name; 246 { 247 register char *beg, *end; 248 register DIR *dp; 249 register int res; 250 char fname[MAXPATHLEN + 1], *index(); 251 252 for (beg = manpath, res = 0;; beg = end + 1) { 253 if (end = index(beg, ':')) 254 *end = '\0'; 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; 262 } 263 if (how & WHERE) 264 printf("man: found in %s.\n", fname); 265 else if (how & CAT) 266 cat(fname); 267 else 268 add(fname); 269 if (!(how & ALL)) 270 return(1); 271 res = 1; 272 } 273 if (!end) 274 return(res); 275 *end = ':'; 276 } 277 /*NOTREACHED*/ 278 } 279 280 /* 281 * cat -- 282 * cat out the file 283 */ 284 static 285 cat(fname) 286 char *fname; 287 { 288 register int fd, n; 289 char buf[BUFSIZ]; 290 291 if (!(fd = open(fname, O_RDONLY, 0))) { 292 perror("man: open"); 293 exit(1); 294 } 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); 305 } 306 307 /* 308 * add -- 309 * add a file name to the list for future paging 310 */ 311 static 312 add(fname) 313 char *fname; 314 { 315 static u_int buflen; 316 static int len; 317 static char *cp; 318 int flen; 319 char *malloc(), *realloc(), *strcpy(); 320 321 if (!command) { 322 if (!(command = malloc(buflen = 1024))) { 323 fputs("man: out of space.\n", stderr); 324 exit(1); 325 } 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); 333 exit(1); 334 } 335 cp = command + len; 336 } 337 *cp++ = ' '; 338 len += flen + 1; /* +1 = space */ 339 (void)strcpy(cp, fname); 340 cp += flen; 341 } 342 343 /* 344 * getsect -- 345 * return a point to the section structure for a particular suffix 346 */ 347 static DIR * 348 getsect(s) 349 char *s; 350 { 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); 404 } 405 406 /* 407 * jump -- 408 * strip out flag argument and jump 409 */ 410 static 411 jump(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 */ 432 static 433 usage() 434 { 435 fputs("usage: man [-] [-a] [-M path] [section] title ...\n", stderr); 436 exit(1); 437 } 438