Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!uunet!husc6!necntc!ncoast!allbery From: tcjones@watdragon.UUCP (Crocodile Dundee) Newsgroups: comp.sources.misc Subject: e - a friendly interface to vi Message-ID: <4848@ncoast.UUCP> Date: Mon, 12-Oct-87 22:45:52 EDT Article-I.D.: ncoast.4848 Posted: Mon Oct 12 22:45:52 1987 Date-Received: Wed, 14-Oct-87 06:19:12 EDT Sender: allbery@ncoast.UUCP Organization: U. of Waterloo, Ontario Lines: 1029 Approved: allbery@ncoast.UUCP X-Archive: comp.sources.misc/8710/9 here is a little thing i use all the time to get into vi without having to type a filename terribly often. all in the name of fewer keystrokes. try it. - terry. # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # Contents: README Makefile MANIFEST edoc e.c echo x - README sed 's/^@//' > "README" <<'@//E*O*F README//' basically e is a little thingamy to let you get into vi with more ease. i find it useful - even given a shell with history. it keeps a short history of the files that have been vi'ed most recently in each directory. thus it is nice to be able to do cd somewhere e and get straight into the file you were last working on in the directory somewhere. there is some documentation and examples in the file edoc. before compiling you should change the #define VI "/usr/ucb/vi" line in e.c if your vi is located elsewhere. and the DEST line in Makefile. this will run just fine on 4.[23] but i suspect other versions will have a little trouble. the readdir/opendir stuff of BSD is used for spelling corrections and can be easily rewritten for nonBSD - or you can simply comment out the last two functions of e.c [spell_help() and sp_dist()] and all references to them at the cost of faster but maybe a less helpful program. this appears free of major bugs (famous last words) - i'd really like to hear about anything that goes wrong &/| fixes/enhancements. comments on what is wrong and how it could be better/faster are also welcome... terry jones ----------------------here's every email address i can find--------------------- USENET : {ihnp4,allegra,decvax,utzoo,utcsri}!watmath!watdragon!tcjones UUCP : {ihnp4,decvax,utzoo}!watmath!watdragon!tcjones CSNET : tcjones%watdragon@waterloo.csnet ARPA : tcjones%watdragon%waterloo.csnet@csnet-relay.arpa Internet: tcjones@er.waterloo.edu Bitnet : tcjones@watdragon CDNnet : tcjones@er.waterloo.cdn from oz : tcjones@er.waterloo.cdn@munnari SNAIL : Terry Jones/Department of Computer Science MAIL /University of Waterloo/Waterloo/Ontario/Canada/N2L 3G1 PHONENET: 1 519 885-6686 (work?) or 1 519 884-6338 (home) -------------------------------------------------------------------------------- @//E*O*F README// chmod u=rw,g=r,o=r README echo x - Makefile sed 's/^@//' > "Makefile" <<'@//E*O*F Makefile//' CC = cc CFLAGS = -O DEST = /u/tcjones/bin/e SOURCE = e.c $(DEST) : $(SOURCE) $(DEST): $(CC) $(CFLAGS) -o $(DEST) $(SOURCE) strip $(DEST) @//E*O*F Makefile// chmod u=rw,g=rx,o=rx Makefile echo x - MANIFEST sed 's/^@//' > "MANIFEST" <<'@//E*O*F MANIFEST//' edoc e.c Makefile MANIFEST @//E*O*F MANIFEST// chmod u=rw,g=r,o=r MANIFEST echo x - edoc sed 's/^@//' > "edoc" <<'@//E*O*F edoc//' e is an interface to vi that maintains a history of the most recently e'ed files for each directory. it is used with exactly the same syntax etc as vi is. a file called .e is kept in each directory. this contains the history and is kept to a small length (<10 lines). spelling corrections are suggested for simple mistakes (omitted character, interchanged characters, extra character). the history file is rearranged with each use to place the last e'ed file at the end of the list. duplicate entries are removed (in most situations). a list of the command line variations is given below. e [vi's the last file that was e'ed in this directory] e - [prints the history for this directory and allows selection of a previous file - or a new one] e . [prints the history for this directory without asking for input] e -t tag [the usual tag entry to vi] e -r [invokes vi -r] e -r file [invokes vi -r file] e -pat [vi's the last file that was e'ed with the string 'pat' on the command line] e +cmd [vi's the last file that was e'ed in this directory but executes 'cmd' on the way into vi] e file [vi's the file and adds it to the history list. minor spelling corrections are suggested if 'file' does not exist but is close (in spelling) to some file that does] e files [vi's the files and adds them (as a single entry) to the history] "features". ========== when using "e -" the terminal is put into cbreak mode. if the first character typed is a digit (in the acceptable range of history items) then you will get the history item without further ado. thus if you have a file called 4play and you try and "e" it from within a "e -" then you'll probably end up it the wrong place. also the history length must be less than or equal to 9 (the code sets it to 8 at present (HIST_LINES)). this, i find, is enough but you may want more. the problem with having more is that with "e -" you go into cbreak and the first digit entered is taken to mean i want the nth last file. this saves the need for hitting return. do what you will, but i like it this way. with the history being kept as a most recently used list 8 file names should be enough. the numbering on "e -" and "e ." is slightly different. the "e -" option is meant so that you can look at the number next to the file name and hit it easily. with "e ." you see what files you would get if you did a "e -3" (etc) and so it makes sense that "e -1" should give the second last file. that way you can pop in and out of two files by repeated "e -1"'s (if you don't do ctrl-^). once again, that's the way it is... examples. ========= with a ".e" file containing fred.c [5]: fred.c jane [4]: jane alison "e ." will give [3]: alison +/main pete.c [2]: +/main pete.c bigmac [1]: bigmac fries juice [0]: fries juice so "e" will get you fries and then juice. "e -" will present the above and ask for a number (RETURN=0). "e -ali" will get you alison. "e +/ketchup" will get you fries and juice, searching for ketchup in fries. "e bigamc" will ask if you want to correct to bigmac. n or N will do the correction, q or Q will quit, anything else will go ahead. to be added? ============ allow backspacing on a select line. uncompress and recompress .Z files automatically? plenty of fancy things a man page @//E*O*F edoc// chmod u=rw,g=rx,o=rx edoc echo x - e.c sed 's/^@//' > "e.c" <<'@//E*O*F e.c//' /* * e.c - a friendly interface to vi * * Terry Jones {ihnp4,allegra,decvax,utzoo,utcsri}!watmath!watdragon!tcjones * Department of Computer Science * University of Waterloo * Waterloo, Ontario, Canada. N2L 3G1 * * * best vi'ed with ts=4 sw=4 * to compile: cc -o e -O e.c */ #include #include #include #include #include #include #include #include #include #include #define VI "/usr/ucb/vi" #define HIST ".e" #define HIST_LINES 8 #define HIST_CHARS 512 #define ARG_CHARS 100 #define MAX_ARGS 10 #define RUBOUT '\077' char history[HIST_CHARS]; char arg[ARG_CHARS]; char *hist[HIST_LINES]; char temp[HIST_CHARS]; char *tmp_file="._vihistXXXXXX"; void do_vi(thing) char *thing; { /* split the arguments in 'thing' up and exec vi on them */ 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){ perror(VI); exit(1); } } main(c,v) int c; char **v; { char *last_file(); extern clean_up(); /* make sure we reset the terminal on our way out if we get interrupted */ if (signal(SIGINT, SIG_IGN) != SIG_IGN){ signal(SIGINT, clean_up); } switch (c){ case 1: /* just go and vi the last file that was vi'ed */ last_file(); do_vi(arg); break; case 2: switch ((*++v)[0]){ case '-': if ((c=(*v)[1])=='\0'){ /* this is a select from history, ask what they want */ ask_hist(); do_vi(arg); } else if (isdigit(c)){ /* 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'){ /* this is an empty tag - ignore it */ do_vi(*v); } else if (c=='r'&&(*v)[2]=='\0'){ /* a recover, just pass it to vi and don't interfere */ do_vi(*v); } else{ /* this is a pattern - try to match it */ find_match(++*v); do_vi(arg); } break; case '+': /* a command, put it before the last file name etc */ insert_command(*v); do_vi(arg); break; case '.': /* just give a history list if there is only a dot */ if ((*v)[1]=='\0'){ register ct; register i; read_hist(); ct=split_hist(); for (i=0;icount-1){ if (count>1){ fprintf(stderr,"Only %d history items exist.\n",count); } else{ fprintf(stderr,"Only one history item exists.\n"); } exit(1); } 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 "); /* set the terminal up */ set_term(); /* get their response */ option=getc(stdin); /* make the terminal 'safe' again */ unset_term(); /* 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==RUBOUT){ /* they want to leave */ fprintf(stderr,"\n"); exit(1); } else if (option>='1'&&option<='0'+count){ /* they have requested a file by it's number */ option=option-'0'; fprintf(stderr,"%s\n",hist[option-1]); sprintf(arg,"%s",hist[option-1]); } else{ /* looks like they want to name a specific file. echo the characters back to the screen. */ fprintf(stderr,"%c",option); arg[0]=option; i=1; while ((arg[i]=getc(stdin))!='\n'){ i++; } arg[i]='\0'; option=count-1; /* a kludge for the history re-make to follow */ /* seeing as they typed in the name, try and help with spelling */ spell_help(); } /* rebuild the history with the selected name at the bottom */ reconstruct(option-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){ perror(tmp_file); exit(1); } return(fp); } close_temp(fp) FILE *fp; { /* move the temporary file to be the new history */ FILE *fclose(); if (fclose(fp)==(FILE *)EOF){ fprintf(stderr,"Could not close %s\n",tmp_file); exit(1); } if (rename(tmp_file,HIST)!=0){ perror("rename"); exit(1); } } set_term() { /* go into cbreak and no echo mode */ struct sgttyb blk; gtty(0, &blk); blk.sg_flags |= CBREAK; blk.sg_flags ^= ECHO; stty(0, &blk); } unset_term() { /* get out of cbreak and no echo */ struct sgttyb blk; gtty(0, &blk); blk.sg_flags &= ~CBREAK; blk.sg_flags ^= ECHO; stty(0, &blk); } 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? */ 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=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;i1){ strcat(arg," "); } } /* 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;id_ino&&entry->d_namlen>=len-1&&entry->d_namlen<=len+1){ /* get the 'distance' between what we want and this file name */ new_dist=sp_dist(entry->d_name,arg); /* if this name is close enough and better than our current best */ if (new_dist<=dist&&new_dist!=3){ if (!new_dist){ /* if the dist is 0 then they are identical */ closedir(dp); return; } /* remember the new name and distance */ strcpy(new,entry->d_name); dist=new_dist; } } } /* close up. if we got no suitable result then simply return */ closedir(dp); if (dist==3){ return; } /* offer them "new" */ set_term(); fprintf(stderr,"correct to %s [y]? ",new); /* process the reply */ switch (getc(stdin)){ case 'N': case 'n': fprintf(stderr,"no\n"); break; case 'q': case 'Q': fprintf(stderr,"quit\n"); unset_term(); exit(0); break; default : fprintf(stderr,"yes\n"); strcpy(arg,new); } unset_term(); } 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); } @//E*O*F e.c// chmod u=rw,g=rx,o=rx e.c echo Inspecting for damage in transit... temp=/tmp/shar$$; dtemp=/tmp/.shar$$ trap "rm -f $temp $dtemp; exit" 0 1 2 3 15 cat > $temp <<\!!! 45 262 1867 README 10 23 141 Makefile 4 4 27 MANIFEST 84 627 3415 edoc 831 2311 16691 e.c 974 3227 22141 total !!! wc README Makefile MANIFEST edoc e.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp if [ -s $dtemp ] then echo "Ouch [diff of wc output]:" ; cat $dtemp else echo "No problems found." fi exit 0 ZZ