Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!uunet!husc6!hao!ames!aurora!labrea!decwrl!pyramid!uccba!hal!ncoast!allbery From: simon@its63b.ed.ac.uk (Simon Brown) Newsgroups: comp.sources.misc Subject: Re: e - a friendly interface to vi Message-ID: <4932@ncoast.UUCP> Date: Fri, 23-Oct-87 20:44:34 EST Article-I.D.: ncoast.4932 Posted: Fri Oct 23 20:44:34 1987 Date-Received: Mon, 26-Oct-87 01:39:34 EST Sender: allbery@ncoast.UUCP Organization: Computer Science Department, Edinburgh University Lines: 989 Approved: allbery@ncoast.UUCP X-Archive: comp.sources.misc/8710/17 Here's a cleaned-up (I hope) version of e - o Ported to System V. o Puts history in the correct file even if you try to edit files not in the current directory. o "e -" and "e ." use the same numbering scheme. o Multiple file-entries in history-files no longer occur. o Uses $EDITOR instead of always using "vi". Warning: this changed version has not been tested under BSD (cos I was too lazy). ------------------------ cut here ------------------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # e.c # This archive created: Thu Oct 15 20:17:37 1987 # By: Simon Brown (Computer Science Department, Edinburgh University) export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'e.c'" '(19244 characters)' if test -f 'e.c' then echo shar: "will not over-write existing file 'e.c'" else cat << \SHAR_EOF > '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 #ifdef sysV # ifdef GEC # include # else # include # endif # include # include # include #else # include # ifdef NFS # include # else # include # endif # include # include #endif sysV #include #include #ifdef sysV # define VI "/usr/bin/vi" # define index strchr # define rindex strrchr #else # define VI "/usr/ucb/vi" #endif sysV #ifndef L_SET # define L_SET 0 #endif #ifndef IREAD # define IREAD 0400 # define IWRITE 0200 #endif #define HIST ".e" #define HIST_LINES 8 #define HIST_CHARS 512 #define ARG_CHARS 100 #define MAX_ARGS 10 #define RUBOUT '\177' #define HFILELEN 512 char history[HIST_CHARS]; char arg[ARG_CHARS]; char *hist[HIST_LINES]; char temp[HIST_CHARS]; char *tmp_file="._vihistXXXXXX"; char dotstring[] = "."; char rubout = RUBOUT; #ifdef sysV struct termio oldblk; #else struct sgttyb oldblk; #endif sysV int term_messy = 0; char *got_vi(); 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; char *getenv(); if ((args[0]=getenv("EDITOR")) == 0) 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(args[0],args)==-1){ perror(args[0]); exit(1); } } main(c,v) int c; char **v; { 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(dotstring); 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,dotstring); } 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; /* read and split the history file */ read_hist(dotstring); 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>='0' && option<='0'+count){ /* they have requested a file by it's number */ option=option-'0'; fprintf(stderr,"%s\n",hist[option]); sprintf(arg,"%s",hist[option]); } 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,dotstring); } 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,filename) FILE *fp; char *filename; { /* move the temporary file to be the new history */ FILE *fclose(); char dirbuffer[HFILELEN]; char *fptr; if (fclose(fp)==(FILE *)EOF){ fprintf(stderr,"Could not close %s\n",tmp_file); exit(1); } strcpy(dirbuffer, filename); if ((fptr=rindex(dirbuffer,'/'))==0){ strcpy(dirbuffer,dotstring); fptr = &dirbuffer[1]; } sprintf(fptr,"/%s", HIST); if (rename(tmp_file,dirbuffer)!=0){ perror("rename"); exit(1); } } set_term() { /* go into cbreak and no echo mode */ #ifdef sysV struct termio blk; ioctl(0, TCGETA, &blk); oldblk = blk; rubout = blk.c_cc[VERASE]; blk.c_lflag &= ~(ICANON|ECHO|ECHONL); blk.c_cc[VMIN] = 1; blk.c_cc[VTIME] = 0; ioctl(0, TCSETA, &blk); #else struct sgttyb blk; gtty(0, &blk); oldblk = blk; rubout = blk.sg_erase; blk.sg_flags |= CBREAK; blk.sg_flags ^= ECHO; stty(0, &blk); #endif sysV term_messy = 1; } unset_term() { /* get out of cbreak and no echo */ if (term_messy) { #ifdef sysV ioctl(0, TCSETA, &oldblk); #else stty(0, &oldblk); #endif sysV term_messy = 0; } } 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(dotstring); 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 && strcmp(hist[i],fptr)!=0){ fprintf(tv,"%s\n",hist[i]); } } /* put in the new line from 'arg' */ fprintf(tv,"%s\n",fptr); /* rename the temporary to be the new history file */ close_temp(tv,arg); } 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; char *edir; /* put it into 'arg' */ sprintf(arg,"%s",string); /* if there is a history file */ if (edir=got_vi(string)){ /* read it and split it up */ read_hist(edir); 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 (edir=got_vi(*--args)){ read_hist(edir); 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,fptr); /* 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"); if (fptr==arg) strcpy(arg,new); else sprintf(arg,"%s/%s", dirbuffer, 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); } SHAR_EOF chmod +x 'e.c' fi exit 0 # End of shell archive -- ---------------------------------- | Simon Brown | UUCP: seismo!mcvax!ukc!{lfcs,its63b}!simon | Department of Computer Science | JANET: simon@uk.ac.ed.{lfcs,its63b} | University of Edinburgh, | ARPA: simon%lfcs.ed.ac.uk@cs.ucl.ac.uk | Scotland, UK. | or simon%its63b.ed.ac.uk@cs.ucl.ac.uk ---------------------------------- or simon%cstvax.ed.ac.uk@cs.ucl.ac.uk "Life's like that, you know"