1 /* $OpenBSD: diffreg.c,v 1.34 2003/07/16 21:39:06 millert Exp $ */ 2 3 /* 4 * Copyright (C) Caldera International Inc. 2001-2002. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code and documentation must retain the above 11 * copyright notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed or owned by Caldera 18 * International, Inc. 19 * 4. Neither the name of Caldera International, Inc. nor the names of other 20 * contributors may be used to endorse or promote products derived from 21 * this software without specific prior written permission. 22 * 23 * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA 24 * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR 25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 27 * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT, 28 * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 33 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 * POSSIBILITY OF SUCH DAMAGE. 35 */ 36 /*- 37 * Copyright (c) 1991, 1993 38 * The Regents of the University of California. All rights reserved. 39 * 40 * Redistribution and use in source and binary forms, with or without 41 * modification, are permitted provided that the following conditions 42 * are met: 43 * 1. Redistributions of source code must retain the above copyright 44 * notice, this list of conditions and the following disclaimer. 45 * 2. Redistributions in binary form must reproduce the above copyright 46 * notice, this list of conditions and the following disclaimer in the 47 * documentation and/or other materials provided with the distribution. 48 * 3. Neither the name of the University nor the names of its contributors 49 * may be used to endorse or promote products derived from this software 50 * without specific prior written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 62 * SUCH DAMAGE. 63 * 64 * @(#)diffreg.c 8.1 (Berkeley) 6/6/93 65 */ 66 67 #ifndef lint 68 static const char rcsid[] = "$OpenBSD: diffreg.c,v 1.34 2003/07/16 21:39:06 millert Exp $"; 69 #endif /* not lint */ 70 71 #include <sys/param.h> 72 #include <sys/stat.h> 73 #include <sys/wait.h> 74 75 #include <ctype.h> 76 #include <err.h> 77 #include <errno.h> 78 #include <fcntl.h> 79 #include <libgen.h> 80 #include <stdio.h> 81 #include <stdlib.h> 82 #include <string.h> 83 #include <unistd.h> 84 85 #include "diff.h" 86 #include "pathnames.h" 87 88 /* 89 * diff - compare two files. 90 */ 91 92 /* 93 * Uses an algorithm due to Harold Stone, which finds 94 * a pair of longest identical subsequences in the two 95 * files. 96 * 97 * The major goal is to generate the match vector J. 98 * J[i] is the index of the line in file1 corresponding 99 * to line i file0. J[i] = 0 if there is no 100 * such line in file1. 101 * 102 * Lines are hashed so as to work in core. All potential 103 * matches are located by sorting the lines of each file 104 * on the hash (called ``value''). In particular, this 105 * collects the equivalence classes in file1 together. 106 * Subroutine equiv replaces the value of each line in 107 * file0 by the index of the first element of its 108 * matching equivalence in (the reordered) file1. 109 * To save space equiv squeezes file1 into a single 110 * array member in which the equivalence classes 111 * are simply concatenated, except that their first 112 * members are flagged by changing sign. 113 * 114 * Next the indices that point into member are unsorted into 115 * array class according to the original order of file0. 116 * 117 * The cleverness lies in routine stone. This marches 118 * through the lines of file0, developing a vector klist 119 * of "k-candidates". At step i a k-candidate is a matched 120 * pair of lines x,y (x in file0 y in file1) such that 121 * there is a common subsequence of length k 122 * between the first i lines of file0 and the first y 123 * lines of file1, but there is no such subsequence for 124 * any smaller y. x is the earliest possible mate to y 125 * that occurs in such a subsequence. 126 * 127 * Whenever any of the members of the equivalence class of 128 * lines in file1 matable to a line in file0 has serial number 129 * less than the y of some k-candidate, that k-candidate 130 * with the smallest such y is replaced. The new 131 * k-candidate is chained (via pred) to the current 132 * k-1 candidate so that the actual subsequence can 133 * be recovered. When a member has serial number greater 134 * that the y of all k-candidates, the klist is extended. 135 * At the end, the longest subsequence is pulled out 136 * and placed in the array J by unravel 137 * 138 * With J in hand, the matches there recorded are 139 * check'ed against reality to assure that no spurious 140 * matches have crept in due to hashing. If they have, 141 * they are broken, and "jackpot" is recorded--a harmless 142 * matter except that a true match for a spuriously 143 * mated line may now be unnecessarily reported as a change. 144 * 145 * Much of the complexity of the program comes simply 146 * from trying to minimize core utilization and 147 * maximize the range of doable problems by dynamically 148 * allocating what is needed and reusing what is not. 149 * The core requirements for problems larger than somewhat 150 * are (in words) 2*length(file0) + length(file1) + 151 * 3*(number of k-candidates installed), typically about 152 * 6n words for files of length n. 153 */ 154 155 struct cand { 156 int x; 157 int y; 158 int pred; 159 } cand; 160 161 struct line { 162 int serial; 163 int value; 164 } *file[2]; 165 166 static int *J; /* will be overlaid on class */ 167 static int *class; /* will be overlaid on file[0] */ 168 static int *klist; /* will be overlaid on file[0] after class */ 169 static int *member; /* will be overlaid on file[1] */ 170 static int clen; 171 static int inifdef; /* whether or not we are in a #ifdef block */ 172 static int len[2]; 173 static int pref, suff; /* length of prefix and suffix */ 174 static int slen[2]; 175 static int anychange; 176 static long *ixnew; /* will be overlaid on file[1] */ 177 static long *ixold; /* will be overlaid on klist */ 178 static struct cand *clist; /* merely a free storage pot for candidates */ 179 static struct line *sfile[2]; /* shortened by pruning common prefix/suffix */ 180 static u_char *chrtran; /* translation table for case-folding */ 181 182 static FILE *opentemp(const char *); 183 static void fetch(long *, int, int, FILE *, char *, int); 184 static void output(char *, FILE *, char *, FILE *); 185 static void check(char *, FILE *, char *, FILE *); 186 static void range(int, int, char *); 187 static void uni_range(int, int); 188 static void dump_context_vec(FILE *, FILE *); 189 static void dump_unified_vec(FILE *, FILE *); 190 static void prepare(int, FILE *); 191 static void prune(void); 192 static void equiv(struct line *, int, struct line *, int, int *); 193 static void unravel(int); 194 static void unsort(struct line *, int, int *); 195 static void change(char *, FILE *, char *, FILE *, int, int, int, int); 196 static void sort(struct line *, int); 197 static int asciifile(FILE *); 198 static int newcand(int, int, int); 199 static int search(int *, int, int); 200 static int skipline(FILE *); 201 static int stone(int *, int, int *, int *); 202 static int readhash(FILE *); 203 static int files_differ(FILE *, FILE *, int); 204 205 /* 206 * chrtran points to one of 2 translation tables: cup2low if folding upper to 207 * lower case clow2low if not folding case 208 */ 209 u_char clow2low[256] = { 210 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 211 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 212 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 213 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 214 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 215 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 216 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 217 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 218 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 219 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 220 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 221 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 222 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 223 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 224 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 225 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 226 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 227 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 228 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 229 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 230 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 231 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 232 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 233 0xfd, 0xfe, 0xff 234 }; 235 236 u_char cup2low[256] = { 237 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 238 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 239 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 240 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 241 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 242 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61, 243 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 244 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 245 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62, 246 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 247 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 248 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 249 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 250 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 251 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 252 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 253 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 254 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 255 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 256 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 257 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 258 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 259 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 260 0xfd, 0xfe, 0xff 261 }; 262 263 int 264 diffreg(char *ofile1, char *ofile2, int flags) 265 { 266 char *file1 = ofile1; 267 char *file2 = ofile2; 268 FILE *f1 = NULL; 269 FILE *f2 = NULL; 270 int rval = D_SAME; 271 int i, ostdout = -1; 272 pid_t pid = -1; 273 274 anychange = 0; 275 chrtran = (iflag ? cup2low : clow2low); 276 if (S_ISDIR(stb1.st_mode) != S_ISDIR(stb2.st_mode)) 277 return (D_MISMATCH); 278 if (strcmp(file1, "-") == 0 && strcmp(file2, "-") == 0) 279 goto notsame; 280 281 if (flags & D_EMPTY1) 282 f1 = fopen(_PATH_DEVNULL, "r"); 283 else { 284 if (!S_ISREG(stb1.st_mode)) { 285 if ((f1 = opentemp(file1)) == NULL || 286 fstat(fileno(f1), &stb1) < 0) { 287 warn("%s", file1); 288 status |= 2; 289 goto closem; 290 } 291 } else if (strcmp(file1, "-") == 0) 292 f1 = stdin; 293 else 294 f1 = fopen(file1, "r"); 295 } 296 if (f1 == NULL) { 297 warn("%s", file1); 298 status |= 2; 299 goto closem; 300 } 301 302 if (flags & D_EMPTY2) 303 f2 = fopen(_PATH_DEVNULL, "r"); 304 else { 305 if (!S_ISREG(stb2.st_mode)) { 306 if ((f2 = opentemp(file2)) == NULL || 307 fstat(fileno(f2), &stb2) < 0) { 308 warn("%s", file2); 309 status |= 2; 310 goto closem; 311 } 312 } else if (strcmp(file2, "-") == 0) 313 f2 = stdin; 314 else 315 f2 = fopen(file2, "r"); 316 } 317 if (f2 == NULL) { 318 warn("%s", file2); 319 status |= 2; 320 goto closem; 321 } 322 323 switch (files_differ(f1, f2, flags)) { 324 case 0: 325 goto closem; 326 case 1: 327 break; 328 default: 329 /* error */ 330 status |= 2; 331 goto closem; 332 } 333 334 notsame: 335 /* 336 * Files certainly differ at this point; set status accordingly 337 */ 338 status |= 1; 339 rval = D_DIFFER; 340 if (!asciifile(f1) || !asciifile(f2)) { 341 rval = D_BINARY; 342 goto closem; 343 } 344 if (format == D_BRIEF) 345 goto closem; 346 if (lflag) { 347 /* redirect stdout to pr */ 348 int pfd[2]; 349 char *header; 350 char *prargv[] = { "pr", "-h", NULL, "-f", NULL }; 351 352 easprintf(&header, "%s %s %s", diffargs, file1, file2); 353 prargv[2] = header; 354 fflush(stdout); 355 rewind(stdout); 356 pipe(pfd); 357 switch ((pid = fork())) { 358 case -1: 359 warnx("No more processes"); 360 status |= 2; 361 free(header); 362 return (D_ERROR); 363 case 0: 364 /* child */ 365 if (pfd[0] != STDIN_FILENO) { 366 dup2(pfd[0], STDIN_FILENO); 367 close(pfd[0]); 368 } 369 close(pfd[1]); 370 execv(_PATH_PR, prargv); 371 _exit(127); 372 default: 373 /* parent */ 374 if (pfd[1] != STDOUT_FILENO) { 375 ostdout = dup(STDOUT_FILENO); 376 dup2(pfd[1], STDOUT_FILENO); 377 close(pfd[1]); 378 } 379 close(pfd[0]); 380 rewind(stdout); 381 free(header); 382 } 383 } else { 384 if (flags & D_HEADER) { 385 if (format == D_EDIT) 386 printf("ed - %s << '-*-END-*-'\n", 387 basename(file1)); 388 else 389 printf("%s %s %s\n", diffargs, file1, file2); 390 } 391 } 392 prepare(0, f1); 393 prepare(1, f2); 394 prune(); 395 sort(sfile[0], slen[0]); 396 sort(sfile[1], slen[1]); 397 398 member = (int *)file[1]; 399 equiv(sfile[0], slen[0], sfile[1], slen[1], member); 400 member = erealloc(member, (slen[1] + 2) * sizeof(int)); 401 402 class = (int *)file[0]; 403 unsort(sfile[0], slen[0], class); 404 class = erealloc(class, (slen[0] + 2) * sizeof(int)); 405 406 klist = emalloc((slen[0] + 2) * sizeof(int)); 407 clist = emalloc(sizeof(cand)); 408 i = stone(class, slen[0], member, klist); 409 free(member); 410 free(class); 411 412 J = erealloc(J, (len[0] + 2) * sizeof(int)); 413 unravel(klist[i]); 414 free(clist); 415 free(klist); 416 417 ixold = erealloc(ixold, (len[0] + 2) * sizeof(long)); 418 ixnew = erealloc(ixnew, (len[1] + 2) * sizeof(long)); 419 check(file1, f1, file2, f2); 420 output(file1, f1, file2, f2); 421 if (ostdout != -1) { 422 int wstatus; 423 424 /* close the pipe to pr and restore stdout */ 425 fflush(stdout); 426 rewind(stdout); 427 if (ostdout != STDOUT_FILENO) { 428 close(STDOUT_FILENO); 429 dup2(ostdout, STDOUT_FILENO); 430 close(ostdout); 431 } 432 waitpid(pid, &wstatus, 0); 433 } else if ((flags & D_HEADER) && format == D_EDIT) 434 printf("w\nq\n-*-END-*-\n"); 435 closem: 436 if (f1 != NULL) 437 fclose(f1); 438 if (f2 != NULL) 439 fclose(f2); 440 if (file1 != ofile1) 441 free(file1); 442 if (file2 != ofile2) 443 free(file2); 444 return (rval); 445 } 446 447 /* 448 * Check to see if the given files differ. 449 * Returns 0 if they are the same, 1 if different, and -1 on error. 450 * XXX - could use code from cmp(1) [faster] 451 */ 452 static int 453 files_differ(FILE *f1, FILE *f2, int flags) 454 { 455 char buf1[BUFSIZ], buf2[BUFSIZ]; 456 size_t i, j; 457 458 if ((flags & (D_EMPTY1|D_EMPTY2)) || stb1.st_size != stb2.st_size || 459 (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT)) 460 return (1); 461 for (;;) { 462 i = fread(buf1, 1, sizeof(buf1), f1); 463 j = fread(buf2, 1, sizeof(buf2), f2); 464 if (i != j) 465 return (1); 466 if (i == 0 && j == 0) { 467 if (ferror(f1) || ferror(f2)) 468 return (1); 469 return (0); 470 } 471 if (memcmp(buf1, buf2, i) != 0) 472 return (1); 473 } 474 } 475 476 static FILE * 477 opentemp(const char *file) 478 { 479 char buf[BUFSIZ], *tempdir, tempfile[MAXPATHLEN]; 480 ssize_t nread; 481 int ifd, ofd; 482 483 if (strcmp(file, "-") == 0) 484 ifd = STDIN_FILENO; 485 else if ((ifd = open(file, O_RDONLY, 0644)) < 0) 486 return (NULL); 487 488 if ((tempdir = getenv("TMPDIR")) == NULL) 489 tempdir = _PATH_TMP; 490 if (snprintf(tempfile, sizeof(tempfile), "%s/diff.XXXXXXXX", 491 tempdir) >= sizeof(tempfile)) { 492 close(ifd); 493 errno = ENAMETOOLONG; 494 return (NULL); 495 } 496 497 if ((ofd = mkstemp(tempfile)) < 0) 498 return (NULL); 499 unlink(tempfile); 500 while ((nread = read(ifd, buf, BUFSIZ)) > 0) { 501 if (write(ofd, buf, nread) != nread) { 502 close(ifd); 503 close(ofd); 504 return (NULL); 505 } 506 } 507 close(ifd); 508 return (fdopen(ofd, "r")); 509 } 510 511 char * 512 splice(char *dir, char *file) 513 { 514 char *tail, *buf; 515 516 if ((tail = strrchr(file, '/')) == NULL) 517 tail = file; 518 else 519 tail++; 520 easprintf(&buf, "%s/%s", dir, tail); 521 return (buf); 522 } 523 524 static void 525 prepare(int i, FILE *fd) 526 { 527 struct line *p; 528 int j, h; 529 530 rewind(fd); 531 p = emalloc(3 * sizeof(struct line)); 532 for (j = 0; (h = readhash(fd));) { 533 p = erealloc(p, (++j + 3) * sizeof(struct line)); 534 p[j].value = h; 535 } 536 len[i] = j; 537 file[i] = p; 538 } 539 540 static void 541 prune(void) 542 { 543 int i, j; 544 545 for (pref = 0; pref < len[0] && pref < len[1] && 546 file[0][pref + 1].value == file[1][pref + 1].value; 547 pref++) 548 ; 549 for (suff = 0; suff < len[0] - pref && suff < len[1] - pref && 550 file[0][len[0] - suff].value == file[1][len[1] - suff].value; 551 suff++) 552 ; 553 for (j = 0; j < 2; j++) { 554 sfile[j] = file[j] + pref; 555 slen[j] = len[j] - pref - suff; 556 for (i = 0; i <= slen[j]; i++) 557 sfile[j][i].serial = i; 558 } 559 } 560 561 static void 562 equiv(struct line *a, int n, struct line *b, int m, int *c) 563 { 564 int i, j; 565 566 i = j = 1; 567 while (i <= n && j <= m) { 568 if (a[i].value < b[j].value) 569 a[i++].value = 0; 570 else if (a[i].value == b[j].value) 571 a[i++].value = j; 572 else 573 j++; 574 } 575 while (i <= n) 576 a[i++].value = 0; 577 b[m + 1].value = 0; 578 j = 0; 579 while (++j <= m) { 580 c[j] = -b[j].serial; 581 while (b[j + 1].value == b[j].value) { 582 j++; 583 c[j] = b[j].serial; 584 } 585 } 586 c[j] = -1; 587 } 588 589 static int 590 stone(int *a, int n, int *b, int *c) 591 { 592 int i, k, y, j, l; 593 int oldc, tc, oldl; 594 595 k = 0; 596 c[0] = newcand(0, 0, 0); 597 for (i = 1; i <= n; i++) { 598 j = a[i]; 599 if (j == 0) 600 continue; 601 y = -b[j]; 602 oldl = 0; 603 oldc = c[0]; 604 do { 605 if (y <= clist[oldc].y) 606 continue; 607 l = search(c, k, y); 608 if (l != oldl + 1) 609 oldc = c[l - 1]; 610 if (l <= k) { 611 if (clist[c[l]].y <= y) 612 continue; 613 tc = c[l]; 614 c[l] = newcand(i, y, oldc); 615 oldc = tc; 616 oldl = l; 617 } else { 618 c[l] = newcand(i, y, oldc); 619 k++; 620 break; 621 } 622 } while ((y = b[++j]) > 0); 623 } 624 return (k); 625 } 626 627 static int 628 newcand(int x, int y, int pred) 629 { 630 struct cand *q; 631 632 clist = erealloc(clist, ++clen * sizeof(cand)); 633 q = clist + clen - 1; 634 q->x = x; 635 q->y = y; 636 q->pred = pred; 637 return (clen - 1); 638 } 639 640 static int 641 search(int *c, int k, int y) 642 { 643 int i, j, l, t; 644 645 if (clist[c[k]].y < y) /* quick look for typical case */ 646 return (k + 1); 647 i = 0; 648 j = k + 1; 649 while (1) { 650 l = i + j; 651 if ((l >>= 1) <= i) 652 break; 653 t = clist[c[l]].y; 654 if (t > y) 655 j = l; 656 else if (t < y) 657 i = l; 658 else 659 return (l); 660 } 661 return (l + 1); 662 } 663 664 static void 665 unravel(int p) 666 { 667 struct cand *q; 668 int i; 669 670 for (i = 0; i <= len[0]; i++) 671 J[i] = i <= pref ? i : 672 i > len[0] - suff ? i + len[1] - len[0] : 0; 673 for (q = clist + p; q->y != 0; q = clist + q->pred) 674 J[q->x + pref] = q->y + pref; 675 } 676 677 /* 678 * Check does double duty: 679 * 1. ferret out any fortuitous correspondences due 680 * to confounding by hashing (which result in "jackpot") 681 * 2. collect random access indexes to the two files 682 */ 683 static void 684 check(char *file1, FILE *f1, char *file2, FILE *f2) 685 { 686 int i, j, jackpot, c, d; 687 long ctold, ctnew; 688 689 rewind(f1); 690 rewind(f2); 691 j = 1; 692 ixold[0] = ixnew[0] = 0; 693 jackpot = 0; 694 ctold = ctnew = 0; 695 for (i = 1; i <= len[0]; i++) { 696 if (J[i] == 0) { 697 ixold[i] = ctold += skipline(f1); 698 continue; 699 } 700 while (j < J[i]) { 701 ixnew[j] = ctnew += skipline(f2); 702 j++; 703 } 704 if (bflag || wflag || iflag) { 705 for (;;) { 706 c = getc(f1); 707 d = getc(f2); 708 /* 709 * GNU diff ignores a missing newline 710 * in one file if bflag || wflag. 711 */ 712 if ((bflag || wflag) && 713 ((c == EOF && d == '\n') || 714 (c == '\n' && d == EOF))) { 715 break; 716 } 717 ctold++; 718 ctnew++; 719 if (bflag && isspace(c) && isspace(d)) { 720 do { 721 if (c == '\n') 722 break; 723 ctold++; 724 } while (isspace(c = getc(f1))); 725 do { 726 if (d == '\n') 727 break; 728 ctnew++; 729 } while (isspace(d = getc(f2))); 730 } else if (wflag) { 731 while (isspace(c) && c != '\n') { 732 c = getc(f1); 733 ctold++; 734 } 735 while (isspace(d) && d != '\n') { 736 d = getc(f2); 737 ctnew++; 738 } 739 } 740 if (chrtran[c] != chrtran[d]) { 741 jackpot++; 742 J[i] = 0; 743 if (c != '\n' && c != EOF) 744 ctold += skipline(f1); 745 if (d != '\n' && c != EOF) 746 ctnew += skipline(f2); 747 break; 748 } 749 if (c == '\n' || c == EOF) 750 break; 751 } 752 } else { 753 for (;;) { 754 ctold++; 755 ctnew++; 756 if ((c = getc(f1)) != (d = getc(f2))) { 757 /* jackpot++; */ 758 J[i] = 0; 759 if (c != '\n' && c != EOF) 760 ctold += skipline(f1); 761 if (d != '\n' && c != EOF) 762 ctnew += skipline(f2); 763 break; 764 } 765 if (c == '\n' || c == EOF) 766 break; 767 } 768 } 769 ixold[i] = ctold; 770 ixnew[j] = ctnew; 771 j++; 772 } 773 for (; j <= len[1]; j++) 774 ixnew[j] = ctnew += skipline(f2); 775 /* 776 * if (jackpot) 777 * fprintf(stderr, "jackpot\n"); 778 */ 779 } 780 781 /* shellsort CACM #201 */ 782 static void 783 sort(struct line *a, int n) 784 { 785 struct line *ai, *aim, w; 786 int j, m = 0, k; 787 788 if (n == 0) 789 return; 790 for (j = 1; j <= n; j *= 2) 791 m = 2 * j - 1; 792 for (m /= 2; m != 0; m /= 2) { 793 k = n - m; 794 for (j = 1; j <= k; j++) { 795 for (ai = &a[j]; ai > a; ai -= m) { 796 aim = &ai[m]; 797 if (aim < ai) 798 break; /* wraparound */ 799 if (aim->value > ai[0].value || 800 (aim->value == ai[0].value && 801 aim->serial > ai[0].serial)) 802 break; 803 w.value = ai[0].value; 804 ai[0].value = aim->value; 805 aim->value = w.value; 806 w.serial = ai[0].serial; 807 ai[0].serial = aim->serial; 808 aim->serial = w.serial; 809 } 810 } 811 } 812 } 813 814 static void 815 unsort(struct line *f, int l, int *b) 816 { 817 int *a, i; 818 819 a = emalloc((l + 1) * sizeof(int)); 820 for (i = 1; i <= l; i++) 821 a[f[i].serial] = f[i].value; 822 for (i = 1; i <= l; i++) 823 b[i] = a[i]; 824 free(a); 825 } 826 827 static int 828 skipline(FILE *f) 829 { 830 int i, c; 831 832 for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++) 833 continue; 834 return (i); 835 } 836 837 static void 838 output(char *file1, FILE *f1, char *file2, FILE *f2) 839 { 840 int m, i0, i1, j0, j1; 841 842 rewind(f1); 843 rewind(f2); 844 m = len[0]; 845 J[0] = 0; 846 J[m + 1] = len[1] + 1; 847 if (format != D_EDIT) { 848 for (i0 = 1; i0 <= m; i0 = i1 + 1) { 849 while (i0 <= m && J[i0] == J[i0 - 1] + 1) 850 i0++; 851 j0 = J[i0 - 1] + 1; 852 i1 = i0 - 1; 853 while (i1 < m && J[i1 + 1] == 0) 854 i1++; 855 j1 = J[i1 + 1] - 1; 856 J[i1] = j1; 857 change(file1, f1, file2, f2, i0, i1, j0, j1); 858 } 859 } else { 860 for (i0 = m; i0 >= 1; i0 = i1 - 1) { 861 while (i0 >= 1 && J[i0] == J[i0 + 1] - 1 && J[i0] != 0) 862 i0--; 863 j0 = J[i0 + 1] - 1; 864 i1 = i0 + 1; 865 while (i1 > 1 && J[i1 - 1] == 0) 866 i1--; 867 j1 = J[i1 - 1] + 1; 868 J[i1] = j1; 869 change(file1, f1, file2, f2, i1, i0, j1, j0); 870 } 871 } 872 if (m == 0) 873 change(file1, f1, file2, f2, 1, 0, 1, len[1]); 874 if (format == D_IFDEF) { 875 for (;;) { 876 #define c i0 877 if ((c = getc(f1)) == EOF) 878 return; 879 putchar(c); 880 } 881 #undef c 882 } 883 if (anychange != 0) { 884 if (format == D_CONTEXT) 885 dump_context_vec(f1, f2); 886 else if (format == D_UNIFIED) 887 dump_unified_vec(f1, f2); 888 } 889 } 890 891 static __inline void 892 range(int a, int b, char *separator) 893 { 894 printf("%d", a > b ? b : a); 895 if (a < b) 896 printf("%s%d", separator, b); 897 } 898 899 static __inline void 900 uni_range(int a, int b) 901 { 902 if (a < b) 903 printf("%d,%d", a, b - a + 1); 904 else if (a == b) 905 printf("%d", b); 906 else 907 printf("%d,0", b); 908 } 909 910 /* 911 * The following struct is used to record change information when 912 * doing a "context" or "unified" diff. (see routine "change" to 913 * understand the highly mnemonic field names) 914 */ 915 struct context_vec { 916 int a; /* start line in old file */ 917 int b; /* end line in old file */ 918 int c; /* start line in new file */ 919 int d; /* end line in new file */ 920 }; 921 922 struct context_vec *context_vec_start, *context_vec_end, *context_vec_ptr; 923 924 #define MAX_CONTEXT 128 925 926 /* 927 * Indicate that there is a difference between lines a and b of the from file 928 * to get to lines c to d of the to file. If a is greater then b then there 929 * are no lines in the from file involved and this means that there were 930 * lines appended (beginning at b). If c is greater than d then there are 931 * lines missing from the to file. 932 */ 933 static void 934 change(char *file1, FILE *f1, char *file2, FILE *f2, int a, int b, int c, int d) 935 { 936 if (format != D_IFDEF && a > b && c > d) 937 return; 938 if (anychange == 0) { 939 anychange = 1; 940 if (format == D_CONTEXT || format == D_UNIFIED) { 941 printf("%s %s %s", format == D_CONTEXT ? "***" : "---", 942 file1, ctime(&stb1.st_mtime)); 943 printf("%s %s %s", format == D_CONTEXT ? "---" : "+++", 944 file2, ctime(&stb2.st_mtime)); 945 if (context_vec_start == NULL) 946 context_vec_start = emalloc(MAX_CONTEXT * 947 sizeof(struct context_vec)); 948 context_vec_end = context_vec_start + MAX_CONTEXT; 949 context_vec_ptr = context_vec_start - 1; 950 } 951 } 952 if (format == D_CONTEXT || format == D_UNIFIED) { 953 /* 954 * If this new change is within 'context' lines of 955 * the previous change, just add it to the change 956 * record. If the record is full or if this 957 * change is more than 'context' lines from the previous 958 * change, dump the record, reset it & add the new change. 959 */ 960 if (context_vec_ptr >= context_vec_end || 961 (context_vec_ptr >= context_vec_start && 962 a > (context_vec_ptr->b + 2 * context) && 963 c > (context_vec_ptr->d + 2 * context))) { 964 if (format == D_CONTEXT) 965 dump_context_vec(f1, f2); 966 else 967 dump_unified_vec(f1, f2); 968 } 969 context_vec_ptr++; 970 context_vec_ptr->a = a; 971 context_vec_ptr->b = b; 972 context_vec_ptr->c = c; 973 context_vec_ptr->d = d; 974 return; 975 } 976 switch (format) { 977 978 case D_NORMAL: 979 case D_EDIT: 980 range(a, b, ","); 981 putchar(a > b ? 'a' : c > d ? 'd' : 'c'); 982 if (format == D_NORMAL) 983 range(c, d, ","); 984 putchar('\n'); 985 break; 986 case D_REVERSE: 987 putchar(a > b ? 'a' : c > d ? 'd' : 'c'); 988 range(a, b, " "); 989 putchar('\n'); 990 break; 991 case D_NREVERSE: 992 if (a > b) 993 printf("a%d %d\n", b, d - c + 1); 994 else { 995 printf("d%d %d\n", a, b - a + 1); 996 if (!(c > d)) 997 /* add changed lines */ 998 printf("a%d %d\n", b, d - c + 1); 999 } 1000 break; 1001 } 1002 if (format == D_NORMAL || format == D_IFDEF) { 1003 fetch(ixold, a, b, f1, "< ", 1); 1004 if (a <= b && c <= d && format == D_NORMAL) 1005 puts("---"); 1006 } 1007 fetch(ixnew, c, d, f2, format == D_NORMAL ? "> " : "", 0); 1008 if ((format == D_EDIT || format == D_REVERSE) && c <= d) 1009 puts("."); 1010 if (inifdef) { 1011 printf("#endif /* %s */\n", ifdefname); 1012 inifdef = 0; 1013 } 1014 } 1015 1016 static void 1017 fetch(long *f, int a, int b, FILE *lb, char *s, int oldfile) 1018 { 1019 int i, j, c, col, nc; 1020 1021 /* 1022 * When doing #ifdef's, copy down to current line 1023 * if this is the first file, so that stuff makes it to output. 1024 */ 1025 if (format == D_IFDEF && oldfile) { 1026 long curpos = ftell(lb); 1027 /* print through if append (a>b), else to (nb: 0 vs 1 orig) */ 1028 nc = f[a > b ? b : a - 1] - curpos; 1029 for (i = 0; i < nc; i++) 1030 putchar(getc(lb)); 1031 } 1032 if (a > b) 1033 return; 1034 if (format == D_IFDEF) { 1035 if (inifdef) { 1036 printf("#else /* %s%s */\n", 1037 oldfile == 1 ? "!" : "", ifdefname); 1038 } else { 1039 if (oldfile) 1040 printf("#ifndef %s\n", ifdefname); 1041 else 1042 printf("#ifdef %s\n", ifdefname); 1043 } 1044 inifdef = 1 + oldfile; 1045 } 1046 for (i = a; i <= b; i++) { 1047 fseek(lb, f[i - 1], SEEK_SET); 1048 nc = f[i] - f[i - 1]; 1049 if (format != D_IFDEF) 1050 fputs(s, stdout); 1051 col = 0; 1052 for (j = 0; j < nc; j++) { 1053 if ((c = getc(lb)) == EOF) { 1054 puts("\n\\ No newline at end of file"); 1055 return; 1056 } 1057 if (c == '\t' && tflag) { 1058 do { 1059 putchar(' '); 1060 } while (++col & 7); 1061 } else { 1062 putchar(c); 1063 col++; 1064 } 1065 } 1066 } 1067 } 1068 1069 #define POW2 /* define only if HALFLONG is 2**n */ 1070 #define HALFLONG 16 1071 #define low(x) (x&((1L<<HALFLONG)-1)) 1072 #define high(x) (x>>HALFLONG) 1073 1074 /* 1075 * hashing has the effect of 1076 * arranging line in 7-bit bytes and then 1077 * summing 1-s complement in 16-bit hunks 1078 */ 1079 static int 1080 readhash(FILE *f) 1081 { 1082 unsigned int shift; 1083 int t, space; 1084 long sum; 1085 1086 sum = 1; 1087 space = 0; 1088 if (!bflag && !wflag) { 1089 if (iflag) 1090 for (shift = 0; (t = getc(f)) != '\n'; shift += 7) { 1091 if (t == EOF) { 1092 if (shift == 0) 1093 return (0); 1094 break; 1095 } 1096 sum += (long)chrtran[t] << (shift 1097 #ifdef POW2 1098 &= HALFLONG - 1); 1099 #else 1100 %= HALFLONG); 1101 #endif 1102 } 1103 else 1104 for (shift = 0; (t = getc(f)) != '\n'; shift += 7) { 1105 if (t == EOF) { 1106 if (shift == 0) 1107 return (0); 1108 break; 1109 } 1110 sum += (long)t << (shift 1111 #ifdef POW2 1112 &= HALFLONG - 1); 1113 #else 1114 %= HALFLONG); 1115 #endif 1116 } 1117 } else { 1118 for (shift = 0;;) { 1119 switch (t = getc(f)) { 1120 case '\t': 1121 case ' ': 1122 space++; 1123 continue; 1124 default: 1125 if (space && !wflag) { 1126 shift += 7; 1127 space = 0; 1128 } 1129 sum += (long)chrtran[t] << (shift 1130 #ifdef POW2 1131 &= HALFLONG - 1); 1132 #else 1133 %= HALFLONG); 1134 #endif 1135 shift += 7; 1136 continue; 1137 case EOF: 1138 if (shift == 0) 1139 return (0); 1140 /* FALLTHROUGH */ 1141 case '\n': 1142 break; 1143 } 1144 break; 1145 } 1146 } 1147 sum = low(sum) + high(sum); 1148 return ((short) low(sum) + (short) high(sum)); 1149 } 1150 1151 int 1152 asciifile(FILE *f) 1153 { 1154 char buf[BUFSIZ], *cp; 1155 int cnt; 1156 1157 if (aflag || f == NULL) 1158 return (1); 1159 1160 rewind(f); 1161 cnt = fread(buf, 1, sizeof(buf), f); 1162 cp = buf; 1163 while (--cnt >= 0) 1164 if (*cp++ & 0200) 1165 return (0); 1166 return (1); 1167 } 1168 1169 static __inline int min(int a, int b) 1170 { 1171 return (a < b ? a : b); 1172 } 1173 1174 static __inline int max(int a, int b) 1175 { 1176 return (a > b ? a : b); 1177 } 1178 1179 /* dump accumulated "context" diff changes */ 1180 static void 1181 dump_context_vec(FILE *f1, FILE *f2) 1182 { 1183 struct context_vec *cvp = context_vec_start; 1184 int lowa, upb, lowc, upd, do_output; 1185 int a, b, c, d; 1186 char ch; 1187 1188 if (context_vec_start > context_vec_ptr) 1189 return; 1190 1191 b = d = 0; /* gcc */ 1192 lowa = max(1, cvp->a - context); 1193 upb = min(len[0], context_vec_ptr->b + context); 1194 lowc = max(1, cvp->c - context); 1195 upd = min(len[1], context_vec_ptr->d + context); 1196 1197 printf("***************\n*** "); 1198 range(lowa, upb, ","); 1199 printf(" ****\n"); 1200 1201 /* 1202 * Output changes to the "old" file. The first loop suppresses 1203 * output if there were no changes to the "old" file (we'll see 1204 * the "old" lines as context in the "new" list). 1205 */ 1206 do_output = 0; 1207 for (; cvp <= context_vec_ptr; cvp++) 1208 if (cvp->a <= cvp->b) { 1209 cvp = context_vec_start; 1210 do_output++; 1211 break; 1212 } 1213 if (do_output) { 1214 while (cvp <= context_vec_ptr) { 1215 a = cvp->a; 1216 b = cvp->b; 1217 c = cvp->c; 1218 d = cvp->d; 1219 1220 if (a <= b && c <= d) 1221 ch = 'c'; 1222 else 1223 ch = (a <= b) ? 'd' : 'a'; 1224 1225 if (ch == 'a') 1226 fetch(ixold, lowa, b, f1, " ", 0); 1227 else { 1228 fetch(ixold, lowa, a - 1, f1, " ", 0); 1229 fetch(ixold, a, b, f1, 1230 ch == 'c' ? "! " : "- ", 0); 1231 } 1232 lowa = b + 1; 1233 cvp++; 1234 } 1235 fetch(ixold, b + 1, upb, f1, " ", 0); 1236 } 1237 /* output changes to the "new" file */ 1238 printf("--- "); 1239 range(lowc, upd, ","); 1240 printf(" ----\n"); 1241 1242 do_output = 0; 1243 for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++) 1244 if (cvp->c <= cvp->d) { 1245 cvp = context_vec_start; 1246 do_output++; 1247 break; 1248 } 1249 if (do_output) { 1250 while (cvp <= context_vec_ptr) { 1251 a = cvp->a; 1252 b = cvp->b; 1253 c = cvp->c; 1254 d = cvp->d; 1255 1256 if (a <= b && c <= d) 1257 ch = 'c'; 1258 else 1259 ch = (a <= b) ? 'd' : 'a'; 1260 1261 if (ch == 'd') 1262 fetch(ixnew, lowc, d, f2, " ", 0); 1263 else { 1264 fetch(ixnew, lowc, c - 1, f2, " ", 0); 1265 fetch(ixnew, c, d, f2, 1266 ch == 'c' ? "! " : "+ ", 0); 1267 } 1268 lowc = d + 1; 1269 cvp++; 1270 } 1271 fetch(ixnew, d + 1, upd, f2, " ", 0); 1272 } 1273 context_vec_ptr = context_vec_start - 1; 1274 } 1275 1276 /* dump accumulated "unified" diff changes */ 1277 static void 1278 dump_unified_vec(FILE *f1, FILE *f2) 1279 { 1280 struct context_vec *cvp = context_vec_start; 1281 int lowa, upb, lowc, upd; 1282 int a, b, c, d; 1283 char ch; 1284 1285 if (context_vec_start > context_vec_ptr) 1286 return; 1287 1288 b = d = 0; /* gcc */ 1289 lowa = max(1, cvp->a - context); 1290 upb = min(len[0], context_vec_ptr->b + context); 1291 lowc = max(1, cvp->c - context); 1292 upd = min(len[1], context_vec_ptr->d + context); 1293 1294 fputs("@@ -", stdout); 1295 uni_range(lowa, upb); 1296 fputs(" +", stdout); 1297 uni_range(lowc, upd); 1298 fputs(" @@\n", stdout); 1299 1300 /* 1301 * Output changes in "unified" diff format--the old and new lines 1302 * are printed together. 1303 */ 1304 for (; cvp <= context_vec_ptr; cvp++) { 1305 a = cvp->a; 1306 b = cvp->b; 1307 c = cvp->c; 1308 d = cvp->d; 1309 1310 /* 1311 * c: both new and old changes 1312 * d: only changes in the old file 1313 * a: only changes in the new file 1314 */ 1315 if (a <= b && c <= d) 1316 ch = 'c'; 1317 else 1318 ch = (a <= b) ? 'd' : 'a'; 1319 1320 switch (ch) { 1321 case 'c': 1322 fetch(ixold, lowa, a - 1, f1, " ", 0); 1323 fetch(ixold, a, b, f1, "-", 0); 1324 fetch(ixnew, c, d, f2, "+", 0); 1325 break; 1326 case 'd': 1327 fetch(ixold, lowa, a - 1, f1, " ", 0); 1328 fetch(ixold, a, b, f1, "-", 0); 1329 break; 1330 case 'a': 1331 fetch(ixnew, lowc, c - 1, f2, " ", 0); 1332 fetch(ixnew, c, d, f2, "+", 0); 1333 break; 1334 } 1335 lowa = b + 1; 1336 lowc = d + 1; 1337 } 1338 fetch(ixnew, d + 1, upd, f2, " ", 0); 1339 1340 context_vec_ptr = context_vec_start - 1; 1341 } 1342