1 /* 2 * Copyright (c) 1987, 1993, 1994 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, 1994\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[] = "@(#)apropos.c 8.8 (Berkeley) 05/04/95"; 16 #endif /* not lint */ 17 18 #include <sys/param.h> 19 #include <sys/queue.h> 20 21 #include <ctype.h> 22 #include <err.h> 23 #include <limits.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <unistd.h> 28 29 #include "../man/config.h" 30 #include "../man/pathnames.h" 31 32 static int *found, foundman; 33 34 void apropos __P((char **, char *, int)); 35 void lowstr __P((char *, char *)); 36 int match __P((char *, char *)); 37 void usage __P((void)); 38 39 int 40 main(argc, argv) 41 int argc; 42 char *argv[]; 43 { 44 ENTRY *ep; 45 TAG *tp; 46 int ch, rv; 47 char *conffile, **p, *p_augment, *p_path; 48 49 conffile = NULL; 50 p_augment = p_path = NULL; 51 while ((ch = getopt(argc, argv, "C:M:m:P:")) != EOF) 52 switch (ch) { 53 case 'C': 54 conffile = optarg; 55 break; 56 case 'M': 57 case 'P': /* backward compatible */ 58 p_path = optarg; 59 break; 60 case 'm': 61 p_augment = optarg; 62 break; 63 case '?': 64 default: 65 usage(); 66 } 67 argv += optind; 68 argc -= optind; 69 70 if (argc < 1) 71 usage(); 72 73 if ((found = malloc((u_int)argc * sizeof(int))) == NULL) 74 err(1, NULL); 75 memset(found, 0, argc * sizeof(int)); 76 77 for (p = argv; *p; ++p) /* convert to lower-case */ 78 lowstr(*p, *p); 79 80 if (p_augment) 81 apropos(argv, p_augment, 1); 82 if (p_path || (p_path = getenv("MANPATH"))) 83 apropos(argv, p_path, 1); 84 else { 85 config(conffile); 86 ep = (tp = getlist("_whatdb")) == NULL ? 87 NULL : tp->list.tqh_first; 88 for (; ep != NULL; ep = ep->q.tqe_next) 89 apropos(argv, ep->s, 0); 90 } 91 92 if (!foundman) 93 errx(1, "no %s file found", _PATH_WHATIS); 94 95 rv = 1; 96 for (p = argv; *p; ++p) 97 if (found[p - argv]) 98 rv = 0; 99 else 100 (void)printf("%s: nothing appropriate\n", *p); 101 exit(rv); 102 } 103 104 void 105 apropos(argv, path, buildpath) 106 char **argv, *path; 107 int buildpath; 108 { 109 char *end, *name, **p; 110 char buf[LINE_MAX + 1], wbuf[LINE_MAX + 1]; 111 112 for (name = path; name; name = end) { /* through name list */ 113 if (end = strchr(name, ':')) 114 *end++ = '\0'; 115 116 if (buildpath) { 117 char hold[MAXPATHLEN + 1]; 118 119 (void)sprintf(hold, "%s/%s", name, _PATH_WHATIS); 120 name = hold; 121 } 122 123 if (!freopen(name, "r", stdin)) 124 continue; 125 126 foundman = 1; 127 128 /* for each file found */ 129 while (fgets(buf, sizeof(buf), stdin)) { 130 if (!strchr(buf, '\n')) { 131 warnx("%s: line too long", name); 132 continue; 133 } 134 lowstr(buf, wbuf); 135 for (p = argv; *p; ++p) 136 if (match(wbuf, *p)) { 137 (void)printf("%s", buf); 138 found[p - argv] = 1; 139 140 /* only print line once */ 141 while (*++p) 142 if (match(wbuf, *p)) 143 found[p - argv] = 1; 144 break; 145 } 146 } 147 } 148 } 149 150 /* 151 * match -- 152 * match anywhere the string appears 153 */ 154 int 155 match(bp, str) 156 char *bp, *str; 157 { 158 int len; 159 char test; 160 161 if (!*bp) 162 return (0); 163 /* backward compatible: everything matches empty string */ 164 if (!*str) 165 return (1); 166 for (test = *str++, len = strlen(str); *bp;) 167 if (test == *bp++ && !strncmp(bp, str, len)) 168 return (1); 169 return (0); 170 } 171 172 /* 173 * lowstr -- 174 * convert a string to lower case 175 */ 176 void 177 lowstr(from, to) 178 char *from, *to; 179 { 180 char ch; 181 182 while ((ch = *from++) && ch != '\n') 183 *to++ = isupper(ch) ? tolower(ch) : ch; 184 *to = '\0'; 185 } 186 187 /* 188 * usage -- 189 * print usage message and die 190 */ 191 void 192 usage() 193 { 194 195 (void)fprintf(stderr, 196 "usage: apropos [-C file] [-M path] [-m path] keyword ...\n"); 197 exit(1); 198 } 199