URI:
       tidiff.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tidiff.c (6460B)
       ---
            1 /*
            2  * interactive diff, inspired/stolen from
            3  * kernighan and pike, _unix programming environment_.
            4  */
            5 
            6 #include <u.h>
            7 #include <libc.h>
            8 #include <bio.h>
            9 
           10 int diffbflag;
           11 int diffwflag;
           12 
           13 void copy(Biobuf*, char*, Biobuf*, char*);
           14 void idiff(Biobuf*, char*, Biobuf*, char*, Biobuf*, char*, Biobuf*, char*);
           15 void rundiff(char*, char*, int);
           16 
           17 void
           18 usage(void)
           19 {
           20         fprint(2, "usage: idiff [-bw] file1 file2\n");
           21         exits("usage");
           22 }
           23 
           24 void
           25 main(int argc, char **argv)
           26 {
           27         int fd, ofd;
           28         char diffout[40], idiffout[40];
           29         Biobuf *b1, *b2, bdiff, bout, bstdout;
           30         Dir *d;
           31 
           32         ARGBEGIN{
           33         default:
           34                 usage();
           35         case 'b':
           36                 diffbflag++;
           37                 break;
           38         case 'w':
           39                 diffwflag++;
           40                 break;
           41         }ARGEND
           42 
           43         if(argc != 2)
           44                 usage();
           45 
           46         if((d = dirstat(argv[0])) == nil)
           47                 sysfatal("stat %s: %r", argv[0]);
           48         if(d->mode&DMDIR)
           49                 sysfatal("%s is a directory", argv[0]);
           50         free(d);
           51         if((d = dirstat(argv[1])) == nil)
           52                 sysfatal("stat %s: %r", argv[1]);
           53         if(d->mode&DMDIR)
           54                 sysfatal("%s is a directory", argv[1]);
           55         free(d);
           56 
           57         if((b1 = Bopen(argv[0], OREAD)) == nil)
           58                 sysfatal("open %s: %r", argv[0]);
           59         if((b2 = Bopen(argv[1], OREAD)) == nil)
           60                 sysfatal("open %s: %r", argv[1]);
           61 
           62         strcpy(diffout, "/tmp/idiff.XXXXXX");
           63         fd = opentemp(diffout, ORDWR|ORCLOSE);
           64         strcpy(idiffout, "/tmp/idiff.XXXXXX");
           65         ofd = opentemp(idiffout, ORDWR|ORCLOSE);
           66         rundiff(argv[0], argv[1], fd);
           67         seek(fd, 0, 0);
           68         Binit(&bdiff, fd, OREAD);
           69         Binit(&bout, ofd, OWRITE);
           70         idiff(b1, argv[0], b2, argv[1], &bdiff, diffout, &bout, idiffout);
           71         Bterm(&bdiff);
           72         Bflush(&bout);
           73         seek(ofd, 0, 0);
           74         Binit(&bout, ofd, OREAD);
           75         Binit(&bstdout, 1, OWRITE);
           76         copy(&bout, idiffout, &bstdout, "<stdout>");
           77         exits(nil);
           78 }
           79 
           80 void
           81 rundiff(char *arg1, char *arg2, int outfd)
           82 {
           83         char *arg[10], *p;
           84         int narg, pid;
           85         Waitmsg *w;
           86 
           87         narg = 0;
           88         arg[narg++] = "9";
           89         arg[narg++] = "diff";
           90         arg[narg++] = "-n";
           91         if(diffbflag)
           92                 arg[narg++] = "-b";
           93         if(diffwflag)
           94                 arg[narg++] = "-w";
           95         arg[narg++] = arg1;
           96         arg[narg++] = arg2;
           97         arg[narg] = nil;
           98 
           99         switch(pid = fork()){
          100         case -1:
          101                 sysfatal("fork: %r");
          102 
          103         case 0:
          104                 dup(outfd, 1);
          105                 close(0);
          106                 exec("9", arg);
          107                 sysfatal("exec: %r");
          108 
          109         default:
          110                 w = wait();
          111                 if(w==nil)
          112                         sysfatal("wait: %r");
          113                 if(w->pid != pid)
          114                         sysfatal("wait got unexpected pid %d", w->pid);
          115                 if((p = strchr(w->msg, ':')) && strcmp(p, ": some") != 0)
          116                         sysfatal("%s", w->msg);
          117                 free(w);
          118         }
          119 }
          120 
          121 void
          122 runcmd(char *cmd)
          123 {
          124         char *arg[10];
          125         int narg, pid, wpid;
          126 
          127         narg = 0;
          128         arg[narg++] = "rc";
          129         arg[narg++] = "-c";
          130         arg[narg++] = cmd;
          131         arg[narg] = nil;
          132 
          133         switch(pid = fork()){
          134         case -1:
          135                 sysfatal("fork: %r");
          136 
          137         case 0:
          138                 exec("rc", arg);
          139                 sysfatal("exec: %r");
          140 
          141         default:
          142                 wpid = waitpid();
          143                 if(wpid < 0)
          144                         sysfatal("wait: %r");
          145                 if(wpid != pid)
          146                         sysfatal("wait got unexpected pid %d", wpid);
          147         }
          148 }
          149 
          150 void
          151 parse(char *s, int *pfrom1, int *pto1, int *pcmd, int *pfrom2, int *pto2)
          152 {
          153         *pfrom1 = *pto1 = *pfrom2 = *pto2 = 0;
          154 
          155         s = strchr(s, ':');
          156         if(s == nil)
          157                 sysfatal("bad diff output0");
          158         s++;
          159         *pfrom1 = strtol(s, &s, 10);
          160         if(*s == ','){
          161                 s++;
          162                 *pto1 = strtol(s, &s, 10);
          163         }else
          164                 *pto1 = *pfrom1;
          165         if(*s++ != ' ')
          166                 sysfatal("bad diff output1");
          167         *pcmd = *s++;
          168         if(*s++ != ' ')
          169                 sysfatal("bad diff output2");
          170         s = strchr(s, ':');
          171         if(s == nil)
          172                 sysfatal("bad diff output3");
          173         s++;
          174         *pfrom2 = strtol(s, &s, 10);
          175         if(*s == ','){
          176                 s++;
          177                 *pto2 = strtol(s, &s, 10);
          178         }else
          179                 *pto2 = *pfrom2;
          180 }
          181 
          182 void
          183 skiplines(Biobuf *b, char *name, int n)
          184 {
          185         int i;
          186 
          187         for(i=0; i<n; i++){
          188                 while(Brdline(b, '\n')==nil){
          189                         if(Blinelen(b) <= 0)
          190                                 sysfatal("early end of file on %s", name);
          191                         Bseek(b, Blinelen(b), 1);
          192                 }
          193         }
          194 }
          195 
          196 void
          197 copylines(Biobuf *bin, char *nin, Biobuf *bout, char *nout, int n)
          198 {
          199         char buf[4096], *p;
          200         int i, m;
          201 
          202         for(i=0; i<n; i++){
          203                 while((p=Brdline(bin, '\n'))==nil){
          204                         if(Blinelen(bin) <= 0)
          205                                 sysfatal("early end of file on %s", nin);
          206                         m = Blinelen(bin);
          207                         if(m > sizeof buf)
          208                                 m = sizeof buf;
          209                         m = Bread(bin, buf, m);
          210                         if(Bwrite(bout, buf, m) != m)
          211                                 sysfatal("error writing %s: %r", nout);
          212                 }
          213                 if(Bwrite(bout, p, Blinelen(bin)) != Blinelen(bin))
          214                         sysfatal("error writing %s: %r", nout);
          215         }
          216 }
          217 
          218 void
          219 copy(Biobuf *bin, char *nin, Biobuf *bout, char *nout)
          220 {
          221         char buf[4096];
          222         int m;
          223 
          224         USED(nin);
          225         while((m = Bread(bin, buf, sizeof buf)) > 0)
          226                 if(Bwrite(bout, buf, m) != m)
          227                         sysfatal("error writing %s: %r", nout);
          228 }
          229 
          230 void
          231 idiff(Biobuf *b1, char *name1, Biobuf *b2, char *name2, Biobuf *bdiff, char *namediff, Biobuf *bout, char *nameout)
          232 {
          233         char buf[256], *p;
          234         int interactive, defaultanswer, cmd, diffoffset;
          235         int n, from1, to1, from2, to2, nf1, nf2;
          236         Biobuf berr;
          237 
          238         nf1 = 1;
          239         nf2 = 1;
          240         interactive = 1;
          241         defaultanswer = 0;
          242         Binit(&berr, 2, OWRITE);
          243         while(diffoffset = Boffset(bdiff), p = Brdline(bdiff, '\n')){
          244                 p[Blinelen(bdiff)-1] = '\0';
          245                 parse(p, &from1, &to1, &cmd, &from2, &to2);
          246                 p[Blinelen(bdiff)-1] = '\n';
          247                 n = to1-from1 + to2-from2 + 1;        /* #lines from diff */
          248                 if(cmd == 'c')
          249                         n += 2;
          250                 else if(cmd == 'a')
          251                         from1++;
          252                 else if(cmd == 'd')
          253                         from2++;
          254                 to1++;        /* make half-open intervals */
          255                 to2++;
          256                 if(interactive){
          257                         p[Blinelen(bdiff)-1] = '\0';
          258                         fprint(2, "%s\n", p);
          259                         p[Blinelen(bdiff)-1] = '\n';
          260                         copylines(bdiff, namediff, &berr, "<stderr>", n);
          261                         Bflush(&berr);
          262                 }else
          263                         skiplines(bdiff, namediff, n);
          264                 do{
          265                         if(interactive){
          266                                 fprint(2, "? ");
          267                                 memset(buf, 0, sizeof buf);
          268                                 if(read(0, buf, sizeof buf - 1) < 0)
          269                                         sysfatal("read console: %r");
          270                         }else
          271                                 buf[0] = defaultanswer;
          272 
          273                         switch(buf[0]){
          274                         case '>':
          275                                 copylines(b1, name1, bout, nameout, from1-nf1);
          276                                 skiplines(b1, name1, to1-from1);
          277                                 skiplines(b2, name2, from2-nf2);
          278                                 copylines(b2, name2, bout, nameout, to2-from2);
          279                                 break;
          280                         case '<':
          281                                 copylines(b1, name1, bout, nameout, to1-nf1);
          282                                 skiplines(b2, name2, to2-nf2);
          283                                 break;
          284                         case '=':
          285                                 copylines(b1, name1, bout, nameout, from1-nf1);
          286                                 skiplines(b1, name1, to1-from1);
          287                                 skiplines(b2, name2, to2-nf2);
          288                                 if(Bseek(bdiff, diffoffset, 0) != diffoffset)
          289                                         sysfatal("seek in diff output: %r");
          290                                 copylines(bdiff, namediff, bout, nameout, n+1);
          291                                 break;
          292                         case '!':
          293                                 runcmd(buf+1);
          294                                 break;
          295                         case 'q':
          296                                 if(buf[1]=='<' || buf[1]=='>' || buf[1]=='='){
          297                                         interactive = 0;
          298                                         defaultanswer = buf[1];
          299                                 }else
          300                                         fprint(2, "must be q<, q>, or q=\n");
          301                                 break;
          302                         default:
          303                                 fprint(2, "expect: <, >, =, q<, q>, q=, !cmd\n");
          304                                 break;
          305                         }
          306                 }while(buf[0] != '<' && buf[0] != '>' && buf[0] != '=');
          307                 nf1 = to1;
          308                 nf2 = to2;
          309         }
          310         copy(b1, name1, bout, nameout);
          311 }