home *** CD-ROM | disk | FTP | other *** search
-
- /******************************************************************************
- # #
- # #
- # e - Command line preprocessor for vi. Version 1.2 - November 1987. #
- # ====================================================================== #
- # #
- # #
- # Terry Jones #
- # Department of Computer Science #
- # University of Waterloo #
- # Waterloo, Ontario, Canada. N2L 3G1 #
- # #
- # {ihnp4,allegra,decvax,utzoo,utcsri,clyde}!watmath!watdragon!tcjones #
- # tcjones@dragon.waterloo.{cdn,edu} tcjones@WATER.bitnet #
- # tcjones%watdragon@waterloo.csnet #
- # #
- #*****************************************************************************/
-
-
- #include "e.h"
-
-
- /*
- * Globals. All in the name of ease and perhaps speed.
- *
- */
-
- char history[HIST_CHARS];
- char arg[ARG_CHARS];
- char *hist[HIST_LINES];
- char temp[HIST_CHARS];
- char *tmp_file=".e_tmpXXXXXX";
- char erase;
- char *myname;
-
-
- clean_up()
- {
- /*
- * This is where we come when an interrupt is received.
- * Just get out after making sure things are tidy.
- *
- */
-
- e_error("\n%s","Interrupt.");
- }
-
-
-
- main(c,v)
- int c;
- char **v;
- {
- myname=v[0];
- terminal(TERM_RECORD);
-
- /* handle SIGINT */
- if (signal(SIGINT,SIG_IGN)!=SIG_IGN){
- signal(SIGINT,clean_up);
- }
-
-
-
- /*
- * Process the command line. This gets a little messy as there are so
- * many ways e can be invoked. They are listed below and there is an
- * example provided in each case statement to illustrate the
- * particular case we are trying to handle.
- *
- * The idea in most cases is to get the arguments that will be passed
- * to vi into a character array (arg), and pass it to do_vi(). do_vi()
- * splits up the arguments and execs vi. Occasionally it is simpler and
- * do_vi() can be called as do_vi("fred").
- *
- *
- * Command Line Options.
- * =====================
- *
- * No arguments.
- *
- * (1) "e"
- *
- * One argument.
- *
- * (3) "e -"
- * (2) "e -n" Where n is some valid history item.
- * (4) "e -t" If they do this then they are in error!
- * (5) "e -r"
- * (6) "e -pat"
- * (7) "e +100"
- * (8) "e ."
- * (9) "e fred"
- *
- * Multiple arguments.
- *
- * (10) "e fred harry joe" Also handles "e -t tag", "e -r file" etc.
- *
- */
-
-
- switch (c){
- case 1: {
-
- /*
- * Command line option (1).
- * Example: "e"
- *
- * Just go and vi the last file that was e'ed.
- *
- */
-
- last_file();
- do_vi(arg);
- break;
- }
-
- case 2:{
- switch ((*++v)[0]){
-
- case '-':{
-
- if ((c=(*v)[1])=='\0'){
-
- /*
- * Command line option (2).
- * Example: "e -"
- *
- * This is a select from history, ask what they want.
- *
- */
-
- ask_hist();
- do_vi(arg);
- }
- else if (isdigit(c)){
-
- /*
- * Command line option (3).
- * Example: "e -3"
- *
- * Get the nth last file from the history and vi it.
- *
- */
-
- nth_hist(c-'0');
- do_vi(arg);
- }
- else if (c=='t'&&(*v)[2]=='\0'){
-
- /*
- * Command line option (4).
- * Example: "e -t"
- *
- * This is an empty tag - ignore it.
- * They have made a mistake, but let vi tell them.
- *
- */
-
- do_vi(*v);
- }
- else if (c=='r'&&(*v)[2]=='\0'){
-
- /*
- * Command line option (5).
- * Example: "e -r"
- *
- * A recover, just pass it to vi and don't interfere.
- *
- */
-
- do_vi(*v);
- }
- else{
-
- /*
- * Command line option (6).
- * Example: "e -pat"
- *
- * This is a pattern - try to match it.
- *
- */
-
- find_match(++*v);
- do_vi(arg);
- }
- break;
- }
-
- case '+':{
-
- /*
- * Command line option (7).
- * Example: "e +100"
- *
- * A command, put it before the last file name.
- *
- */
-
- insert_command(*v);
- do_vi(arg);
- break;
- }
-
- case '.':{
-
- /*
- * Command line option (8).
- * Example: "e ."
- * Example: "e .login" (falls through to option (9)).
- *
- * Just give a history list if there is only a dot.
- * Otherwise fall through as it must be a filename.
- *
- */
-
- if ((*v)[1]=='\0'){
- register ct;
- register i;
-
- read_hist();
- ct=split_hist();
-
- for (i=0;i<ct;i++){
- fprintf(stderr,"\t[%d]: %s\n",ct-i-1,hist[ct-i-1]);
- }
- exit(0);
- }
- /*
- * WARNING!
- * The switch falls through in the case where there is a
- * filename that starts with a period.
- *
- */
- }
-
- default :{
-
- /*
- * Command line option (9).
- * Example: "e fred"
- * Example: "e .login" (fell through from option (8)).
- *
- * Looks like it's just a plain old file name. vi it!
- *
- */
-
- normal(*v);
- do_vi(arg);
- break;
- }
- }
- }
-
- default:{
-
- /*
- * Command line option (10).
- * Example: "e fred harry joe"
- *
- * A bunch of arguments, fix the history & vi them all as normal.
- *
- */
-
- multiple(c,v,ARG_CHARS);
- do_vi(arg);
- break;
- }
- }
- }
-
-
-
- do_vi(thing)
- char *thing;
- {
- /*
- * Split the arguments (if any) in 'thing' up and exec vi on them.
- * The arguments must be space separated.
- *
- */
- char *args[MAX_ARGS];
- char *this,*next;
- register i;
-
- args[0]="vi";
- args[1]=thing;
-
- i=1;
- while (*thing!='\0'&&(thing=index(thing,' '))!=NULL){
- *thing++='\0';
- if (*thing!='\0'){
- args[++i]=thing;
- }
- }
- args[++i]=NULL;
-
- if (execvp(VI,args)==-1){
- e_error("%s %s","Could not execvp",VI);
- }
- }
-
-
-
- read_hist()
- {
- /*
- Read the history file and break it up into lines in the global variable
- 'history'. Do the appropriate checks to see that it exists.
- */
-
- register vh;
- register bytes;
- register offset;
- struct stat buf;
-
- /*
- * If there is no history file then say so and get out of here - they
- * had no business asking for access to the history.
- *
- */
-
- if ((vh=open(HIST,O_RDONLY))==-1){
- e_error("%s %s","Could not open",HIST);
- }
-
- /* Stat it */
- if (fstat(vh,&buf)==-1){
- e_error("%s %s","Could not stat",HIST);
- }
-
- /*
- * Set 'offset' so that we can read the last portion of the history
- * file only. If there are less than HIST_CHARS characters in the
- * file then we will start reading at 0, otherwise at HIST_CHARS
- * characters before the end of the file.
- *
- */
-
- offset=(int)buf.st_size-HIST_CHARS<0 ? 0 : buf.st_size-HIST_CHARS;
-
- /* Move (if it's non zero) to that place in the file. */
-
- if (offset&&lseek(vh,(long)offset,L_SET)==-1){
- e_error("%s %s","Could not lseek in",HIST);
- }
-
- /* And read. */
- if ((bytes=read(vh,history,HIST_CHARS))==-1){
- e_error("%s %s","read: Could not read",HIST);
- }
-
- /* If we didn't come up with ANYTHING we may as well leave. */
- if (!bytes){
- e_error("%s %s %s","Empty",HIST,"file.");
- }
-
- /* Zap the newline (which *should* be there) for now. */
- if (history[--bytes]=='\n'){
- history[bytes]='\0';
- }
-
- /* And get out of here. */
- return(bytes);
- }
-
-
-
- last_file()
- {
- /*
- * Get the last name from the 'history' array and put it into 'arg'.
- *
- */
-
- read_hist();
- if (index(history,'\n')==NULL){
- if (*history=='\0'){
- e_error("%s %s",HIST,"has a line with no newline character.");
- }
- else{
- sprintf(arg,"%s",history);
- }
- }
- else{
- sprintf(arg,"%s",rindex(history,'\n')+1);
- }
- }
-
-
-
- split_hist()
- {
- /*
- * Set the array of pointers in 'hist' to point to the succesive names
- * in the 'history' array. These are delimited (presumably) by newlines
- * and so they're easy to catch.
-
- * What in fact is done is that the history array is copied and we set
- * a pointer (in "hist") to the start of each new item and set the
- * newline characters to be NULLs. This way we don't mess up the history
- * array as we will want it intact later on (maybe).
- *
- */
-
- char *tmp;
- register count;
-
- /* Copy it. */
- sprintf(temp,"%s",history);
-
- /*
- * Now run through breaking it up, setting pointers and return the number
- * of lines we found.
- *
- */
-
- for (count=0;count<HIST_LINES;count++){
- if ((tmp=hist[count]=rindex(temp,'\n'))==NULL){
- break;
- }
- *tmp='\0';
- hist[count]++;
- }
- if (count<HIST_LINES){
- hist[count++]=temp;
- }
- return(count);
- }
-
-
-
- nth_hist(n)
- int n;
- {
- /*
- * Get the nth last filename from the list. Make use (of course) of
- * read_hist and split_hist.
- *
- */
-
- register count;
- register i;
-
- read_hist();
- count=split_hist();
- if (n>count-1){
- if (count>1){
- e_error("%s %d %s","Only",count,"history items exist.");
- }
- else{
- e_error("%s","Only one history item exists.");
- }
- }
- sprintf(arg,"%s",hist[n]);
-
- /* Rebuild the history with the selected name at the bottom. */
-
- reconstruct(n,count);
- }
-
-
-
- ask_hist()
- {
- /*
- * Ask the outside world which of the files in the history is wanted.
- * set the terminal to cbreak.
- *
- */
-
- register i;
- register count;
- char *last;
- register option;
- struct sgttyb blk;
-
- /* Read and split the history file. */
- read_hist();
- count=split_hist();
-
- /* Print the history. */
- for (i=0;i<count;i++){
- fprintf(stderr,"\t[%d]: %s\n",count-i-1,hist[count-i-1]);
- }
-
- /* Give them a prompt (of sorts). */
- fprintf(stderr,"select -> ");
-
- /* Set the terminal up. */
- terminal(TERM_SET);
-
- /* Get their response. */
- option=getc(stdin);
-
- /* Make the terminal 'safe' again. */
- terminal(TERM_RESET);
-
- /*
- * Process the option and put the appropriate file name into the
- * arg variable.
- *
- */
-
- if (option=='\n'){
- /* They want the last file of the list. */
- fprintf(stderr,"%s\n",hist[0]);
- sprintf(arg,"%s",hist[0]);
- return;
- }
- else if (option==(int)erase){
- /* They want to leave. */
- fprintf(stderr,"\n");
- exit(1);
- }
- else if (option>='0'&&option<='0'+count-1){
- /* They have requested a file by its number. */
- option=option-'0';
- fprintf(stderr,"%s\n",hist[option]);
- sprintf(arg,"%s",hist[option]);
- reconstruct(option,count);
- return;
- }
- else{
- /*
- * Looks like they want to name a specific file. Echo the
- * character back to the screen.
- *
- */
-
- fprintf(stderr,"%c",option);
- arg[0]=option;
- i=1;
- while ((arg[i]=getc(stdin))!='\n'){
- i++;
- }
- arg[i]='\0';
-
- /* Seeing as they typed in the name, try and help with spelling. */
- if (!spell_help()){
- find();
- }
-
- /* If it is in the history then reconstruct and return. */
- for (i=0;i<count;i++){
- if (!strcmp(hist[i],arg)){
- reconstruct(i,count);
- return;
- }
- }
-
- /* Otherwise reconstruct, leaving out the oldest name. */
- reconstruct(count-1,count);
- }
- }
-
-
-
- FILE *
- get_temp()
- {
- /* Get ourselves a temporary file for the reconstructed history. */
- FILE *fp,*fopen();
-
- mktemp(tmp_file);
- if ((fp=fopen(tmp_file,"w"))==NULL){
- e_error("%s %s","Could not open temporary file",tmp_file);
- }
- return(fp);
- }
-
-
-
- close_temp(fp)
- FILE *fp;
- {
- /* Move the temporary file to be the new history. */
- FILE *fclose();
-
- if (fclose(fp)==(FILE *)EOF){
- e_error("%s %s","Could not close",tmp_file);
- }
-
- if (rename(tmp_file,HIST)!=0){
- e_error("%s %s %s %s","Could not rename",tmp_file,"to",HIST);
- }
- }
-
-
-
- terminal(what)
- int what;
- {
- /*
- * Handles the terminal. Must be first called as
- *
- * terminal(TERM_RECORD)
- *
- * which remembers the initial terminal charcteristics and sets up the
- * "erase" variable. Thereafter can be called as
- *
- * terminal(TERM_SET) -- to turn on CBREAK and ECHO off.
- * terminal(TERM_RESET) -- to set the terminal to its original state.
- *
- */
-
- register i;
-
-
- #ifdef sysV
- static struct termio initial_blk;
- static struct termio set_blk;
- #else
- static struct sgttyb initial_blk;
- static struct sgttyb set_blk;
- #endif sysV
-
-
-
- switch(what){
-
- case TERM_RECORD:{
-
- #ifdef sysV
- if (ioctl(0, TCGETA, &initial_blk)==-1){
- e_error("%s","Could not ioctl stdin.");
- }
-
- #ifdef STRUCT_ASST
- /* Copy the structure in one hit. */
- set_blk=initial_blk;
- #else
- /* Copy the structure field by field. */
- set_blk.c_iflag=initial_blk.c_iflag;
- set_blk.c_oflag=initial_blk.c_oflag;
- set_blk.c_cflag=initial_blk.c_cflag;
- set_blk.c_line=initial_blk.c_line;
-
- for (i=0;i<NCC;i++){
- set_blk.c_cc[i]=initial_blk.c_cc[i];
- }
- #endif STRUCT_ASST
-
- /* And now set up the set_blk. */
- set_blk.c_lflag=(initial_blk.c_lflag &= ~(ICANON|ECHO|ECHONL));
- erase=set_blk.c_cc[VERASE];
- set_blk.c_cc[VMIN]=1;
- set_blk.c_cc[VTIME]=0;
- #else
- if (ioctl(0, TIOCGETP, &initial_blk)==-1){
- e_error("%s","Could not ioctl stdin.");
- }
-
- #ifdef STRUCT_ASST
- /* Copy the structure in one hit. */
- set_blk=initial_blk;
- #else
- /* Copy the structure field by field. */
- set_blk.sg_ispeed=initial_blk.sg_ispeed;
- set_blk.sg_ospeed = initial_blk.sg_ospeed;
- set_blk.sg_erase = initial_blk.sg_erase;
- set_blk.sg_kill = initial_blk.sg_kill;
- set_blk.sg_flags = initial_blk.sg_flags;
- #endif STRUCT_ASST
-
-
- /* And now set up the set_blk. */
- erase = set_blk.sg_erase;
-
- /* Go into CBREAK mode or stay that way if we are already. */
- set_blk.sg_flags |= CBREAK;
-
- /* Turn off echo. */
- set_blk.sg_flags &= ~ECHO;
-
- #endif sysV
-
- break;
- }
-
-
-
- case TERM_SET:{
-
- #ifdef sysV
- if (ioctl(0, TCSETA, &set_blk)==-1){
- e_error("%s","Could not ioctl stdin");
- }
- #else
- if (ioctl(0, TIOCSETP, &set_blk)==-1){
- e_error("%s","Could not ioctl stdin");
- };
- #endif sysV
-
- break;
- }
-
-
-
-
-
- case TERM_RESET:{
-
- #ifdef sysV
- if (ioctl(0, TCSETA, &initial_blk)==-1){
- e_error("%s","Could not ioctl stdin");
- }
- #else
- if (ioctl(0, TIOCSETP, &initial_blk)==-1){
- e_error("%s","Could not ioctl stdin");
- }
- #endif sysV
-
- break;
- }
-
-
- default:{
- /* Look! - no ifdefs here. */
- e_error("%s %d","terminal() called with unknown parameter",what);
- }
- }
- }
-
-
-
- match(argument,pattern)
- char *argument;
- char *pattern;
- {
- /*
- * Boneheaded but easy pattern matcher. Just see if the 'pattern'
- * exists anywhere in the 'argument'. Boyer-Moore who?
- * In general our patterns will be so short that it wouldn't be
- * worth the effort to set up a better algorithm.
- *
- */
-
- register length=strlen(pattern);
-
- while (strlen(argument)>=length){
- if (!strncmp(argument++,pattern,length)){
- return(1);
- }
- }
- return(0);
- }
-
-
-
- find_match(pattern)
- char *pattern;
- {
- /*
- * Find the name in the history list that contains the 'pattern'.
- * if it exists then put it into the 'arg' variable and otherwise
- * announce that a match couldn't be found and leave.
- *
- */
-
- register count;
- register i;
-
- /* Read and split the history file. */
- read_hist();
- count=split_hist();
-
- /*
- * Try for a match with each file in turn (note that we are working
- * from most-recently-used backwards - probably a good thing).
- *
- */
-
- for (i=0;i<count;i++){
- if (match(hist[i],pattern)){
- sprintf(arg,"%s",hist[i]);
- reconstruct(i,count);
- return;
- }
- }
-
- /* We couldn't match so get out of here. */
- e_error("%s \"%s\".","Unable to match with",pattern);
- }
-
-
-
- insert_command(command)
- char *command;
- {
- /*
- * They want the last file in the history but want to preceed it
- * this time with a command - no problems here.
- *
- */
-
- register count;
- char *place;
-
- /* read and split the history. */
- read_hist();
- count=split_hist();
-
- /*
- * If there was already a command there (indicated by a '+') then we
- * want to get rid of it. If there is a '+' but no ' ' (somewhere) after
- * it then the history file is in disarray and we will not try to recover.
- *
- */
-
- if (*hist[0]=='+'){
- if ((place=index(hist[0],' '))==NULL){
- e_error("%s %s",HIST,"file corrupted, + but no following space");
- }
- /* Move over white space - if there is any. */
- while (*place==' '||*place=='\t'){
- place++;
- }
- }
- else{
- /* There was no command preceeding the last file in the history. */
- place=hist[0];
- }
-
- /* Put the new command and the filename into 'arg' */
- sprintf(arg,"%s %s",command,place);
-
- /* Rebuild the history with the selected command and name at the bottom. */
- reconstruct(0,count);
- }
-
-
-
- reconstruct(except,count)
- int except;
- int count;
- {
- /*
- * Reconstruct history file excepting the 'except' last.
- * So just copy all lines but the 'except'th last and then put in 'arg'
- * which contains the new line for the history.
- *
- */
-
- register i;
- FILE *tv,*get_temp();
-
- /* Get a temporary file. */
- tv=get_temp();
-
- /* Put in the lines we still want. */
- for (i=count-1;i>=0;i--){
- if (i!=except){
- fprintf(tv,"%s\n",hist[i]);
- }
- }
-
- /* Put in the new line from 'arg'. */
- fprintf(tv,"%s\n",arg);
-
- /* Rename the temporary to be the new history file. */
- close_temp(tv);
- }
-
-
-
- normal(string)
- char *string;
- {
- /*
- * A normal filename was found, put it into arg. First of all if there
- * is a history and the file is already in it (which means they could
- * have gotten to this file in other ways), then reconstruct the history
- * as though they had. Also offer spelling help.
- *
- */
-
- register count;
- register i;
-
- /* Put it into 'arg'. */
- sprintf(arg,"%s",string);
-
- /* If there is a history file. */
- if (got_vi()){
-
- /* Read it and split it up. */
- read_hist();
- count=split_hist();
-
- /* If it is in the history then reconstruct and return. */
- for (i=0;i<count;i++){
- if (!strcmp(hist[i],arg)){
- reconstruct(i,count);
- return;
- }
- }
-
- /* It's not in the history, help with spelling then reconstruct. */
- if (!spell_help()){
- find();
- }
-
- /* If it is in the history then reconstruct and return. */
- for (i=0;i<count;i++){
- if (!strcmp(hist[i],arg)){
- reconstruct(i,count);
- return;
- }
- }
-
- reconstruct(HIST_LINES,count);
- }
- else{
-
- /*
- * There is no history around so help with spelling and set up a
- * history for next time.
- *
- */
-
- if (!spell_help()){
- find();
- }
- new_vi();
- }
-
- }
-
-
-
- multiple(number,args,size)
- int number;
- char **args;
- {
- /*
- * There were several names on the command line so we just strcat them
- * into the 'arg' array. Check to see that the length of all the args
- * will not be greater than "size" or else we will overflow arg.
- *
- * The total argument length must be at most size-1 characters, including
- * spaces. arg needs to have a trailing '\0' so that do_vi() wont break.
- *
- */
-
- register count;
- register i;
- register total=0;
-
- *arg='\0';
- while (--number){
- if ((total+=strlen(*(args+1)))>=size){
-
- /*
- * If you are running e and you find that this condition occurs,
- * the solution is to simply increase the value of the #define
- * line for ARG_CHARS in e.h.
- *
- */
-
- fprintf(stderr,
- "%c%c%cWarning! Argument list too long, truncated after \"%s\".\n",
- BELL,BELL,BELL,*args);
- sleep(2); /* Give them some chance to see what happened. */
- break;
- }
-
- strcat(arg,*++args);
- if (number>1){
- strcat(arg," ");
-
- /*
- * Add one to total for the space. There's no need to check for
- * overflow here as we know there is another argument since
- * number > 1 still. Thus if this overflows arg, then it is going
- * to be caught anyway in the test at the top of the while loop.
- *
- */
-
- total++;
- }
- }
-
- /*
- * Now if there is a history file and we can find an identical line
- * then reconstruct with that line at the bottom.
- *
- */
-
- if (got_vi()){
- read_hist();
- count=split_hist();
- for (i=0;i<count;i++){
- if (!strcmp(hist[i],arg)){
- reconstruct(i,count);
- return;
- }
- }
-
- /*
- * Rebuild, including everything but the counth last (i.e. make
- * a new history by omitting the oldest file in the current one and
- * putting 'arg' on the end.
- *
- */
-
- reconstruct(HIST_LINES,count);
- }
- else{
- /* There was no history file so try to give them one for next time. */
- new_vi();
- }
- }
-
-
-
- got_vi()
- {
- /* Indicate if there is a history file that they own or otherwise. */
- struct stat buf;
-
- if (stat(HIST,&buf)==-1){
- return(0);
- }
- else{
- return(getuid()==buf.st_uid);
- }
- }
-
-
-
- new_vi()
- {
- /*
- * Attempt to make a new history file.
- * During which several things could go wrong.
- *
- */
-
- FILE *vh,*fopen(),*fclose();
- struct stat buf;
-
- /* If you can't read the current directory, get out. */
- if (stat(".",&buf)==-1){
- e_error("%s \".\"","Could not stat");
- }
-
- /* If you own the directory (you can't get a history in /tmp). */
- if (getuid()==buf.st_uid){
-
- /* If we can't make a history, get out. */
- if ((vh=fopen(HIST,"w"))==NULL){
- e_error("%s %s %s","Could not open",HIST,"for writing.");
- }
-
- /* Put in the 'arg' that we will be vi'ing in a second. */
- fprintf(vh,"%s\n",arg);
-
- /* Close the history. */
- if (fclose(vh)==(FILE *)EOF){
- e_error("%s %s","Could not close",HIST);
- }
-
- /* Give the history some protection - for those who want it! */
- if (chmod(HIST,E_MODE)==-1){
- e_error("%s %s","Could not chmod",HIST);
- }
- }
- }
-
-
-
-
- spell_help()
- {
- /*
- Unashamedly stolen (and modified) from "The UNIX Programming
- Environment" - Kernighan and Pike.
-
- Read the directory and if the file they want (in 'arg') does not
- exist then see if there is one that does that has similar spelling
- to what they requested. Offer the change and handle the reply.
- */
-
- register dist;
- DIR *dp, *opendir();
- struct direct *readdir();
- struct direct *entry;
- register len;
- struct stat buf;
- int ch;
-
- /* If the file already exists just return - they don't need help. */
- if (stat(arg,&buf)==0){
- return(1);
- }
-
- /* If the current directory can't be read then return. */
- if ((dp=opendir("."))==NULL){
- return(0);
- }
-
- /* Get the length of what we are seeking to cut down on strcmping time. */
- len=strlen(arg);
-
- for (entry=readdir(dp);entry!=NULL;entry=readdir(dp)){
-
- register int dlen=entry->d_namlen;
-
- /* Try to stat the entry. */
- if (stat(entry->d_name,&buf)==-1){
- continue;
- }
-
- /* If it's not a regular file then continue. */
- if ((buf.st_mode&S_IFMT)!=S_IFREG){
- continue;
- }
-
- /*
- * If this entry has
- *
- * length == sought length +/- 1
- *
- * then it should be checked.
- *
- */
-
- if (entry->d_ino && dlen>=len-1 && dlen<=len+1){
-
- /*
- * If the distance between this name and the one the user enetered
- * is too great then just continue.
- *
- */
-
- if (sp_dist(entry->d_name,arg)==3) continue;
-
-
- /* Otherwise offer them this one. */
- terminal(TERM_SET);
- fprintf(stderr,"correct to %s [y]? ",entry->d_name);
-
- /* Get and process the reply. */
-
- ch=getc(stdin);
- terminal(TERM_RESET);
-
- if (ch=='N'){
-
- /* No, and they mean it. Offer no more help. */
- fprintf(stderr,"No!\n");
- break;
- }
-
- else if (ch=='n'){
-
- /* No, but they'd like more help. */
- fprintf(stderr,"no\n");
- continue;
- }
-
- else if (ch=='q'||ch=='Q'||ch==(int)erase){
-
- /* Quit. */
- fprintf(stderr,"quit\n");
- closedir(dp);
- exit(0);
- }
-
- else{
-
- /* Yes. */
- fprintf(stderr,"yes\n");
- closedir(dp);
- strcpy(arg,entry->d_name);
- return(1);
- }
- }
- }
-
- closedir(dp);
- return(0);
- }
-
-
-
- sp_dist(s,t)
- char *s;
- char *t;
- {
- /*
- * Stolen from the same place as spell_help() above.
-
- * Work out the distance between the strings 's' and 't' according
- * to the rough metric that
- *
- * Identical = 0
- * Interchanged characters = 1
- * Wrong character/extra character/missing character = 2
- * Forget it = 3
- *
- */
-
- while (*s++==*t){
- if (*t++=='\0'){
- /* identical */
- return(0);
- }
- }
-
- if (*--s){
- if (*t){
- if (s[1]&&t[1]&&*s==t[1]&&*t==s[1]&&!strcmp(s+2,t+2)){
- /* Interchanged chars. */
- return(1);
- }
- if (!strcmp(s+1,t+1)){
- /* Wrong char. */
- return(2);
- }
- }
- if (!strcmp(s+1,t)){
- /* Extra char in 't'. */
- return(2);
- }
- }
- if (!strcmp(s,t+1)){
- /* Extra char in 's'. */
- return(2);
- }
-
- /* Forget it. */
- return(3);
- }
-
-
-
-
- find()
- {
- /*
- * This takes the environment variable which is #defined as PATH and
- * extracts the directory names from it. They may be separated by
- * arbitrary numbers of delimiter characters (currently "\n", "\t", " "
- * and ":"). Each directory is then checked to see if it contains the
- * desired filename (with a call to check). Spelling corrections are
- * not attempted.
- *
- */
-
- extern char *getwd();
- extern char *getenv();
- char *p;
- char path[MAX_PATH];
- char *dir;
- char *space;
- char *current_dir;
- char wd[MAXPATHLEN];
- char what[ARG_CHARS];
-
- if (!(p=getenv(E_PATH))) return;
-
- if (strlen(p)>=MAX_PATH){
- e_error("%s %s %s %d.","Length of",E_PATH,"variable exceeds",MAX_PATH);
- }
-
- strcpy(path,p);
- strcpy(what,arg);
-
- if (!(current_dir=getwd(wd))){
- e_error("%s","Could not get working directory.");
- }
-
- dir=path;
-
- /* Skip initial delimiters in the PATH variable. */
- while (*dir && is_delim(dir)) dir++;
-
- if (!*dir) return(0); /* There was nothing there but delimiters! */
-
- space=dir+1;
-
- while (*space){
-
- /* Move "space" along to the first non delimiter. */
- while (*space && !is_delim(space)) space++;
-
- if (*space){
- *space='\0';
- space++;
- }
-
- /* Skip any white space between directory names. */
- while (*space && is_delim(space)) space++;
-
- /* Check the directory "dir" for the filename "what". */
- if (check(what,dir)){
-
- /* Offer them dir/what. */
- terminal(TERM_SET);
- fprintf(stderr,"%s/%s [y]? ",dir,what);
-
- /* Process the reply. */
- switch (getc(stdin)){
- case 'N':
- case 'n':{
- fprintf(stderr,"no\n");
- terminal(TERM_RESET);
- break;
- }
-
- case 'q':
- case 'Q':{
- fprintf(stderr,"quit\n");
- terminal(TERM_RESET);
- exit(0);
- break;
- }
-
- default :{
- fprintf(stderr,"yes\n");
- terminal(TERM_RESET);
- if (chdir(current_dir)==-1){
- e_error("%s %s","Could not chdir to",current_dir);
- }
- sprintf(arg,"%s/%s",dir,what);
- return(1);
- }
- }
- }
- dir=space;
- }
-
- /* Go back to the original directory. */
- if (chdir(current_dir)==-1){
- e_error("%s %s","Could not chdir to",current_dir);
- }
- return(0);
- }
-
-
-
- check(target,dir)
- char *target;
- char *dir;
- {
- /*
- * Checks to see if the name "target" can be found in the directory "dir".
- *
- */
-
- DIR *dp, *opendir();
- struct direct *readdir();
- struct direct *entry;
- struct stat buf;
-
- if ((dp=opendir(dir))==NULL){
- fprintf(stderr,"Cannot open \"%s\"\n",dir);
- return(0);
- }
-
- for (entry=readdir(dp);entry!=NULL;entry=readdir(dp)){
- if (!strcmp(entry->d_name,target)){
- if (chdir(dir)==-1){
- perror("chdir");
- return(0);
- }
-
- if (stat(entry->d_name,&buf)==-1){
- /*
- * At this point I used to have perror() give a message and
- * the function return. Then one day e ran across an unresolved
- * symbolic link at this point. The filename existed in the
- * search directory, but it could not be stat'd as the thing
- * it was supposedly linked to had been removed.
- *
- * The easiest thing (I think) to do is to ignore it.
- */
-
- fprintf(stderr,
- "%c%c%cWarning: Suspected unresolved symbolic link %s/%s\n",
- BELL,BELL,BELL,dir,entry->d_name);
- sleep(2);
- continue;
- }
-
- /*
- If it is not a directory and EITHER you own it and can
- read it OR you don't own it and it is readable by others,
- OR you are in the group of the owner and it's group readable
- - then this is it.
- */
-
- if ( ((buf.st_mode&S_IFMT)==S_IFREG) &&
- (
- (buf.st_uid==getuid() && buf.st_mode&S_IREAD)
- ||
- (buf.st_gid==getgid() && buf.st_mode&G_READ)
- ||
- (buf.st_uid!=getuid() && buf.st_mode&O_READ)
- )
- )
- {
- return(1);
- }
- }
- }
- return(0);
- }
-
-
-
- /* VARARGS1 */
- e_error(a,b,c,d,e,f)
- char *a;
- {
- /*
- * Print the error message, clean up and get out.
- *
- */
-
- fprintf(stderr,"%s: ",myname);
- fprintf(stderr,a,b,c,d,e,f);
- fputc('\n',stderr);
- terminal(TERM_RESET);
- unlink(tmp_file);
- exit(1);
- }
-