Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: utzoo!mnetor!seismo!lll-lcc!mordor!styx!ames!ucbcad!ucbvax!decvax!decwrl!mosaic.dec.com!scott
From: scott@mosaic.dec.com (Robert P. Scott)
Newsgroups: net.sources
Subject: A version of HELP for PC/MS-DOS.
Message-ID: <7388@decwrl.DEC.COM>
Date: Tue, 6-Jan-87 20:04:06 EST
Article-I.D.: decwrl.7388
Posted: Tue Jan  6 20:04:06 1987
Date-Received: Wed, 7-Jan-87 02:42:37 EST
Sender: daemon@decwrl.DEC.COM
Organization: Digital Equipment Corporation
Lines: 698


Recently, a package called GNUPLOT was posted which required a VMS-like
help utility.  Unfortunately, the help utility provided for unix would not
work under MS-DOS/PC-DOS.  What follows is a first pass at a MS-DOS version
of this utility.   It may be compiled with MSC V4.0.  My apologies for:

    1. Not "ifdef'ing" this into the original code.  My code is too dirty
       in it's present state.
    2. Not "shar'ing" this posting.  I'm on a MS-DOS machine at the moment.
    3. Not including any documentation.  I have not written any.

Please note the problem of token size due to the filename size limitation of
MS-DOS.  This will require the user to rename the GNUPLOT help files slightly
to work.  When I have time to fully test (read as debug) my version using 
lookup tables, I will post it.   

Also note that you must include an appropriate help file definition in the
plot.h file.  What I use is:

#define HELP  "\\usr\\bin\\help gnuplot"

The following file is 'help.h':
======================= Beginning of HELP.H ==============================
/*
 *    help.h: header file for the VMS-help emulator
 */
#define HELPVER         "HELP - VMS-like help utility\nVersion 1.0.014NL  -  6 January 1986\nRobert P. Scott\nDTS Engineering & Consulting\n29 Heritage Circle\nHudson, NH  03051-3427\n(603)886-1383\n"

#define STDDELIM        " \r\n\t;"          /* delimiters for parsing */
#define HELPMAIN        "HELP.HLM"          /* main help text filename */
#define MAXENT          128                 /* maximum number of help entries*/
#define HELPEX          ".HLP"              /* help extension */
#define SWITCHR1        '-'                 /* unix like switch char */
#define SWITCHR2        '/'                 /* PC/MS-DOS std switch char */
#define PROMPT          "Topic? "
#define DEFSRCH1        ".\\*.*"             /* Default search string */
#define R_OK            04
#define MAXLINELEN      90
#define MAXNAMELEN      12                  /* max length of NAME */
#define COLUMNSPACE     4
#define TERMWID         76
#define HELPDIR         "/usr/help"
#define ALL             0x37
#define DEFSCRLEN       17

int         scrnlen;
int         status;
int         second;                     /* first time indicator for allsel */
char        dent[MAXENT][MAXNAMELEN];
char        lent1[MAXENT][MAXLINELEN];
char        lent2[MAXENT][MAXLINELEN];
char        progname[MAXNAMELEN+4];
char        olddir[MAXLINELEN];
char        newdir[MAXLINELEN];
char        currdir[MAXLINELEN];
char        prompt[MAXLINELEN];
char        *helpdir;

/*global*/  int help(char *);
/*global*/  int outhelp(char *);
/*global*/  int allsel(void);
/*global*/  void set_dta(char *);
/*global*/  int dir_get(char *,int );
/*global*/  char *gettok(char *,char *,char *,char *);
/*global*/  int isdelim(char ,char *);
/*global*/  int more(char *);
/*global*/  int pgbrk(void);
/*global*/  void stripnl(char *);
/*global*/  void setprompt(void);
/*global*/  void fstobs(char *);
/*global*/  void handler(void);
/*global*/  int kget(void);
======================= END of HELP.H ====================================

The following file is 'help.c':
======================= Beginning of HELP.C ==============================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "help.h"
 
/*****************************************************************************
 *  HELP
 *
 *  This program provides a HELP environment similar to the VMS help facility.
 *  Instead of using a help library, however, this version uses a directory 
 *  tree of text files.  This was done primarily to allow easy editing and 
 *  addition of help entries.
 *
 *  The set of arguments passed from DOS is first scanned for switches.  Then 
 *  the remaining arguments are passed to the main help routine as a single
 *  string which is reparsed.  This was done to allow easy recursion.
 *
 *  Similar to the public domain help utility found on many UNIX systems, a 
 *  help directory contains five possible entries in it.
 *        main help text:          HELP.HLM
 *        manual page name:        HELP.MAN
 *        subtopic texts:          .HLP
 *        subtopic directories:    
 *
 *  Due to the filename size limitation of DOS, command line tokens are
 *  limited to eight characters.
 */
