A version of HELP for PC/MS-DOS.
Robert P. Scott
scott at mosaic.dec.com
Wed Jan 7 11:04:06 AEST 1987
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 <stdio.h>
#include <io.h>
#include <stdlib.h>
#include <conio.h>
#include <signal.h>
#include <ctype.h>
#include <direct.h>
#include <process.h>
#include <string.h>
#include <dos.h>
#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: <topicname>.HLP
* subtopic directories: <topicname>
*
* 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 <totalnames; j++)
if (strcmp(dent[counter],dent[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<dc; i++ ) /* loop through the delimiters */
{
if ( c == delim[i] ) /* if a delimiter */
return( 1 ); /* return found */
}
return( 0 ); /* return not found */
}
more( fn )
char *fn;
{
FILE *sfd;
char buf[512];
int lct;
lct = 0;
scrnlen = DEFSCRLEN;
if ( ( sfd = fopen( fn, "r" ) ) != NULL ) /* got a file */
{
while( fgets( buf, 512, sfd ) )
{
if ( lct == scrnlen )
{
lct = 0;
if ( pgbrk() )
{
fclose( sfd );
return( 1 );
}
}
fputs( buf, stdout );
lct++;
}
}
fclose( sfd );
return( 0 );
}
pgbrk()
{
char c;
fprintf( stdout, "More (Y|N)" );
c = ( char )kget();
fputc( '\n', stdout );
if ( c == 'N' )
return( 1 );
return( 0 );
}
void stripnl( buf )
char *buf;
{
while ( *buf )
{
if ( *buf == '\n' )
*buf = 0;
buf++;
}
}
void setprompt()
{
char *hs; /* pointer to the first / in helpdir */
char *cs; /* pointer to first / in current dir */
char *pp; /* pointer into prompt */
getcwd( currdir, MAXLINELEN ); /* save our startind directory */
hs = strchr( helpdir, '\\' ); /* ignore drive names */
cs = strchr( currdir, '\\' ); /* ignore drive names */
while ( *hs && ( *hs == *cs ) ) /* skip help directory root */
{
hs++;
cs++;
}
pp = prompt;
while ( *cs ) /* use the rest for the prompt */
{
if ( *cs == '\\' ) /* convert to spaces for readablty */
*cs = ' ';
*pp++ = *cs++;
}
if ( pp != prompt ) /* not at root... */
*pp++ = ' '; /* add a pleasing space */
*pp = 0; /* terminate the string */
strcat( prompt, PROMPT ); /* and the default ending */
}
void fstobs( str )
char *str;
{
strupr( str ); /* dos only understands one case */
while ( *str )
{
if ( *str == '/' ) /* convert UNIX to MSDOS seps */
*str = '\\';
str++;
}
}
void handler()
{
char c;
if ( signal( SIGINT, handler ) < 0 ) /* uh oh */
{
fprintf( stderr, "%s could not set SIGINT. Action undefined upon INT\n", progname );
}
fprintf( stdout, "Terminate process [y|N]" );
c = ( char )kget();
fputc( '\n', stdout );
if ( c == 'Y' )
{
chdir( olddir ); /* go back to our origin */
exit(0);
}
}
kget()
{
char c;
while ( kbhit() == 0 )
{
}
c = ( char )getche();
c = toupper( c );
return( c );
}
======================= END of HELP.C ====================================
The following file is a MSC V4.0 makefile:
======================= Beginning of HELP ================================
OBJS = help.obj
LIBFLAGS =
# LIBFLAGS = /CO /MAP /LINE
# /AL means use large model
CFLAGS = /AS /Ot
# CFLAGS = /AS /Od /Zd /Zi
# default rules
.c.obj:
msc $(CFLAGS) $*;
.asm.obj:
masm $*;
help.obj: help.c help.h
help.exe: $(OBJS)
link$(LIBFLAGS) help,help;
# help.exe: $(OBJS)
# link$(LIBFLAGS) help,help ;
# mapsym /L help.map
======================= END of HELP ======================================
More information about the Comp.sources.unix
mailing list