#undef DEBUG /* * apropos - find pertinent on-line documentation * * apropos name ... */ static char *rcsid_apropos_c = "$Id: apropos.c,v 2.0 1992/09/13 05:02:44 rosenkra Exp $"; /* * $Log: apropos.c,v $ * Revision 2.0 1992/09/13 05:02:44 rosenkra * total rewrite. this if first rev of this file. * * */ #include #include #include #include #include "whatis.h" /* * we only need one record here. we read a record and check all the * command line args against it for a match */ struct rec r; char *libpath = MANPATH; /* path to database files (-P) */ int debugging = 0; /* for -d */ /* * fcn prototypes */ void apropos (int, int, int, char **); int match_up (char *, char *); int advance (char *, char *); int cmp_ign_case (int, int); void usage (int); int parse_record (char *, struct rec *); void print_record (int, struct rec *); #ifdef CHECK_MAGIC int check_magic (void); #endif /*------------------------------*/ /* main */ /*------------------------------*/ void main (int argc, char *argv[]) { int sect = -1; int verbose = 0; char *lpath; char *ps; /* * see if there is MANPATH in env. use it over default... */ if ((lpath = getenv ("MANPATH")) != (char *) NULL) libpath = lpath; else if ((lpath = getenv ("MANDIR")) != (char *) NULL) libpath = lpath; #ifdef DEBUG else fprintf (stderr, "apropos: environment variable MANPATH not set, using default\n"); #endif /* * parse args */ for (argc--, argv++; argc && **argv == '-'; argc--, argv++) { switch (*(*argv+1)) { case 'P': /* path for db */ case 'M': argc--, argv++; if (argc < 1) usage (1); libpath = *argv; break; case 's': /* specific section */ argc--, argv++; if (argc < 1) usage (1); sect = **argv; break; case 'd': /* debug mode */ debugging = 1; break; case 'h': /* help */ usage (0); /* NOTREACHED */ } } /* * apropos what? */ if (argc == 0) { fprintf (stderr, "apropos: you must specify an argument. consider using whatisin.\n"); usage (1); } /* * not documented: if first arg is a number, use it for section... */ if (isdigit(**argv)) { sect = **argv; argc--, argv++; } /* * do it. if specific section, search it only. else search all * sections */ if (sect != -1) { apropos (verbose, sect, argc, argv); } else { for (ps = SECTIONS; *ps; ps++) { sect = *ps; apropos (verbose, sect, argc, argv); } } exit (0); } /*------------------------------*/ /* apropos */ /*------------------------------*/ void apropos (int verbose, int sect, int argc, char **argv) { #define MAX_ARGS 100 char dbname[256]; char buf[REC_SIZE]; char srch[REC_SIZE]; register char **vp; int notfound[MAX_ARGS]; int i, j; /* * FIXME: we should allocate space for the notfound list. */ if (argc > MAX_ARGS) argc = MAX_ARGS; /* * set up db name. section is really a single ascii char, not an * int. this is so we can have whatis for local, new, etc. */ if (sect == -1) /* orig behavior (just single "whatis" file, never used here) */ sprintf (dbname, "%s%s%s", libpath, SLASH, WHATIS); else /* new: whatis._[0-9lno]_ */ sprintf (dbname, "%s%s%s._%c_", libpath, SLASH, WHATIS, sect); /* * reopen stdin as this file... */ if (debugging) fprintf (stderr, "checking database file %s...\n", dbname); if (freopen (dbname, "r", stdin) == (FILE *) NULL) { /* if (verbose || debugging)*/ if (debugging) fprintf (stderr, "apropos: could not access file %s\n", dbname); return; } #ifdef CHECK_MAGIC /* * check file's magic */ if (check_magic ()) { fprintf (stderr, "whatis: magic number is wrong for file %s\n", dbname); return; } #endif /* * read file and compare. first assume we will not find anything * and flag list... */ for (i = 0; i < MAX_ARGS; i++) notfound[i] = 1; while (1) { /* * get raw record from file */ fgets (buf, REC_SIZE-1, stdin); if (feof (stdin)) break; if (debugging) fprintf (stderr, "%s", buf); /* * skip comment or blank lines */ if (buf[0] == '#' || buf[0] == '\0' || buf[0] == '\n') continue; /* * parse the record and store in r */ parse_record (buf, &r); if (debugging) { fprintf (stderr, "name: %s\n", r.name ? r.name : "(NULL)"); fprintf (stderr, "section: %s\n", r.section ? r.section : "(NULL)"); fprintf (stderr, "subsect: %s\n", r.subsect ? r.subsect : "(NULL)"); fprintf (stderr, "desc: %s\n", r.desc ? r.desc : "(NULL)"); } /* * compare record's name field to all args from orig * command line. if we find one, flag notfound list */ for (i = 0, vp = argv; i < argc && *vp; vp++, i++) { /* * make a long list of things to search */ srch[0] = '\0'; if (r.name) { strcat (srch, r.name); strcat (srch, " "); } if (r.desc) { strcat (srch, r.desc); strcat (srch, " "); } for (j = 0; j < MAX_ALIAS; j++) { if (r.alias[j] == NULL) break; strcat (srch, r.alias[j]); strcat (srch, " "); } for (j = 0; j < MAX_KEYW; j++) { if (r.keyw[j] == NULL) break; strcat (srch, r.keyw[j]); strcat (srch, " "); } /* * check for a match... */ if (match_up (srch, *vp)) { print_record (verbose, &r); notfound[i] = 0; } } } /* * print a message if we didn't find anything for the cmdline arg */ if (verbose) { for (i = 0; i < argc; i++) { if (notfound[i]) { printf ("nothing appropriate in section %c for %s\n", sect, argv[i]); } } } return; } /*------------------------------*/ /* match_up */ /*------------------------------*/ int match_up (char *buf, char *str) { /* * search for string in str from a list contained in buf. this part * just advances a ptr to buf and calls a lower level routine. ret 1 * if there is a match, else 0. */ register char *pbuf; for (pbuf = buf; *pbuf; pbuf++) if (advance (pbuf, str)) return (1); return (0); } /*------------------------------*/ /* advance */ /*------------------------------*/ int advance (char *s1, char *s2) { /* * main matching driver. advances along each string. cmp_ign_case does * theactual char match. ret 1 if match, else 0. search is finished when * s2 is at EOS. */ while (*s1 && *s2 && ((*s1 == *s2) || cmp_ign_case ((int) (*s1), (int) (*s2)))) s1++, s2++; if (*s2 == 0) return (1); return (0); } /*------------------------------*/ /* cmp_ign_case */ /*------------------------------*/ int cmp_ign_case (int c1, int c2) { /* * char matching part. ignores case. must be alpha chars only! if * chars match, ret 1, else 0. c1 should probably always be alpha, * but check anyway. c2 comes from user. check it first for possible * quick exit. compare in lower case (since that is more probable * and the tolower could be skipped). * * consider inlining this... */ if (!isalpha (c2) || !isalpha (c1)) return (0); if (isupper (c1)) c1 = tolower (c1); if (isupper (c2)) c2 = tolower (c2); return (c1 == c2); } /*------------------------------*/ /* usage */ /*------------------------------*/ void usage (int excode) { #define U(x) fprintf(stderr,x); U("usage: apropos [-P path] [-s section] name ...\n"); U(" -P path alternative path to databases (MANPATH)\n"); U(" -s section limit search to single section, not all\n"); U(" name name of a keyword (any single word).\n"); exit (excode); }