home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume1 / 8710 / 15 / e.c next >
Encoding:
C/C++ Source or Header  |  1990-07-13  |  24.9 KB  |  1,024 lines

  1. /*
  2.  * e.c - a friendly interface to vi
  3.  * version 1.1
  4.  *
  5.  * Terry Jones
  6.  * Department of Computer Science
  7.  * University of Waterloo
  8.  * Waterloo, Ontario, Canada. N2L 3G1
  9.  *
  10.  * {ihnp4,allegra,decvax,utzoo,utcsri,clyde}!watmath!watdragon!tcjones
  11.  * tcjones@dragon.waterloo.{cdn,edu} tcjones@WATER.bitnet
  12.  * tcjones%watdragon@waterloo.csnet 
  13.  *
  14.  * version 1.1 changes 17/10/87
  15.  *
  16.  *      - Added VIPATH environment variable
  17.  *      - Fixed bug with argument lists that exceeded ARG_CHARS characters.
  18.  *        in multiple().
  19.  *      - Added #ifdef statements to enable compilation on a SUN.
  20.  *      - Put #defines etc into e.h as they were getting in the way.
  21.  */
  22.  
  23. #include "e.h"
  24.  
  25. /*
  26.  * globals. all in the name of ease and perhaps speed.
  27.  */
  28.  
  29. char history[HIST_CHARS];
  30. char arg[ARG_CHARS];
  31. char *hist[HIST_LINES];
  32. char temp[HIST_CHARS];
  33. char *tmp_file="._vihistXXXXXX";
  34.  
  35. void 
  36. do_vi(thing)
  37. char *thing;
  38. {
  39.     /* split the arguments in 'thing' up and exec vi on them */
  40.     char *args[MAX_ARGS];
  41.     char *this,*next;
  42.     register i;
  43.  
  44.     args[0]=VI;
  45.     args[1]=thing;
  46.  
  47.     i=1;
  48.     while (*thing!='\0'&&(thing=index(thing,' '))!=NULL){
  49.         *thing++='\0';
  50.         if (*thing!='\0'){
  51.             args[++i]=thing;
  52.         }
  53.     }
  54.     args[++i]=NULL;
  55.         
  56.     if (execvp(VI,args)==-1){
  57.         perror(VI);
  58.         exit(1);
  59.     }
  60. }
  61.  
  62. main(c,v)
  63. int c;
  64. char **v;
  65. {
  66.     char *last_file();
  67.     extern clean_up();
  68.  
  69.     /* make sure we reset the terminal on our way out if we get interrupted */
  70.     if (signal(SIGINT, SIG_IGN) != SIG_IGN){
  71.         signal(SIGINT, clean_up);
  72.     }
  73.  
  74.     switch (c){
  75.         case 1: 
  76.             /* just go and vi the last file that was vi'ed */
  77.             last_file();
  78.             do_vi(arg);
  79.             break;
  80.         
  81.         case 2:
  82.             switch ((*++v)[0]){
  83.                 case '-':
  84.                     if ((c=(*v)[1])=='\0'){
  85.                         /* this is a select from history, ask what they want */
  86.                         ask_hist();
  87.                         do_vi(arg);
  88.                     }
  89.                     else if (isdigit(c)){
  90.                         /* get the nth last file from the history and vi it */
  91.                         nth_hist(c-'0');
  92.                         do_vi(arg);
  93.                     }
  94.                     else if (c=='t'&&(*v)[2]=='\0'){
  95.                         /* this is an empty tag - ignore it */
  96.                         do_vi(*v);
  97.                     }
  98.                     else if (c=='r'&&(*v)[2]=='\0'){
  99.                         /* a recover, just pass it to vi and don't interfere */
  100.                         do_vi(*v);
  101.                     }
  102.                     else{
  103.                         /* this is a pattern - try to match it */
  104.                         find_match(++*v);
  105.                         do_vi(arg);
  106.                     }
  107.                     break;
  108.  
  109.                 case '+':
  110.                     /* a command, put it before the last file name etc */
  111.                     insert_command(*v);
  112.                     do_vi(arg);
  113.                     break;
  114.  
  115.                 case '.':
  116.                     /* just give a history list if there is only a dot */
  117.                     if ((*v)[1]=='\0'){
  118.                         register ct;
  119.                         register i;
  120.  
  121.                         read_hist();
  122.                         ct=split_hist();
  123.  
  124.                         for (i=0;i<ct;i++){
  125.                             fprintf(stderr,"\t[%d]: %s\n",ct-i-1,hist[ct-i-1]);
  126.                         }
  127.                         exit(0);
  128.                     }
  129.                     /* 
  130.                        WARNING!
  131.                        the switch falls through in the case where there is a
  132.                        pattern that starts with a period
  133.                     */
  134.  
  135.                 default :
  136.                     /* looks like it's just a plain old file name. vi it! */
  137.                     normal(*v);
  138.                     do_vi(arg);
  139.                     break;
  140.             }
  141.         default:
  142.             /* a bunch of arguments, fix the history & vi them all as normal */
  143.             multiple(c,v,ARG_CHARS);
  144.             do_vi(arg);
  145.             break;
  146.     }
  147. }
  148.  
  149.  
  150.  
  151. read_hist()
  152. {
  153.     /*
  154.        read the history file and break it up into lines in the global variable
  155.        'history'. do the appropriate checks to see that it exists etc...
  156.     */
  157.  
  158.     register vh;
  159.     register bytes;
  160.     register offset;
  161.     struct stat buf;
  162.  
  163.     /* 
  164.        if there is no history file then say so and get out of here - they 
  165.        had nobusiness asking for access to the history
  166.     */
  167.     if ((vh=open(HIST,O_RDONLY))==-1){
  168.         perror(HIST);
  169.         exit(1);
  170.     }
  171.  
  172.     /* stat it */
  173.     if (fstat(vh,&buf)==-1){
  174.         perror(HIST);
  175.         exit(1);
  176.     }
  177.  
  178.     /* 
  179.        set 'offset' so that we can read the last portion of the history
  180.        file only. if there are less than HIST_CHARS characters in the
  181.        file then we will start reading at 0, otherwise at HIST_CHARS
  182.        characters before the end of the file.
  183.     */
  184.     offset=(int)buf.st_size-HIST_CHARS<0 ? 0 : buf.st_size-HIST_CHARS;
  185.  
  186.     /* move to that place in the file */
  187.     if (lseek(vh,(long)offset,L_SET)==-1){
  188.         perror(HIST);
  189.         exit(1);
  190.     }
  191.  
  192.     /* and READ! */
  193.     if ((bytes=read(vh,history,HIST_CHARS))==-1){
  194.         perror(HIST);
  195.         exit(1);
  196.     }
  197.  
  198.     /* if we didn't come up with ANYTHING we may as well leave */
  199.     if (!bytes){
  200.         fprintf(stderr,"Empty %s file.\n",HIST);
  201.         exit(1);
  202.     }
  203.  
  204.     /* zap the newline (which should be there) for now */
  205.     if (history[--bytes]=='\n'){
  206.         history[bytes]='\0';    
  207.     }
  208.  
  209.     /* and get out of here */
  210.     return(bytes);
  211. }
  212.  
  213. char *
  214. last_file()
  215. {
  216.     /*
  217.        get the last name from the 'history' array and put it into 'arg'
  218.     */
  219.  
  220.     read_hist();
  221.     if (index(history,'\n')==NULL){
  222.         if (*history=='\0'){
  223.             fprintf(stderr,"%s: badly formatted.\n",HIST);
  224.             exit(1);
  225.         }
  226.         else{
  227.             sprintf(arg,"%s",history);
  228.         }
  229.     }
  230.     else{
  231.         sprintf(arg,"%s",rindex(history,'\n')+1);
  232.     }
  233. }
  234.  
  235. split_hist()
  236. {
  237.     /*
  238.        set the array of pointers in 'hist' to point to the succesive names
  239.        in the 'history' array. these are delimited (presumably) by newlines
  240.        and so they're easy to catch...
  241.  
  242.        what in fact is done is that the history array is copied and we set
  243.        the pointers up and set the newlines to be '\0' s. this way we don't
  244.        mess up the history array as we will want it intact later on (maybe).
  245.     */
  246.  
  247.     char *tmp;
  248.     register count;
  249.  
  250.     /* copy it */
  251.     sprintf(temp,"%s",history);
  252.  
  253.     /* 
  254.        now run through breaking it up, setting pointers and return the number 
  255.        of lines we found.
  256.     */
  257.     for (count=0;count<HIST_LINES;count++){
  258.         if ((tmp=hist[count]=rindex(temp,'\n'))==NULL){
  259.             break;
  260.         }
  261.         *tmp='\0';
  262.         hist[count]++;
  263.     }
  264.     if (count<HIST_LINES){
  265.         hist[count++]=temp;
  266.     }
  267.     return(count);
  268. }
  269.  
  270. nth_hist(n)
  271. int n;
  272. {
  273.     /*
  274.        get the nth last filename from the list. make use (of course) of
  275.        read_hist and split_hist.
  276.     */
  277.     register count;
  278.     register i;
  279.  
  280.     read_hist();
  281.     count=split_hist();
  282.     if (n>count-1){
  283.         if (count>1){
  284.             fprintf(stderr,"Only %d history items exist.\n",count);
  285.         }
  286.         else{
  287.             fprintf(stderr,"Only one history item exists.\n");
  288.         }
  289.         exit(1);
  290.     }
  291.     sprintf(arg,"%s",hist[n]);
  292.  
  293.     /* rebuild the history with the selected name at the bottom */
  294.     reconstruct(n,count);
  295. }
  296.  
  297. ask_hist()
  298. {
  299.     /*
  300.        ask the outside world which of the files in the history is wanted.
  301.        set the terminal to cbreak.
  302.     */
  303.     register i;
  304.     register count;
  305.     char *last;
  306.     register option;
  307.     struct sgttyb blk;
  308.  
  309.     /* read and split the history file */
  310.     read_hist();
  311.     count=split_hist();
  312.  
  313.     /* print the history */
  314.     for (i=0;i<count;i++){
  315.         fprintf(stderr,"\t[%d]: %s\n",count-i,hist[count-i-1]);
  316.     }
  317.  
  318.     /* give them a prompt (of sorts) */
  319.     fprintf(stderr,"select -> ");
  320.  
  321.     /* set the terminal up */
  322.     set_term();
  323.  
  324.     /* get their response */
  325.     option=getc(stdin);
  326.  
  327.     /* make the terminal 'safe' again */
  328.     unset_term();
  329.  
  330.     /* 
  331.        process the option and put the appropriate file name into the 
  332.        arg variable.
  333.     */
  334.     if (option=='\n'){
  335.         /* they want the last file of the list */
  336.         fprintf(stderr,"%s\n",hist[0]);
  337.         sprintf(arg,"%s",hist[0]);
  338.         return;
  339.     }
  340.     else if (option==RUBOUT){
  341.         /* they want to leave */
  342.         fprintf(stderr,"\n");
  343.         exit(1);
  344.     }
  345.     else if (option>='1'&&option<='0'+count){
  346.         /* they have requested a file by it's number */
  347.         option=option-'0';
  348.         fprintf(stderr,"%s\n",hist[option-1]);
  349.         sprintf(arg,"%s",hist[option-1]);
  350.     }
  351.     else{
  352.         /* 
  353.            looks like they want to name a specific file. echo the 
  354.            characters back to the screen.
  355.         */
  356.         fprintf(stderr,"%c",option);
  357.         arg[0]=option;
  358.         i=1;
  359.         while ((arg[i]=getc(stdin))!='\n'){
  360.             i++;
  361.         }
  362.         arg[i]='\0';
  363.         option=count-1;     /* a kludge for the history re-make to follow */
  364.  
  365.         /* seeing as they typed in the name, try and help with spelling */
  366.         if (!spell_help()){
  367.             find();
  368.         }
  369.  
  370.         /* if it is in the history then reconstruct and return */
  371.         for (i=0;i<count;i++){
  372.             if (!strcmp(hist[i],arg)){
  373.                 reconstruct(i,count);
  374.                 return;
  375.             }
  376.         }
  377.     }
  378.  
  379.     /* rebuild the history with the selected name at the bottom */
  380.     reconstruct(option-1,count);
  381. }
  382.  
  383. FILE *
  384. get_temp()
  385. {
  386.     /* get ourselves a temporary file for the reconstructed history */
  387.     FILE *fp,*fopen();
  388.  
  389.     mktemp(tmp_file);
  390.     if ((fp=fopen(tmp_file,"w"))==NULL){
  391.         perror(tmp_file);
  392.         exit(1);
  393.     }
  394.     return(fp);
  395. }
  396.  
  397. close_temp(fp)
  398. FILE *fp;
  399. {
  400.     /* move the temporary file to be the new history */
  401.     FILE *fclose();
  402.  
  403.     if (fclose(fp)==(FILE *)EOF){
  404.         fprintf(stderr,"Could not close %s\n",tmp_file);
  405.         exit(1);
  406.     }
  407.  
  408.     if (rename(tmp_file,HIST)!=0){
  409.         perror("rename");
  410.         exit(1);
  411.     }
  412. }
  413.  
  414. set_term()
  415. {
  416.     /* go into cbreak and no echo mode */
  417.     struct sgttyb   blk;
  418.  
  419.     gtty(0, &blk);
  420.     blk.sg_flags |= CBREAK;
  421.     blk.sg_flags ^= ECHO;
  422.     stty(0, &blk);
  423. }
  424.  
  425. unset_term()
  426. {
  427.     /* get out of cbreak and no echo */
  428.     struct sgttyb   blk;
  429.  
  430.     gtty(0, &blk);
  431.     blk.sg_flags &= ~CBREAK;
  432.     blk.sg_flags ^= ECHO;
  433.     stty(0, &blk);
  434. }
  435.  
  436. match(argument,pattern)
  437. char    *argument;
  438. char    *pattern;
  439. {
  440.     /*
  441.        boneheaded but easy pattern matcher. just see if the 'pattern'
  442.        exists anywhere in the 'argument'. boyer-moore who?
  443.     */
  444.     register length=strlen(pattern);
  445.  
  446.     while (strlen(argument)>=length){
  447.         if (!strncmp(argument++,pattern,length)){
  448.             return(1);
  449.         }
  450.     }
  451.     return(0);
  452. }
  453.  
  454. find_match(pattern)
  455. char *pattern;
  456. {
  457.     /*
  458.        find the name in the history list that contains the 'pattern'.
  459.        if it exists then put it into the 'arg' variable and otherwise
  460.        announce that a match couldn't be found and leave.
  461.     */
  462.     register count;
  463.     register i;
  464.  
  465.     /* read and split the history file */
  466.     read_hist();
  467.     count=split_hist();
  468.  
  469.     /* try for a match with each file in turn (note that we are working
  470.        from most-recently-used backwards - probably a good thing)
  471.     */
  472.     for (i=0;i<count;i++){
  473.         if (match(hist[i],pattern)){
  474.             sprintf(arg,"%s",hist[i]);
  475.             reconstruct(i,count);
  476.             return;
  477.         }
  478.     }
  479.  
  480.     /* we couldn't match so get out of here */
  481.     fprintf(stderr,"Unable to match with \"%s\"\n",pattern);
  482.     exit(1);
  483. }
  484.  
  485. insert_command(command)
  486. char *command;
  487. {
  488.     /*
  489.        they want the last file in the history but want to preceed it
  490.        this time with a command - no problems here.
  491.     */
  492.     register count;
  493.     char *place;
  494.  
  495.     /* read and split the history */
  496.     read_hist();
  497.     count=split_hist();
  498.     
  499.     /* 
  500.        if there was already a command there (indicated by a '+') then we
  501.        want to get rid of it. if there is a '+' but no ' ' after it then
  502.        the history file is in disarray and we will not try to recover
  503.     */
  504.     if (*hist[0]=='+'){
  505.         if ((place=index(hist[0],' '))==NULL){
  506.             fprintf("Serious weirdenss in insert_command\n");
  507.             exit(1);
  508.         }
  509.         /* move over white space - if there is any */
  510.         while (*place==' '||*place=='\t'){
  511.             place++;
  512.         }
  513.     }
  514.     else{
  515.         /* there was no command preceeding the last file in the history */
  516.         place=hist[0];
  517.     }
  518.  
  519.     /* put the new command and the filename into 'arg' */
  520.     sprintf(arg,"%s %s",command,place);
  521.  
  522.     /* rebuild the history with the selected command and name at the bottom */
  523.     reconstruct(0,count);
  524. }
  525.  
  526. reconstruct(except,count)
  527. int except;
  528. int count;
  529. {
  530.     /* 
  531.        reconstruct history file excepting the 'except' last.
  532.        so just copy all lines but the 'except'th last and then put in 'arg'
  533.        which contains the new line for the history
  534.     */
  535.     register i;
  536.     FILE *tv,*get_temp();
  537.  
  538.     /* get a temporary file */
  539.     tv=get_temp();
  540.  
  541.     /* put in the line we still want */
  542.     for (i=count-1;i>=0;i--){
  543.         if (i!=except){
  544.             fprintf(tv,"%s\n",hist[i]);
  545.         }
  546.     }
  547.  
  548.     /* put in the new line from 'arg' */
  549.     fprintf(tv,"%s\n",arg);
  550.  
  551.     /* rename the temporary to be the new history file */
  552.     close_temp(tv);
  553. }
  554.  
  555. normal(string)
  556. char *string;
  557. {
  558.     /* 
  559.        a normal filename was found, put it into arg. first of all if there
  560.        is a history and the file is already in it (which means they could
  561.        have gotten to this file in other ways), then reconstruct the history
  562.        as though they had. also offer spelling help.
  563.     */
  564.     register count;
  565.     register i;
  566.  
  567.     /* put it into 'arg' */
  568.     sprintf(arg,"%s",string);
  569.  
  570.     /* if there is a history file */
  571.     if (got_vi()){
  572.  
  573.         /* read it and split it up */
  574.         read_hist();
  575.         count=split_hist();
  576.  
  577.         /* if it is in the history then reconstruct and return */
  578.         for (i=0;i<count;i++){
  579.             if (!strcmp(hist[i],arg)){
  580.                 reconstruct(i,count);
  581.                 return;
  582.             }
  583.         }
  584.  
  585.         /* it's not in the history, help with spelling then reconstruct */
  586.         if (!spell_help()){
  587.             find();
  588.         }
  589.  
  590.         /* if it is in the history then reconstruct and return */
  591.         for (i=0;i<count;i++){
  592.             if (!strcmp(hist[i],arg)){
  593.                 reconstruct(i,count);
  594.                 return;
  595.             }
  596.         }
  597.  
  598.         reconstruct(HIST_LINES,count);
  599.     }
  600.     else{
  601.  
  602.         /* 
  603.            there is no history around so help with spelling and set up a 
  604.            history for next time.
  605.         */
  606.         if (!spell_help()){
  607.             find();
  608.         }
  609.         new_vi();
  610.     }
  611.  
  612. }
  613.  
  614. multiple(number,args,size)
  615. int number;
  616. char **args;
  617. {
  618.     /*
  619.        there were several names on the command line so we just strcat them
  620.        into the 'arg' array. check to see that the length of all the args
  621.        will not be greater than "size" or else we will overflow arg.
  622.     */
  623.     register count;
  624.     register i;
  625.     register total=0;
  626.  
  627.     *arg='\0';
  628.     while (--number){
  629.         if ((total+=strlen(*args)+1)>=size){    /* add one for a space */
  630.             /*
  631.              * If you are running e and you find that this condition occurs,
  632.              * the solution is to simply increase the value of the #define
  633.              * line for ARG_CHARS near the top.
  634.              */
  635.             fprintf(stderr,
  636.             "%c%c%cWarning! Argument list too long, truncated after \"%s\".\n",
  637.                 BELL,BELL,BELL,*args);      
  638.             sleep(2);   /* give the user some chance to see what happened */
  639.             break;
  640.         }
  641.         strcat(arg,*++args);
  642.         if (number>1){
  643.             strcat(arg," ");
  644.         }
  645.     }
  646.  
  647.     /*
  648.        now if there is a history file and we can find an identical line
  649.        then reconstruct with that line at the bottom.
  650.     */
  651.     if (got_vi()){
  652.         read_hist();
  653.         count=split_hist();
  654.         for (i=0;i<count;i++){
  655.             if (!strcmp(hist[i],arg)){
  656.                 reconstruct(i,count);
  657.                 return;
  658.             }
  659.         }
  660.         /* 
  661.            rebuild, including everything but the counth last (i.e. make
  662.            a new history by omitting the oldest file in the current one and
  663.            putting 'arg' on the end.
  664.         */
  665.         reconstruct(HIST_LINES,count);
  666.     }
  667.     else{
  668.         /* there was no history file so try to give them one for next time */
  669.         new_vi();
  670.     }
  671. }
  672.  
  673. got_vi()
  674. {
  675.     /* indicate if there is a history file that they own or otherwise */
  676.     struct stat buf;
  677.  
  678.     if (stat(HIST,&buf)==-1){
  679.         return(0);
  680.     }
  681.     else{
  682.         return(getuid()==buf.st_uid);
  683.     }
  684. }
  685.  
  686. new_vi()
  687. {
  688.     /* attempt to make a new history file - several things could go wrong */
  689.     FILE *vh,*fopen(),*fclose();
  690.     struct stat buf;
  691.  
  692.     /* if you can't read the current directory, get out */
  693.     if (stat(".",&buf)==-1){
  694.         perror("stat");
  695.         exit(1);
  696.     }
  697.  
  698.     /* if you own the directory (you can't normally get a history in /tmp) */
  699.     if (getuid()==buf.st_uid){
  700.  
  701.         /* if we can't make a history, get out */
  702.         if ((vh=fopen(HIST,"w"))==NULL){
  703.             perror(HIST);
  704.             exit(1);
  705.         }
  706.  
  707.         /* put in the 'arg' that we will be vi'ing in a second */
  708.         fprintf(vh,"%s\n",arg);
  709.  
  710.         /* close the history */
  711.         if (fclose(vh)==(FILE *)EOF){
  712.             fprintf(stderr,"Could not close %s\n",VI);
  713.             exit(1);
  714.         }
  715.  
  716.         /* give the history some protection - for those who want it! */
  717.         if (chmod(HIST,IREAD|IWRITE)==-1){
  718.             perror("chmod");
  719.             exit(1);
  720.         }
  721.     }
  722. }
  723.  
  724. clean_up()
  725. {
  726.     /* just get out after making sure things are tidy */
  727.     fprintf(stderr,"Interrupt.\n");
  728.     unlink(tmp_file);
  729.     unset_term();
  730.     exit(1);
  731. }
  732.  
  733. spell_help()
  734. {
  735.     /*
  736.        unashamedly stolen (and modified) from "The UNIX Programming
  737.        Environment" - Kernighan and Pike
  738.  
  739.        read the directory and if the file they want (in 'arg') does not
  740.        exist then see if there is one that does that has similar spelling
  741.        to what they requested. offer the change and handle the reply.
  742.     */
  743.  
  744.     char new[ARG_CHARS];
  745.     register dist=3;
  746.     register new_dist;
  747.     DIR *dp, *opendir();
  748.     struct direct *readdir();
  749.     struct direct *entry;
  750.     register len;
  751.     struct stat buf;
  752.  
  753.     /* if the file already exists just return - they don't need help */
  754.     if (stat(arg,&buf)==0){
  755.         return(1);
  756.     }
  757.  
  758.     /* if the current directory can't be read then return */
  759.     if ((dp=opendir("."))==NULL){
  760.         return(0);
  761.     }
  762.     
  763.     /* get the length of what we are seeking to cut down on strcmping time */
  764.     len=strlen(arg);
  765.  
  766.     for (entry=readdir(dp);entry!=NULL;entry=readdir(dp)){
  767.  
  768.         /* if this entry has length = sought length +/- 1 then it may be ok */
  769.         if (entry->d_ino&&entry->d_namlen>=len-1&&entry->d_namlen<=len+1){
  770.  
  771.             /* get the 'distance' between what we want and this file name */
  772.             new_dist=sp_dist(entry->d_name,arg);
  773.  
  774.             /* if this name is close enough and better than our current best */
  775.             if (new_dist<=dist&&new_dist!=3){
  776.  
  777.                 if (!new_dist){
  778.                     /* if the dist is 0 then they are identical */
  779.                     closedir(dp);
  780.                     return(1);
  781.                 }
  782.                 /* remember the new name and distance */
  783.                 strcpy(new,entry->d_name);
  784.                 dist=new_dist;
  785.             }
  786.         }
  787.     }
  788.  
  789.     /* close up. if we got no suitable result then simply return */
  790.     closedir(dp);
  791.     if (dist==3){
  792.         return(0);
  793.     }
  794.  
  795.     /* offer them "new" */
  796.     set_term();
  797.     fprintf(stderr,"correct to %s [y]? ",new);
  798.  
  799.     /* process the reply */
  800.     switch (getc(stdin)){
  801.         case 'N':
  802.         case 'n':
  803.             fprintf(stderr,"no\n");
  804.             unset_term();
  805.             return(0);
  806.         case 'q':
  807.         case 'Q':
  808.             fprintf(stderr,"quit\n");
  809.             unset_term();
  810.             exit(0);
  811.         default :
  812.             fprintf(stderr,"yes\n");
  813.             unset_term();
  814.             strcpy(arg,new);
  815.             return(1);
  816.     }
  817. }
  818.  
  819. sp_dist(s,t)
  820. char *s;
  821. char *t;
  822. {
  823.     /* 
  824.        stolen from the same place as spell_help() above.
  825.  
  826.        work out the distance between the strings 's' and 't' according
  827.        to the rough metric that
  828.        
  829.        identical = 0
  830.        interchanged characters = 1
  831.        wrong character/extra character/missing character = 2
  832.        forget it = 3
  833.     */
  834.  
  835.     while (*s++==*t){
  836.         if (*t++=='\0'){
  837.             /* identical */
  838.             return(0);
  839.         }
  840.     }
  841.  
  842.     if (*--s){
  843.         if (*t){
  844.             if (s[1]&&t[1]&&*s==t[1]&&*t==s[1]&&!strcmp(s+2,t+2)){
  845.                 /* interchanged chars */
  846.                 return(1);
  847.             }
  848.             if (!strcmp(s+1,t+1)){
  849.                 /* wrong char */
  850.                 return(2);
  851.             }
  852.         }
  853.         if (!strcmp(s+1,t)){
  854.             /* extra char in 't' */
  855.             return(2);
  856.         }
  857.     }
  858.     if (!strcmp(s,t+1)){
  859.         /* extra char in 's' */
  860.         return(2);
  861.     }
  862.  
  863.     /* forget it */
  864.     return(3);
  865. }
  866.  
  867.  
  868. find()
  869. {
  870.     extern char *getenv();
  871.     extern char *getwd();
  872.     char *p;
  873.     char path[MAX_PATH];
  874.     char *dir;
  875.     char *space;
  876.     char *us;
  877.     char wd[MAXPATHLEN];
  878.     char what[ARG_CHARS];
  879.  
  880.     p=getenv("VIPATH");
  881.  
  882.     if (!p||!strlen(p)) return;
  883.     
  884.     if (strlen(p)>=MAX_PATH){
  885.         fprintf(stderr,"Could not get VIPATH from environment.\n");
  886.         exit(1);
  887.     }
  888.  
  889.     strcpy(path,p);
  890.     strcpy(what,arg);
  891.  
  892.     if (!(us=getwd(wd))){
  893.         fprintf(stderr,"%s\n",wd);
  894.         exit(1);
  895.     }
  896.  
  897.     dir=path;
  898.     while ((space=index(dir,' '))!=NULL){
  899.         *space='\0';
  900.         if (check(what,dir)){
  901.  
  902.             /* offer them dir/what */
  903.             set_term();
  904.             fprintf(stderr,"%s/%s [y]? ",dir,what);
  905.  
  906.             /* process the reply */
  907.             switch (getc(stdin)){
  908.                 case 'N':
  909.                 case 'n':
  910.                     fprintf(stderr,"no\n");
  911.                     break;
  912.                 case 'q':
  913.                 case 'Q':
  914.                     fprintf(stderr,"quit\n");
  915.                     unset_term();
  916.                     exit(0);
  917.                     break;
  918.                 default :
  919.                     fprintf(stderr,"yes\n");
  920.                     unset_term();
  921.                     if (chdir(us)==-1){
  922.                         perror("chdir");
  923.                         exit(1);
  924.                     }
  925.                     sprintf(arg,"%s/%s",dir,what);
  926.                     return(1);
  927.             }
  928.             unset_term();
  929.         }
  930.         dir=space+1;
  931.     }
  932.  
  933.     if (check(what,dir)){
  934.  
  935.         /* offer them dir/what */
  936.         set_term();
  937.         fprintf(stderr,"%s/%s [y]? ",dir,what);
  938.  
  939.         /* process the reply */
  940.         switch (getc(stdin)){
  941.             case 'N':
  942.             case 'n':
  943.                 fprintf(stderr,"no\n");
  944.                 break;
  945.             case 'q':
  946.             case 'Q':
  947.                 fprintf(stderr,"quit\n");
  948.                 unset_term();
  949.                 exit(0);
  950.                 break;
  951.             default :
  952.                 fprintf(stderr,"yes\n");
  953.                 unset_term();
  954.                 if (chdir(us)==-1){
  955.                     perror("chdir");
  956.                     exit(1);
  957.                 }
  958.                 sprintf(arg,"%s/%s",dir,what);
  959.                 return(1);
  960.         }
  961.         unset_term();
  962.     }
  963.  
  964.     if (chdir(us)==-1){
  965.         perror("chdir");
  966.         exit(1);
  967.     }
  968.     return(0);
  969. }
  970.  
  971. check(target,dir)
  972. char *target;
  973. char *dir;
  974. {
  975.     DIR *dp, *opendir();
  976.     struct direct *readdir();
  977.     struct direct *entry;
  978.     struct stat buf;
  979.  
  980.     if ((dp=opendir(dir))==NULL){
  981.         /* watch here for the chance that they are sitting in someone
  982.            else's directory which may be unreadable - if not, complain. */
  983.         if (strcmp(dir,".")){
  984.             fprintf(stderr,"Cannot open \"%s\"\n",dir);
  985.         }
  986.         return;
  987.     }
  988.     
  989.     for (entry=readdir(dp);entry!=NULL;entry=readdir(dp)){
  990.         if (!strcmp(entry->d_name,target)){
  991.             if (chdir(dir)==-1){
  992.                 perror("chdir");
  993.                 return;
  994.             }
  995.  
  996.             if (stat(entry->d_name,&buf)==-1){
  997.                 perror("stat");
  998.                 return;
  999.             }
  1000.  
  1001.             /* 
  1002.                 if it is not a directory and EITHER you own it and can
  1003.                 read it OR you don't own it and it is readable by others, 
  1004.                 OR you are in the group of the owner and it's group readable
  1005.                     - then this is it.
  1006.             */
  1007.  
  1008.             if (    !(buf.st_mode&S_IFDIR)  &&  
  1009.                     (
  1010.                         (buf.st_uid==getuid() && buf.st_mode&S_IREAD)
  1011.                         ||
  1012.                         (buf.st_gid==getgid() && buf.st_mode&G_READ)
  1013.                         ||
  1014.                         (buf.st_uid!=getuid() && buf.st_mode&O_READ)
  1015.                     )
  1016.                 )
  1017.             {
  1018.                 return(1);
  1019.             }
  1020.         }
  1021.     }
  1022.     return(0);
  1023. }
  1024.