home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume6 / help / part1 / help.c < prev    next >
Encoding:
C/C++ Source or Header  |  1986-11-30  |  29.4 KB  |  1,146 lines

  1. #include "help.h"
  2. #define MAXMATCHES    24
  3.  
  4.  /* ************************************************************
  5.   *             H   E   L   P    !!
  6.   *             =========================
  7.   *
  8.   *     This program emulates the VMS help facility in the UNIX
  9.   *  environment.  The main routine HELP1 looks up help and 
  10.   *  subtopics for help.  The help texts for various topix 
  11.   *  are tree-structured.  A directory is defined as a "help"
  12.   *  directory, it has four types of files in it:
  13.   *        main help text:     .HLP
  14.   *        manual page name:    .MANUAL
  15.   *        subtopic texts:        <topicname>.HLP
  16.   *        subtopic directories:    <topicname>
  17.   *             subtopic cross-refs:    <topicname>.XREF
  18.   *
  19.   *    Subtopic names must start with an alphanumeric
  20.   *  character.  Preferably all subtopics will start with
  21.   *  lowercase letters.
  22.   *
  23.   *    The routine help1 is recursive, it descends the    tree
  24.   *  structure.
  25.   */
  26.  
  27.   main(argc, argv)
  28.       int argc;
  29.     char *argv[];
  30.   {
  31.     int i,j,k;
  32.     int longlen;
  33.     char yesno[10];
  34.     char *opts;
  35.         
  36.     strcpy(progname,argv[0]);
  37.     helpdir = NULL;
  38.     getwd(olddir);
  39.     dumb_flag = 0;
  40.     list_flag = 0;
  41.     frst_flag = 1;
  42.     col_flag  = 1;
  43.  
  44.     argc--;
  45.     if (*(*++argv) == '-') {        /* must be options */
  46.         for (opts = *argv; *opts != '\0'; opts++) {
  47.             switch(*opts) {
  48.                case '-' :    
  49.                    break;
  50.  
  51.                case 'd' :    
  52.                    if (argc > 1) {
  53.                     helpdir = *++argv;
  54.                     argc--;
  55.                 }
  56.                 break;
  57.  
  58.                case 'l' :
  59.                    list_flag = 1;
  60.                 col_flag = 0;
  61.                 break;
  62.  
  63.                case 'q' :
  64.                    dumb_flag = 1;
  65.                 break;
  66.  
  67.                case 'C' :
  68.                    col_flag = 1;
  69.                 break;
  70.  
  71.                case 'n' :
  72.                    col_flag = 0;
  73.  
  74.                default:
  75.                     fprintf(stderr,"%s: %c: bad option.\n",
  76.                     progname,*opts);
  77.                 break;
  78.             }
  79.         }
  80.         argc--; argv++;
  81.     }
  82.  
  83.     if (helpdir == NULL) helpdir = HELPDIR;
  84.  
  85.     if (chdir(helpdir) < 0) {
  86.         fprintf(stderr,"%s: %s: help directory not found.\n",
  87.             progname,helpdir);
  88.         exit(1);
  89.     }
  90.  
  91. #ifdef BSD
  92.     init_funky();
  93. #endif
  94.     if (argc >= 1) {        /* treat vector as a help path */
  95. #ifdef DEBUG
  96.         fprintf(stderr," help path vector, argc=%d, *argv=%s.\n",
  97.             argc,*argv);
  98. #endif
  99.         help1( "", argv, 0, 0, 0);
  100.  
  101.     }
  102.     else    help1( "", NULL, 0, 0, 0);
  103.  
  104.     exit(0);
  105.  }
  106.  
  107.  
  108.  
  109.  
  110.  /* **************************************************************
  111.   * printhelp: given a string, pop .HLP on the end and do more(1).
  112.   *
  113.   *    This routine sends a help file to more(1).  A string
  114.   *  is passed in, which is the name of a help.  If the string
  115.   *  is nil, then just use the name HLP.
  116.   *    The return value is -1 if no help file is accessible, 
  117.   *  0 if the more(1) command was called okay with fkoff();
  118.   */
  119.  
  120.   printhelp(hs, path)
  121.       char *hs;
  122.   {
  123.     char filename[MAXNAMELEN], comm[MAXNAMELEN + 20];
  124.  
  125.     if (hs == NULL) strcpy(filename,HELPEX);
  126.     else if (strlen(hs) < 1) strcpy(filename, HELPEX);
  127.     else {
  128.         strcpy(filename, hs);
  129.         strcat(filename,HELPEX);
  130.     }
  131.  
  132.     if ( access(filename, R_OK) < 0 ) {
  133.         printf("\n Sorry, no help text for %s.\n",
  134.             (hs==NULL)?"this topic":hs );
  135.         return(-1);
  136.     }
  137.  
  138.     if (path != NULL) printf("\n HELP: %s\n",path);
  139.  
  140.     fkoff(VIEWPROGRAM,VIEWPROGOPTS1,VIEWPROGOPTS2,filename,NULL);
  141.  
  142.     return(0);
  143.   }
  144.  
  145.  
  146.  
  147. /* *************************************************************
  148.  * printtopix: print the topics available in this directory
  149.  *        in a nice format.
  150.  *
  151.  *    This routine does a directory of help options, 
  152.  *  and prints them out in a manner similar to that of ls(1).
  153.  *  All filenames which start with anything other than numbers 
  154.  *  or letters are not kept.  Extensions are stripped off.
  155.  *    
  156.  *    The number of subtopics found is returned, along with
  157.  *  a pointer to a null-terminated vector of string pointers.
  158.  *  If the mode is non-zero, it means just use the strings stored
  159.  *  in topix.  If mode==0, then re-allocate space and re-check
  160.  *  the directory.  If mode
  161.  */
  162.  
  163.  printtopix( topix, mode, supress)
  164.      char *topix[];
  165.     int mode, supress;
  166. {
  167.     int i,j,k,l,xrefs = 0;
  168.     int  namewidth, totalnames;
  169.     char *malloc(), *nbuf;
  170.     char *thisname, *index();
  171.     char *s1, *s2, **nxtname, *nxcnt;
  172.     struct direct *readdir(), *filedat;
  173.     DIR *dirp, *opendir();
  174.  
  175.     if ( mode ) {
  176.         for(nxtname=topix, i=0; *nxtname++ != NULL; i++ );
  177.         totalnames = i;
  178.         goto inputtopix;
  179.     }
  180.  
  181.     /* if mode is zero, then allocate space and search the directory */
  182.     nbuf = malloc( MAXNAMES * MAXNAMELEN );
  183.     nxcnt=nbuf;
  184.     dirp = opendir(".");
  185.     if (dirp == NULL) {
  186.         fprintf(stderr,"%s: Cannot open help directory.\n",progname);
  187.         return(-1);
  188.     }
  189.  
  190.     for(nxtname=topix, i=0; i < (MAXNAMES-1) ; ) {
  191.         filedat = readdir(dirp);
  192.         if (filedat == NULL) break;
  193.         thisname = filedat->d_name;
  194.         if ( !(isalnum(*thisname)) )    /* if not in [0-9A-Za-z] */
  195.                 continue;    /* do the next one.      */
  196.         *nxcnt = '\0';
  197.         if ( (s1 = index(thisname,'.')) != NULL) {
  198.             if (strcmp(s1, HELPEX) == 0) *s1 = '\0';
  199.             else if ( strcmp(s1, MANEX) == 0) continue;
  200.             else if ( strcmp(s1, XREFEX) == 0) {
  201.                 /* mark xref with at sign in first char */
  202.                 strcpy(nxcnt,"@");
  203.             }
  204.         }
  205.         if (strlen(thisname) >= MAXNAMELEN - 1)
  206.             *(thisname+MAXNAMELEN-1) = '\0';
  207.         /* copy in data from this loop */
  208.         if (*nxcnt == '\0') strcpy(nxcnt,thisname);
  209.         else strcat(nxcnt,thisname);
  210.         *nxtname++ = nxcnt; 
  211.         /* update pointers for next loop */
  212.         nxcnt += strlen(thisname) + ((*nxcnt=='@')?(2):(1));
  213.         i++;
  214.     }
  215.     *nxtname++ = NULL;
  216.     totalnames = i;
  217.     closedir(dirp);
  218.  
  219.     if (totalnames == 0) return(0);
  220.     
  221.     /* sort the names in ascending order with exchange algorithm */
  222.     for(i=0; i < totalnames-1; i++)
  223.         for(j=i+1; j <totalnames; j++)
  224.             if (strcmp(topix[i],topix[j]) > 0) {
  225.                 thisname = topix[i];
  226.                 topix[i] = topix[j];
  227.                 topix[j] = thisname;
  228.             }
  229.         
  230.  
  231.  inputtopix:
  232.     if (supress) return(totalnames);
  233.     else {
  234.         if (col_flag) printf("\n Subtopics:\n");
  235.         printlist(topix, totalnames);
  236.         return(totalnames);
  237.     }
  238.  
  239. }
  240.  
  241.  
  242.  
  243.  /* ****************************************************************
  244.   * help1: descend recursive help tree and provide some
  245.   *         user services.
  246.   *
  247.   *    This routine is the heart of the new UNIX help facility.
  248.   *  It climbs recursively around an n-tree of documentation files
  249.   *  and directories.  The routine printhelp() outputs a file of
  250.   *  help text, the routine printtopix prints out all subtopics.
  251.   *
  252.   *    This routine can operate in interactive mode, or not.  The
  253.   *  basic cycle of operation is:
  254.   *        if (not list-only-mode) print help.
  255.   *        if (not quiet-mode)    print list.
  256.   *        if (not interactive)    return.
  257.   *        print prompt and do commands.
  258.   *        return.
  259.   *
  260.   *    There are a number of commands available in the
  261.   *  interactive mode, they are:
  262.   *
  263.   *        blank line:    up recursive level.
  264.   *        subtopic name:    recurse to subtopic.
  265.   *        ?:        list topics again.
  266.   *        *:        get list of commands
  267.   *        $:        get info on topix
  268.   *        ^D:        quit help program.
  269.   *        .:        man page (if any).
  270.   *        #        list topics again
  271.   *        
  272.   */
  273.  
  274.   help1(ppt,svec,skip,xref,comp_flag)
  275.       char *ppt;
  276.     char *svec[];
  277.     short  skip;
  278.     short  xref;       /* non-zero if doing xref */
  279.     short  comp_flag;  /* non-zero if doing completions */
  280. {                             /* this routine is too big, eh? */
  281.     int i,j,k, err;
  282.     int no_subs;
  283.     char answer[MAXLINELEN];
  284.     char fullpath[MAXNAMELEN + 29];
  285.     char yesno[10];
  286.     char *topix[MAXNAMES], *mx[MAXMATCHES];
  287.     int  matchv();
  288.     char *index(), *getenv();
  289.     char *s1, *s2, *s3, *rest;
  290.     char *wvec[MAXMATCHES];
  291.     char cmdbuf[90];
  292.  
  293.  
  294.     if (comp_flag) {
  295.         printtopix( topix, 0, 1);
  296.         if ( (k = matchv( *svec, topix, mx)) == 0) return(-1);
  297.         /* if there is more than one match, and we are not at end, flub */
  298.         if ( svec[1] != NULL  &&  k > 1 ) return(-1);
  299.         /* if we are not at end, go down one level */
  300.         if ( svec[1] != NULL ) {
  301.         if ( chdir( mx[0] ) < 0) return(-1);
  302.         else {
  303.             k = help1(ppt, svec+1, 0, 0, comp_flag);
  304.             chdir("..");
  305.             return(k);
  306.         }
  307.         }
  308.         /* if we are at end, then do appropriate action on comp_flag */
  309.         else {
  310.         strcpy(gbl_match, mx[0]);
  311.         if ( comp_flag == CTRLD_COMP ) {
  312.             fputs("\n",stdout);
  313.             printlist(mx,k);
  314.         }
  315.         }
  316.         return(0);
  317.     }
  318.  
  319.     if ( svec != NULL  && *svec != NULL) {
  320.         printtopix( topix, 0, 1);
  321.         if ( (k = matchv(*svec, topix, mx)) == 0) {
  322.         printf(" sorry, no direct help path to %s %s\n",ppt,*svec);
  323.         if ( (k = xref_matchv(*svec, topix, mx)) == 0) {
  324.             printf("    no cross-references, either.\n\n");
  325.             if (strlen(ppt) == 0) help1(ppt,NULL,1,0, 0);
  326.             return(-1);
  327.         }
  328.         else {
  329.             do_xref(ppt,*svec,mx);
  330.             if (strlen(ppt) == 0) help1(ppt,NULL,1,0, 0);
  331.             return(-1);
  332.         }
  333.         }
  334.  
  335.         for(i = 0, j = 0, err = -1; mx[i] != NULL; ) {
  336.         if (i > 0)
  337.           j = takeit(" Next help path: %s %s\nTry it?",ppt,mx[i]);
  338.         if (j == 1) { i++; continue; }
  339.         if (j < 0)  break;
  340.         strcpy(fullpath,ppt);
  341.         strcat(fullpath," ");
  342.         strcat(fullpath,mx[i]);
  343.  
  344.         if ( chdir(mx[i]) < 0) {
  345.             if ( *(svec + 1) != NULL ) {
  346.             i++;
  347.             continue;
  348.             }
  349.             if (list_flag) 
  350.             return(-1);
  351.             else
  352.             printhelp( mx[i],fullpath);
  353.             printf("\n");
  354.             if (!dumb_flag) help1(ppt,NULL,1,0, 0);
  355.             err = 0;
  356.         }
  357.         else {
  358.             if ( help1(fullpath,(svec+1),0,0, 0) >= 0) {
  359.             err = 0;
  360.             chdir("..");
  361.             printf("\n");
  362.             if (!dumb_flag) help1(ppt,NULL,1,xref, 0);
  363.             }
  364.             else chdir("..");
  365.         }
  366.         i++;
  367.         }
  368.         return(err);
  369.     }
  370.  
  371.     if ( !list_flag && !skip) 
  372.         if (printhelp(NULL, ppt) < 0) {
  373.         return(-1);
  374.         }
  375.  
  376.     if ( !list_flag && !dumb_flag && ( access(MANEX, R_OK) >= 0) && !skip)
  377.         printf("\n    Manual page IS available.\n");
  378.  
  379.     if ( !dumb_flag ) {
  380.         if ( printtopix( topix, 0, skip) <= 0 ) {
  381.         no_subs = 1;
  382.         }
  383.         else no_subs= 0;
  384.     }
  385.  
  386.     if ( dumb_flag  ||  list_flag ) return(0);
  387.  
  388.     while (xref == 0) {
  389.         if (frst_flag) {
  390.             printf(" (type '*' for commands)\n");
  391.             frst_flag = 0;
  392.         }
  393.  
  394.  
  395.         /* now, prompt and wait for user response */
  396.  
  397.         if ( strlen(ppt) < 1 ) sprintf(gbl_ppt," Help %s",PROMPT);
  398.         else         sprintf(gbl_ppt,"  %s %s",ppt,SUBPROMPT);
  399.         fputs(gbl_ppt,stdout);
  400. #ifndef BSD
  401.         if ( fgets(answer, MAXLINELEN-1, stdin) == NULL ) {
  402.             printf("\nbye...\n");
  403.             exit(1);
  404.         }
  405. #else
  406.         if (read_gets(stdin->_file, answer, MAXLINELEN - 1, 1) < 0) {
  407.                 printf("\nbye...\n");
  408.             exit(1);
  409.         }
  410. #endif
  411.  
  412.         /* first remove any leading blanks or tabs */
  413.         for(s1=answer; *s1 == ' ' || *s1 == '    '; s1++);
  414.  
  415.         /* chop off all of answer after first word. */
  416.         s2 = index(s1,' ');
  417.         if (s2 == NULL)  s2 = index(s1,'    ');
  418.         if (s2 == NULL)  s2 = index(s1,'\n');
  419.         if (s2)
  420.           { *s2 = '\0'; rest = ++s2; }
  421.         else {
  422.             rest = s2 = s1 + strlen(s1);
  423.         }
  424.         makewvec(rest,wvec,MAXMATCHES);
  425.  
  426.         if ( strlen(s1) == 0 )        /*  on blank line, */
  427.             break;            /* pop up one level*/
  428.  
  429.         switch (*s1) {
  430.                 case '?':            /* ?: print stuff again */
  431.                 printhelp(NULL, ppt);
  432.             if ( access(MANEX, R_OK) >= 0 )
  433.                 printf("\n    Manual page IS available.\n");
  434.                 if (!no_subs) printtopix(topix, 1,0);
  435.             else    printf("\n Sorry, no subtopics.\n\n");
  436.             break;
  437.  
  438.             case '#':
  439.             if (!no_subs) printtopix( topix, 1,0);
  440.             else    printf("\n Sorry, no subtopics.\n\n");
  441.             break;
  442.  
  443.             case '*':            /* *: list commands */
  444.             printf("\n");
  445.                 for(i=0; helpcmds[i] != NULL; i++) {
  446.                 printf("    %s\n",helpcmds[i]);
  447.             }
  448.             printf("\n");
  449.             break;
  450.  
  451.             case '$':            /* $: find out about files */
  452.                 s2 = s1 + 1;
  453.             if (no_subs) {
  454.                 printf("\n Sorry, no subtopics.\n\n");
  455.                     break;
  456.             }
  457.             strcpy(cmdbuf, INFOHEAD);
  458.             strcat(cmdbuf,s2);
  459.             strcat(cmdbuf,"* ");
  460.             strcat(cmdbuf, INFOTAIL);
  461.             printf("\n File information: \n");
  462.             system(cmdbuf);
  463.             printf("\n");
  464.             break;
  465.                 
  466.  
  467.                 case '.':            /* .: do manpage if any */
  468.                 s2 = s1 + 1;
  469.             if (no_subs) {
  470.                 printf("\n Sorry, no subtopics.\n\n");
  471.                     break;
  472.             }
  473.             if ( *s2 == '\0' ) {
  474.                 mx[0] = "";  mx[1] = NULL; k = 1;
  475.             }
  476.             else if ( (k = matchv( s2, topix, mx)) == 0 ) {
  477.                printf("\n Sorry, no topics match %s\n",s2);
  478.                printf(" (list cmnds with '*', topix with '#')\n");
  479.             }
  480.             for( i=0; i < k; i++ ) {
  481.                 strcpy(cmdbuf,mx[i]);
  482.                 strcat(cmdbuf,MANEX);
  483.                 if (access(cmdbuf, R_OK|X_OK ) < 0) {
  484.                         strcpy(cmdbuf,mx[i]);
  485.                     strcat(cmdbuf,MAN_SUBEX);
  486.                     if (access(cmdbuf,R_OK|X_OK) < 0) {
  487.                         printf("\n Sorry, %s for %s.\n\n",
  488.                           "No manual reference available",
  489.                           (strlen(s2)==0)?ppt:mx[i]);
  490.                         continue;
  491.                     }
  492.                 }
  493.                 if (i > 0) {
  494.                       k=
  495.                      takeit(" Next man page: %s.\n Take it? ",
  496.                     mx[i]);
  497.                      if (k ==  1) continue;
  498.                      if (k == -1) break;
  499.                 }
  500.                 printf(" ...doing %s\n\n",cmdbuf);
  501.                 fkoff(SHELLPROG,SHELLOPTS,cmdbuf,NULL);
  502.                     /* source contents of .MANUAL file */
  503.             }
  504.             break;
  505.  
  506.  
  507.             default:            /* must be a topic spec */
  508.                 if ( no_subs )  {
  509.                 printf("\n Sorry, no subtopics.\n\n");
  510.                     break;
  511.             }
  512.             if ( (k = matchv( s1, topix, mx)) == 0 ) {
  513.                printf("\n Sorry, no direct help for  %s\n",s1);
  514.                if ( (k = xref_matchv(s1,topix,mx)) == 0) {
  515.                    printf(" no cross-references to  %s\n",s1);
  516.                    printf(
  517.                 " (list cmnds with '*', topics with '#')\n");
  518.                    break;    /* leave switch */
  519.                }
  520.                else {
  521.                    do_xref(ppt,s1,mx);
  522.                    break;   /* leave switch */
  523.                }
  524.             }
  525.  
  526.             /* step thru sub-topics that match src */
  527.                 for(i=0; i < k; i++) {
  528.                 s3 = mx[i];
  529.  
  530.                 if (i > 0) {
  531.                     j=takeit("\nNext %s subtopic: %s\nTake it?",
  532.                      ppt,s3);
  533.                 if (j ==  1) continue;
  534.                 if (j == -1) break;
  535.                 }
  536.  
  537.                 if ( chdir(s3) >= 0 ) {  /* directory  subtopic */
  538.                     strcpy(fullpath,ppt);
  539.                     strcat(fullpath," ");
  540.                     strcat(fullpath,s3);
  541.                     help1(fullpath,wvec,0,0, 0);    /* recurse */
  542.                 if ( strcmp(getwd(newdir),helpdir) )
  543.                     chdir("..");
  544.                 }            /* else text subtopic */
  545.                 else  {
  546.                 strcpy(fullpath, ppt);
  547.                 strcat(fullpath, " ");
  548.                 strcat(fullpath, s3);
  549.                 printhelp(s3, fullpath);
  550.                 }
  551.             }
  552.  
  553.         }    /* end of switch */
  554.  
  555.     }    /* end of while(1) */
  556.  
  557.     if (!no_subs && (*topix != NULL) ) free( *topix );
  558.  
  559.     return(0);
  560.  
  561.   }    /* end of help1 */
  562.  
  563.  
  564. /* ****************************************************************
  565.  * takeit: ask user whether to take next topic, return t or f
  566.  *
  567.  *    This routine takes a message, with format, and asks the 
  568.  *  user whether he wants to do the action, not do the action,
  569.  *  or quit the cycle of actions.
  570.  *    y - return  0
  571.  *    n - return  1
  572.  *    q - return -1
  573.  *    ? - tell what y, n, and q do.
  574.  *      * - tell what y, n, and q do.
  575.  *     <CR> - carriage return
  576.  *
  577.  *    Naturally, case is insignificant.
  578.  */
  579.  
  580.  takeit(fmt,m1,m2,m3)
  581.    char *fmt, *m1, *m2, *m3;
  582.  {
  583.  
  584.     char ans[40];
  585.     int  done, ret;
  586.     char *fgets();
  587.  
  588.     for(done = 0; !done; ) {
  589.         if (fmt != NULL) printf(fmt, m1, m2, m3);
  590.         printf(" [ynq] ");
  591.         if (fgets(ans, 39, stdin) == NULL) exit(1);  /* abort */
  592.         isupper(*ans)?(*ans=tolower(*ans)):0;
  593.         if (*ans == 'n') {
  594.             done = 1;
  595.             ret = 1;
  596.         }
  597.         else if (*ans == 'y' || *ans == '\n') {
  598.             done = 1;
  599.             ret = 0;
  600.         }
  601.         else if (*ans == 'q') {
  602.             done = 1;
  603.             ret = -1;
  604.         }
  605.         else printf("  Answer y to get next subtopic, n to skip, q to quit.\n");
  606.     }
  607.     return(ret);
  608.  }
  609.  
  610.  
  611.  /* ***************************************************************
  612.   * matchv: return all matches of a string in a vector.
  613.   *
  614.   *    This routine accepts a string and a vector of strings. 
  615.   *  The string is supposed to be an abbreviation of one or more
  616.   *  strings in the vector.  The full versions of the strings are
  617.   *  placed in a another vector (which is in a static area) and
  618.   *  a pointer to that vector returned.  Note that the input
  619.   *  vector of pointers must have NULL as its terminating element.
  620.   *  The output vector will also terminate with NULL.
  621.   *    NOTE: The vector returned contains pointers into the input
  622.   *  vector.  Do not, therefore, mess with the contents of the output
  623.   *  vector.
  624.   *
  625.   *  If NULL is returned, nothing matched, or there was some other
  626.   *  error.
  627.   */
  628.  
  629.   matchv( src, vec, mx)
  630.     char *src;
  631.     char *vec[];
  632.     char *mx[];
  633.   {
  634.     char *m[MAXMATCHES];
  635.     char  *s1, *s2;
  636.     int i,j, slen;
  637.  
  638.     if ( (slen = strlen(src)) == 0 ) return(NULL);  
  639.  
  640.     for(i=0, j=0; vec[i] != NULL && j < MAXMATCHES; i++) {
  641.         if  ( strcmp(src,vec[i]) == 0 ) {   /* exact match! */
  642.         m[0] = vec[i];  j = 1;
  643.         break;
  644.         }
  645.         else if ( strncmp(src,vec[i],slen) == 0   && 
  646.              strlen(vec[i]) >= slen )
  647.                    m[j++] = vec[i];
  648.     }
  649.     m[j] = NULL;
  650.     for(i=0; i <= j; mx[i] = m[i], i++);
  651.     return(j);
  652.   }
  653.  
  654.  /* *****************************************************************
  655.   * fkoff: fork a process and return the exit status of the process.
  656.   *
  657.   *    This routine takes a command line separated into words, and
  658.   *  uses vfork(2) and execve(2) to quickly run the program.
  659.   *
  660.   *  This is a simplified version of the fkoff() routine from dcon(8).
  661.   *  Here, a program name and up to four arguments may be passed in.
  662.   *  If one of them is null, FINE, but the fifth 
  663.   *
  664.   */
  665.  
  666.   fkoff(prg,arg1,arg2,arg3,arg4)
  667.     char *prg, *arg1, *arg2, *arg3, *arg4;
  668.  
  669.   {
  670.     char *command, *malloc(), *argvec[6];
  671.     int pid, stat, i;
  672.  
  673.     for(i=0; i < 6; argvec[i++] = NULL);
  674.     argvec[0] = prg;
  675.     argvec[1] = arg1;
  676.     argvec[2] = arg2;
  677.     argvec[3] = arg3;
  678.     argvec[4] = arg4;
  679.     if (argvec[0] == NULL) return(-1);
  680.     command = malloc( strlen(argvec[0]) + 1);
  681.     strcpy(command,argvec[0]);
  682.  
  683.     Set_Tc_Init( (stdin->_file) ) ;
  684. #ifdef USG
  685.     pid = fork();
  686. #else
  687.     pid = vfork();    /* fork 2 copies of us */
  688. #endif
  689.     if (pid < 0 )  {
  690.             fflush(stdout);
  691.         perror("help: fork");
  692.         Set_Tc_Here( (stdin->_file) ) ; 
  693.         return(0);
  694.     }
  695.     else if (pid == 0)  { /* we are child, execve the program. */
  696.            pid = execve(command,argvec,environ);
  697.         _exit(1);   
  698.     }
  699.     else {          /* we are parent, wait for child */
  700.         pid = wait(&stat);
  701.         Set_Tc_Here( (stdin->_file) ) ;
  702.         if (pid == -1) return(pid);
  703.         stat = stat / 0400;   /* get hi byte of status */
  704.         return(stat);
  705.         }
  706.   }
  707.  
  708. /* **************************************************************
  709.  * makewvec: make a vector of words, up to N of them
  710.  *
  711.  *    This routine uses index to simply parse a string
  712.  *  made up of (possibly) several blank-separated words.
  713.  *  The words are stored into the slots of a vector, as pointers
  714.  *  into the original string.  Therefore, the original string
  715.  *  is destroyed.
  716.  */
  717.  makewvec(sstr, rvec, veccnt)
  718.      char *sstr;
  719.      char *rvec[];
  720.      int veccnt;
  721. {
  722.     int mcnt = 0;
  723.     int done = 0;
  724.     int i,j;
  725.     char *s1, *s2, *index();
  726.  
  727.     if (strlen(sstr) == 0) {
  728.     rvec[0] = NULL;
  729.     }
  730.     else {
  731.     /* skip leading whitespace */
  732.     for(s1=sstr; iswhite(*s1); s1++);
  733.     for(mcnt = 0, done = 0; !done; mcnt++) {
  734.         s2 = index(s1,' ');
  735.         if (s2 == NULL) s2 = index(s1,'    ');
  736.         if (s2 == NULL) s2 = index(s1,'\n');
  737.         if (s2 != NULL) *s2 = '\0';
  738.         rvec[mcnt] = s1;
  739.         if (mcnt + 1 >= veccnt) break;
  740.         if (s2 == NULL) done = 1;
  741.         else {
  742.         /* skip more white space */
  743.         for(s1 = s2+1; iswhite(*s1); s1++);
  744.         if (*s1 == '\0') done = 1;
  745.         }
  746.     }
  747.     rvec[mcnt] = NULL;
  748.     }
  749.  
  750.     return(mcnt);
  751. }
  752.  
  753.  
  754.  /* ***************************************************************
  755.   * xref_matchv: return all cross-ref matches for a topic
  756.   *
  757.   *    This routine accepts a string and a vector of strings. 
  758.   *  The string is supposed to be a referal to one or more
  759.   *  cross-reference file names in the vector.  The matches are
  760.   *  placed in a another vector (which is in a static area) and
  761.   *  a pointer to that vector returned.  Note that the input
  762.   *  vector of pointers must have NULL as its terminating element.
  763.   *  The output vector will also terminate with NULL.
  764.   *    NOTE: The vector returned contains pointers into the input
  765.   *  vector.  Do not, therefore, mess with the contents of the output
  766.   *  vector.  
  767.   *     NOTE: cross-ref file names in the topix vector begin with 
  768.   *  the flag char '@' and still have their extension of .XREF.
  769.   *
  770.   *  If 0 is returned, nothing matched, or there was some other error.
  771.   */
  772.  
  773.   xref_matchv( src, vec, mx)
  774.     char *src;
  775.     char *vec[];
  776.     char *mx[];
  777.   {
  778.     char *m[MAXMATCHES];
  779.     char  *s1, *s2;
  780.     char  one_ref[MAXNAMELEN];
  781.     int i,j, slen;
  782.  
  783.     if ( (slen = strlen(src)) == 0 ) return(NULL);  
  784.  
  785.     for(i=0, j=0; vec[i] != NULL && j < MAXMATCHES; i++) {
  786.         if ( *vec[i] != '@' ) break;
  787.         for(s1 = vec[i], s2 = one_ref; 
  788.         *s1 != '\0' && *s1 != '.';
  789.         *s2++ = *s1++);
  790.         *s2 = '\0';
  791.         if ( strcmp(src,one_ref) == 0) {    /* exact match! */
  792.         m[0] = vec[i]; j = 1;
  793.         break;
  794.         }
  795.         else if
  796.           (strncmp(src,(vec[i] + 1),slen) == 0 && strlen(vec[i]) >= slen)
  797.               m[j++] = vec[i];
  798.         }
  799.     m[j] = NULL;
  800.     for(i=0; i <= j; mx[i] = m[i], i++);
  801.     return(j);
  802.   }
  803.  
  804.  
  805. /* **************************************************************
  806.  * do_xref: given a vector of cross-reference filenames, do them
  807.  *
  808.  *     This routine follows a set of cross references.  
  809.  *   Technically, there are two kinds of cross-reference files, 
  810.  *   distinguished by the first character of their contents.
  811.  *   If the first character is an at sign (@) the file is a 
  812.  *   ``direct'' cross-reference and the rest of the line starting
  813.  *   with the @ sign is a vector of words that give an ABSOLUTE
  814.  *   help path.  If the first char is not @ then the file is an
  815.  *   ``apologetic'' cross-reference, and the text in it is a 
  816.  *   lame-brained excuse for why the topic in question is not
  817.  *   documented in help.
  818.  *
  819.  *      For each filename in the vector, the following actions 
  820.  *   are performed.
  821.  *
  822.  *        1. check that the file exists and is readable, by
  823.  *           opening it for reading.
  824.  *        2. If this is not the first xref, ask the user if
  825.  *           he wants to look at this one, if so, proceed to 
  826.  *        3. Read the first line of the file and check it.
  827.  *               a. if first char is not '@', do a more(1)
  828.  *                  on the file with fkoff.
  829.  *               b. otherwise, read the first line, and
  830.  *                  use makewvec to parse it into words.
  831.  *                  pass these words to a new invokation
  832.  *                  of help1.  Make the ppt for help1
  833.  *                  be something that denotes a cross-ref.
  834.  */
  835.  do_xref(ppt,src,mx)
  836.      char *ppt, *src;
  837.      char *mx[];
  838. {
  839.     int i,j,k;
  840.     char one_ref[MAXNAMELEN];
  841.     char *s1, *index();
  842.  
  843.     for(i=0; mx[i] != NULL; i++) {
  844.     strcpy(one_ref,mx[i]+1);
  845.     if (i > 0) {
  846.         j = takeit("\nNext %s %s cross-reference: %s\nTake it?",
  847.                ppt,src,one_ref);
  848.         if (j == 1) continue;
  849.         if (j == -1) break;
  850.     }
  851.     do_one_xref(one_ref,src,ppt);
  852.     }
  853.  
  854.     putchar('\n');
  855.     return(0);
  856. }
  857.  
  858.     
  859. /* **************************************************************
  860.  * do_one_xref: evaluate and do a single cross-reference
  861.  *
  862.  *      This routine accepts a single file name and src name
  863.  *   for a single cross-reference.  It then checks the file
  864.  *   for type, and performs the appropriate action.
  865.  *
  866.  *      If the file is not accessible, an error message is 
  867.  *   printed.
  868.  */
  869.  do_one_xref(xref_file,src,ppt)
  870.      char *xref_file, *src, *ppt;
  871. {
  872.     int i,j;
  873.     FILE *xr_fp, *fopen();
  874.     char *wvec[MAXMATCHES];
  875.     char *index(), *s1, lbuf[MAXLINELEN], xbuf[MAXNAMELEN+15];
  876.  
  877.     if ( (xr_fp = fopen(xref_file,"r")) == NULL) {
  878.     printf(" Sorry, %s cross-reference file inaccessible.\n",xref_file);
  879.     return(-1);
  880.     }
  881.  
  882.     if (fgets(lbuf,MAXLINELEN,xr_fp) != NULL) {
  883.     if ( *lbuf != '@' ) {
  884.         s1 = index(xref_file,'.');
  885.         if (s1 != NULL) *s1 = '\0';
  886.         printf(" Cross reference text called \"%s\" is available\n",
  887.            xref_file);
  888.         if (ppt != NULL)
  889.           printf("\n HELP: %s <<%s>>\n",ppt,xref_file);
  890.         if (s1 != NULL) *s1 = '.';
  891.         fclose(xr_fp);
  892.         fkoff(VIEWPROGRAM,VIEWPROGOPTS1,VIEWPROGOPTS2,xref_file,NULL);
  893.     }
  894.     else {
  895.         char pptbuf[MAXNAMELEN+13], pathbuf[256];
  896.         makewvec(lbuf+1,wvec,MAXMATCHES);
  897.         printf("\n following cross-reference `help");
  898.         for(i=0; wvec[i] != NULL; i++) printf(" %s",wvec[i]);
  899.         s1 = index(xref_file,'.');
  900.         if (s1 != NULL) *s1 = '\0';
  901.         printf("' to find help for %s\n\n",xref_file);
  902.         sprintf(pptbuf,"<<%s>> ",xref_file);
  903.         if (s1 != NULL) *s1  = '.';
  904.         getwd(pathbuf);
  905.         chdir(helpdir);
  906.         help1(pptbuf,wvec,1,1, 0);
  907.         chdir(pathbuf);
  908.         putchar('\n');
  909.     }
  910.     }
  911.     else {
  912.     printf(" Sorry, cross-ref file %s seems to be empty!\n\n",xref_file);
  913.     }
  914.     return(0);
  915. }
  916.  
  917.  
  918. /* **************************************************************
  919.  * printlist - print a list of strings in columnar or list form
  920.  *
  921.  *     This routine takes a list of names in a vector and prints
  922.  *   the list according to the current help mode.  This code
  923.  *   used to be in printtopix() but I decided to move it here
  924.  *   so it could be used for other things.
  925.  */
  926. printlist(vec,namecnt)
  927.      char *vec[];
  928.      int namecnt;
  929. {
  930.     int i,j,k;
  931.     int longlen, namewidth, rowcnt, colcnt, xrefs;
  932.     char *s1, *s2;
  933.     static char row[TERMWID+1];
  934.  
  935.     longlen = xrefs = 0;
  936.     for(i=0; i < namecnt; i++ ) {
  937.             if (*vec[i] == '@') xrefs++;
  938.         else
  939.           longlen = ((k=strlen(vec[i]))>longlen)?k:longlen;
  940.     }
  941.  
  942.     /* here print the names out in nice columns */
  943.     namewidth = longlen + COLUMNSPACE;
  944.     rowcnt = TERMWID / namewidth;
  945.     colcnt = (namecnt + (rowcnt-1)) / rowcnt ;
  946.     if (colcnt <= 0) colcnt = 1;
  947.  
  948.     if (col_flag && rowcnt >= 1) {
  949.             printf("\n");
  950.         for(i=0; i < colcnt ; i++ ) {
  951.             for(k=0; k < TERMWID; row[k++] = ' ');
  952.             row[k] = '\0';
  953.             for(j=0, s1 = row; 
  954.                     (i+j+xrefs) < namecnt; 
  955.                     j += colcnt) {
  956.                 row[strlen(row)] = ' ';
  957.                 strcpy(s1,vec[i+j+xrefs]);
  958.                 s1 = s1 + namewidth;
  959.             }
  960.             printf("    %s\n",row);
  961.         }
  962.         printf("\n");
  963.     }
  964.     else {
  965.         for(i=xrefs; i < namecnt; i++)
  966.             printf("%s\n",vec[i]);
  967.      }
  968.  
  969. }
  970.  
  971. #ifdef BSD
  972.  
  973. /* ****************  funky keyboard stuff below here  ************* */
  974.  
  975. /* **************************************************************
  976.  * pushback - push a string back onto the tty input queue
  977.  *
  978.  *     This routine accepts a string and pushes it onto the tty
  979.  *   input queue, as if the user had typed it.
  980.  *   The input is a file descriptor that points to a tty, and
  981.  *   a pointer to the string.
  982.  */
  983. pushback(fd, str)
  984.      int fd;
  985.      char *str;
  986. {
  987.     char *s1;
  988.  
  989.     for(s1=str; *s1; ioctl( fd, TIOCSTI, s1++));
  990. }
  991.  
  992.  
  993. /* **************************************************************
  994.  * getpending - get pending chars from tty, then push them back
  995.  *
  996.  *     This routine grabs chars pending on a specified file
  997.  *   descriptor, puts them into the specified buffer, and pushes
  998.  *   them back onto the tty input queue.
  999.  *   The buffer passed by the caller had BETTER be long enough,
  1000.  *   or everything gets munched.
  1001.  *   If the parameter no_pushback is non-zero, the characters are
  1002.  *   not pushed back.
  1003.  *   The number of characters obtained is returned.
  1004.  */
  1005. getpending(fd, buf, buflen, no_pushback)
  1006.      int fd;
  1007.      char *buf;
  1008.      int buflen, no_pushback;
  1009. {
  1010.     int i,j;
  1011.     char x = '\n';
  1012.  
  1013.     ioctl(fd, FIONREAD, &i);
  1014.     if (i <= 0) return(0);
  1015.     j = read(fd, buf, buflen);
  1016.     buf[j-1] = '\0';
  1017.     for(i = j-2; buf[i] > '\030' ; buf[i--] = '\0' );
  1018.     if (i <= 0) return(0);
  1019.     if ( !no_pushback) pushback(fd, buf);
  1020.     return(i);
  1021. }
  1022.  
  1023.  
  1024. /* **************************************************************
  1025.  * init_funky - initialize funky stuff, and save startup values
  1026.  *
  1027.  *         This routine starts up breaking for ESC, and
  1028.  *   saves the initial tchars structure.
  1029.  */
  1030. init_funky()
  1031. {
  1032.     char *malloc();
  1033.  
  1034.     if ( isatty(0) ) {
  1035.     is_tty = 1;
  1036.     init_tchars = (struct tchars *) malloc( sizeof(struct tchars));
  1037.     here_tchars = (struct tchars *) malloc( sizeof(struct tchars));
  1038.     ioctl(0,TIOCGETC,init_tchars);
  1039.     ioctl(0,TIOCGETC,here_tchars);
  1040.     here_tchars->t_brkc = '\033';
  1041.     ioctl(0,TIOCSETC,here_tchars);
  1042.     }
  1043.     
  1044.     return;
  1045. }
  1046.  
  1047.  
  1048. /* **************************************************************
  1049.  * read_gets: do a gets by read(2)ing a line
  1050.  *
  1051.  *       Read a string into a buffer, from the given file
  1052.  *    descriptor.  There are four arguments:
  1053.  *
  1054.  *        fd    file descriptor (usually 0)  
  1055.  *          buf     char * to buffer
  1056.  *         blen    buffer length in bytes
  1057.  *        spec    do special ^[ and ^D processing
  1058.  *
  1059.  *    If the special processing is enabled, ESC invokes helpname
  1060.  *    completion, and ^D invokes possible completion listing.
  1061.  *    When the user finally inputs a line that does not require
  1062.  *    special processing, it is returned as a string to caller.
  1063.  *    If anything goes wrong, or a true EOF is given, -1 is
  1064.  *    returned.
  1065.  *    When special processing is not enabled, the input line is 
  1066.  *    simply returned to the caller as a string.
  1067.  */
  1068. read_gets(fd, buf, blen, do_special)
  1069.      int fd, blen, do_special;
  1070.      char *buf;
  1071. {
  1072.     char *s1, *s2, endc;
  1073.     static  char bufcopy[MAXLINELEN];
  1074.     static  char *wvec[MAXMATCHES];
  1075.     int i,j,k;
  1076.  
  1077.  
  1078.     for(endc = '\0'; endc != LF ; ) {
  1079.     fflush(stdout);
  1080.     i = read(fd, buf, blen);
  1081.  
  1082.     /* now, having read data, analyze it for special stuff */
  1083.     if (i <= 0) return(-1);
  1084.     else {
  1085.         s1 = buf + (i - 1);
  1086.         if (*s1 == ESC || *s1 == LF || *s1 == RET) {
  1087.         endc = (*s1 == ESC)?(*s1):(LF);
  1088.         *s1 = '\0';
  1089.         }
  1090.         else {
  1091.         *++s1 = '\0';
  1092.         endc = *s1;
  1093.         }
  1094.         for(s1 = buf + (strlen(buf) - 1);
  1095.         *s1 == ' '  ||   *s1 == '    ';
  1096.         *s1-- = '\0');
  1097.         for(s1 = buf; *s1 == ' ' || *s1 == '    '; s1++);
  1098.  
  1099.         if (endc == LF || endc == RET || do_special == 0) return(i-1);
  1100.         else {
  1101.         *gbl_match = '\0';
  1102.  
  1103.         /* if this is a non-topic command, just beep */
  1104.         if ( index( SPEC_CHARS, *s1) ) {
  1105.             fputs("  ",stdout);
  1106.             for(s1 = buf; *s1; s1++) fputs("",stdout);
  1107.             fflush(stdout);
  1108.             pushback(stdin->_file,buf);
  1109.             continue;
  1110.         }
  1111.         else
  1112.         /* do special stuff here */
  1113.         if (endc == ESC) {
  1114.             strcpy(bufcopy,buf);
  1115.             i = makewvec(bufcopy, wvec, MAXMATCHES);
  1116.             help1("", wvec, 0, 0, ESC_COMP);
  1117.             fputs("  ",stdout);
  1118.             for(s1 = buf; *s1; s1++) fputs("",stdout);
  1119.             fflush(stdout);
  1120.             pushback(stdin->_file,buf);
  1121.             if ( *gbl_match ) {
  1122.             pushback(stdin->_file,(gbl_match + strlen(wvec[i-1])));
  1123.             }
  1124.             else fputs("",stdout);
  1125.             fflush(stdout);
  1126.         }
  1127.         else
  1128.           if (endc == '\0') {   /* must have been eof */
  1129.               strcpy(bufcopy,buf);
  1130.               i = makewvec(bufcopy, wvec, MAXMATCHES);
  1131.               help1("" , wvec, 0, 0, CTRLD_COMP);
  1132.               printf("\n%s ",gbl_ppt);
  1133.               if (*gbl_match == '\0') fputs("", stdout);
  1134.               fflush(stdout);
  1135.               pushback(stdin->_file,buf);
  1136.  
  1137.           }
  1138.         }
  1139.     }
  1140.     }
  1141. }
  1142.         
  1143.  
  1144.  
  1145. #endif 
  1146.