#include "help.h" #define MAXMATCHES 24 /* ************************************************************ * H E L P !! * ========================= * * This program emulates the VMS help facility in the UNIX * environment. The main routine HELP1 looks up help and * subtopics for help. The help texts for various topix * are tree-structured. A directory is defined as a "help" * directory, it has four types of files in it: * main help text: .HLP * manual page name: .MANUAL * subtopic texts: .HLP * subtopic directories: * subtopic cross-refs: .XREF * * Subtopic names must start with an alphanumeric * character. Preferably all subtopics will start with * lowercase letters. * * The routine help1 is recursive, it descends the tree * structure. */ main(argc, argv) int argc; char *argv[]; { int i,j,k; int longlen; char yesno[10]; char *opts; strcpy(progname,argv[0]); helpdir = NULL; getwd(olddir); dumb_flag = 0; list_flag = 0; frst_flag = 1; col_flag = 1; argc--; if (argc != 0 && *(*++argv) == '-') { /* must be options */ for (opts = *argv; *opts != '\0'; opts++) { switch(*opts) { case '-' : break; case 'd' : if (argc > 1) { helpdir = *++argv; argc--; } break; case 'l' : list_flag = 1; col_flag = 0; break; case 'q' : dumb_flag = 1; break; case 'C' : col_flag = 1; break; case 'n' : col_flag = 0; default: fprintf(stderr,"%s: %c: bad option.\n", progname,*opts); break; } } argc--; argv++; } if (helpdir == NULL) helpdir = HELPDIR; if (chdir(helpdir) < 0) { fprintf(stderr,"%s: %s: help directory not found.\n", progname,helpdir); exit(1); } #ifdef BSD init_funky(); #endif if (argc >= 1) { /* treat vector as a help path */ #ifdef DEBUG fprintf(stderr," help path vector, argc=%d, *argv=%s.\n", argc,*argv); #endif help1( "", argv, 0, 0, 0); } else help1( "", NULL, 0, 0, 0); exit(0); } /* ************************************************************** * printhelp: given a string, pop .HLP on the end and do more(1). * * This routine sends a help file to more(1). A string * is passed in, which is the name of a help. If the string * is nil, then just use the name HLP. * The return value is -1 if no help file is accessible, * 0 if the more(1) command was called okay with fkoff(); */ printhelp(hs, path) char *hs; { char filename[MAXNAMELEN], comm[MAXNAMELEN + 20]; if (hs == NULL) strcpy(filename,HELPEX); else if (strlen(hs) < 1) strcpy(filename, HELPEX); else { strcpy(filename, hs); strcat(filename,HELPEX); } if ( access(filename, R_OK) < 0 ) { printf("\n Sorry, no help text for %s.\n", (hs==NULL)?"this topic":hs ); return(-1); } if (path != NULL) printf("\n HELP: %s\n",path); fkoff(VIEWPROGRAM,VIEWPROGOPTS1,VIEWPROGOPTS2,filename,NULL); return(0); } /* ************************************************************* * printtopix: print the topics available in this directory * in a nice format. * * This routine does a directory of help options, * and prints them out in a manner similar to that of ls(1). * All filenames which start with anything other than numbers * or letters are not kept. Extensions are stripped off. * * The number of subtopics found is returned, along with * a pointer to a null-terminated vector of string pointers. * If the mode is non-zero, it means just use the strings stored * in topix. If mode==0, then re-allocate space and re-check * the directory. If mode */ printtopix( topix, mode, supress) char *topix[]; int mode, supress; { int i,j,k,l,xrefs = 0; int namewidth, totalnames; char *malloc(), *nbuf; char *thisname, *index(); char *s1, *s2, **nxtname, *nxcnt; struct direct *readdir(), *filedat; DIR *dirp, *opendir(); if ( mode ) { for(nxtname=topix, i=0; *nxtname++ != NULL; i++ ); totalnames = i; goto inputtopix; } /* if mode is zero, then allocate space and search the directory */ nbuf = malloc( MAXNAMES * MAXNAMELEN ); nxcnt=nbuf; dirp = opendir("."); if (dirp == NULL) { fprintf(stderr,"%s: Cannot open help directory.\n",progname); return(-1); } for(nxtname=topix, i=0; i < (MAXNAMES-1) ; ) { filedat = readdir(dirp); if (filedat == NULL) break; thisname = filedat->d_name; if ( !(isalnum(*thisname)) ) /* if not in [0-9A-Za-z] */ continue; /* do the next one. */ *nxcnt = '\0'; if ( (s1 = index(thisname,'.')) != NULL) { if (strcmp(s1, HELPEX) == 0) *s1 = '\0'; else if ( strcmp(s1, MANEX) == 0) continue; else if ( strcmp(s1, XREFEX) == 0) { /* mark xref with at sign in first char */ strcpy(nxcnt,"@"); } } if (strlen(thisname) >= MAXNAMELEN - 1) *(thisname+MAXNAMELEN-1) = '\0'; /* copy in data from this loop */ if (*nxcnt == '\0') strcpy(nxcnt,thisname); else strcat(nxcnt,thisname); *nxtname++ = nxcnt; /* update pointers for next loop */ nxcnt += strlen(thisname) + ((*nxcnt=='@')?(2):(1)); i++; } *nxtname++ = NULL; totalnames = i; closedir(dirp); if (totalnames == 0) return(0); /* sort the names in ascending order with exchange algorithm */ for(i=0; i < totalnames-1; i++) for(j=i+1; j 0) { thisname = topix[i]; topix[i] = topix[j]; topix[j] = thisname; } inputtopix: if (supress) return(totalnames); else { if (col_flag) printf("\n Subtopics:\n"); printlist(topix, totalnames); return(totalnames); } } /* **************************************************************** * help1: descend recursive help tree and provide some * user services. * * This routine is the heart of the new UNIX help facility. * It climbs recursively around an n-tree of documentation files * and directories. The routine printhelp() outputs a file of * help text, the routine printtopix prints out all subtopics. * * This routine can operate in interactive mode, or not. The * basic cycle of operation is: * if (not list-only-mode) print help. * if (not quiet-mode) print list. * if (not interactive) return. * print prompt and do commands. * return. * * There are a number of commands available in the * interactive mode, they are: * * blank line: up recursive level. * subtopic name: recurse to subtopic. * ?: list topics again. * *: get list of commands * $: get info on topix * ^D: quit help program. * .: man page (if any). * # list topics again * */ help1(ppt,svec,skip,xref,comp_flag) char *ppt; char *svec[]; short skip; short xref; /* non-zero if doing xref */ short comp_flag; /* non-zero if doing completions */ { /* this routine is too big, eh? */ int i,j,k, err; int no_subs; char answer[MAXLINELEN]; char fullpath[MAXNAMELEN + 29]; char yesno[10]; char *topix[MAXNAMES], *mx[MAXMATCHES]; int matchv(); char *index(), *getenv(); char *s1, *s2, *s3, *rest; char *wvec[MAXMATCHES]; char cmdbuf[90]; if (comp_flag) { printtopix( topix, 0, 1); if ( (k = matchv( *svec, topix, mx)) == 0) return(-1); /* if there is more than one match, and we are not at end, flub */ if ( svec[1] != NULL && k > 1 ) return(-1); /* if we are not at end, go down one level */ if ( svec[1] != NULL ) { if ( chdir( mx[0] ) < 0) return(-1); else { k = help1(ppt, svec+1, 0, 0, comp_flag); chdir(".."); return(k); } } /* if we are at end, then do appropriate action on comp_flag */ else { strcpy(gbl_match, mx[0]); if ( comp_flag == CTRLD_COMP ) { fputs("\n",stdout); printlist(mx,k); } } return(0); } if ( svec != NULL && *svec != NULL) { printtopix( topix, 0, 1); if ( (k = matchv(*svec, topix, mx)) == 0) { printf(" sorry, no direct help path to %s %s\n",ppt,*svec); if ( (k = xref_matchv(*svec, topix, mx)) == 0) { printf(" no cross-references, either.\n\n"); if (strlen(ppt) == 0) help1(ppt,NULL,1,0, 0); return(-1); } else { do_xref(ppt,*svec,mx); if (strlen(ppt) == 0) help1(ppt,NULL,1,0, 0); return(-1); } } for(i = 0, j = 0, err = -1; mx[i] != NULL; ) { if (i > 0) j = takeit(" Next help path: %s %s\nTry it?",ppt,mx[i]); if (j == 1) { i++; continue; } if (j < 0) break; strcpy(fullpath,ppt); strcat(fullpath," "); strcat(fullpath,mx[i]); if ( chdir(mx[i]) < 0) { if ( *(svec + 1) != NULL ) { i++; continue; } if (list_flag) return(-1); else printhelp( mx[i],fullpath); printf("\n"); if (!dumb_flag) help1(ppt,NULL,1,0, 0); err = 0; } else { if ( help1(fullpath,(svec+1),0,0, 0) >= 0) { err = 0; chdir(".."); printf("\n"); if (!dumb_flag) help1(ppt,NULL,1,xref, 0); } else chdir(".."); } i++; } return(err); } if ( !list_flag && !skip) if (printhelp(NULL, ppt) < 0) { return(-1); } if ( !list_flag && !dumb_flag && ( access(MANEX, R_OK) >= 0) && !skip) printf("\n Manual page IS available.\n"); if ( !dumb_flag ) { if ( printtopix( topix, 0, skip) <= 0 ) { no_subs = 1; } else no_subs= 0; } if ( dumb_flag || list_flag ) return(0); while (xref == 0) { if (frst_flag) { printf(" (type '*' for commands)\n"); frst_flag = 0; } /* now, prompt and wait for user response */ if ( strlen(ppt) < 1 ) sprintf(gbl_ppt," Help %s",PROMPT); else sprintf(gbl_ppt," %s %s",ppt,SUBPROMPT); fputs(gbl_ppt,stdout); #ifndef BSD if ( fgets(answer, MAXLINELEN-1, stdin) == NULL ) { printf("\nbye...\n"); exit(1); } #else if (read_gets(stdin->_file, answer, MAXLINELEN - 1, 1) < 0) { printf("\nbye...\n"); exit(1); } #endif /* first remove any leading blanks or tabs */ for(s1=answer; *s1 == ' ' || *s1 == ' '; s1++); /* chop off all of answer after first word. */ s2 = index(s1,' '); if (s2 == NULL) s2 = index(s1,' '); if (s2 == NULL) s2 = index(s1,'\n'); if (s2) { *s2 = '\0'; rest = ++s2; } else { rest = s2 = s1 + strlen(s1); } makewvec(rest,wvec,MAXMATCHES); if ( strlen(s1) == 0 ) /* on blank line, */ break; /* pop up one level*/ switch (*s1) { case '?': /* ?: print stuff again */ printhelp(NULL, ppt); if ( access(MANEX, R_OK) >= 0 ) printf("\n Manual page IS available.\n"); if (!no_subs) printtopix(topix, 1,0); else printf("\n Sorry, no subtopics.\n\n"); break; case '#': if (!no_subs) printtopix( topix, 1,0); else printf("\n Sorry, no subtopics.\n\n"); break; case '*': /* *: list commands */ printf("\n"); for(i=0; helpcmds[i] != NULL; i++) { printf(" %s\n",helpcmds[i]); } printf("\n"); break; case '$': /* $: find out about files */ s2 = s1 + 1; if (no_subs) { printf("\n Sorry, no subtopics.\n\n"); break; } strcpy(cmdbuf, INFOHEAD); strcat(cmdbuf,s2); strcat(cmdbuf,"* "); strcat(cmdbuf, INFOTAIL); printf("\n File information: \n"); system(cmdbuf); printf("\n"); break; case '.': /* .: do manpage if any */ s2 = s1 + 1; if (no_subs) { printf("\n Sorry, no subtopics.\n\n"); break; } if ( *s2 == '\0' ) { mx[0] = ""; mx[1] = NULL; k = 1; } else if ( (k = matchv( s2, topix, mx)) == 0 ) { printf("\n Sorry, no topics match %s\n",s2); printf(" (list cmnds with '*', topix with '#')\n"); } for( i=0; i < k; i++ ) { strcpy(cmdbuf,mx[i]); strcat(cmdbuf,MANEX); if (access(cmdbuf, R_OK|X_OK ) < 0) { strcpy(cmdbuf,mx[i]); strcat(cmdbuf,MAN_SUBEX); if (access(cmdbuf,R_OK|X_OK) < 0) { printf("\n Sorry, %s for %s.\n\n", "No manual reference available", (strlen(s2)==0)?ppt:mx[i]); continue; } } if (i > 0) { k= takeit(" Next man page: %s.\n Take it? ", mx[i]); if (k == 1) continue; if (k == -1) break; } printf(" ...doing %s\n\n",cmdbuf); fkoff(SHELLPROG,SHELLOPTS,cmdbuf,NULL); /* source contents of .MANUAL file */ } break; default: /* must be a topic spec */ if ( no_subs ) { printf("\n Sorry, no subtopics.\n\n"); break; } if ( (k = matchv( s1, topix, mx)) == 0 ) { printf("\n Sorry, no direct help for %s\n",s1); if ( (k = xref_matchv(s1,topix,mx)) == 0) { printf(" no cross-references to %s\n",s1); printf( " (list cmnds with '*', topics with '#')\n"); break; /* leave switch */ } else { do_xref(ppt,s1,mx); break; /* leave switch */ } } /* step thru sub-topics that match src */ for(i=0; i < k; i++) { s3 = mx[i]; if (i > 0) { j=takeit("\nNext %s subtopic: %s\nTake it?", ppt,s3); if (j == 1) continue; if (j == -1) break; } if ( chdir(s3) >= 0 ) { /* directory subtopic */ strcpy(fullpath,ppt); strcat(fullpath," "); strcat(fullpath,s3); help1(fullpath,wvec,0,0, 0); /* recurse */ if ( strcmp(getwd(newdir),helpdir) ) chdir(".."); } /* else text subtopic */ else { strcpy(fullpath, ppt); strcat(fullpath, " "); strcat(fullpath, s3); printhelp(s3, fullpath); } } } /* end of switch */ } /* end of while(1) */ if (!no_subs && (*topix != NULL) ) free( *topix ); return(0); } /* end of help1 */ /* **************************************************************** * takeit: ask user whether to take next topic, return t or f * * This routine takes a message, with format, and asks the * user whether he wants to do the action, not do the action, * or quit the cycle of actions. * y - return 0 * n - return 1 * q - return -1 * ? - tell what y, n, and q do. * * - tell what y, n, and q do. * - carriage return * * Naturally, case is insignificant. */ takeit(fmt,m1,m2,m3) char *fmt, *m1, *m2, *m3; { char ans[40]; int done, ret; char *fgets(); for(done = 0; !done; ) { if (fmt != NULL) printf(fmt, m1, m2, m3); printf(" [ynq] "); if (fgets(ans, 39, stdin) == NULL) exit(1); /* abort */ isupper(*ans)?(*ans=tolower(*ans)):0; if (*ans == 'n') { done = 1; ret = 1; } else if (*ans == 'y' || *ans == '\n') { done = 1; ret = 0; } else if (*ans == 'q') { done = 1; ret = -1; } else printf(" Answer y to get next subtopic, n to skip, q to quit.\n"); } return(ret); } /* *************************************************************** * matchv: return all matches of a string in a vector. * * This routine accepts a string and a vector of strings. * The string is supposed to be an abbreviation of one or more * strings in the vector. The full versions of the strings are * placed in a another vector (which is in a static area) and * a pointer to that vector returned. Note that the input * vector of pointers must have NULL as its terminating element. * The output vector will also terminate with NULL. * NOTE: The vector returned contains pointers into the input * vector. Do not, therefore, mess with the contents of the output * vector. * * If NULL is returned, nothing matched, or there was some other * error. */ matchv( src, vec, mx) char *src; char *vec[]; char *mx[]; { char *m[MAXMATCHES]; char *s1, *s2; int i,j, slen; if ( (slen = strlen(src)) == 0 ) return(NULL); for(i=0, j=0; vec[i] != NULL && j < MAXMATCHES; i++) { if ( strcmp(src,vec[i]) == 0 ) { /* exact match! */ m[0] = vec[i]; j = 1; break; } else if ( strncmp(src,vec[i],slen) == 0 && strlen(vec[i]) >= slen ) m[j++] = vec[i]; } m[j] = NULL; for(i=0; i <= j; mx[i] = m[i], i++); return(j); } /* ***************************************************************** * fkoff: fork a process and return the exit status of the process. * * This routine takes a command line separated into words, and * uses vfork(2) and execve(2) to quickly run the program. * * This is a simplified version of the fkoff() routine from dcon(8). * Here, a program name and up to four arguments may be passed in. * If one of them is null, FINE, but the fifth * */ fkoff(prg,arg1,arg2,arg3,arg4) char *prg, *arg1, *arg2, *arg3, *arg4; { char *command, *malloc(), *argvec[6]; int pid, stat, i; for(i=0; i < 6; argvec[i++] = NULL); argvec[0] = prg; argvec[1] = arg1; argvec[2] = arg2; argvec[3] = arg3; argvec[4] = arg4; if (argvec[0] == NULL) return(-1); command = malloc( strlen(argvec[0]) + 1); strcpy(command,argvec[0]); Set_Tc_Init( (stdin->_file) ) ; #ifdef USG pid = fork(); #else pid = vfork(); /* fork 2 copies of us */ #endif if (pid < 0 ) { fflush(stdout); perror("help: fork"); Set_Tc_Here( (stdin->_file) ) ; return(0); } else if (pid == 0) { /* we are child, execve the program. */ pid = execve(command,argvec,environ); _exit(1); } else { /* we are parent, wait for child */ pid = wait(&stat); Set_Tc_Here( (stdin->_file) ) ; if (pid == -1) return(pid); stat = stat / 0400; /* get hi byte of status */ return(stat); } } /* ************************************************************** * makewvec: make a vector of words, up to N of them * * This routine uses index to simply parse a string * made up of (possibly) several blank-separated words. * The words are stored into the slots of a vector, as pointers * into the original string. Therefore, the original string * is destroyed. */ makewvec(sstr, rvec, veccnt) char *sstr; char *rvec[]; int veccnt; { int mcnt = 0; int done = 0; int i,j; char *s1, *s2, *index(); if (strlen(sstr) == 0) { rvec[0] = NULL; } else { /* skip leading whitespace */ for(s1=sstr; iswhite(*s1); s1++); for(mcnt = 0, done = 0; !done; mcnt++) { s2 = index(s1,' '); if (s2 == NULL) s2 = index(s1,' '); if (s2 == NULL) s2 = index(s1,'\n'); if (s2 != NULL) *s2 = '\0'; rvec[mcnt] = s1; if (mcnt + 1 >= veccnt) break; if (s2 == NULL) done = 1; else { /* skip more white space */ for(s1 = s2+1; iswhite(*s1); s1++); if (*s1 == '\0') done = 1; } } rvec[mcnt] = NULL; } return(mcnt); } /* *************************************************************** * xref_matchv: return all cross-ref matches for a topic * * This routine accepts a string and a vector of strings. * The string is supposed to be a referal to one or more * cross-reference file names in the vector. The matches are * placed in a another vector (which is in a static area) and * a pointer to that vector returned. Note that the input * vector of pointers must have NULL as its terminating element. * The output vector will also terminate with NULL. * NOTE: The vector returned contains pointers into the input * vector. Do not, therefore, mess with the contents of the output * vector. * NOTE: cross-ref file names in the topix vector begin with * the flag char '@' and still have their extension of .XREF. * * If 0 is returned, nothing matched, or there was some other error. */ xref_matchv( src, vec, mx) char *src; char *vec[]; char *mx[]; { char *m[MAXMATCHES]; char *s1, *s2; char one_ref[MAXNAMELEN]; int i,j, slen; if ( (slen = strlen(src)) == 0 ) return(NULL); for(i=0, j=0; vec[i] != NULL && j < MAXMATCHES; i++) { if ( *vec[i] != '@' ) break; for(s1 = vec[i], s2 = one_ref; *s1 != '\0' && *s1 != '.'; *s2++ = *s1++); *s2 = '\0'; if ( strcmp(src,one_ref) == 0) { /* exact match! */ m[0] = vec[i]; j = 1; break; } else if (strncmp(src,(vec[i] + 1),slen) == 0 && strlen(vec[i]) >= slen) m[j++] = vec[i]; } m[j] = NULL; for(i=0; i <= j; mx[i] = m[i], i++); return(j); } /* ************************************************************** * do_xref: given a vector of cross-reference filenames, do them * * This routine follows a set of cross references. * Technically, there are two kinds of cross-reference files, * distinguished by the first character of their contents. * If the first character is an at sign (@) the file is a * ``direct'' cross-reference and the rest of the line starting * with the @ sign is a vector of words that give an ABSOLUTE * help path. If the first char is not @ then the file is an * ``apologetic'' cross-reference, and the text in it is a * lame-brained excuse for why the topic in question is not * documented in help. * * For each filename in the vector, the following actions * are performed. * * 1. check that the file exists and is readable, by * opening it for reading. * 2. If this is not the first xref, ask the user if * he wants to look at this one, if so, proceed to * 3. Read the first line of the file and check it. * a. if first char is not '@', do a more(1) * on the file with fkoff. * b. otherwise, read the first line, and * use makewvec to parse it into words. * pass these words to a new invokation * of help1. Make the ppt for help1 * be something that denotes a cross-ref. */ do_xref(ppt,src,mx) char *ppt, *src; char *mx[]; { int i,j,k; char one_ref[MAXNAMELEN]; char *s1, *index(); for(i=0; mx[i] != NULL; i++) { strcpy(one_ref,mx[i]+1); if (i > 0) { j = takeit("\nNext %s %s cross-reference: %s\nTake it?", ppt,src,one_ref); if (j == 1) continue; if (j == -1) break; } do_one_xref(one_ref,src,ppt); } putchar('\n'); return(0); } /* ************************************************************** * do_one_xref: evaluate and do a single cross-reference * * This routine accepts a single file name and src name * for a single cross-reference. It then checks the file * for type, and performs the appropriate action. * * If the file is not accessible, an error message is * printed. */ do_one_xref(xref_file,src,ppt) char *xref_file, *src, *ppt; { int i,j; FILE *xr_fp, *fopen(); char *wvec[MAXMATCHES]; char *index(), *s1, lbuf[MAXLINELEN], xbuf[MAXNAMELEN+15]; if ( (xr_fp = fopen(xref_file,"r")) == NULL) { printf(" Sorry, %s cross-reference file inaccessible.\n",xref_file); return(-1); } if (fgets(lbuf,MAXLINELEN,xr_fp) != NULL) { if ( *lbuf != '@' ) { s1 = index(xref_file,'.'); if (s1 != NULL) *s1 = '\0'; printf(" Cross reference text called \"%s\" is available\n", xref_file); if (ppt != NULL) printf("\n HELP: %s <<%s>>\n",ppt,xref_file); if (s1 != NULL) *s1 = '.'; fclose(xr_fp); fkoff(VIEWPROGRAM,VIEWPROGOPTS1,VIEWPROGOPTS2,xref_file,NULL); } else { char pptbuf[MAXNAMELEN+13], pathbuf[256]; makewvec(lbuf+1,wvec,MAXMATCHES); printf("\n following cross-reference `help"); for(i=0; wvec[i] != NULL; i++) printf(" %s",wvec[i]); s1 = index(xref_file,'.'); if (s1 != NULL) *s1 = '\0'; printf("' to find help for %s\n\n",xref_file); sprintf(pptbuf,"<<%s>> ",xref_file); if (s1 != NULL) *s1 = '.'; getwd(pathbuf); chdir(helpdir); help1(pptbuf,wvec,1,1, 0); chdir(pathbuf); putchar('\n'); } } else { printf(" Sorry, cross-ref file %s seems to be empty!\n\n",xref_file); } return(0); } /* ************************************************************** * printlist - print a list of strings in columnar or list form * * This routine takes a list of names in a vector and prints * the list according to the current help mode. This code * used to be in printtopix() but I decided to move it here * so it could be used for other things. */ printlist(vec,namecnt) char *vec[]; int namecnt; { int i,j,k; int longlen, namewidth, rowcnt, colcnt, xrefs; char *s1, *s2; static char row[TERMWID+1]; longlen = xrefs = 0; for(i=0; i < namecnt; i++ ) { if (*vec[i] == '@') xrefs++; else longlen = ((k=strlen(vec[i]))>longlen)?k:longlen; } /* here print the names out in nice columns */ namewidth = longlen + COLUMNSPACE; rowcnt = TERMWID / namewidth; colcnt = (namecnt + (rowcnt-1)) / rowcnt ; if (colcnt <= 0) colcnt = 1; if (col_flag && rowcnt >= 1) { printf("\n"); for(i=0; i < colcnt ; i++ ) { for(k=0; k < TERMWID; row[k++] = ' '); row[k] = '\0'; for(j=0, s1 = row; (i+j+xrefs) < namecnt; j += colcnt) { row[strlen(row)] = ' '; strcpy(s1,vec[i+j+xrefs]); s1 = s1 + namewidth; } printf(" %s\n",row); } printf("\n"); } else { for(i=xrefs; i < namecnt; i++) printf("%s\n",vec[i]); } } #ifdef BSD /* **************** funky keyboard stuff below here ************* */ /* ************************************************************** * pushback - push a string back onto the tty input queue * * This routine accepts a string and pushes it onto the tty * input queue, as if the user had typed it. * The input is a file descriptor that points to a tty, and * a pointer to the string. */ pushback(fd, str) int fd; char *str; { char *s1; for(s1=str; *s1; ioctl( fd, TIOCSTI, s1++)); } /* ************************************************************** * getpending - get pending chars from tty, then push them back * * This routine grabs chars pending on a specified file * descriptor, puts them into the specified buffer, and pushes * them back onto the tty input queue. * The buffer passed by the caller had BETTER be long enough, * or everything gets munched. * If the parameter no_pushback is non-zero, the characters are * not pushed back. * The number of characters obtained is returned. */ getpending(fd, buf, buflen, no_pushback) int fd; char *buf; int buflen, no_pushback; { int i,j; char x = '\n'; ioctl(fd, FIONREAD, &i); if (i <= 0) return(0); j = read(fd, buf, buflen); buf[j-1] = '\0'; for(i = j-2; buf[i] > '\030' ; buf[i--] = '\0' ); if (i <= 0) return(0); if ( !no_pushback) pushback(fd, buf); return(i); } /* ************************************************************** * init_funky - initialize funky stuff, and save startup values * * This routine starts up breaking for ESC, and * saves the initial tchars structure. */ init_funky() { char *malloc(); if ( isatty(0) ) { is_tty = 1; init_tchars = (struct tchars *) malloc( sizeof(struct tchars)); here_tchars = (struct tchars *) malloc( sizeof(struct tchars)); ioctl(0,TIOCGETC,init_tchars); ioctl(0,TIOCGETC,here_tchars); here_tchars->t_brkc = '\033'; ioctl(0,TIOCSETC,here_tchars); } return; } /* ************************************************************** * read_gets: do a gets by read(2)ing a line * * Read a string into a buffer, from the given file * descriptor. There are four arguments: * * fd file descriptor (usually 0) * buf char * to buffer * blen buffer length in bytes * spec do special ^[ and ^D processing * * If the special processing is enabled, ESC invokes helpname * completion, and ^D invokes possible completion listing. * When the user finally inputs a line that does not require * special processing, it is returned as a string to caller. * If anything goes wrong, or a true EOF is given, -1 is * returned. * When special processing is not enabled, the input line is * simply returned to the caller as a string. */ read_gets(fd, buf, blen, do_special) int fd, blen, do_special; char *buf; { char *s1, *s2, endc; static char bufcopy[MAXLINELEN]; static char *wvec[MAXMATCHES]; int i,j,k; for(endc = '\0'; endc != LF ; ) { fflush(stdout); i = read(fd, buf, blen); /* now, having read data, analyze it for special stuff */ if (i <= 0) return(-1); else { s1 = buf + (i - 1); if (*s1 == ESC || *s1 == LF || *s1 == RET) { endc = (*s1 == ESC)?(*s1):(LF); *s1 = '\0'; } else { *++s1 = '\0'; endc = *s1; } for(s1 = buf + (strlen(buf) - 1); *s1 == ' ' || *s1 == ' '; *s1-- = '\0'); for(s1 = buf; *s1 == ' ' || *s1 == ' '; s1++); if (endc == LF || endc == RET || do_special == 0) return(i-1); else { *gbl_match = '\0'; /* if this is a non-topic command, just beep */ if ( index( SPEC_CHARS, *s1) ) { fputs(" ",stdout); for(s1 = buf; *s1; s1++) fputs("",stdout); fflush(stdout); pushback(stdin->_file,buf); continue; } else /* do special stuff here */ if (endc == ESC) { strcpy(bufcopy,buf); i = makewvec(bufcopy, wvec, MAXMATCHES); help1("", wvec, 0, 0, ESC_COMP); fputs(" ",stdout); for(s1 = buf; *s1; s1++) fputs("",stdout); fflush(stdout); pushback(stdin->_file,buf); if ( *gbl_match ) { pushback(stdin->_file,(gbl_match + strlen(wvec[i-1]))); } else fputs("",stdout); fflush(stdout); } else if (endc == '\0') { /* must have been eof */ strcpy(bufcopy,buf); i = makewvec(bufcopy, wvec, MAXMATCHES); help1("" , wvec, 0, 0, CTRLD_COMP); printf("\n%s ",gbl_ppt); if (*gbl_match == '\0') fputs("", stdout); fflush(stdout); pushback(stdin->_file,buf); } } } } } #endif