#define UPPER_CASE 1 /* set this to 1 to make 'help' case insensitive set this to 0 to make 'help' case sensitive. */ #define ALPHABETIZE 0 /* set this to 1 for alphbetized topic listings set this to 0 for listings in .hlp file order*/ /* help.c Written by D. Woodruff Uses a VMS-style .hlp file to display help. The routine is interactive. At the execution of get_help, the users responds to the prompt 'subtopic?> '. The valid responses are: 1) subtopic name. The help associated with this subtopic is displayed, and the next level of subtopics is shown, if it exists. 2) '?'. The current help item is redisplayed, and the next level of subtopics is shown, if it exists. 3) A carriage return. The previous help level is returned to. The next level of subtopics for that level is redisplayed. The previous help item is not redisplayed: if the user wishes to see it, he can enter '?'. Routines: ( shown in C function prototype ) init_help(char *fname) fname is name of the help file. This must be run before get_help. get_help(char *itemstring) use this to return help information. 'itemstring' must be a string that starts at an item on level one, and proceeds down to the item. 'init_help' creates a help 'tree' from the help file 'fname'. Branching is determined by the help file levels that are set by 1, 2, 3, etc. Lines starting with ! or # are treated as comments. This supports levels of help down to '9'. (The VMS / 'qualifiers' method of branching is not supported.) 'get_help' searches the help tree for the first instance of 'itemstring'. If it finds it, it displays the path through the tree to the item, displays the help entry, then displays topics for which additional help can be had... i.e., the next highest branches. Files: init_help reads files named '(name).hlp'. An extension other than .hlp may be used, but then it must be explicitly used in the call to init_help (e.g., init_help("asdf.help"). Lines in the .hlp file that start with '!' or '#' in column one are treated as comments. Lines that start with a number between 1 and 9 cause help.c to create a new help item. The name or label of the item must be given on the same line. All other lines are taken to be part of the current help item. Example: (on VAX VMS in fortran) user has created 'test.help' with level one entries of 'oranges' and 'lemons'. 'ST_Clements' is a level two subtopic of 'lemons'. $ for test $ link test,help,sys$library:vaxcrtl/lib test.for has the following: ..... call init_help("test.help") ! read the help tree into physical memory ..... call get_help("oranges") ! enter the help tree for 'oranges' ..... call get_help("lemons ST_Clements") ! help for 'ST_Clements' and its ..... ! subtopics (under 'lemons') ..... call get_help("lemons") ! help for 'lemons' and its subtopics */ #include #include /* for strtok */ #include /* for isdigit */ #include /* for atoi, malloc */ #define NAME_SIZE 30 #define LINE_SIZE 90 #define ITEM_SIZE 8192 /* temporary storage for help message. Increase this number to prevent item truncation*/ #define WIDTH 10 /* minimum field width for subtopic list columns*/ /* storage for a help item */ struct item_node { char *name; /* one word label for the help item */ char *path; /* string showing path to this item from the root */ char *item; /* storage for the information */ struct item_node *sibptr; /* pointer to an item on the same level */ struct item_node *firstsibptr; /* pointer to the first item on a level*/ struct item_node *parentptr; /* pointer to the parent item */ struct item_node *childptr; /* pointer to a lower level */ int status; /* =1, okay. =0, information was truncated */ }; struct item_node help_root; /* root of the help tree */ struct item_node *rptr = &help_root; /* pointer to the root */ char answer[NAME_SIZE]; /* buffer for user input */ /* read a line from standard input (adapted from Kernigan and Ritchie) function value is the the last character read. */ /**/ char getlin(FILE *fptr, char *buff, int lim) { char c; int i; i = 0; while (--lim > 0 && (c=getc(fptr)) != EOF && c != '\n') buff[i++] = c; if (c == '\n') buff[i++] = c; buff[i] = '\0'; return(c); } /* define and enter data for a new node for the help tree. */ struct item_node *makenode (int new_level, char *linebuf) { struct item_node *new_node; char nambuf[NAME_SIZE], pathbuf[LINE_SIZE]; static char namestack[10][NAME_SIZE]; int i,j; new_node = (struct item_node *) malloc(sizeof(struct item_node)); new_node->sibptr = NULL; new_node->firstsibptr = NULL; new_node->childptr = NULL; new_node->parentptr = NULL; new_node->item = NULL; new_node->status = 1; /* enter the node name for the help item */ for (i=1; linebuf[i]==' '; i++); /* skip spaces */ j = 0; while (linebuf[i] != ' ' && linebuf[i] != '\0' && linebuf[i] != 10 ) #if UPPER_CASE nambuf[j++] = toupper(linebuf[i++]); #else nambuf[j++] = linebuf[i++]; #endif nambuf[j] = '\0'; new_node->name = (char *) malloc(strlen(nambuf)+1); strcpy(new_node->name,nambuf); /* enter the string giving the path to the new node */ strcpy( namestack[new_level], nambuf); /* put new name in stack */ strcpy (pathbuf, namestack[1]); /* collect the stack names into */ for (i=2; i<=new_level; i++) /* new_node->path */ { strcat(pathbuf," "); strcat(pathbuf, namestack[i]); } new_node->path = (char *) malloc(strlen(pathbuf)+1); strcpy(new_node->path,pathbuf); return(new_node); } /* Streight insertion sort routine. Used by maketree */ void insertion_sort(struct item_node *firstptr, struct item_node *newptr) { struct item_node *cursor; struct item_node *saveptr; struct item_node *previous; /* if new item is earlier than earliest */ if (strcmp(newptr->name,firstptr->name) < 0) { cursor = saveptr = firstptr; /* new item is lower alphabetically. Reset all firstptr's */ while(cursor != NULL) { cursor->firstsibptr = newptr; cursor = cursor->sibptr; } saveptr->parentptr->childptr = newptr; newptr->sibptr = saveptr; newptr->firstsibptr = newptr; return; } /* find out where new word should be put in the list */ newptr->firstsibptr = firstptr; cursor = firstptr->sibptr; previous = firstptr; while (cursor != NULL) { if (strcmp(newptr->name,cursor->name) < 0) { previous->sibptr = newptr; newptr->sibptr = cursor; return; } cursor = cursor->sibptr; previous = previous->sibptr; } previous->sibptr = newptr; return; } /* read the help file and create the help tree in memory */ void maketree(char *filname, struct item_node *rptr) { FILE *fptr; char linebuf[LINE_SIZE], getlin(FILE*,char*,int); char filn[NAME_SIZE]; struct item_node *current_item, *new_branch; char itembuf[ITEM_SIZE]; /* temporary storage for help items*/ int current_level, number_back; int i, count, truncated=0; strcpy(filn,filname); /* if no extension is entered, supply the default:*/ if ( strchr(filn,'.') == 0 ) strcat(filn,".hlp"); if((fptr = fopen(filn,"r")) == NULL) { printf("The help utility can't read file %s.\n",filn); printf("Please note that it must be in your default directory.\n"); return; } current_level = 0; current_item = rptr; /* rptr points to the root */ itembuf[0] = '\0'; count = 0; while(1) { /* at end of file, clean up and quit */ if (getlin(fptr, linebuf, LINE_SIZE) == EOF) { if (strlen(itembuf) == 0) { current_item->item = (char *) malloc(1); current_item->item[0] = '\0'; } else { itembuf[strlen(itembuf) - 1] = '\0'; /* drop the last \n */ if (truncated) { truncated = 0; current_item->status = 0; printf("-- help info for %s was truncated\n",current_item->path); strcat(itembuf,"\n\n << truncated >>"); } current_item->item = (char *) malloc(strlen(itembuf)); strcpy(current_item->item,itembuf); } if (truncated) { truncated = 0; current_item->status = 0; printf("-- help info for %s was truncated\n",current_item->path); strcat(current_item->item,"\n\n << truncated >>"); } itembuf[0] = '\0'; count = 0; break; } /* skip comment lines */ else if (linebuf[0] == '!' || linebuf[0] == '#') /* skip comments */ continue; /* found a new help item. close out the current item */ else if (isdigit(linebuf[0]) && linebuf[1] == ' ') /* levels 0 - 9*/ { if (strlen(itembuf) == 0) { current_item->item = (char *) malloc(1); current_item->item[0] = '\0'; } else { itembuf[strlen(itembuf) - 1] = '\0'; if (truncated) { truncated = 0; current_item->status = 0; printf("-- help info for %s was truncated\n",current_item->path); strcat(itembuf,"\n\n << truncated >>"); } current_item->item = (char *) malloc(strlen(itembuf)); strcpy(current_item->item,itembuf); } itembuf[0] = '\0'; count = 0; /* create storage and pointers for a new help item */ if (atoi(linebuf) == current_level) { new_branch = makenode(current_level,linebuf); new_branch->parentptr = current_item->parentptr; #if ALPHABETIZE insertion_sort (current_item->firstsibptr, new_branch); #else current_item->sibptr = new_branch; new_branch->firstsibptr = current_item->firstsibptr; #endif current_item = new_branch; } else if (atoi(linebuf) == current_level + 1) { current_level++; if (current_level == 9) printf("help levels higher than 9 are not supported.\n"); new_branch = makenode(current_level,linebuf); new_branch->parentptr = current_item; current_item->childptr = new_branch; new_branch->firstsibptr = new_branch; current_item = new_branch; } else if (atoi(linebuf) < current_level ) { number_back = current_level - atoi(linebuf); current_level -= number_back; new_branch = makenode(current_level,linebuf); for (i=0; iparentptr; new_branch->parentptr = current_item->parentptr; #if ALPHABETIZE insertion_sort (current_item->firstsibptr, new_branch); #else current_item->sibptr = new_branch; new_branch->firstsibptr = current_item->firstsibptr; #endif current_item = new_branch; } else printf("invalid key\n"); } /* continuation line for a help item */ else { count += strlen(linebuf); if ( count < ITEM_SIZE - 3*LINE_SIZE ) strcat(itembuf,linebuf); else truncated = 1; } } fclose(fptr); return; } /* used by get_help to show subtopics */ void show_subtopics(struct item_node *itemptr) { struct item_node *cursor; int i, count, wordlen, numfield, filler; cursor = itemptr->childptr; printf("\n-----additional help available for: \n"); printf(" "); count = 7; while(cursor != NULL) { wordlen = strlen(cursor->name); /* we print 'name' and fill out the rest of the field with spaces */ numfield = 1 + wordlen / WIDTH; /* number of WIDTHs taken by word */ filler = numfield * WIDTH - wordlen; /* number of spaces to insert*/ if ( (count + numfield*WIDTH) >= 80) { /* not enough room, start new line */ printf("\n "); count = 7; } count += numfield*WIDTH; /* print 'name' and filler spaces */ printf("%s",cursor->name); for (i=1; i<=filler; i++) printf(" "); cursor = cursor->sibptr; } } /* used by get_help. look for a sub-topic name one level down. */ struct item_node *find_item(struct item_node *ptr, char *name) { struct item_node *cursor; int i,okay; if (ptr->childptr == NULL) return (NULL); cursor = ptr->childptr; /* This handles abbreviations*/ while (cursor != NULL) { i = 0; okay = 1; while (/*cursor->name[i] != '\0' &&*/ name[i] != '\0') { if ( cursor->name[i] != name[i] || cursor->name[i] == '\0' ) { okay = 0; break; } i++; } if (okay == 1) return(cursor); cursor = cursor->sibptr; } return (NULL); } /* get help on 'itemstring', which is a partial path on the help tree. E.g., "set display" or "set". */ void get_help(char *itemstring) { char NextItemString[LINE_SIZE]; char itemstringbuf[LINE_SIZE]; /* buffer for 'toupper' */ struct item_node *itemptr, *saveptr; static struct item_node; char *itemword; int i; static int firstcall=1; firstcall = firstcall; /* Prevent "not used" warning */ i = 0; while( itemstring[i] != '\0') { #if UPPER_CASE itemstringbuf[i] = toupper(itemstring[i]); #else itemstringbuf[i] = itemstring[i]; #endif i++; } itemstringbuf[i] = '\0'; /* For crude help on get_help, remove comment marks around this block. if (firstcall) { firstcall = 0; printf("\n\n for help on help, enter 'HELPHELP'.\n\n"); } if (! strstr(itemstringbuf,"HELPHELP") == NULL) { printf("\n\n Press 'return' to exit help, or to see the previously\n\ viewed subtopic. \n\n\ Enter '?' to redisplay the current topic. \n\n\ Enter the subtopic name exactly as it is to see help on the\n\ subtopic.\n\n"); return; } */ /* find the item by moving down the help tree word by word*/ printf("\n(Press 'enter' to exit or to see last item)"); itemptr = rptr; /* start at the root */ itemword = strtok(itemstringbuf," "); while (itemword != NULL) { saveptr = itemptr; itemptr = find_item(itemptr,itemword); if (itemptr == NULL) { printf("\n\n-----there's no help for: %s %s\n", saveptr->path,itemword); return; } itemword = strtok(NULL," "); } /* display the item */ printf("\n**** %s ****\n%s\n",itemptr->path,itemptr->item); /* list any subtopics */ if ( itemptr->childptr != NULL ) show_subtopics(itemptr); /* ask the user what to do next */ while(1) { printf("\nsubtopic?> "); getlin(stdin,answer, NAME_SIZE); answer[strlen(answer)-1] = '\0';/* drop the carriage return character*/ if (strcmp(answer,"?")==0) { printf("\n**** %s ****\n%s\n",itemptr->path,itemptr->item); if ( itemptr->childptr != NULL ) show_subtopics(itemptr); } else if (strlen(answer) == 0) /* pop back to last item */ return; else /* wants to view subtopic */ { strcpy(NextItemString,itemptr->path); strcat(NextItemString," "); strcat(NextItemString,answer); get_help(NextItemString); printf("\n**** %s ****\n",itemptr->path); if ( itemptr->childptr != NULL ) show_subtopics(itemptr); } } } /* initialize the help program. Note that each additional call to 'init_help' increases program memory. Memory from earlier calls is not freed. */ init_help(char *fname) { /* help tree root: */ rptr->sibptr = NULL; rptr->parentptr = NULL; rptr->childptr = NULL; rptr->status = 1; rptr->item = NULL; rptr->name = (char *) malloc(10); rptr->path = (char *) malloc(10); strcpy(rptr->name,""); strcpy(rptr->path,""); /* read the help file, and build the tree: */ maketree(fname,rptr); }