plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

idiff.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 }