void main(argc, argv)
int argc;
char *argv[];
    {
    char        inbuf[MAXLINELEN];
    int         il;                         /* input buffer length */
    int         sl;                         /* token length */
    int         done;                       /* temporary flag */
    char        c;                          /* temporary char holder */
    char        *op;                        /* single argument pointer */
        
    strcpy( progname,*argv++ );         /* Always save the program name */

    second = 1;                         /* to tell allsel "first pass" */
    
    helpdir = getenv( "HELP" );         /* use EV for directory if set */    
    getcwd( olddir, MAXLINELEN );       /* save our startind directory */

    if ( signal( SIGINT, handler ) < 0 )    /* uh oh */
        {
        fprintf( stderr, "%s could not set SIGINT.  Action undefined upon INT\n", progname );
        }
        
    argc--;
    while (( argc ) && ( ( c = *(*argv) ) == SWITCHR1 ) || ( c == SWITCHR2 ))
        {        /* must be options */
        for (op = *argv; *op != '\0'; op++) 
            {
            switch(*op) 
                {
                case SWITCHR1 :    /* ignore first and extras */
                case SWITCHR2 :
                    break;
 
                case 'd' :    /* specifying help directory path explicitly */
                    helpdir = *++argv;
                    break;
 
                case 'h' :
                case 'v' :      /* show version number */
                    fprintf( stdout, HELPVER );
                    exit( 0 );
                    
                default:
                    fprintf(stderr,"%s: %c: bad option.\n",
                    progname,*op);
                    break;
                }   /* switch */
            }   /* for ( steps through token ) */
        argc--; argv++;
        }   /* while ( loops on argv arguments ) */
 
    if (helpdir == NULL)        /* if all else fails, use default */
        helpdir = HELPDIR;
 
    fstobs( helpdir );           /* normalize to dos directory seps */
    
    if (chdir(helpdir) < 0)     /* move to the help root */
        {
        fprintf(stderr,"%s: %s: help directory not found.\n",
            progname,helpdir);
        exit(1);
        }
 
    done = il = 0;                             /* reset input length */
    inbuf[0] = 0;                       /* start with a null buffer */
    while ( ( argc ) && ( *argv ) && !done )   /* concat. all remaining args */
        {
        sl = strlen( *argv );           /* new token length */
        if ( ( il + sl ) < MAXLINELEN ) /* if enough room for added token */
            {
            if ( il && ( il < MAXLINELEN ) )    /* if not the first token */
                {
                strcat( inbuf, " " );   /* add a space */
                strcat( inbuf, *argv ); /* and add the token */
                }
            else                        /* the first token */
                strcpy( inbuf, *argv ); /* copy in the first token */
            il += sl + 1;               /* adjust the inbuf length ctr */
            }
        else                            /* no more room for added tokens */
            done = 1;                   /* and we don't want junk added */            
        argv++;
        argc--;
        }

    strupr( inbuf );                    /* convert to upper case */    
    help( inbuf );
         
    chdir( olddir );                    /* go back to our origin */
    exit(0);
    }
 

/****************************************************************************
 */
help( buf )
char    *buf;
    {
    int     done = 0;
    int     subj = 0;
    char    lbuf[MAXLINELEN];           /* local temporary copy */
    char    tbuf[MAXLINELEN];           /* local token buffer */
    int     lbufl, bufl;                /* buffer lengths */
    
    while( !done )                      /* loop until user is tired */
        {
        if ( !buf || !*buf ) /* was input specified? */
            {
            if ( !subj )
                outhelp( NULL );
            subj = 0;                   /* no DEFAULT menu after subject */
            allsel();
            setprompt();
            fputs( prompt, stdout );        /* prompt user for input */
            if ( fgets( buf, MAXLINELEN, stdin ) == NULL )   /* EOF or error */
                return( -1 );
            }
        strupr( buf );                      /* convert to upper case */
        stripnl( buf );                     /* get rid of new line */
        if ( gettok( buf, STDDELIM, tbuf, lbuf ) == NULL ) /* no input token */
            return( 0 );

        strcpy( buf, lbuf );                /* for next pass */
        if ( tbuf[0] == '?' )           /* redisplay current main help */
            {
            }
        else
            {
/* go ahead and assume the input is a request for more help! */
            if ( ( status = outhelp( tbuf ) ) == -1 )
                {                       /* no help found, sorry */
                fprintf( stdout, "\nSorry, no help found on '%s'\n", tbuf );
                }
            else if ( status == 1 )
                {                       /* it was a subdir! */
                if ( ( status = help( buf ) ) < 0) /* parse further but... */
                    return( status );
                else                                /* otherwise, print main text */
                    {
                    chdir( ".." );              /* go back up for dos */
                    }
                }
            else
                subj = 1;
            }
        }    
    }


