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