Home | History | Annotate | Line # | Download | only in patch
inp.c revision 1.7
      1  1.7  kristerw /*	$NetBSD: inp.c,v 1.7 2002/03/08 21:57:33 kristerw Exp $	*/
      2  1.4  christos #include <sys/cdefs.h>
      3  1.2   mycroft #ifndef lint
      4  1.7  kristerw __RCSID("$NetBSD: inp.c,v 1.7 2002/03/08 21:57:33 kristerw Exp $");
      5  1.2   mycroft #endif /* not lint */
      6  1.1       cgd 
      7  1.1       cgd #include "EXTERN.h"
      8  1.7  kristerw #include "backupfile.h"
      9  1.1       cgd #include "common.h"
     10  1.1       cgd #include "util.h"
     11  1.1       cgd #include "pch.h"
     12  1.1       cgd #include "INTERN.h"
     13  1.1       cgd #include "inp.h"
     14  1.1       cgd 
     15  1.4  christos #include <stdlib.h>
     16  1.4  christos #include <unistd.h>
     17  1.4  christos #include <fcntl.h>
     18  1.4  christos 
     19  1.1       cgd /* Input-file-with-indexable-lines abstract type */
     20  1.1       cgd 
     21  1.1       cgd static long i_size;			/* size of the input file */
     22  1.1       cgd static char *i_womp;			/* plan a buffer for entire file */
     23  1.1       cgd static char **i_ptr;			/* pointers to lines in i_womp */
     24  1.1       cgd 
     25  1.1       cgd static int tifd = -1;			/* plan b virtual string array */
     26  1.1       cgd static char *tibuf[2];			/* plan b buffers */
     27  1.1       cgd static LINENUM tiline[2] = {-1, -1};	/* 1st line in each buffer */
     28  1.1       cgd static LINENUM lines_per_buf;		/* how many lines per buffer */
     29  1.1       cgd static int tireclen;			/* length of records in tmp file */
     30  1.1       cgd 
     31  1.1       cgd /* New patch--prepare to edit another file. */
     32  1.1       cgd 
     33  1.1       cgd void
     34  1.7  kristerw re_input(void)
     35  1.1       cgd {
     36  1.1       cgd     if (using_plan_a) {
     37  1.1       cgd 	i_size = 0;
     38  1.7  kristerw 
     39  1.7  kristerw 	if (i_ptr != NULL)
     40  1.7  kristerw 	    free(i_ptr);
     41  1.1       cgd 	if (i_womp != Nullch)
     42  1.1       cgd 	    free(i_womp);
     43  1.1       cgd 	i_womp = Nullch;
     44  1.1       cgd 	i_ptr = Null(char **);
     45  1.1       cgd     }
     46  1.1       cgd     else {
     47  1.1       cgd 	using_plan_a = TRUE;		/* maybe the next one is smaller */
     48  1.1       cgd 	Close(tifd);
     49  1.1       cgd 	tifd = -1;
     50  1.1       cgd 	free(tibuf[0]);
     51  1.1       cgd 	free(tibuf[1]);
     52  1.1       cgd 	tibuf[0] = tibuf[1] = Nullch;
     53  1.1       cgd 	tiline[0] = tiline[1] = -1;
     54  1.1       cgd 	tireclen = 0;
     55  1.1       cgd     }
     56  1.1       cgd }
     57  1.1       cgd 
     58  1.1       cgd /* Constuct the line index, somehow or other. */
     59  1.1       cgd 
     60  1.1       cgd void
     61  1.7  kristerw scan_input(char *filename)
     62  1.1       cgd {
     63  1.1       cgd     if (!plan_a(filename))
     64  1.1       cgd 	plan_b(filename);
     65  1.1       cgd     if (verbose) {
     66  1.1       cgd 	say3("Patching file %s using Plan %s...\n", filename,
     67  1.1       cgd 	  (using_plan_a ? "A" : "B") );
     68  1.1       cgd     }
     69  1.1       cgd }
     70  1.1       cgd 
     71  1.1       cgd /* Try keeping everything in memory. */
     72  1.1       cgd 
     73  1.1       cgd bool
     74  1.7  kristerw plan_a(char *filename)
     75  1.1       cgd {
     76  1.1       cgd     int ifd, statfailed;
     77  1.7  kristerw     char *s;
     78  1.7  kristerw     LINENUM iline;
     79  1.1       cgd     char lbuf[MAXLINELEN];
     80  1.1       cgd 
     81  1.1       cgd     statfailed = stat(filename, &filestat);
     82  1.1       cgd     if (statfailed && ok_to_create_file) {
     83  1.1       cgd 	if (verbose)
     84  1.1       cgd 	    say2("(Creating file %s...)\n",filename);
     85  1.1       cgd 	makedirs(filename, TRUE);
     86  1.1       cgd 	close(creat(filename, 0666));
     87  1.1       cgd 	statfailed = stat(filename, &filestat);
     88  1.1       cgd     }
     89  1.1       cgd     /* For nonexistent or read-only files, look for RCS or SCCS versions.  */
     90  1.1       cgd     if (statfailed
     91  1.1       cgd 	/* No one can write to it.  */
     92  1.1       cgd 	|| (filestat.st_mode & 0222) == 0
     93  1.1       cgd 	/* I can't write to it.  */
     94  1.1       cgd 	|| ((filestat.st_mode & 0022) == 0 && filestat.st_uid != myuid)) {
     95  1.1       cgd 	struct stat cstat;
     96  1.1       cgd 	char *cs = Nullch;
     97  1.1       cgd 	char *filebase;
     98  1.1       cgd 	int pathlen;
     99  1.1       cgd 
    100  1.1       cgd 	filebase = basename(filename);
    101  1.1       cgd 	pathlen = filebase - filename;
    102  1.1       cgd 
    103  1.1       cgd 	/* Put any leading path into `s'.
    104  1.1       cgd 	   Leave room in lbuf for the diff command.  */
    105  1.1       cgd 	s = lbuf + 20;
    106  1.1       cgd 	strncpy(s, filename, pathlen);
    107  1.1       cgd 
    108  1.1       cgd #define try(f, a1, a2) (Sprintf(s + pathlen, f, a1, a2), stat(s, &cstat) == 0)
    109  1.4  christos #define try1(f, a1) (Sprintf(s + pathlen, f, a1), stat(s, &cstat) == 0)
    110  1.1       cgd 	if (   try("RCS/%s%s", filebase, RCSSUFFIX)
    111  1.4  christos 	    || try1("RCS/%s"  , filebase)
    112  1.1       cgd 	    || try(    "%s%s", filebase, RCSSUFFIX)) {
    113  1.1       cgd 	    Sprintf(buf, CHECKOUT, filename);
    114  1.1       cgd 	    Sprintf(lbuf, RCSDIFF, filename);
    115  1.1       cgd 	    cs = "RCS";
    116  1.1       cgd 	} else if (   try("SCCS/%s%s", SCCSPREFIX, filebase)
    117  1.1       cgd 		   || try(     "%s%s", SCCSPREFIX, filebase)) {
    118  1.1       cgd 	    Sprintf(buf, GET, s);
    119  1.1       cgd 	    Sprintf(lbuf, SCCSDIFF, s, filename);
    120  1.1       cgd 	    cs = "SCCS";
    121  1.1       cgd 	} else if (statfailed)
    122  1.1       cgd 	    fatal2("can't find %s\n", filename);
    123  1.1       cgd 	/* else we can't write to it but it's not under a version
    124  1.1       cgd 	   control system, so just proceed.  */
    125  1.1       cgd 	if (cs) {
    126  1.1       cgd 	    if (!statfailed) {
    127  1.1       cgd 		if ((filestat.st_mode & 0222) != 0)
    128  1.1       cgd 		    /* The owner can write to it.  */
    129  1.1       cgd 		    fatal3("file %s seems to be locked by somebody else under %s\n",
    130  1.1       cgd 			   filename, cs);
    131  1.1       cgd 		/* It might be checked out unlocked.  See if it's safe to
    132  1.1       cgd 		   check out the default version locked.  */
    133  1.1       cgd 		if (verbose)
    134  1.1       cgd 		    say3("Comparing file %s to default %s version...\n",
    135  1.1       cgd 			 filename, cs);
    136  1.1       cgd 		if (system(lbuf))
    137  1.1       cgd 		    fatal3("can't check out file %s: differs from default %s version\n",
    138  1.1       cgd 			   filename, cs);
    139  1.1       cgd 	    }
    140  1.1       cgd 	    if (verbose)
    141  1.1       cgd 		say3("Checking out file %s from %s...\n", filename, cs);
    142  1.1       cgd 	    if (system(buf) || stat(filename, &filestat))
    143  1.1       cgd 		fatal3("can't check out file %s from %s\n", filename, cs);
    144  1.1       cgd 	}
    145  1.1       cgd     }
    146  1.6  sommerfe     if (old_file_is_dev_null && ok_to_create_file && (filestat.st_size != 0)) {
    147  1.6  sommerfe 	fatal2("patch creates new file but existing file %s not empty\n",
    148  1.6  sommerfe 	       filename);
    149  1.6  sommerfe     }
    150  1.6  sommerfe 
    151  1.1       cgd     filemode = filestat.st_mode;
    152  1.1       cgd     if (!S_ISREG(filemode))
    153  1.1       cgd 	fatal2("%s is not a normal file--can't patch\n", filename);
    154  1.1       cgd     i_size = filestat.st_size;
    155  1.1       cgd     if (out_of_mem) {
    156  1.1       cgd 	set_hunkmax();		/* make sure dynamic arrays are allocated */
    157  1.1       cgd 	out_of_mem = FALSE;
    158  1.1       cgd 	return FALSE;			/* force plan b because plan a bombed */
    159  1.1       cgd     }
    160  1.7  kristerw 
    161  1.7  kristerw     i_womp = malloc((MEM)(i_size+2));
    162  1.1       cgd     if (i_womp == Nullch)
    163  1.1       cgd 	return FALSE;
    164  1.1       cgd     if ((ifd = open(filename, 0)) < 0)
    165  1.1       cgd 	pfatal2("can't open file %s", filename);
    166  1.7  kristerw     if (read(ifd, i_womp, i_size) != i_size) {
    167  1.1       cgd 	Close(ifd);	/* probably means i_size > 15 or 16 bits worth */
    168  1.1       cgd 	free(i_womp);	/* at this point it doesn't matter if i_womp was */
    169  1.1       cgd 	return FALSE;	/*   undersized. */
    170  1.1       cgd     }
    171  1.1       cgd     Close(ifd);
    172  1.1       cgd     if (i_size && i_womp[i_size-1] != '\n')
    173  1.1       cgd 	i_womp[i_size++] = '\n';
    174  1.1       cgd     i_womp[i_size] = '\0';
    175  1.1       cgd 
    176  1.1       cgd     /* count the lines in the buffer so we know how many pointers we need */
    177  1.1       cgd 
    178  1.1       cgd     iline = 0;
    179  1.1       cgd     for (s=i_womp; *s; s++) {
    180  1.1       cgd 	if (*s == '\n')
    181  1.1       cgd 	    iline++;
    182  1.1       cgd     }
    183  1.1       cgd     i_ptr = (char **)malloc((MEM)((iline + 2) * sizeof(char *)));
    184  1.1       cgd     if (i_ptr == Null(char **)) {	/* shucks, it was a near thing */
    185  1.1       cgd 	free((char *)i_womp);
    186  1.1       cgd 	return FALSE;
    187  1.1       cgd     }
    188  1.1       cgd 
    189  1.1       cgd     /* now scan the buffer and build pointer array */
    190  1.1       cgd 
    191  1.1       cgd     iline = 1;
    192  1.1       cgd     i_ptr[iline] = i_womp;
    193  1.1       cgd     for (s=i_womp; *s; s++) {
    194  1.1       cgd 	if (*s == '\n')
    195  1.1       cgd 	    i_ptr[++iline] = s+1;	/* these are NOT null terminated */
    196  1.1       cgd     }
    197  1.1       cgd     input_lines = iline - 1;
    198  1.1       cgd 
    199  1.1       cgd     /* now check for revision, if any */
    200  1.1       cgd 
    201  1.1       cgd     if (revision != Nullch) {
    202  1.1       cgd 	if (!rev_in_string(i_womp)) {
    203  1.1       cgd 	    if (force) {
    204  1.1       cgd 		if (verbose)
    205  1.1       cgd 		    say2(
    206  1.1       cgd "Warning: this file doesn't appear to be the %s version--patching anyway.\n",
    207  1.1       cgd 			revision);
    208  1.1       cgd 	    }
    209  1.1       cgd 	    else if (batch) {
    210  1.1       cgd 		fatal2(
    211  1.1       cgd "this file doesn't appear to be the %s version--aborting.\n", revision);
    212  1.1       cgd 	    }
    213  1.1       cgd 	    else {
    214  1.1       cgd 		ask2(
    215  1.1       cgd "This file doesn't appear to be the %s version--patch anyway? [n] ",
    216  1.1       cgd 		    revision);
    217  1.1       cgd 	    if (*buf != 'y')
    218  1.1       cgd 		fatal1("aborted\n");
    219  1.1       cgd 	    }
    220  1.1       cgd 	}
    221  1.1       cgd 	else if (verbose)
    222  1.1       cgd 	    say2("Good.  This file appears to be the %s version.\n",
    223  1.1       cgd 		revision);
    224  1.1       cgd     }
    225  1.1       cgd     return TRUE;			/* plan a will work */
    226  1.1       cgd }
    227  1.1       cgd 
    228  1.1       cgd /* Keep (virtually) nothing in memory. */
    229  1.1       cgd 
    230  1.1       cgd void
    231  1.7  kristerw plan_b(char *filename)
    232  1.1       cgd {
    233  1.7  kristerw     FILE *ifp;
    234  1.7  kristerw     int i = 0;
    235  1.7  kristerw     int maxlen = 1;
    236  1.7  kristerw     bool found_revision = (revision == Nullch);
    237  1.1       cgd 
    238  1.1       cgd     using_plan_a = FALSE;
    239  1.1       cgd     if ((ifp = fopen(filename, "r")) == Nullfp)
    240  1.1       cgd 	pfatal2("can't open file %s", filename);
    241  1.1       cgd     if ((tifd = creat(TMPINNAME, 0666)) < 0)
    242  1.1       cgd 	pfatal2("can't open file %s", TMPINNAME);
    243  1.1       cgd     while (fgets(buf, sizeof buf, ifp) != Nullch) {
    244  1.1       cgd 	if (revision != Nullch && !found_revision && rev_in_string(buf))
    245  1.1       cgd 	    found_revision = TRUE;
    246  1.1       cgd 	if ((i = strlen(buf)) > maxlen)
    247  1.1       cgd 	    maxlen = i;			/* find longest line */
    248  1.1       cgd     }
    249  1.1       cgd     if (revision != Nullch) {
    250  1.1       cgd 	if (!found_revision) {
    251  1.1       cgd 	    if (force) {
    252  1.1       cgd 		if (verbose)
    253  1.1       cgd 		    say2(
    254  1.1       cgd "Warning: this file doesn't appear to be the %s version--patching anyway.\n",
    255  1.1       cgd 			revision);
    256  1.1       cgd 	    }
    257  1.1       cgd 	    else if (batch) {
    258  1.1       cgd 		fatal2(
    259  1.1       cgd "this file doesn't appear to be the %s version--aborting.\n", revision);
    260  1.1       cgd 	    }
    261  1.1       cgd 	    else {
    262  1.1       cgd 		ask2(
    263  1.1       cgd "This file doesn't appear to be the %s version--patch anyway? [n] ",
    264  1.1       cgd 		    revision);
    265  1.1       cgd 		if (*buf != 'y')
    266  1.1       cgd 		    fatal1("aborted\n");
    267  1.1       cgd 	    }
    268  1.1       cgd 	}
    269  1.1       cgd 	else if (verbose)
    270  1.1       cgd 	    say2("Good.  This file appears to be the %s version.\n",
    271  1.1       cgd 		revision);
    272  1.1       cgd     }
    273  1.1       cgd     Fseek(ifp, 0L, 0);		/* rewind file */
    274  1.1       cgd     lines_per_buf = BUFFERSIZE / maxlen;
    275  1.1       cgd     tireclen = maxlen;
    276  1.1       cgd     tibuf[0] = malloc((MEM)(BUFFERSIZE + 1));
    277  1.1       cgd     tibuf[1] = malloc((MEM)(BUFFERSIZE + 1));
    278  1.1       cgd     if (tibuf[1] == Nullch)
    279  1.1       cgd 	fatal1("out of memory\n");
    280  1.1       cgd     for (i=1; ; i++) {
    281  1.1       cgd 	if (! (i % lines_per_buf))	/* new block */
    282  1.1       cgd 	    if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
    283  1.1       cgd 		pfatal1("can't write temp file");
    284  1.1       cgd 	if (fgets(tibuf[0] + maxlen * (i%lines_per_buf), maxlen + 1, ifp)
    285  1.1       cgd 	  == Nullch) {
    286  1.1       cgd 	    input_lines = i - 1;
    287  1.1       cgd 	    if (i % lines_per_buf)
    288  1.1       cgd 		if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
    289  1.1       cgd 		    pfatal1("can't write temp file");
    290  1.1       cgd 	    break;
    291  1.1       cgd 	}
    292  1.1       cgd     }
    293  1.1       cgd     Fclose(ifp);
    294  1.1       cgd     Close(tifd);
    295  1.1       cgd     if ((tifd = open(TMPINNAME, 0)) < 0) {
    296  1.1       cgd 	pfatal2("can't reopen file %s", TMPINNAME);
    297  1.1       cgd     }
    298  1.1       cgd }
    299  1.1       cgd 
    300  1.1       cgd /* Fetch a line from the input file, \n terminated, not necessarily \0. */
    301  1.1       cgd 
    302  1.1       cgd char *
    303  1.7  kristerw ifetch(LINENUM line, int whichbuf)
    304  1.1       cgd {
    305  1.1       cgd     if (line < 1 || line > input_lines)
    306  1.1       cgd 	return "";
    307  1.1       cgd     if (using_plan_a)
    308  1.1       cgd 	return i_ptr[line];
    309  1.1       cgd     else {
    310  1.1       cgd 	LINENUM offline = line % lines_per_buf;
    311  1.1       cgd 	LINENUM baseline = line - offline;
    312  1.1       cgd 
    313  1.1       cgd 	if (tiline[0] == baseline)
    314  1.1       cgd 	    whichbuf = 0;
    315  1.1       cgd 	else if (tiline[1] == baseline)
    316  1.1       cgd 	    whichbuf = 1;
    317  1.1       cgd 	else {
    318  1.1       cgd 	    tiline[whichbuf] = baseline;
    319  1.1       cgd 	    Lseek(tifd, (long)baseline / lines_per_buf * BUFFERSIZE, 0);
    320  1.1       cgd 	    if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0)
    321  1.1       cgd 		pfatal2("error reading tmp file %s", TMPINNAME);
    322  1.1       cgd 	}
    323  1.1       cgd 	return tibuf[whichbuf] + (tireclen*offline);
    324  1.1       cgd     }
    325  1.1       cgd }
    326  1.1       cgd 
    327  1.1       cgd /* True if the string argument contains the revision number we want. */
    328  1.1       cgd 
    329  1.1       cgd bool
    330  1.7  kristerw rev_in_string(char *string)
    331  1.1       cgd {
    332  1.7  kristerw     char *s;
    333  1.7  kristerw     int patlen;
    334  1.1       cgd 
    335  1.1       cgd     if (revision == Nullch)
    336  1.1       cgd 	return TRUE;
    337  1.1       cgd     patlen = strlen(revision);
    338  1.5  christos     if (strnEQ(string,revision,patlen) && isspace((unsigned char)string[patlen]))
    339  1.1       cgd 	return TRUE;
    340  1.1       cgd     for (s = string; *s; s++) {
    341  1.5  christos 	if (isspace((unsigned char)*s) && strnEQ(s+1, revision, patlen) &&
    342  1.5  christos 		isspace((unsigned char)s[patlen+1] )) {
    343  1.1       cgd 	    return TRUE;
    344  1.1       cgd 	}
    345  1.1       cgd     }
    346  1.1       cgd     return FALSE;
    347  1.1       cgd }
    348