home *** CD-ROM | disk | FTP | other *** search
/ Atari FTP / ATARI_FTP_0693.zip / ATARI_FTP_0693 / Mint / mintman4.zoo / mintman / man.c < prev    next >
C/C++ Source or Header  |  1992-12-15  |  25KB  |  756 lines

  1. /******************************************************************/
  2. /* man.c - a simple-minded man program for MiNT and TOS           */
  3. /* Copyright (c) 1992 by HPP Biersma (schuller@dutiag.tudelft.nl) */
  4. /* This program comes under the GNU public license -              */
  5. /*                            see the file "copying" for details. */
  6. /******************************************************************/
  7. /* bugs: - not UN*X compatible, either in source-code or behavior */
  8. /*       - string operations are not safe, ie not limits checked  */
  9. /******************************************************************/
  10. /* version: 0.4 (fourth released version), December 15, 1992      */
  11. /* written for: - GCC version 2.3.1, patchlevel 1                 */
  12. /*              - mintlib patchlevel 25                           */
  13. /* compile with: gcc -o man.ttp man.c -mbaserel -mpcrel -O2       */
  14. /******************************************************************/
  15.  
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <unistd.h>
  20. #include <stat.h>
  21. #include <dirent.h> /* used for opendir(), readdir(), closedir() */
  22. #include <mintbind.h>
  23.  
  24. /* Comment out the following definitions if you want subprograms  */
  25. /* (nroff, less and cat) to get UN*X (not GEMDOS-like) filenames. */
  26. #define SUBPROGRAMS_GET_GEMDOS_NAMES /* the longest #define yet ? */
  27.  
  28. /* Define a enumerated type used to indicated mode of operation */
  29. typedef enum {MODE_UNKNOWN, MODE_MAN, MODE_MAN_F, MODE_MAN_K} mode_type;
  30.  
  31. /* Maximum length of path + file name */
  32. #define NAME_LENGTH 256
  33.  
  34. /* prototypes for local (static) functions */
  35. static void volatile option_error(const int error);
  36. static int search_man_path(const char *section, const char *title);
  37. static int search_man_dir(const char *section, const char *title, 
  38.                           const char *dir_name);
  39. static int search_section_specific(const char *section, const char *title,
  40.                                    const char *dir_name);
  41. static int search_section_general(const char section_code, const char *title,
  42.                                   const char *dir_name);
  43. static int find_best_file(const char *dir_name, const char *title,
  44.                           const char section_code,
  45.                           char *result_basename, char *result_fullname);
  46. static void display_file(const char *filename);
  47. static void format_file(const char *mandir_name, const char *manfile_name, 
  48.                         const char *catfile_name);
  49. static int check_directory_name(const char *dirname);
  50. static void build_catdir_name(const char *dirname, const char section_code,
  51.                               char *catdir_name);
  52. static void build_mandir_name(const char *dirname, const char section_code,
  53.                               char *mandir_name);
  54. static int handle_whatis(const char *name, const mode_type mode);
  55. static void strip_filename(const char *name, char *stripped_name);
  56. static int print_apropos(const char *mandir, const char *name);
  57.  
  58. /* Extern and local variables */
  59. static char man_path[NAME_LENGTH] = "/usr/man";
  60. static char pager[NAME_LENGTH] = "less -r -e";
  61. static char macro_package[NAME_LENGTH] = "an";
  62. static int  use_pager = 1; /* Use a pager unless '-' option or not a tty */
  63.  
  64.  
  65. void main(int argc, char *argv[])
  66. {
  67.   char *section = NULL, *ptr;
  68.   mode_type use_mode = MODE_UNKNOWN;
  69.   int real_parameter_found = 0;
  70.   
  71.   /* Set the search path for the man pages*/
  72.   if ((ptr = getenv("MANPATH")) != NULL)
  73.     strncpy(man_path, ptr, NAME_LENGTH - 1);
  74.   
  75.   /* Set the name of the pager used to show the man pages */
  76.   if ((ptr = getenv("PAGER")) != NULL)
  77.     strncpy(pager, ptr, NAME_LENGTH - 1);
  78.     
  79.   /* We are very forgiving in reading the command-line; if an option is */
  80.   /* given several times, the last one given counts at the time it is   */
  81.   /* used. Also, several section names may be given without a title in  */
  82.   /* between. We only give an error message if no real parameter is     */
  83.   /* given, when an unknown or incomplete option is given, or when      */
  84.   /* options of different modes of operation are combined. Options that */
  85.   /* take a parameter may have a space between the option and the value.*/
  86.   argv += 1;
  87.   ptr = argv[0];
  88.   while (argc > 1)
  89.   {
  90.     if (*ptr == '-')
  91.     {
  92.       switch(ptr[1])
  93.       {
  94.         case 0x00:
  95.           /* Option '-' given: do not use a pager; use `cat' instead */
  96.           if ((use_mode == MODE_MAN_F) || (use_mode == MODE_MAN_K))
  97.             option_error(1); /* doesn't return */
  98.           use_mode = MODE_MAN;
  99.           use_pager = 0;
  100.           break;
  101.         case 'M':
  102.           /* Option '-M' given: alternative manual directory search path */
  103.           if (ptr[2] != 0x00)
  104.             strcpy(man_path, &(ptr[2]));
  105.           else
  106.           {
  107.             /* space used after '-M') */
  108.             if (argc == 2)
  109.             {
  110.               fprintf(stderr, "man: -M option needs an argument\n");
  111.               exit(1);
  112.             }
  113.             else
  114.             {
  115.               argv += 1;
  116.               strcpy(man_path, argv[0]);
  117.               argc -= 1;
  118.             }
  119.           } /* End of else (space after -M option) */
  120.           break;
  121.         case 'T':
  122.           /* Option '-T' given: alternative macro package to be used by nroff */
  123.           if ((use_mode == MODE_MAN_F) || (use_mode == MODE_MAN_K))
  124.             option_error(1); /* doesn't return */
  125.           use_mode = MODE_MAN;
  126.           if (ptr[2] != 0x00)
  127.             strcpy(macro_package, &(ptr[2]));
  128.           else
  129.           {
  130.             /* space used after '-T') */
  131.             if (argc == 2)
  132.             {
  133.               fprintf(stderr, "man: -T option needs an argument\n");
  134.               exit(1);
  135.             }
  136.             else
  137.             {
  138.               argv += 1;
  139.               strcpy(macro_package, argv[0]);
  140.               argc -= 1;
  141.             }
  142.           } /* End of else (space after -T option) */
  143.           break;
  144.         case 'k':
  145.           if ((use_mode == MODE_MAN) || (use_mode == MODE_MAN_F))
  146.             option_error(1); /* doesn't return */
  147.           use_mode = MODE_MAN_K;
  148.           break;
  149.         case 'f':
  150.           if ((use_mode == MODE_MAN) || (use_mode == MODE_MAN_K))
  151.             option_error(1); /* doesn't return */
  152.           use_mode = MODE_MAN_F;
  153.           break;
  154.         default:
  155.           fprintf(stderr, "man: no option %s supported\n", ptr);
  156.           option_error(0); /* doesn't return */
  157.       } /* End of switch (letter after `-') */
  158.     } /* End of if (option) */
  159.     else if ((strlen(ptr) < 3) && (*ptr >= '1') && (*ptr <= '8') &&
  160.              (use_mode != MODE_MAN_F) && (use_mode != MODE_MAN_K))
  161.     {
  162.       /* A section has been found */
  163.       section = ptr;
  164.       use_mode = MODE_MAN;
  165.     }
  166.     else
  167.     {
  168.       /* A real parameter has been found */
  169.       real_parameter_found = 1;
  170.       switch(use_mode)
  171.       {
  172.         case MODE_UNKNOWN:
  173.           use_mode = MODE_MAN;
  174.         case MODE_MAN:
  175.           if (search_man_path(section, ptr) == 0)
  176.           {
  177.             if (section == NULL)
  178.               fprintf(stderr, "man: %s not found\n", ptr);
  179.             else
  180.               fprintf(stderr, "man: %s (%s) not found\n", ptr, section);
  181.           }
  182.           break;
  183.         case MODE_MAN_F:
  184.         case MODE_MAN_K:
  185.           if (handle_whatis(ptr, use_mode) == 0)
  186.             fprintf(stderr, "man: %s not found in whatis database\n", ptr);
  187.           break;
  188.       } /* End of switch(use_mode) */
  189.     } /* End of else (parameter or title found) */
  190.     argc -= 1;
  191.     argv += 1;
  192.     ptr = argv[0];
  193.   }
  194.  
  195.   if (real_parameter_found == 0)  
  196.     option_error(0); /* no real parameters given during use */
  197.   
  198.   exit(0);
  199. } /* End of main() */
  200.  
  201.  
  202. /* Either no real parameter was given (just options: error == 0) */
  203. /* or options for different modes were combined (error == 1).    */
  204. static void volatile option_error(const int error)
  205. {
  206.   if (error == 1)
  207.     fprintf(stderr, "man: options for different modes of operation cannot be combined\n");
  208.   fprintf(stderr, "usage: man [-] [-M path] [-T macro-package] [[section] title ...] ...\n");
  209.   fprintf(stderr, "       man [-M path] -k keyword ...\n");
  210.   fprintf(stderr, "       man [-M path] -f command ...\n");
  211.   exit(1);
  212. } /* End of option_exit() */
  213.  
  214.  
  215. /* Search all man directories until ready or end of path */
  216. static int search_man_path(const char *section, const char *title)
  217. {
  218.   char this_path[NAME_LENGTH], *path_ptr;
  219.   int  title_done;
  220.   
  221.   title_done = 0;
  222.   path_ptr = man_path;
  223.   while ((*path_ptr != 0x00) && (title_done == 0))
  224.   {
  225.     int done;
  226.     char *ptr;
  227.     
  228.     strcpy(this_path, path_ptr);
  229.     ptr = this_path;
  230.     /* Find first path separator or end of path */
  231.     done = 0;
  232.     while (!done)
  233.     {
  234.       if ((*path_ptr == 0x00) ||
  235.           (*path_ptr == ';')  ||
  236.           (*path_ptr == ','))
  237.         done = 1;
  238.       else
  239.       {
  240.         path_ptr += 1;
  241.         ptr += 1;
  242.       }
  243.     }
  244.     if (*path_ptr != 0x00)
  245.       path_ptr += 1;
  246.     *ptr = 0x00;
  247.     
  248.     /* We now have a properly terminated search path in this_path   */
  249.     /* while path_ptr points to the rest of the manual search path. */
  250.     title_done = search_man_dir(section, title, this_path);
  251.   } /* End of while (not end of manual search path && title not done) */
  252.   return(title_done);
  253. } /* End of search_man_path */
  254.  
  255.  
  256. /* Search a directory containing man?, cat? directories */
  257. static int search_man_dir(const char *section, const char *title, 
  258.                           const char *dir_name)
  259. {
  260.   int  done;
  261.   char section_counter;
  262.   
  263.   if (check_directory_name(dir_name) == 0)
  264.   {
  265.     fprintf(stderr, "man: directory %s on search path not found\n",
  266.             dir_name);
  267.     return(0);
  268.   }
  269.   
  270.   if (section != NULL)
  271.   {
  272.     /* check whether a general or specific section is named */
  273.     if (section[1] == 0x00)
  274.       return(search_section_general(section[0], title, dir_name));
  275.     else
  276.       return(search_section_specific(section, title, dir_name));
  277.   }
  278.  
  279.   for (section_counter = '1', done = 0;
  280.        section_counter <= '8' && done == 0;
  281.        section_counter++)
  282.   {
  283.     done = search_section_general(section_counter, title, dir_name);
  284.   }
  285.   return(done);
  286. } /* End of search_man_dir() */
  287.  
  288.  
  289. /* Search a section directory of the man dir for a specific man page */
  290. /* This isused when the section name is of the form number+code.     */
  291. /* We only look for files with the name "dir/title.section"          */
  292. static int search_section_specific(const char *section, const char *title,
  293.                                    const char *dir_name)
  294. {
  295.   char catdir_name[NAME_LENGTH], 
  296.        catfile_name[NAME_LENGTH],
  297.        mandir_name[NAME_LENGTH],
  298.        manfile_name[NAME_LENGTH], 
  299.        basename[NAME_LENGTH];
  300.   int catdir_exists, catfile_exists, manfile_exists;
  301.   struct stat catfile_stat, manfile_stat;
  302.   
  303.   strcpy(basename, title);
  304.   strcat(basename, ".");
  305.   strcat(basename, section);
  306.   
  307.   build_catdir_name(dir_name, section[0], catdir_name);
  308.   catdir_exists = check_directory_name(catdir_name);
  309.   strcpy(catfile_name, catdir_name);
  310.   strcat(catfile_name, "/");
  311.   strcat(catfile_name, basename);
  312.   catfile_exists = !stat(catfile_name, &catfile_stat);
  313.   
  314.   build_mandir_name(dir_name, section[0], mandir_name);
  315.   strcpy(manfile_name, mandir_name);
  316.   strcat(manfile_name, "/");
  317.   strcat(manfile_name, basename);
  318.   manfile_exists = !stat(manfile_name, &manfile_stat);
  319.   
  320.   if (catfile_exists == 1)
  321.   {
  322.     if (manfile_exists == 0)
  323.     {
  324.       /* Only the catfile exists */
  325.       display_file(catfile_name);
  326.       return(1);
  327.     }
  328.     else if (catfile_stat.st_mtime > manfile_stat.st_mtime)
  329.     {
  330.       /* Both files exist, catfile is up-to-date */
  331.       display_file(catfile_name);
  332.       return(1);
  333.     }
  334.     else
  335.     {
  336.       /* Both file exist, catfile is not up-of-date */
  337.       format_file(mandir_name, basename, catfile_name);
  338.       display_file(catfile_name);
  339.       return(1);
  340.     }
  341.   }
  342.   else /* catfile does not exist */
  343.   {
  344.     if (manfile_exists == 0)
  345.       return(0);
  346.     
  347.     if (catdir_exists == 1)
  348.     {
  349.       /* Make a formatted version of the manfile in the catdir, */
  350.       /* then display the formatted manpage.                    */
  351.       format_file(mandir_name, basename, catfile_name);
  352.       display_file(catfile_name);
  353.       return(1);
  354.     }
  355.     else
  356.     {
  357.       /* Make a formatted version of the manfile in a temporary file, */
  358.       /* then display the temporary file.                             */
  359.       tmpnam(catfile_name);
  360.       format_file(mandir_name, basename, catfile_name);
  361.       display_file(catfile_name);
  362.       unlink(catfile_name);
  363.       return(1);
  364.     }
  365.   } /* End of else (catfile doesn't exist) */
  366.   return(0); /* This statement shouldn't be reached - better be safe */
  367. } /* End of search_section_specific() */
  368.  
  369.  
  370. /* Search a manual section looking for the best matching manual page */
  371. static int search_section_general(const char section_code, const char *title,
  372.                                   const char *dir_name)
  373. {
  374.   char catdir_name[NAME_LENGTH], mandir_name[NAME_LENGTH];
  375.   char catfile_basename[NAME_LENGTH], catfile_fullname[NAME_LENGTH];
  376.   char manfile_basename[NAME_LENGTH], manfile_fullname[NAME_LENGTH];
  377.   int catdir_exists, mandir_exists;
  378.   int catfile_type, manfile_type;
  379.   
  380.   build_catdir_name(dir_name, section_code, catdir_name);
  381.   catdir_exists = check_directory_name(catdir_name);
  382.   build_mandir_name(dir_name, section_code, mandir_name);
  383.   mandir_exists = check_directory_name(mandir_name);
  384.   
  385.   if (catdir_exists == 1)
  386.     catfile_type = find_best_file(catdir_name, title, section_code,
  387.                                   catfile_basename, catfile_fullname);
  388.   else
  389.     catfile_type = 0; /* indicates no catfile found */
  390.   if (mandir_exists == 1)
  391.     manfile_type = find_best_file(mandir_name, title, section_code,
  392.                                   manfile_basename, manfile_fullname);
  393.   else
  394.     manfile_type = 0; /* indicates no nanfile found */
  395.     
  396.   if ((catfile_type == 0) && (manfile_type == 0)) /* no files found */
  397.     return(0);
  398.     
  399.   if (catfile_type > manfile_type) /* better type catfile than manfile */
  400.   {
  401.     display_file(catfile_fullname);
  402.     return(1);
  403.   }
  404.   else if (catfile_type < manfile_type) /* better manfile than catfile */
  405.   {
  406.     if (catdir_exists == 1)
  407.     {
  408.       /* result from format is new catfile */
  409.       strcpy(catfile_fullname, catdir_name);
  410.       strcat(catfile_fullname, "/");
  411.       strcat(catfile_fullname, manfile_basename);
  412.     }
  413.     else
  414.     {
  415.       /* result from format is a temporary file */
  416.       tmpnam(catfile_fullname);
  417.     }
  418.     format_file(mandir_name, manfile_basename, catfile_fullname);
  419.     display_file(catfile_fullname);
  420.     if (catdir_exists == 0)
  421.       unlink(catfile_fullname);
  422.     return(1);
  423.   } /* End of else (better manfile than catfile) */
  424.   else /* catfile and manfile same quality */
  425.   {
  426.     if (strcmp(catfile_basename, manfile_basename) == 0)
  427.     {
  428.       /* Files have the same name - check if catfile up-to-date */
  429.       struct stat catfile_stat, manfile_stat;
  430.       
  431.       stat(catfile_fullname, &catfile_stat);
  432.       stat(manfile_fullname, &manfile_stat);
  433.       if (catfile_stat.st_mtime > manfile_stat.st_mtime)
  434.       {
  435.         /* catfile is up-to-date */
  436.         display_file(catfile_fullname);
  437.         return(1);
  438.       }
  439.       else
  440.       {
  441.         /* manfile is newer than catfile */
  442.         format_file(mandir_name, manfile_basename, catfile_fullname);
  443.         display_file(catfile_fullname);
  444.         return(1);
  445.       }
  446.     } /* end of if (same basename) */
  447.     else
  448.     {
  449.       /* differing catfile and manfile - always take the catfile */
  450.       display_file(catfile_fullname);
  451.       return(1);
  452.     }
  453.   }
  454.   return(0); /* should never be reached - better be safe */
  455. } /* End of search_section_general() */
  456.                                  
  457.  
  458. /* Find the best matching file in a directory */
  459. static int find_best_file(const char *dir_name, const char *title,
  460.                           const char section_code,
  461.                           char *result_basename, char *result_fullname)
  462. {
  463.   /* Note on the return codes: higher number is better */
  464.   /* code 5: short section extension (.3 in xxxxcat3)  */
  465.   /* code 4: extension .man                            */
  466.   /* code 3: extension .nr                             */
  467.   /* code 2: long section extension (.3x in xxxxcat3)  */
  468.   /* code 1: any other extension (first found is kept) */
  469.   /* code 0: no file found at all                      */
  470.   int return_code;
  471.   DIR *search_dir;
  472.   struct dirent *this_entry;
  473.   char name_prefix[NAME_LENGTH];
  474.   size_t prefix_size;
  475.   
  476.   return_code = 0;
  477.   strcpy(name_prefix, title);
  478.   strcat(name_prefix, ".");
  479.   prefix_size = strlen(name_prefix);
  480.   if ((search_dir = opendir(dir_name)) == (DIR *)NULL)
  481.     return(0);
  482.   while ((this_entry = readdir(search_dir)) != (struct dirent *)NULL)
  483.   {
  484.     if (strncmp(name_prefix, this_entry->d_name, prefix_size) == 0)
  485.     {
  486.       /* prefix matches - now check for type */
  487.       int this_type = 0;
  488.       char *extension;
  489.       
  490.       if (strlen(this_entry->d_name) > prefix_size)
  491.       {
  492.         extension = &(this_entry->d_name[prefix_size]);
  493.         if ((extension[0] == section_code) &&
  494.             (extension[1] == 0x00))
  495.           this_type = 5;
  496.         else if (strcmp(extension, "man") == 0)
  497.           this_type = 4;
  498.         else if (strcmp(extension, "nr") == 0)
  499.           this_type = 3;
  500.         else if ((extension[0] == section_code) &&
  501.                /*(extension[1] != 0x00) && - we know this already */
  502.                  (extension[2] == 0x00))
  503.           this_type = 2;
  504.         else
  505.           this_type = 1;
  506.       }
  507.       if (this_type > return_code)
  508.       {
  509.         return_code = this_type;
  510.         strcpy(result_basename, this_entry->d_name);
  511.       }
  512.     } /* end of if (prefix matches) */
  513.   } /* end of while (not end of directory) */
  514.   closedir(search_dir);
  515.   if (return_code > 0)
  516.   {
  517.     /* build result_fullname */
  518.     strcpy(result_fullname, dir_name);
  519.     strcat(result_fullname, "/");
  520.     strcat(result_fullname, result_basename);
  521.   }
  522.   return(return_code);
  523. } /* End of find_best_file() */
  524.  
  525.  
  526. /* Display a file. Use pager for tty, cat for file or pipe */
  527. static void display_file(const char *filename)
  528. {
  529.   char command_line[NAME_LENGTH], dosname[NAME_LENGTH];
  530.  
  531. #ifdef SUBPROGRAMS_GET_GEMDOS_NAMES  
  532.   _unx2dos(filename, dosname); /* Send valid GEMDOS name through system */
  533. #else
  534.   strcpy(dosname, filename);
  535. #endif
  536.   if ((isatty(1) == 1) && /* GEMDOS handle 1 is stdout */
  537.       (use_pager == 1))   /* No option '-' given       */
  538.   {
  539.     /* Use a pager to display on a terminal */
  540.     strcpy(command_line, pager);
  541.     strcat(command_line, " ");
  542.     strcat(command_line, dosname);
  543.     if (system(command_line) == 0) /* 0: successful execution */
  544.       return;
  545.     
  546.     fprintf(stderr, "man: error executing pager - aborted (sorry)\n");
  547.     exit(1);
  548.   }
  549.   else
  550.   {
  551.     /* Use cat for anything but a tty or when '-' option is given */
  552.     strcpy(command_line, "cat ");
  553.     strcat(command_line, dosname);
  554.     if (system(command_line) == 0) /* 0: successful execution */
  555.       return;
  556.       
  557.     fprintf(stderr, "man: error executing cat - aborted (sorry)\n");
  558.     exit(1);
  559.   }
  560. } /* End of display_file() */
  561.  
  562.  
  563. /* Format (nroff) a file and write the result to another file */
  564. static void format_file(const char *mandir, const char *manfile,
  565.                         const char *catfile)
  566. {
  567.   char command_line[NAME_LENGTH], cwd[NAME_LENGTH];
  568. #ifdef SUBPROGRAMS_GROK_GEMDOS_NAMES  
  569.   char dosname[NAME_LENGTH];
  570. #endif
  571.  
  572.   getcwd(cwd, NAME_LENGTH);
  573.   if (chdir(mandir) != 0)
  574.   {
  575.     /* Note that the existence of the directory has been checked before */
  576.     fprintf(stderr, "man: change directory to %s failed\n", mandir);
  577.     exit(1);
  578.   }
  579.   strcpy(command_line, "nroff -m");
  580.   strcat(command_line, macro_package);
  581.   strcat(command_line, " ");
  582. #ifdef SUBPROGRAMS_GROK_GEMDOS_NAMES  
  583.   _unx2dos(manfile, dosname); /* send nroff a proper GEMDOS name */
  584.   strcat(command_line, dosname);
  585. #else
  586.   strcat(command_line, manfile);
  587. #endif
  588.   strcat(command_line, " >");
  589. #ifdef SUBPROGRAMS_GROK_GEMDOS_NAMES  
  590.   _unx2dos(catfile, dosname); /* redirect to a proper GEMDOS name */
  591.   strcat(command_line, dosname);
  592. #else
  593.   strcat(command_line, catfile);
  594. #endif
  595.   fprintf(stderr, "Formatting file. Wait...");
  596.   if (system(command_line) == 0) /* successful execution */
  597.   {
  598.     fprintf(stderr, "Done\n");
  599.     return;
  600.   }
  601.   else
  602.   {
  603.     fprintf(stderr, "Aborted (sorry)\n");
  604.     exit(1);
  605.   }
  606.   
  607.   if (chdir(cwd) != 0)
  608.   {
  609.     fprintf(stderr, "man: cannot change back to directory %s\n", cwd);
  610.     exit(1);
  611.   }
  612. } /* End of format_file() */
  613.  
  614.  
  615. /* Check if a dirname is a valid directory */
  616. static int check_directory_name(const char *dirname)
  617. {
  618.   struct stat dir_stat;
  619.   
  620.   if (stat(dirname, &dir_stat) == -1)
  621.     return(0);
  622.   else if (dir_stat.st_mode & S_IFDIR == 0)
  623.   {
  624.     fprintf(stderr, "man: %s is not a directory\n", dirname);
  625.     return(0);
  626.   }
  627.   else
  628.     return(1);
  629. } /* End of check_directory_name() */
  630.  
  631.  
  632. /* Build the name of cat dir #section_code within the man dir */
  633. static void build_catdir_name(const char *dirname, const char section_code,
  634.                               char *catdir_name)
  635. {
  636.   static char extension[] = "/cat0";
  637.   
  638.   strcpy(catdir_name, dirname);
  639.   extension[4] = section_code;
  640.   strcat(catdir_name, extension);
  641. } /* End of build_catdir_name() */  
  642.  
  643.                            
  644. /* Build the name of man dir #section_code within the man dir */
  645. static void build_mandir_name(const char *dirname, const char section_code,
  646.                               char *mandir_name)
  647. {
  648.   static char extension[] = "/man0";
  649.   
  650.   strcpy(mandir_name, dirname);
  651.   extension[4] = section_code;
  652.   strcat(mandir_name, extension);
  653. } /* End of build_mandir_name() */
  654.  
  655.  
  656. /* Handle the `man -k' (apropos) and `man -f' (whatis) options */
  657. static int handle_whatis(const char *name, const mode_type mode)
  658. {
  659.   char *path_ptr, name_used[NAME_LENGTH];
  660.   int  did_print;
  661.  
  662.   /* `man -f' (whatis) needs to have the filename stripped first */
  663.   if (mode == MODE_MAN_F)
  664.     strip_filename(name, name_used);
  665.   else
  666.     strcpy(name_used, name);
  667.     
  668.   did_print = 0;      
  669.   /* walk along manual path and check whatis database */
  670.   path_ptr = man_path;
  671.   while (*path_ptr != 0x00)
  672.   {
  673.     char mandir[NAME_LENGTH], *manptr;
  674.             
  675.     /* Copy one directory from the manual search path into mandir */
  676.     manptr = mandir;
  677.     while ((*path_ptr != 0x00) && (*path_ptr != ';') && 
  678.            (*path_ptr != ','))
  679.       *manptr++ = *path_ptr++;
  680.     *manptr = 0x00;
  681.     if (*path_ptr != 0x00)
  682.       path_ptr += 1;
  683.  
  684.     if (check_directory_name(mandir) == 0)
  685.     {
  686.       fprintf(stderr, "man: directory %s on search path not found\n",
  687.               mandir);
  688.         continue; /* go on to next part of manual search path */
  689.     }
  690.  
  691.     did_print |= print_apropos(mandir, name_used);
  692.   } /* End of while (walk along manual search path) */
  693.   return(did_print);
  694. } /* End of handle_whatis() */
  695.  
  696.  
  697. /* Strip the leading path-name and extension components from a filename. */
  698. /* This is needed for the `man -f' (whatis) option, which is the same as */
  699. /* `man -k' (apropos), except the whatis version needs a stripped name.  */
  700. static void strip_filename(const char *name, char *stripped_name)
  701. {
  702.   char buffer[NAME_LENGTH], *ptr1, *ptr2;
  703.   
  704.   strcpy(buffer, name);
  705.   ptr1 = ptr2 = buffer;
  706.   while (*ptr2 != 0x00)
  707.   {
  708.     if ((*ptr2 == '/') || (*ptr2 == '\\'))
  709.       ptr1 = ptr2 + 1;
  710.     ptr2 += 1;
  711.   }
  712.   ptr2 = ptr1;
  713.   while ((*ptr2 != 0x00) && (*ptr2 != '.'))
  714.     ptr2 += 1;
  715.   *ptr2 = 0x00;
  716.   strcpy(stripped_name, ptr1);
  717. } /* End of strip_filename() */
  718.  
  719.  
  720. /* Handle the `man -k' (apropos) search within a specific directory. */
  721. /* This is similar to the `man -f' (whatis) search, which also makes */
  722. /* use of this routine. The whatis argument must be stripped first.  */
  723. static int print_apropos(const char *mandir, const char *name)
  724. {
  725.   FILE *whatis;
  726.   char line_read[NAME_LENGTH], line_buffer[NAME_LENGTH], 
  727.        name_buffer[NAME_LENGTH];
  728.   int  did_print;
  729.   
  730.   strcpy(name_buffer, mandir);
  731.   strcat(name_buffer, "/whatis");
  732.   if ((whatis = fopen(name_buffer, "r")) == NULL)
  733.   {
  734.     fprintf(stderr, "%s: no such file\n", name_buffer);
  735.     return(0);
  736.   }
  737.   
  738.   /* No case-insensitive strstr exists. Improvise with all-lowercase. */
  739.   strcpy(name_buffer, name);
  740.   strlwr(name_buffer);
  741.   did_print = 0;
  742.   while (!feof(whatis))
  743.   {
  744.     fgets(line_read, NAME_LENGTH - 1, whatis);
  745.     strcpy(line_buffer, line_read);
  746.     strlwr(line_buffer);
  747.     if (strstr(line_buffer, name_buffer) != NULL)
  748.     {
  749.       fputs(line_read, stdout);
  750.       did_print = 1;
  751.     }
  752.   }
  753.   fclose(whatis);
  754.   return(did_print);
  755. } /* End of print_apropos() */                             
  756.