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 this notice is preserved and that due credit is given 7 * to the University of California at Berkeley. The name of the University 8 * may not be used to endorse or promote products derived from this 9 * software without specific prior written permission. This software 10 * is provided ``as is'' without express or implied warranty. 11 */ 12 13 #ifndef lint 14 char copyright[] = 15 "@(#) Copyright (c) 1987 Regents of the University of California.\n\ 16 All rights reserved.\n"; 17 #endif /* not lint */ 18 19 #ifndef lint 20 static char sccsid[] = "@(#)man.c 5.12 (Berkeley) 01/14/88"; 21 #endif /* not lint */ 22 23 #include <sys/param.h> 24 #include <sys/file.h> 25 #include <ctype.h> 26 27 #define DEF_PAGER "/usr/ucb/more -s" 28 #define DEF_PATH "/usr/man:/usr/new/man:/usr/local/man" 29 #define LOCAL_PATH "/usr/local/man" 30 #define NEW_PATH "/usr/new/man" 31 32 #define NO 0 33 #define YES 1 34 35 typedef struct { 36 char *name, *msg; 37 } DIR; 38 39 static int nomore, /* copy file to stdout */ 40 where; /* just tell me where */ 41 static char *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 48 main(argc, argv) 49 int argc; 50 register char **argv; 51 { 52 char **arg_start, **arg, *getenv(), *malloc(), *strcpy(); 53 54 arg_start = argv; 55 for (--argc, ++argv; argc && (*argv)[0] == '-'; --argc, ++argv) 56 switch((*argv)[1]) { 57 case 0: /* just write to stdout */ 58 nomore = YES; 59 break; 60 case 'M': 61 case 'P': /* backward compatibility */ 62 if ((*argv)[2]) 63 defpath = *argv + 2; 64 else { 65 if (argc < 2) { 66 fprintf(stderr, "%s: missing path\n", *argv); 67 exit(1); 68 } 69 --argc; 70 defpath = *++argv; 71 } 72 break; 73 /* 74 * "man -f" and "man -k" are undocumented ways of calling 75 * whatis(1) and apropos(1). Just strip out the flag 76 * argument and jump. 77 */ 78 case 'f': 79 for (arg = argv; arg[0] = arg[1]; ++arg); 80 *arg_start = "whatis"; 81 execvp(*arg_start, arg_start); 82 fputs("whatis: Command not found.\n", stderr); 83 exit(1); 84 case 'k': 85 for (arg = argv; *arg = arg[1]; ++arg); 86 *arg_start = "apropos"; 87 execvp(*arg_start, arg_start); 88 fputs("apropos: Command not found.\n", stderr); 89 exit(1); 90 /* 91 * Deliberately undocumented; really only useful when 92 * you're moving man pages around. Not worth adding. 93 */ 94 case 'w': 95 where = YES; 96 break; 97 case '?': 98 default: 99 fprintf(stderr, "man: illegal option -- %c\n", (*argv)[1]); 100 goto usage; 101 } 102 103 if (!argc) { 104 usage: fputs("usage: man [-] [-M path] [section] title ...\n", stderr); 105 exit(1); 106 } 107 108 if (!nomore) 109 if (!isatty(1)) 110 nomore = YES; 111 else if (pager = getenv("PAGER")) { 112 register char *p; 113 114 /* 115 * if the user uses "more", we make it "more -s" 116 * watch out for PAGER = "mypager /usr/ucb/more" 117 */ 118 for (p = pager; *p && !isspace(*p); ++p); 119 for (; p > pager && *p != '/'; --p); 120 if (p != pager) 121 ++p; 122 /* make sure it's "more", not "morex" */ 123 if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))) { 124 p += 4; 125 /* 126 * allocate for the rest of the PAGER 127 * environment variable, a space, and the EOS. 128 */ 129 if (!(pager = malloc((u_int)(strlen(p) + sizeof(DEF_PAGER) + 1)))) { 130 fputs("man: out of space.\n", stderr); 131 exit(1); 132 } 133 (void)sprintf(pager, "%s %s", DEF_PAGER, p); 134 } 135 } 136 else 137 pager = DEF_PAGER; 138 if (!(machine = getenv("MACHINE"))) 139 machine = MACHINE; 140 if (!defpath && !(defpath = getenv("MANPATH"))) 141 defpath = DEF_PATH; 142 locpath = LOCAL_PATH; 143 newpath = NEW_PATH; 144 man(argv); 145 exit(0); 146 } 147 148 static DIR list1[] = { /* section one list */ 149 "cat1", "1st", "cat8", "8th", "cat6", "6th", 150 "cat.old", "old", NULL, NULL, 151 }, list2[] = { /* rest of the list */ 152 "cat2", "2nd", "cat3", "3rd", "cat4", "4th", 153 "cat5", "5th", "cat7", "7th", "cat3f", "3rd (F)", 154 NULL, NULL, 155 }, list3[2]; /* single section */ 156 157 static 158 man(argv) 159 char **argv; 160 { 161 register char *p; 162 DIR *section, *getsect(); 163 int res; 164 165 for (; *argv; ++argv) { 166 manpath = defpath; 167 section = NULL; 168 switch(**argv) { 169 case 'l': /* local */ 170 for (p = *argv; isalpha(*p); ++p); 171 if (!strncmp(*argv, "l", p - *argv) || 172 !strncmp(*argv, "local", p - *argv)) { 173 manpath = locpath; 174 if (section = getsect(p)) 175 goto argtest; 176 } 177 break; 178 case 'n': /* new */ 179 for (p = *argv; isalpha(*p); ++p); 180 if (!strncmp(*argv, "n", p - *argv) || 181 !strncmp(*argv, "new", p - *argv)) { 182 manpath = newpath; 183 if (section = getsect(p)) 184 goto argtest; 185 } 186 break; 187 /* 188 * old isn't really a separate section of the manual, 189 * and its entries are all in a single directory. 190 */ 191 case 'o': /* old */ 192 for (p = *argv; isalpha(*p); ++p); 193 if (!strncmp(*argv, "o", p - *argv) || 194 !strncmp(*argv, "old", p - *argv)) { 195 list3[0] = list1[3]; 196 section = list3; 197 goto argtest; 198 } 199 break; 200 case '1': case '2': case '3': case '4': 201 case '5': case '6': case '7': case '8': 202 if (!(section = getsect(*argv))) 203 break; 204 argtest: if (!*++argv) { 205 fprintf(stderr, "man: what do you want from the %s section of the manual?\n", section->msg); 206 exit(1); 207 } 208 } 209 210 res = section ? manual(section, *argv) : 211 manual(list1, *argv) || manual(list2, *argv); 212 if (!res && !where) 213 if (manpath == locpath) 214 if (section) 215 fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, section->msg); 216 else 217 fprintf(stderr, "No entry for %s in the local manual.\n", *argv); 218 else if (manpath == newpath) 219 if (section) 220 fprintf(stderr, "No entry for %s in the %s section of the new manual.\n", *argv, section->msg); 221 else 222 fprintf(stderr, "No entry for %s in the new manual.\n", *argv); 223 else if (section) 224 fprintf(stderr, "No entry for %s in the %s section of the manual.\n", *argv, section->msg); 225 else 226 fprintf(stderr, "No entry for %s in the manual.\n", *argv); 227 } 228 } 229 230 /* 231 * manual -- 232 * given a section number and a file name go through the directory 233 * list and find a file that matches. 234 */ 235 static 236 manual(section, name) 237 DIR *section; 238 char *name; 239 { 240 register char *beg, *end; 241 register DIR *dp; 242 char *index(); 243 244 for (beg = manpath;; beg = end + 1) { 245 if (end = index(beg, ':')) 246 *end = '\0'; 247 for (dp = section; dp->name; ++dp) 248 if (find(beg, dp->name, name)) { 249 if (end) 250 *end = ':'; 251 return(YES); 252 } 253 if (!end) 254 return(NO); 255 *end = ':'; 256 } 257 /*NOTREACHED*/ 258 } 259 260 /* 261 * find -- 262 * given a directory path, a sub-directory and a file name, 263 * see if a file exists in ${directory}/${dir}/{file name} 264 * or in ${directory}/${dir}/${machine}/${file name}. 265 */ 266 static 267 find(beg, dir, name) 268 char *beg, *dir, *name; 269 { 270 char fname[MAXPATHLEN + 1]; 271 272 (void)sprintf(fname, "%s/%s/%s.0", beg, dir, name); 273 if (access(fname, R_OK)) { 274 (void)sprintf(fname, "%s/%s/%s/%s.0", beg, dir, machine, name); 275 if (access(fname, R_OK)) 276 return(NO); 277 } 278 if (where) 279 printf("man: found in %s.\n", fname); 280 else 281 show(fname); 282 return(!where); 283 } 284 285 /* 286 * show -- 287 * display the file 288 */ 289 static 290 show(fname) 291 char *fname; 292 { 293 register int fd, n; 294 char buf[BUFSIZ]; 295 296 if (nomore) { 297 if (!(fd = open(fname, O_RDONLY, 0))) { 298 perror("man: open"); 299 exit(1); 300 } 301 while ((n = read(fd, buf, sizeof(buf))) > 0) 302 if (write(1, buf, n) != n) { 303 perror("man: write"); 304 exit(1); 305 } 306 if (n == -1) { 307 perror("man: read"); 308 exit(1); 309 } 310 (void)close(fd); 311 } 312 else { 313 /* 314 * use system(3) in case someone's pager is 315 * "command arg1 arg2" 316 */ 317 (void)sprintf(buf, "%s %s", pager, fname); 318 (void)system(buf); 319 } 320 } 321 322 /* 323 * getsect -- 324 * return a point to the section structure for a particular suffix 325 */ 326 static DIR * 327 getsect(s) 328 char *s; 329 { 330 switch(*s++) { 331 case '1': 332 if (!*s) 333 return(list1); 334 break; 335 case '2': 336 if (!*s) { 337 list3[0] = list2[0]; 338 return(list3); 339 } 340 break; 341 /* sect. 3 requests are for either section 3, or section 3[fF]. */ 342 case '3': 343 if (!*s) { 344 list3[0] = list2[1]; 345 return(list3); 346 } 347 else if ((*s == 'f' || *s == 'F') && !*++s) { 348 list3[0] = list2[5]; 349 return(list3); 350 } 351 break; 352 case '4': 353 if (!*s) { 354 list3[0] = list2[2]; 355 return(list3); 356 } 357 break; 358 case '5': 359 if (!*s) { 360 list3[0] = list2[3]; 361 return(list3); 362 } 363 break; 364 case '6': 365 if (!*s) { 366 list3[0] = list1[2]; 367 return(list3); 368 } 369 break; 370 case '7': 371 if (!*s) { 372 list3[0] = list2[4]; 373 return(list3); 374 } 375 break; 376 case '8': 377 if (!*s) { 378 list3[0] = list1[1]; 379 return(list3); 380 } 381 } 382 return((DIR *)NULL); 383 } 384