idiff.c 6.7 KB

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