home *** CD-ROM | disk | FTP | other *** search
/ CICA 1995 May / cica_0595_4.zip / cica_0595_4 / UTIL / GPT34SRC / HELP.C < prev    next >
C/C++ Source or Header  |  1993-05-25  |  20KB  |  730 lines

  1. #ifndef lint
  2. static char *RCSid = "$Id: help.c%v 3.38.2.81 1993/02/24 02:29:34 woo Exp woo $";
  3. #endif
  4.  
  5.  
  6. /* GNUPLOT - help.c */
  7. /*
  8.  * Copyright (C) 1986 - 1993   Thomas Williams, Colin Kelley
  9.  *
  10.  * Permission to use, copy, and distribute this software and its
  11.  * documentation for any purpose with or without fee is hereby granted, 
  12.  * provided that the above copyright notice appear in all copies and 
  13.  * that both that copyright notice and this permission notice appear 
  14.  * in supporting documentation.
  15.  *
  16.  * Permission to modify the software is granted, but not the right to
  17.  * distribute the modified code.  Modifications are to be distributed 
  18.  * as patches to released version.
  19.  *  
  20.  * This software is provided "as is" without express or implied warranty.
  21.  * 
  22.  *
  23.  * AUTHORS
  24.  * 
  25.  *   Original Software:
  26.  *     Thomas Williams,  Colin Kelley.
  27.  * 
  28.  *   Gnuplot 2.0 additions:
  29.  *       Russell Lang, Dave Kotz, John Campbell.
  30.  *
  31.  *   Gnuplot 3.0 additions:
  32.  *       Gershon Elber and many others.
  33.  * 
  34.  * There is a mailing list for gnuplot users. Note, however, that the
  35.  * newsgroup 
  36.  *     comp.graphics.gnuplot 
  37.  * is identical to the mailing list (they
  38.  * both carry the same set of messages). We prefer that you read the
  39.  * messages through that newsgroup, to subscribing to the mailing list.
  40.  * (If you can read that newsgroup, and are already on the mailing list,
  41.  * please send a message info-gnuplot-request@dartmouth.edu, asking to be
  42.  * removed from the mailing list.)
  43.  *
  44.  * The address for mailing to list members is
  45.  *       info-gnuplot@dartmouth.edu
  46.  * and for mailing administrative requests is 
  47.  *       info-gnuplot-request@dartmouth.edu
  48.  * The mailing list for bug reports is 
  49.  *       bug-gnuplot@dartmouth.edu
  50.  * The list of those interested in beta-test versions is
  51.  *       info-gnuplot-beta@dartmouth.edu
  52.  */
  53.  
  54. #include <stdio.h>
  55. #if defined (ATARI)
  56. #include "plot.h"
  57. #else
  58.  
  59. extern int strcmp();
  60. extern int strlen();
  61. extern char *strcpy();
  62. extern char *strncpy();
  63. extern char *strcat();
  64. extern char *strncat();
  65. extern char *getenv();
  66. extern FILE *fopen();
  67. extern char *malloc();
  68. #endif
  69.  
  70. extern int errno;
  71.  
  72. extern int instring();
  73.  
  74. #define    SAME    0    /* for strcmp() */
  75.  
  76. #include "help.h"    /* values passed back */
  77.  
  78. #if defined(__EMX__) || defined(DJGPP) || defined(DOS386)
  79. #ifdef MSDOS
  80. #undef MSDOS    /* we have plenty of memory under __EMX__ or DJGPP */
  81. #endif
  82. #ifdef unix
  83. #undef unix    /* we are not unix */
  84. #endif
  85. #endif
  86.  
  87. #ifdef OS2
  88.   /* GCC defines unix, but no PAGER, so... */
  89. #ifdef unix
  90. #undef unix
  91. #endif
  92. #endif  /* OS2 */
  93.  
  94. /* help -- help subsystem that understands defined keywords
  95. **
  96. ** Looks for the desired keyword in the help file at runtime, so you
  97. ** can give extra help or supply local customizations by merely editing
  98. ** the help file.
  99. **
  100. ** The original (single-file) idea and algorithm is by John D. Johnson,
  101. ** Hewlett-Packard Company.  Thanx and a tip of the Hatlo hat!
  102. **
  103. ** Much extension by David Kotz for use in gnutex, and then in gnuplot.
  104. ** Added output paging support, both unix and builtin. Rewrote completely
  105. ** to read helpfile into memory, avoiding reread of help file. 12/89.
  106. **
  107. ** Modified by Russell Lang to avoid reading completely into memory
  108. ** if MSDOS defined.  This uses much less memory.  6/91
  109. **
  110. ** The help file looks like this (the question marks are really in column 1):
  111. **
  112. **     ?topic
  113. **     This line is printed when the user wants help on "topic".
  114. **     ?keyword
  115. **     ?Keyword
  116. **     ?KEYWORD
  117. **     These lines will be printed on the screen if the user wanted
  118. **     help on "keyword", "Keyword", or "KEYWORD".  No casefolding is
  119. **    done on the keywords.
  120. **     ?subject
  121. **     ?alias
  122. **     This line is printed for help on "subject" and "alias".
  123. **     ?
  124. **    ??
  125. **     Since there is a null keyword for this line, this section
  126. **     is printed when the user wants general help (when a help
  127. **     keyword isn't given).  A command summary is usually here.
  128. **    Notice that the null keyword is equivalent to a "?" keyword
  129. **    here, because of the '?' and '??' topic lines above.
  130. **   If multiple keywords are given, the first is considered the 
  131. **   'primary' keyword. This affects a listing of available topics.
  132. **     ?last-subject
  133. **     Note that help sections are terminated by the start of the next
  134. **     '?' entry or by EOF.  So you can't have a leading '?' on a line
  135. **     of any help section.  You can re-define the magic character to
  136. **    recognize in column 1, though, if '?' is too useful.  (Try ^A.)
  137. */
  138.  
  139. #define    KEYFLAG    '?'    /* leading char in help file topic lines */
  140.  
  141. /*
  142. ** Calling sequence:
  143. **    int result;        # 0 == success
  144. **    char *keyword;        # topic to give help on
  145. **    char *pathname;        # path of help file
  146. **      int subtopics;        # set to TRUE if only subtopics to be listed
  147. **                # returns TRUE if subtopics were found
  148. **    result = help(keyword, pathname, &subtopics);
  149. ** Sample:
  150. **    cmd = "search\n";
  151. **    helpfile = "/usr/local/lib/program/program.help";
  152. **    subtopics = FALSE;
  153. **    if (help(cmd, helpfile, &subtopics) != H_FOUND)
  154. **        printf("Sorry, no help for %s", cmd);
  155. **
  156. **
  157. ** Speed this up by replacing the stdio calls with open/close/read/write.
  158. */
  159. #ifdef    WDLEN
  160. #  define    PATHSIZE    WDLEN
  161. #else
  162. #  define    PATHSIZE    BUFSIZ
  163. #endif
  164.  
  165. typedef int boolean;
  166. #ifndef TRUE
  167. #define TRUE (1)
  168. #define FALSE (0)
  169. #endif
  170.  
  171. typedef struct line_s LINEBUF;
  172. struct line_s {
  173.     char *line;            /* the text of this line */
  174.     LINEBUF *next;            /* the next line */
  175. };
  176.  
  177. typedef struct linkey_s LINKEY;
  178. struct linkey_s {
  179.     char *key;                /* the name of this key */
  180.     long pos;                /* ftell position */
  181.     LINEBUF *text;            /* the text for this key */
  182.     boolean primary;        /* TRUE -> is a primary name for a text block */
  183.     LINKEY *next;            /* the next key in linked list */
  184. };
  185.  
  186. typedef struct key_s KEY;
  187. struct key_s {
  188.     char *key;                /* the name of this key */
  189.     long pos;                /* ftell position */
  190.     LINEBUF *text;            /* the text for this key */
  191.     boolean primary;        /* TRUE -> is a primary name for a text block */
  192. };
  193. static LINKEY *keylist = NULL;    /* linked list of keys */
  194. static KEY *keys = NULL;        /* array of keys */
  195. static int keycount = 0;        /* number of keys */
  196. static FILE *helpfp = NULL;
  197.  
  198. static int LoadHelp();
  199. static void sortkeys();
  200. static int keycomp();
  201. static LINEBUF *storeline();
  202. static LINKEY *storekey();
  203. static KEY *FindHelp();
  204. static boolean Ambiguous();
  205.  
  206. /* Help output */
  207. static void PrintHelp();
  208. static void ShowSubtopics();
  209. static void StartOutput();
  210. static void OutLine();
  211. static void EndOutput();
  212. static FILE *outfile;        /* for unix pager, if any */
  213. static int pagelines;        /* count for builtin pager */
  214. #define SCREENSIZE 24        /* lines on screen (most have at least 24) */
  215.  
  216. /* help:
  217.  * print a help message 
  218.  * also print available subtopics, if subtopics is TRUE
  219.  */
  220. help(keyword, path, subtopics)
  221.     char *keyword;        /* on this topic */
  222.     char *path;            /* from this file */
  223.     boolean *subtopics;    /* (in) - subtopics only? */
  224.                         /* (out) - are there subtopics? */
  225. {
  226.     static char oldpath[PATHSIZE] = "";    /* previous help file */
  227.     int status;            /* result of LoadHelp */
  228.     KEY *key;            /* key that matches keyword */
  229.  
  230.     /*
  231.     ** Load the help file if necessary (say, first time we enter this routine,
  232.     ** or if the help file changes from the last time we were called).
  233.     ** Also may occur if in-memory copy was freed.
  234.     ** Calling routine may access errno to determine cause of H_ERROR.
  235.     */
  236.     errno = 0;
  237.     if (strncmp(oldpath, path, PATHSIZE) != SAME)
  238.      FreeHelp();
  239.     if (keys == NULL) {
  240.        status = LoadHelp(path);
  241.        if (status == H_ERROR)
  242.         return(status);
  243.  
  244.        /* save the new path in oldpath */
  245.        if (strlen(path) < PATHSIZE)
  246.         (void) strcpy(oldpath, path);
  247.        else {                /* not enough room in oldpath, sigh */
  248.           (void) strncpy(oldpath, path, PATHSIZE - 1);
  249.           oldpath[PATHSIZE - 1] = '\0';
  250.        }
  251.     }
  252.  
  253.     /* look for the keyword in the help file */
  254.     key = FindHelp(keyword);
  255.     if (key != NULL) {
  256.        /* found the keyword: print help and return */
  257.        PrintHelp(key, subtopics);
  258.        status = H_FOUND;
  259.     } else {
  260.        status = H_NOTFOUND;
  261.     }
  262.  
  263.     return(status);
  264. }
  265.  
  266. /* we only read the file once, into memory
  267.  * except for MSDOS when we don't read all the file -
  268.  * just the keys and location of the text
  269.  */
  270. static int
  271. LoadHelp(path)
  272.     char *path;
  273. {
  274.     LINKEY *key;            /* this key */
  275.     long pos;                /* ftell location within help file */
  276.     char buf[BUFSIZ];        /* line from help file */
  277.     LINEBUF *head;            /* head of text list  */
  278.     LINEBUF *firsthead = NULL;
  279.     boolean primary;        /* first ? line of a set is primary */
  280.     boolean flag;
  281.  
  282.     if ((helpfp = fopen(path, "r")) == NULL) {
  283.        /* can't open help file, so error exit */
  284.        return (H_ERROR);
  285.     }
  286.  
  287.     /*
  288.     ** The help file is open.  Look in there for the keyword.
  289.     */
  290.     (void) fgets(buf, BUFSIZ - 1, helpfp);
  291.     while (!feof(helpfp)) {
  292.        /*
  293.         ** Make an entry for each synonym keyword
  294.         */
  295.        primary = TRUE;
  296.        while (buf[0] == KEYFLAG) {
  297.           key = storekey(buf+1);    /* store this key */
  298.           key->primary = primary;
  299.           key->text = NULL;            /* fill in with real value later */
  300.           key->pos = 0;                /* fill in with real value later */
  301.           primary = FALSE;
  302.           pos = ftell(helpfp);
  303.           if (fgets(buf, BUFSIZ - 1, helpfp) == (char *)NULL)
  304.             break;
  305.        }
  306.        /*
  307.         ** Now store the text for this entry.
  308.         ** buf already contains the first line of text.
  309.         */
  310. #ifndef MSDOS
  311.        firsthead = storeline(buf);
  312.        head = firsthead;
  313. #endif
  314.        while ( (fgets(buf, BUFSIZ - 1, helpfp) != (char *)NULL)
  315.         && (buf[0] != KEYFLAG) ){
  316. #ifndef MSDOS
  317.           /* save text line */
  318.           head->next = storeline(buf);
  319.           head = head->next;
  320. #endif
  321.        }
  322.        /* make each synonym key point to the same text */
  323.        do {
  324.           key->pos = pos;
  325.           key->text = firsthead;
  326.           flag = key->primary;
  327.           key = key->next;
  328.        } while ( flag!=TRUE  &&  key!=NULL );
  329.     }
  330. #ifndef MSDOS
  331.     (void) fclose(helpfp);
  332. #endif
  333.  
  334.     /* we sort the keys so we can use binary search later */
  335.     sortkeys();
  336.     return(H_FOUND); /* ok */
  337. }
  338.  
  339. /* make a new line buffer and save this string there */
  340. static LINEBUF *
  341. storeline(text)
  342.     char *text;
  343. {
  344.     LINEBUF *new;
  345.  
  346.     new = (LINEBUF *)malloc(sizeof(LINEBUF));
  347.     if (new == NULL)
  348.      int_error("not enough memory to store help file", -1);
  349.     if (text != NULL) {
  350.        new->line = (char *) malloc((unsigned int)(strlen(text)+1));
  351.        if (new->line == NULL)
  352.         int_error("not enough memory to store help file", -1);
  353.        (void) strcpy(new->line, text);
  354.     } else
  355.      new->line = NULL;
  356.  
  357.     new->next = NULL;
  358.  
  359.     return(new);
  360. }
  361.  
  362. /* Add this keyword to the keys list, with the given text */
  363. static LINKEY *
  364. storekey(key)
  365.     char *key;
  366. {
  367.     LINKEY *new;
  368.  
  369.     key[strlen(key)-1] = '\0'; /* cut off \n  */
  370.  
  371.     new = (LINKEY *)malloc(sizeof(LINKEY));
  372.     if (new == NULL)
  373.      int_error("not enough memory to store help file", -1);
  374.     new->key = (char *) malloc((unsigned int)(strlen(key)+1));
  375.     if (new->key == NULL)
  376.      int_error("not enough memory to store help file", -1);
  377.     (void) strcpy(new->key, key);
  378.  
  379.     /* add to front of list */
  380.     new->next = keylist;
  381.     keylist = new;
  382.     keycount++;
  383.     return(new);
  384. }
  385.  
  386. /* we sort the keys so we can use binary search later */
  387. /* We have a linked list of keys and the number.
  388.  * to sort them we need an array, so we reform them into an array,
  389.  * and then throw away the list.
  390.  */
  391. static void
  392. sortkeys()
  393. {
  394.     LINKEY *p,*n;            /* pointers to linked list */
  395.     int i;                /* index into key array */
  396.     
  397.     /* allocate the array */
  398.     keys = (KEY *)malloc((unsigned int)((keycount+1) * sizeof(KEY)));
  399.     if (keys == NULL)
  400.      int_error("not enough memory to store help file", -1);
  401.     
  402.     /* copy info from list to array, freeing list */
  403.     for (p = keylist, i = 0; p != NULL; p = n, i++) {
  404.        keys[i].key = p->key;
  405.        keys[i].pos = p->pos;
  406.        keys[i].text = p->text;
  407.        keys[i].primary = p->primary;
  408.        n = p->next;
  409.        free( (char *)p );
  410.     }
  411.  
  412.     /* a null entry to terminate subtopic searches */
  413.     keys[keycount].key = NULL;
  414.     keys[keycount].pos = 0;
  415.     keys[keycount].text = NULL;
  416.  
  417.     /* sort the array */
  418.     /* note that it only moves objects of size (two pointers + long + int) */
  419.     /* it moves no strings */
  420.     qsort((char *)keys, keycount, sizeof(KEY), keycomp);
  421. }
  422.  
  423. static int
  424. keycomp(a, b)
  425.     KEY *a,*b;
  426. {
  427.     return (strcmp(a->key, b->key));
  428. }
  429.  
  430. /* Free the help file from memory. */
  431. /* May be called externally if space is needed */
  432. void
  433. FreeHelp()
  434. {
  435.     int i;                /* index into keys[] */
  436.     LINEBUF *t, *next;
  437.  
  438.     if (keys == NULL)
  439.      return;
  440.  
  441.     for (i = 0; i < keycount; i++) {
  442.        free( (char *)keys[i].key );
  443.        if (keys[i].primary)   /* only try to release text once! */
  444.        for (t = keys[i].text; t != NULL; t = next) {
  445.           free( (char *)t->line );
  446.           next = t->next;
  447.           free( (char *)t );
  448.        }
  449.     }
  450.     free( (char *)keys );
  451.     keys = NULL;
  452.     keycount = 0;
  453. #ifdef MSDOS
  454.     (void) fclose(helpfp);
  455. #endif
  456. }
  457.  
  458. /* FindHelp:
  459.  *  Find the key that matches the keyword.
  460.  *  The keys[] array is sorted by key.
  461.  *  We could use a binary search, but a linear search will aid our
  462.  *  attempt to allow abbreviations. We search for the first thing that
  463.  *  matches all the text we're given. If not an exact match, then
  464.  *  it is an abbreviated match, and there must be no other abbreviated
  465.  *  matches -- for if there are, the abbreviation is ambiguous. 
  466.  *  We print the ambiguous matches in that case, and return not found.
  467.  */
  468. static KEY *                /* NULL if not found */
  469. FindHelp(keyword)
  470.     char *keyword;            /* string we look for */
  471. {
  472.     KEY *key;
  473.     int len = strlen(keyword);
  474.     int compare;
  475.  
  476.     for (key = keys, compare = 1; key->key != NULL && compare > 0; key++) {
  477.        compare = strncmp(keyword, key->key, len);
  478.        if (compare == 0)    /* we have a match! */
  479.         if (!Ambiguous(key, len)) {
  480.             /* non-ambiguous abbreviation */
  481.             (void) strcpy(keyword, key->key); /* give back the full spelling */
  482.             return(key);        /* found!! */
  483.         }
  484.     }
  485.  
  486.     /* not found, or ambiguous */
  487.     return(NULL);
  488. }
  489.  
  490. /* Ambiguous:
  491.  * Check the key for ambiguity up to the given length.
  492.  * It is ambiguous if it is not a complete string and there are other
  493.  * keys following it with the same leading substring.
  494.  */
  495. static boolean
  496. Ambiguous(key, len)
  497.     KEY *key;
  498.     int len;
  499. {
  500.     char *first;
  501.     char *prev;
  502.     boolean status = FALSE;    /* assume not ambiguous */
  503.     int compare;
  504.     int sublen;
  505.  
  506.     if (key->key[len] == '\0')
  507.      return(FALSE);
  508.     
  509.     for (prev = first = key->key, compare = 0, key++;
  510.         key->key != NULL && compare == 0; key++) {
  511.        compare = strncmp(first, key->key, len);
  512.        if (compare == 0) {
  513.           /* So this key matches the first one, up to len.
  514.            * But is it different enough from the previous one
  515.            * to bother printing it as a separate choice?
  516.            */
  517.           sublen = instring(prev+len, ' ');
  518.           if (strncmp(key->key, prev, len+sublen) != 0) {
  519.              /* yup, this is different up to the next space */
  520.              if (!status) {
  521.                 /* first one we have printed is special */
  522.                 fprintf(stderr, 
  523.                        "Ambiguous request '%.*s'; possible matches:\n",
  524.                        len, first);
  525.                 fprintf(stderr, "\t%s\n", prev);
  526.                 status = TRUE;
  527.              }
  528.              fprintf(stderr, "\t%s\n", key->key);
  529.              prev = key->key;
  530.           }
  531.        }
  532.     }
  533.     
  534.     return(status);
  535. }
  536.  
  537. /* PrintHelp:
  538.  * print the text for key
  539.  */
  540. static void
  541. PrintHelp(key, subtopics)
  542.     KEY *key;
  543.     boolean *subtopics;        /* (in) - subtopics only? */
  544.                         /* (out) - are there subtopics? */
  545. {
  546.     LINEBUF *t;
  547. #ifdef MSDOS
  548.     char buf[BUFSIZ];        /* line from help file */
  549. #endif
  550.  
  551.     StartOutput();
  552.  
  553.     if (subtopics == NULL || !*subtopics) {
  554. #ifdef MSDOS
  555.        fseek(helpfp,key->pos,0);
  556.        while ( (fgets(buf, BUFSIZ - 1, helpfp) != (char *)NULL)
  557.             && (buf[0] != KEYFLAG) ) {
  558.           OutLine(buf);
  559.        }
  560. #else
  561.        for (t = key->text; t != NULL; t = t->next)
  562.         OutLine(t->line);        /* print text line */
  563. #endif
  564.     }
  565.  
  566.     ShowSubtopics(key, subtopics);
  567.     OutLine("\n");
  568.  
  569.     EndOutput();
  570. }
  571.  
  572. /* ShowSubtopics:
  573.  *  Print a list of subtopic names
  574.  */
  575. #define PER_LINE 4
  576.  
  577. static void
  578. ShowSubtopics(key, subtopics)
  579.     KEY *key;                /* the topic */
  580.     boolean *subtopics;        /* (out) are there any subtopics */
  581. {
  582.     int subt = 0;            /* printed any subtopics yet? */
  583.     KEY *subkey;            /* subtopic key */
  584.     int len;                /* length of key name */
  585.     char line[BUFSIZ];        /* subtopic output line */
  586.     char *start;            /* position of subname in key name */
  587.     int sublen;            /* length of subname */
  588.     int pos;
  589.     char *prev = NULL;        /* the last thing we put on the list */
  590.  
  591.     *line = '\0';
  592.     len = strlen(key->key);
  593.  
  594.     for (subkey = key+1; subkey->key != NULL; subkey++) {
  595.        if (strncmp(subkey->key, key->key, len) == 0) {
  596.           /* find this subtopic name */
  597.           start = subkey->key + len;
  598.           if (len > 0)
  599.             if (*start == ' ')
  600.              start++;        /* skip space */
  601.             else
  602.              break;        /* not the same topic after all  */
  603.           else            /* here we are looking for main topics */
  604.             if (!subkey->primary)
  605.              continue;    /* not a main topic */
  606.           sublen = instring(start, ' ');
  607.           if (prev == NULL || strncmp(start, prev, sublen) != 0) {
  608.              if (subt == 0) {
  609.                 subt++;
  610.                 if (len)
  611.                   (void) sprintf(line, "\nSubtopics available for %s:\n", 
  612.                         key->key);
  613.                 else
  614.                   (void) sprintf(line, "\nHelp topics available:\n");
  615.                 OutLine(line);
  616.                 *line = '\0';
  617.                 pos = 0;
  618.              }
  619.              if (pos == PER_LINE) {
  620.                 (void) strcat(line, "\n");
  621.                 OutLine(line);
  622.                 *line = '\0';
  623.                 pos = 0;
  624.              }
  625.              /* adapted by DvdSchaaf */
  626.              {
  627. #define FIRSTCOL    6
  628. #define COLLENGTH    15
  629.                 int spacelen, ispacelen;
  630.  
  631.                  if( pos == 0 )
  632.                     spacelen = FIRSTCOL;
  633.                  for( ispacelen = 0;
  634.                     ispacelen < spacelen; ispacelen++ )
  635.                     (void) strcat(line, " ");
  636.                  /* commented out *
  637.                  (void) strcat(line, "\t");
  638.                  */
  639.                  (void) strncat(line, start, sublen);
  640.                  spacelen = COLLENGTH - sublen;
  641.                  if( spacelen <= 0 )
  642.                     spacelen = 1;
  643.              }
  644.              pos++;
  645.              prev = start;
  646.           }
  647.        } else {
  648.           /* new topic */
  649.           break;
  650.        }
  651.     }
  652.     
  653.     /* put out the last line */
  654.     if (subt > 0 && pos > 0) {
  655.        (void) strcat(line, "\n");
  656.        OutLine(line);
  657.     }
  658.     
  659. /*
  660.     if (subt == 0) {
  661.        OutLine("\n");
  662.        OutLine("No subtopics available\n");
  663.     }
  664. */
  665.     
  666.     if (subtopics)
  667.      *subtopics = (subt != 0);
  668. }
  669.  
  670.  
  671. /* StartOutput:
  672.  * Open a file pointer to a pipe to user's $PAGER, if there is one,
  673.  * otherwise use our own pager.
  674.  */
  675. static void
  676. StartOutput()
  677. {
  678. #ifdef unix
  679.     char *pager_name = getenv("PAGER");
  680.     extern FILE *popen();
  681.  
  682.     if (pager_name != NULL && *pager_name != '\0')
  683.      if ((outfile = popen(pager_name, "w")) != (FILE *)NULL)
  684.        return;            /* success */
  685.     outfile = stderr;
  686.     /* fall through to built-in pager */
  687. #endif
  688.  
  689.     /* built-in pager */
  690.     pagelines = 0;
  691. }
  692.  
  693. /* write a line of help output  */
  694. /* line should contain only one \n, at the end */
  695. static void
  696. OutLine(line)
  697.     char *line;
  698. {
  699.     int c;                /* dummy input char */
  700. #ifdef unix
  701.     if (outfile != stderr) {
  702.        fputs(line, outfile);
  703.        return;
  704.     }
  705. #endif
  706.  
  707.     /* built-in dumb pager */
  708.     /* leave room for prompt line */
  709.     if (pagelines >= SCREENSIZE - 2) {
  710.        fprintf(stderr,"Press return for more: ");
  711.        do 
  712.         c = getchar();
  713.        while (c != EOF && c != '\n');
  714.        pagelines = 0;
  715.     }
  716.     fputs(line, stderr);
  717.     pagelines++;
  718. }
  719.  
  720. static void
  721. EndOutput()
  722. {
  723. #ifdef unix
  724.     extern int pclose();
  725.  
  726.     if (outfile != stderr)
  727.      (void) pclose(outfile);
  728. #endif
  729. }
  730.