/****************************************************************************
 *  outhelp( str )      -   Output the given help file or ...
 *  char    *str;           A text file name, directory name, or NULL
 *
 *  If str is NULL, then print the HELP.HLM file in the current directory
 *  or append a .HLP to the str and print that file.
 */
outhelp( str )
char    *str;
    {
    char filename[MAXLINELEN];

    getcwd( currdir, MAXLINELEN );       /* save our startind directory */
        
    if ( !str || !*str ) /* if no input specified */
        strcpy( filename, HELPMAIN );       /* assume the default main file */
    else
        strcpy( filename, str );
        
    if ( chdir( filename ) == 0 )            /* see if it is a subdir by */
        {                                   /* trying to go to it */
        return( 1 );                        /* tell caller we are there */
        }
                
    strcat( currdir, "\\" );                 /* cause we gonna add toit */
    strcat( currdir, filename );
    
    if ( access(currdir, R_OK ) == 0 )     /* we got a valid file */
        {   
        more( filename );                   /* output the file */
        return( 0 );
        }
 
    strcat( currdir, HELPEX );              /* msdos kludge */
    strcat(filename,HELPEX);                /* add the default extension */
    
    if ( access( currdir, R_OK ) == 0 )     /* we got a valid file */
        {   
        more( filename );                   /* output the file */
        return( 0 );
        }
 
    return( -1 );                           /* not a help available */
    }
 

/****************************************************************************
 * allsel()                     print the topics available in this directory
 *
 *  This function prints a directory of the help files and dirs within this
 *  dir.  Extensions are stripped off.
 *    
 */
allsel()
    {
    static char     lastdir[MAXLINELEN];
    static int      totalnames;
    char            dta[80];
    char            *np = { DEFSRCH1 };
    int             counter;
    int             j;
    int             longlen;
    int             rowcnt;
    int             colcnt;
    int             namewidth;
    char            *s1;
    char            tmpbuf[MAXLINELEN];
    char            row[TERMWID+1];

    set_dta( dta );             /* for MSDOS */

    getcwd( currdir, MAXLINELEN );       /* save our startind directory */

    if ( second || ( strcmp( lastdir, currdir ) != 0 ) )
        {
        counter = 0;
        totalnames = 0;
        while( ( dir_get( np, ALL ) == 0 ) && ( counter < (MAXENT-1) ) )
            {
            np = NULL;
            strcpy ( tmpbuf, &dta[30] );
            if ( tmpbuf[0] != '.' )  /* if it is a directory or root file */
                {
                if ( ( s1 = strchr( tmpbuf, '.' ) ) == NULL )
                    {                               /* look for ext */
                    strcpy( dent[counter++], tmpbuf );
                    totalnames++;
                    }
                else if ( strcmp( s1, HELPEX ) == 0 )   /* its a .HLP file */
                    {                               /* look for ext */
                    *s1 = 0;                        /* terminate after fn */
                    strcpy( dent[counter++], tmpbuf );
                    totalnames++;
                    }
                }
            }
        dent[counter][0] = 0;           /* terminating entry */

        /* sort the names in ascending order with exchange algorithm */
        for(counter=0; counter < totalnames-1; counter++)
            for(j=counter+1; j  0) 
                    {
                    strcpy( tmpbuf, dent[counter] );
                    strcpy( dent[counter], dent[j] );
                    strcpy( dent[j], tmpbuf );
                    }
        }
    if (totalnames == 0) 
        return(0);

/* this next section will be made into a subroutine */
/* rebuild code from here --\/ */
    longlen = 0;
    for(counter=0; counter < totalnames; counter++ )
        longlen = ((j=strlen(dent[counter]))>longlen)?j:longlen;
 
    /* here print the names out in nice columns */
    namewidth = longlen + COLUMNSPACE;
    rowcnt = TERMWID / namewidth;
    colcnt = (totalnames + (rowcnt-1)) / rowcnt ;
    if (colcnt <= 0) 
        colcnt = 1;
    
/*    if (col_flag && rowcnt > 1)   */
    if ( rowcnt > 1 ) 
        {
        fprintf( stdout, "\n Topics:\n\n");
        for(counter=0; counter < colcnt ; counter++ ) 
            {
            for(j=0; j < TERMWID; row[j++] = ' ');
            row[j] = '\0';
            for(j=0, s1 = row; (counter+j) < totalnames; j += colcnt) 
                {
                row[strlen(row)] = ' ';
                strcpy(s1,dent[counter+j]);
                s1 = s1 + namewidth;
                }
            fprintf( stdout, "    %s\n",row);
            }
        putchar('\n');
        }
    else 
        {
        for(counter=0; counter < totalnames; counter++)
            fprintf( stdout, "%s\n", dent[counter] );
        }
/*                to here --/\ */
 
    return( 0 );
    }
 


