Subject: v13i060: Friendly front-end to vi Newsgroups: comp.sources.unix Sender: sources Approved: rsalz@uunet.UU.NET Submitted-by: Crocodile Dundee Posting-number: Volume 13, Issue 60 Archive-name: e Basically e is a command line preprocessor to get you into vi with ease. I find it useful - even given a shell with history and command completion. It keeps a short history of the files that have been e'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". It also allows fast cross-directory editing and does obvious spelling corrections. #!/bin/sh # 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 e.cat e.1 Makefile MANIFEST MODS e.h e.c echo x - README sed 's/^@//' > "README" <<'@//E*O*F README//' README - version 1.2 -------------------- BLURB. ====== Basically e is a command line preprocessor to get you into vi with ease. I find it useful - even given a shell with history. It keeps a short history of the files that have been e'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". It also allows fast cross-directory editing and does obvious spelling corrections. DOCUMENTATION ============= Documentation and examples can be found in the man page in e.1. This is in troff -man format. e.cat contains the troffed output of e.1. MAKING ====== Before making you should change the #define VI line in e.h depending on the location of your vi. Also change the DESTDIR line in Makefile. This will run just fine on BSD4.[23]. Also on Ultrix-32. It appears to work on a SUN as well, just #define NFS in e.h I am told it will also run on sysV, just #define sysV in e.h THANKS ====== To Phil Oldiges and Simon Brown for the man page and the SysV portability changes. This appears free of major bugs (famous last words) - I'd like to hear about anything that goes wrong and/or fixes/enhancements. Comments on what is wrong and how it could be better/faster are also welcome... ------------------------------------------------------------------------------- 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 [from oz, tcjones@dragon.waterloo.cdn@munnari] ------------------------------------------------------------------------------- @//E*O*F README// chmod u=rw,g=rx,o=rx README echo x - e.1 sed 's/^@//' > "e.1" <<'@//E*O*F e.1//' @.TH E P "December 15, 1987" @.SH NAME e \- command line preprocessor for the vi(1) editor. @.SH SYNOPSIS @.B e [ @.B option ] [ @.B file ] ... @.SH DESCRIPTION @.I e is an interface to @.IR vi (1) that maintains a history of the most recently \fIe\fR'ed files for each directory. Its invocation syntax is (almost) a superset of that of \fIvi\fR. @.PP 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 (wrong character, omitted character, interchanged characters, extra character). The history file is rearranged with each use to place the last \fIe\fR'ed file at the end of the list. Duplicate entries are removed. If set, the environment variable VIPATH is read and the directories it contains are searched for an existing file with the same name before a new file is created. Thus one can vi across directories with ease. @.SH INVOCATION A list of the command line variations is given below. [x] indicates that "x" is optional. "cmd" means a @.I vi command. @.TP 18 @.B e Invokes \fIvi\fR on the last file that was \fIe\fR'ed in this directory. @.TP 18 @.B e \- Prints the history for this directory and allows selection of a previous file - or a new one. @.TP 18 @.B e . Prints the history for this directory without asking for input. @.TP 18 @.B e \-t tag Invokes \fIvi\fR \-t tag @.TP 18 @.B e \-r Invokes \fIvi\fR \-r @.TP 18 @.B e \-r file Invokes \fIvi\fR \-r file @.TP 18 @.B e \-pat \fIvi\fR's the last file that was \fIe\fR'ed with the string "pat" on the command line. @.TP 18 @.B e +[cmd] \fIvi\fR's the last file that was \fIe\fR'ed in this directory but executes command "cmd" on the way into. @.I vi @.TP 18 @.B e [+[cmd]] file \fIvi\fR'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. @.TP 18 @.B e [+[cmd]] files \fIvi\fR's the files and adds them as a single entry to the history. @.SH EXAMPLE @.PP With a ".e" file containing: @.in 1.0i @.B fred.c @.br @.B jane @.br @.B alison @.br @.B +/main pete.c @.br @.B bigmac @.br @.B fries juice @.br @.PP 2 The command "\fBe .\fR" will give: @.in 1.0i @.B [5]: fred.c @.br @.B [4]: jane @.br @.B [3]: alison @.br @.B [2]: +/main pete.c @.br @.B [1]: bigmac @.br @.B [0]: fries juice @.br @.TP 12 @.B e will get you fries and then juice. @.TP 12 @.B e \- will present the same as shown for "\fBe .\fR" above but will ask for a number or another filename. Carriage return is equivalent to zero (i.e. the last filename), backspace or interrupt will quit. @.TP 12 @.B e \-ali will get you alison. @.TP 12 @.B e +/ketchup will get you fries and juice, searching for ketchup in fries. @.TP 12 @.B e bigamc will ask if you want to correct to bigmac. "n" will not do the correction, "Q" or "q" will quit, anything else will go ahead. If there is more than one possible correction you will be prompted for each in turn. A response of "N" will means that you really want what you typed, no further corrections will be offered. @.SH "ENVIRONMENT VARIABLES" With an environment variable VIPATH set to "$HOME /usr/include/sys", @.B "e inode.h" will prompt you with /usr/include/sys/inode.h if no file called inode.h exists in the current directory or your home directory. You can say "y", "n" or "q" to accept, reject or quit. If you accept then the file is put into the history. Spelling corrections are not suggested across directories. There is (of course) no need to put "." in your VIPATH. Doing so will just slow things down and cannot possibly be of help. This should be clear, "." is always searched first for the given filename. Putting it into your VIPATH will have it searched twice. The directory names in VIPATH may be separated by white space (including newlines) and colons. @.SH NOTES When using @.B "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 that history item without further ado. Thus if you have a file called 4play and you try and @.B "e" it from within a @.B "e -" then you'll probably end up in the wrong place. This is to say you'll get the file that was the 4th last in the history. @.PP The history length must be less than or equal to 9 (the code sets it to 8 at present). The problem with having more is that with @.B "e -" you go into cbreak and the first digit entered (say \fIn\fP) is taken to mean "I want the \fIn\fRth last file". This saves the need for hitting return. With the history being kept as a most recently used list, 8 file names should be enough. @.SH BUGS The first character on a select line cannot be backspaced over. @.SH "THINGS TO DO" Make .exrc files be inherited when \fIe\fR'ing across directories. @.br Do spelling corrections across directories. @.br Plenty of fancy things. Keep a single .e file in $HOME and then it is possible to use history features in unwriteable directories etc. (suggested by ian! allen) @.br Version 2 will be a complete rewrite and will clean up the above and hopefully add cross directory spelling correction and other useful things. @.SH "SEE ALSO" ex (1), edit (1). @.SH AUTHOR Terry Jones @.br Department of Computer Science @.br University of Waterloo @.\" @.\" Man page for e. Version 1.2 @.\" @.\" ------------------------------------------------------------------------ @.\" 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 @.\" ------------------------------------------------------------------------- @.\" @//E*O*F e.1// chmod u=rw,g=rx,o=rx e.1 echo x - Makefile sed 's/^@//' > "Makefile" <<'@//E*O*F Makefile//' # # Makefile for e version 1.2 # CC = /bin/cc CFLAGS = -O DESTDIR = /u/tcjones/bin DEST = $(DESTDIR)/e OBJ = e.o HEADERS = e.h $(DEST) : $(HEADERS) $(OBJ) $(CC) $(CFLAGS) -o $(DEST) $(OBJ) strip $(DEST) @//E*O*F Makefile// chmod u=rw,g=rx,o=rx Makefile echo x - MANIFEST sed 's/^@//' > "MANIFEST" <<'@//E*O*F MANIFEST//' The shar for version 1.2 should come with eight files Makefile MANIFEST README e.1 e.c e.cat e.h MODS @//E*O*F MANIFEST// chmod u=rw,g=rx,o=rx MANIFEST echo x - MODS sed 's/^@//' > "MODS" <<'@//E*O*F MODS//' e Modification History. ======================= Version 1.1 changes October 1987. ========================================== - Added "VIPATH" environment variable. - Fixed bug with argument lists that exceeded ARG_CHARS chars in multiple(). - Added #ifdef statements to enable compilation on a SUN. - Put #defines etc into e.h as they were getting in the way. Version 1.2 changes December 1987. =========================================== - Made the numbering on "e -" the same as for "e .". - System V port (thanks to Simon Brown). - Added man page (thanks to Phil Oldiges). - Fixed handling of the VIPATH variable so that it doesn't screw up when there is more than one delimiter. Added new delimiters. - Cleaned up terminal handling to use ioctl(). - Cleaned up error handling. - Fixed (for sure this time) the bug with long argument lists. - Made returns consistent in check(). - Flagged suspected unsatisfied symbolic link in check(). - Added #define called E_PATH which contains the name of the environment variable containing the PATH for cross directory stuff. (set to "VIPATH"). - Added E_MODE #define for the protection mode for .e (set to 0644). - Fixed spelling correction to offer all likely targets in a directory, not just the first encountered. Added 'N' option to say "I meant what I said, don't give me more possible corrections." - Comments! To be added? ============ - Allow backspacing on a select line. - Make .exrc files be inherited when e'ing across directories. - Spelling corrections across directories. - Plenty of fancy things. - Keep a single .e file in $HOME and then it's easy to use the history in unwriteable directories etc. do this nicely with hashing/binary search etc. (suggested by ian! allen) ------------------------------------------------------------------------------- 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 [from oz, tcjones@dragon.waterloo.cdn@munnari] ------------------------------------------------------------------------------- @//E*O*F MODS// chmod u=rw,g=rx,o=rx MODS echo x - e.h sed 's/^@//' > "e.h" <<'@//E*O*F e.h//' /* * e.h for e version 1.2 * ===================== * *------------------------------------------------------------------------------ * 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 *------------------------------------------------------------------------------ */ /* * #includes common to all versions of uni*x * */ #include #include #include #include #include #include #include /* * un*x version dependent #includes * */ #ifdef sysV # ifdef GEC # include # else # include # endif # include # include # include #else # include # ifdef NFS # include # include # include # include # else # include # include # endif # include #endif sysV /* * un*x dependent #defines * */ #ifdef sysV # define VI "/usr/bin/vi" # define index strchr # define rindex strrchr #else # define VI "/usr/ucb/vi" #endif sysV /* * Other checks... * */ #ifndef L_SET # define L_SET 0 #endif #ifndef IREAD # define IREAD 0400 # define IWRITE 0200 #endif /* * The #defines for things that aren't portability concerns. * */ #define HIST ".e" #define HIST_LINES 8 #define HIST_CHARS 1024 #define E_PATH "VIPATH" #define E_MODE 0644 #define ARG_CHARS 1024 #define MAX_ARGS 100 #define MAX_PATH 1024 #define BELL '\007' #define O_READ 00004 #define G_READ 0004 /* * #defines for the terminal() function. * */ #define TERM_RECORD 0 #define TERM_SET 1 #define TERM_RESET 2 /* * STRUCT_ASST should be defined if your UN*X is capable of doing * structural assignment. This is (supposedly) true for System III, System V, * UNIX/32V, BSD4.[23], v8 and v9. It's also in ANSI C, but *not* in * "The C programming language" by Kernhigan & Ritchie. * * This #define is only used in the function terminal(). Leave STRUCT_ASST * defined and if it doesn't break you're ok. If it does, undefine it and * you'll definitely be ok (but things will run slower - even though you * wont notice that anyway.) Words words words. Ho hum. * */ #define STRUCT_ASST /* * A slightly dangerous one. NEVER call this as (for example) "is_delim(cp++)" * Or the incrementing will be done 4 times (not one). */ #define is_delim(c) ((*c)==' '||(*c)==':'||(*c)=='\t'||(*c)=='\n') @//E*O*F e.h// chmod u=rw,g=rx,o=rx e.h echo x - e.c sed 's/^@//' > "e.c" <<'@//E*O*F e.c//' /****************************************************************************** # # # # # 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;icount-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 "); /* 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=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;i=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;id_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); } @//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 <<\!!! 56 254 1791 README 192 890 6788 e.cat 191 1008 5781 e.1 14 37 208 Makefile 10 18 111 MANIFEST 61 303 2271 MODS 151 375 2697 e.h 1429 3783 35152 e.c 2104 6668 54799 total !!! wc README e.cat e.1 Makefile MANIFEST MODS e.h 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