/*- * Copyright (c) 1999 Chris Costello * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: rtfm.c,v 1.9 2000/01/18 05:06:29 chris Exp $ * */ #include #include #include #include #include #include #include #include #include void chomp(char *); void search_manuals(void); void search_texinfo(void); void usage(void); char *add_spaces(char *); char *Rstrcasestr(char *, char *); char *Rstrstr(char *, char *); static char *prog, *keyword; extern char *__progname; static int cflag, dflag, iflag, mflag, tflag, wflag; /* From /usr/src/contrib/texinfo/info/filesys.h */ const char *info_paths[] = { "/usr/local/info", "/usr/info", "/usr/local/lib/info", "/usr/lib/info", "/usr/local/gnu/info", "/usr/local/gnu/lib/info", "/usr/gnu/info", "/usr/gnu/lib/info", "/opt/gnu/info", "/usr/share/info", "/usr/share/lib/info", "/usr/local/share/info", "/usr/local/share/lib/info", "/usr/gnu/lib/emacs/info", "/usr/local/gnu/lib/emacs/info", "/usr/local/lib/emacs/info", "/usr/local/emacs/info", ".", NULL }; int ninfo_paths = sizeof(info_paths) / sizeof(info_paths[0]); /* * Match strings depending on iflag */ char * Rstrstr(char *s, char *find) { if (iflag) return Rstrcasestr(s, find); return strstr(s, find); } char * Rstrcasestr(char *s, char *find) { register char c, sc; register size_t len; if ((c = *find++) != 0) { len = strlen(find); do { do { if ((sc = *s++) == 0) return (NULL); } while (toupper(sc) != toupper(c)); } while (strncasecmp(s, find, len) != 0); s--; } return ((char *)s); } /* * Put a space at the beginning and end of a string. */ char * add_spaces(char *s) { char *ret = calloc(sizeof(char), strlen(s) +3); sprintf(ret, " %s ", s); return ret; } /* * This function cuts off any trailing line terminators. */ void chomp(char *s) { size_t loc = strlen(s) - 1; if (s[loc] == '\n') s[loc--] = 0; if (s[loc] == '\r') s[loc--] = 0; } void usage(void) { fprintf(stderr, "usage: %s [-cdimtw] keyword\n", __progname); exit(1); } /* * Searches the manuals. */ void search_manuals(void) { FILE *p; char *sp, *prog_args; char fbuf[BUFSIZ]; char *bad_val = NULL; int printed_header = 0; /* * Create what we'll compare to prog's output so we can check if * anything was found. */ asprintf(&bad_val, "%s: nothing appropriate", keyword); /* Create the command string that we'll pass to popen(3) */ prog_args = malloc(strlen(prog) + strlen(keyword) + 1); sprintf(prog_args, "%s %s", prog, keyword); if ((p = popen(prog_args, "r")) == NULL) { free(prog_args); err(1, "popen: %s", prog); return; } while (fgets(fbuf, sizeof(fbuf), p)) { register char *paren, *name; if (!printed_header) { printed_header++; printf("Manual pages found:\n"); } chomp(fbuf); if (!strcmp(fbuf, bad_val)) { if (bad_val) free(bad_val); return; } name = fbuf; paren = strchr(fbuf, '('); if (paren == NULL) return; *paren = 0; paren++; sp = paren; paren = strchr(sp, ')'); if (paren == NULL) return; *paren = 0; if (cflag) printf(" man %s %s\n", sp, name); else printf(" %s(%s)\n", name, sp); if (dflag) { char *dash; dash = strchr(paren + 1, '-'); if (dash == NULL) continue; dash += 2; if (*(dash - 1) == '\0' || *dash == '\0') continue; printf(" %s\n", dash); } } if (bad_val) free(bad_val); free(prog_args); } /* * Search GNU Texinfo pages */ void search_texinfo() { DIR *dir; struct dirent *ent; int printed_header = 0, i; char startdir[MAXPATHLEN]; /* Preserve original directory. */ getcwd(startdir, sizeof(startdir)); for (i = 0; i < ninfo_paths; i++) { if (!printed_header) { printed_header++; printf("\nGNU Texinfo pages found:\n"); } if ((dir = opendir(info_paths[i])) == NULL) continue; /* * Change to the directory so we can more easily access * files. */ chdir(info_paths[i]); while ((ent = readdir(dir)) != NULL) { if (ent->d_type == DT_REG) { char *command, c; int matches = 0; FILE *p; /* * Anything that does not end in '.info.gz' * is not a Texinfo page. */ if (strcasecmp(".info.gz", &ent->d_name[ent->d_namlen - 8]) && strcasecmp(".info", &ent->d_name[ent->d_namlen - 5])) continue; /* * See if we can read it. We don't want * confusing stderr from zgrep annoying the * user. */ if (access(ent->d_name, R_OK) < 0) continue; /* * Create the string we'll be passing to * popen(3) */ command = calloc(sizeof(char), strlen(ent->d_name) + strlen(keyword) + 8); (void)sprintf(command, "zgrep %s %s %s %s", wflag ? "-w" : "", iflag ? "-i" : "", keyword, ent->d_name); if ((p = popen(command, "r")) == NULL) { warn("popen"); return; } while ((c = fgetc(p)) != EOF) if (c == '\n') matches++; pclose(p); if (!matches) continue; if (cflag) { char *iptr; /* * Strip the .info.gz from the end of * the string to create a command the * user can type at his shell prompt. */ iptr = strchr(ent->d_name, '.'); if (iptr) *iptr = 0; (void)printf(" info %s\n", ent->d_name); } else (void)printf(" %s (%d matches)\n", ent->d_name, matches); } } (void)closedir(dir); } /* Switch back to the original directory. */ chdir(startdir); } int main(int argc, char **argv) { int c; prog = "apropos"; while ((c = getopt(argc, argv, "cdimtw")) != -1) switch (c) { case 'c': cflag = 1; break; case 'd': dflag = 1; break; case 'i': iflag = 1; break; case 'm': mflag = 1; break; case 't': tflag = 1; break; case 'w': wflag = 1; prog = "whatis"; break; default: usage(); /* NOTREACHED */ } argc -= optind; argv += optind; if (argc < 1) { usage(); /* NOTREACHED */ } keyword = argv[0]; if (wflag) keyword = add_spaces(keyword); /* Search manuals only */ if (mflag) { search_manuals(); exit(0); } /* Search texinfo only */ if (tflag) { search_texinfo(); exit(0); } search_manuals(); search_texinfo(); return 0; }