1 /* 2 * Copyright (c) 1987, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char copyright[] = 10 "@(#) Copyright (c) 1987, 1993\n\ 11 The Regents of the University of California. All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)man.c 8.1 (Berkeley) 06/18/93"; 16 #endif /* not lint */ 17 18 #include <sys/param.h> 19 20 #include <ctype.h> 21 #include <errno.h> 22 #include <fcntl.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <unistd.h> 27 28 #include "pathnames.h" 29 30 extern int errno; 31 32 int f_all, f_cat, f_how, f_where; 33 char *command, *machine, *p_augment, *p_path, *pager, *progname; 34 extern char **arorder, *pathbuf; 35 36 main(argc, argv) 37 int argc; 38 register char **argv; 39 { 40 extern char *optarg; 41 extern int optind; 42 int ch, res; 43 char *section[2], *check_pager(), *getpath(), **getorder(), *tmp; 44 45 progname = "man"; 46 while ((ch = getopt(argc, argv, "-acfhkM:m:P:w")) != EOF) 47 switch((char)ch) { 48 case 'a': 49 f_all = 1; 50 break; 51 case 'c': 52 case '-': /* deprecated */ 53 f_cat = 1; 54 break; 55 case 'h': 56 f_how = 1; 57 break; 58 case 'm': 59 p_augment = optarg; 60 break; 61 case 'M': 62 case 'P': /* backward compatibility */ 63 p_path = optarg; 64 break; 65 /* 66 * "man -f" and "man -k" are backward compatible, undocumented 67 * ways of calling whatis(1) and apropos(1). 68 */ 69 case 'f': 70 jump(argv, "-f", "whatis"); 71 /* NOTREACHED */ 72 case 'k': 73 jump(argv, "-k", "apropos"); 74 /* NOTREACHED */ 75 case 'w': 76 f_all = f_where = 1; 77 break; 78 case '?': 79 default: 80 usage(); 81 } 82 argv += optind; 83 84 if (!*argv) 85 usage(); 86 87 if (!f_cat && !f_how) 88 if (!isatty(1)) 89 f_cat = 1; 90 else if (pager = getenv("PAGER")) 91 pager = check_pager(pager); 92 else 93 pager = _PATH_PAGER; 94 95 if (!(machine = getenv("MACHINE"))) 96 machine = MACHINE; 97 98 /* see if checking in a specific section */ 99 if (argc > 1 && getsection(*argv)) { 100 section[0] = *argv++; 101 section[1] = (char *)NULL; 102 } else { 103 section[0] = "_default"; 104 section[1] = (char *)NULL; 105 } 106 107 arorder = getorder(); 108 if (p_path || (p_path = getenv("MANPATH"))) { 109 char buf[MAXPATHLEN], **av; 110 111 tmp = strtok(p_path, ":"); 112 while (tmp) { 113 (void)snprintf(buf, sizeof(buf), "%s/", tmp); 114 for (av = arorder; *av; ++av) 115 cadd(buf, strlen(buf), *av); 116 tmp = strtok(NULL, ":"); 117 } 118 p_path = pathbuf; 119 } else if (!(p_path = getpath(section)) && !p_augment) { 120 (void)fprintf(stderr, 121 "man: no place to search for those manual pages.\n"); 122 exit(1); 123 } 124 125 for (; *argv; ++argv) { 126 if (p_augment) 127 res = manual(p_augment, *argv); 128 res = manual(p_path, *argv); 129 if (!res && !f_where) 130 (void)fprintf(stderr, 131 "man: no entry for %s in the manual.\n", *argv); 132 } 133 134 /* use system(3) in case someone's pager is "pager arg1 arg2" */ 135 if (command) 136 (void)system(command); 137 exit(0); 138 } 139 140 /* 141 * manual -- 142 * given a path, a directory list and a file name, find a file 143 * that matches; check ${directory}/${dir}/{file name} and 144 * ${directory}/${dir}/${machine}/${file name}. 145 */ 146 manual(path, name) 147 char *path, *name; 148 { 149 register int res; 150 register char *cp; 151 char fname[MAXPATHLEN + 1]; 152 153 for (res = 0; path != NULL && *path != '\0'; path = cp) { 154 if (cp = strchr(path, ':')) { 155 if (cp == path + 1) { /* foo::bar */ 156 ++cp; 157 continue; 158 } 159 *cp = '\0'; 160 } 161 (void)snprintf(fname, sizeof(fname), "%s/%s.0", path, name); 162 if (access(fname, R_OK)) { 163 (void)snprintf(fname, sizeof(fname), 164 "%s/%s/%s.0", path, machine, name); 165 if (access(fname, R_OK)) { 166 if (cp != NULL) 167 *cp++ = ':'; 168 continue; 169 } 170 } 171 172 if (f_where) 173 (void)printf("man: found in %s.\n", fname); 174 else if (f_cat) 175 cat(fname); 176 else if (f_how) 177 how(fname); 178 else 179 add(fname); 180 if (!f_all) 181 return(1); 182 res = 1; 183 if (cp != NULL) 184 *cp++ = ':'; 185 } 186 return(res); 187 } 188 189 /* 190 * how -- 191 * display how information 192 */ 193 how(fname) 194 char *fname; 195 { 196 register FILE *fp; 197 198 register int lcnt, print; 199 register char *p; 200 char buf[BUFSIZ]; 201 202 if (!(fp = fopen(fname, "r"))) { 203 (void)fprintf(stderr, "man: %s: %s\n", fname, strerror(errno)); 204 exit(1); 205 } 206 #define S1 "SYNOPSIS" 207 #define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS" 208 #define D1 "DESCRIPTION" 209 #define D2 "D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN" 210 for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) { 211 if (!strncmp(buf, S1, sizeof(S1) - 1) || 212 !strncmp(buf, S2, sizeof(S2) - 1)) { 213 print = 1; 214 continue; 215 } else if (!strncmp(buf, D1, sizeof(D1) - 1) || 216 !strncmp(buf, D2, sizeof(D2) - 1)) 217 return; 218 if (!print) 219 continue; 220 if (*buf == '\n') 221 ++lcnt; 222 else { 223 for(; lcnt; --lcnt) 224 (void)putchar('\n'); 225 for (p = buf; isspace(*p); ++p); 226 (void)fputs(p, stdout); 227 } 228 } 229 (void)fclose(fp); 230 } 231 /* 232 * cat -- 233 * cat out the file 234 */ 235 cat(fname) 236 char *fname; 237 { 238 register int fd, n; 239 char buf[BUFSIZ]; 240 241 if ((fd = open(fname, O_RDONLY, 0)) < 0) { 242 (void)fprintf(stderr, "man: %s: %s\n", fname, strerror(errno)); 243 exit(1); 244 } 245 while ((n = read(fd, buf, sizeof(buf))) > 0) 246 if (write(1, buf, n) != n) { 247 (void)fprintf(stderr, 248 "man: write: %s\n", strerror(errno)); 249 exit(1); 250 } 251 if (n == -1) { 252 (void)fprintf(stderr, "man: read: %s\n", strerror(errno)); 253 exit(1); 254 } 255 (void)close(fd); 256 } 257 258 /* 259 * add -- 260 * add a file name to the list for future paging 261 */ 262 add(fname) 263 char *fname; 264 { 265 static u_int buflen; 266 static int len; 267 static char *cp; 268 int flen; 269 270 if (!command) { 271 if (!(command = malloc(buflen = 1024))) 272 enomem(); 273 len = strlen(strcpy(command, pager)); 274 cp = command + len; 275 } 276 flen = strlen(fname); 277 if (len + flen + 2 > buflen) { /* +2 == space, EOS */ 278 if (!(command = realloc(command, buflen += 1024))) 279 enomem(); 280 cp = command + len; 281 } 282 *cp++ = ' '; 283 len += flen + 1; /* +1 = space */ 284 (void)strcpy(cp, fname); 285 cp += flen; 286 } 287 288 /* 289 * check_pager -- 290 * check the user supplied page information 291 */ 292 char * 293 check_pager(name) 294 char *name; 295 { 296 register char *p; 297 char *save; 298 299 /* 300 * if the user uses "more", we make it "more -s"; watch out for 301 * PAGER = "mypager /usr/ucb/more" 302 */ 303 for (p = name; *p && !isspace(*p); ++p); 304 for (; p > name && *p != '/'; --p); 305 if (p != name) 306 ++p; 307 308 /* make sure it's "more", not "morex" */ 309 if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){ 310 save = name; 311 /* allocate space to add the "-s" */ 312 if (!(name = 313 malloc((u_int)(strlen(save) + sizeof("-s") + 1)))) 314 enomem(); 315 (void)sprintf(name, "%s %s", save, "-s"); 316 } 317 return(name); 318 } 319 320 /* 321 * jump -- 322 * strip out flag argument and jump 323 */ 324 jump(argv, flag, name) 325 char **argv, *name; 326 register char *flag; 327 { 328 register char **arg; 329 330 argv[0] = name; 331 for (arg = argv + 1; *arg; ++arg) 332 if (!strcmp(*arg, flag)) 333 break; 334 for (; *arg; ++arg) 335 arg[0] = arg[1]; 336 execvp(name, argv); 337 (void)fprintf(stderr, "%s: Command not found.\n", name); 338 exit(1); 339 } 340 341 /* 342 * usage -- 343 * print usage message and die 344 */ 345 usage() 346 { 347 (void)fprintf(stderr, 348 "usage: man [-ac] [-M path] [-m path] [section] title ...\n"); 349 exit(1); 350 } 351