void set_dta( dta )
char    *dta;
    {
    union REGS rg;

    rg.h.ah = 0x1a;
    rg.x.dx = ( unsigned int )dta;

    int86( 0x21, &rg, &rg );
    }  

dir_get( fn, type )
char    *fn;
int     type;
    {
    union REGS rg;

    if ( fn )
        {
        rg.x.dx = ( unsigned int )fn;
        rg.x.cx = type;
        rg.h.ah = 0x4e;

        int86( 0x21, &rg, &rg );
        if ( rg.x.cflag & 0x01 )
            return( rg.x.ax );
        }
    else
        {
        rg.h.ah = 0x4f;
            
        int86( 0x21, &rg, &rg );
        if ( rg.x.cflag & 0x01 )
            return( rg.x.ax );
        }
    return( 0 );
    }
    

/****************************************************************************
 *  gettok( is, delim, tb, rb )     - Find token in input string        
 *  char    *is;        Pointer to input string
 *  char    *delim;     Pointer to set of delimiting characters
 *  tb      *tb         Pointer to buffer to place token
 *  rb      *rb         Pointer to buffer to place ( input string - token )
 *
 *  This function reads the input string looking for a token delimited by
 *  the beginning of the input buffer or a character in the delimiting set
 *  on the leading edge, and the end of the input buffer or a delimiting 
 *  character on the trailing edge.  The first character of the buffer 
 *  which is not a character in the set of delimiting characters is taken as
 *  the first character of the token.
 *
 *  If a pointer is passed in tb and/or rb, it is assumed to be a buffer of
 *  adequate size to hold the resultant string.  A NULL may be passed in these
 *  variables if the result is not desired.
 *
 *  Returns:    A pointer to the first not delimiting character.
 */
char *gettok( is, delim, tb, rb )
char    *is;     
char    *delim;  
char    *tb;
char    *rb;
    {
    char    *fc;                    /* pointer to first character of token */

    fc = NULL;                      /* default no token found */
        
    if ( tb )                       /* if user gave us a token buffer */
        *tb = 0;                    /* terminate the token buffer */        
    if ( rb )                       /* if user gave us a remainder buffer */
        *rb = 0;                    /* terminate the token buffer */        

    if ( is == NULL )               /* oops, got a 2001 */
        return( NULL );
        
    while ( *is && isdelim( *is, delim ) ) /* while leading delimiter */
        is++;                       /* skip it */
    if ( !*is )
        return( NULL );
    fc = is;                        /* save the pointer the first char */
    while ( *is && !isdelim( *is, delim ) )    /* while not a delimiter */
        {
        if ( tb )                   /* if user gave us a token buffer */
            {
            *tb = *is;              /* move the character */
            tb++;                   /* increment token buffer ptr */
            }
        is++;
        }
    if ( tb )                       /* if user gave us a token buffer */
        *tb = 0;                    /* terminate the token buffer */        

    if ( rb )                       /* if user gave us a remainder buffer */
        strcpy( rb, is );           /* copy the remainder */

    return( fc );
    }
            
isdelim( c, delim )
char    c;
char    *delim;
    {
    int     dc;                     /* delimiter count */
    int     i;                      /* temporary counter */
    char    *tc;                    /* termporary pointer */
    
    tc = delim;                     /* time to count the donuts */
    dc = 0;
    while  ( *tc++ )    
        dc++;

    for ( i=0; i