inp.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. /* inputting files to be patched */
  2. /* $Id: inp.c,v 1.18 1997/07/21 17:59:46 eggert Exp $ */
  3. /*
  4. Copyright 1986, 1988 Larry Wall
  5. Copyright 1991, 1992, 1993, 1997 Free Software Foundation, Inc.
  6. This program is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2, or (at your option)
  9. any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program; see the file COPYING.
  16. If not, write to the Free Software Foundation,
  17. 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18. */
  19. #define XTERN extern
  20. #include <common.h>
  21. #include <backupfile.h>
  22. #include <pch.h>
  23. #include <util.h>
  24. #undef XTERN
  25. #define XTERN
  26. #include <inp.h>
  27. /* Input-file-with-indexable-lines abstract type */
  28. static char *i_buffer; /* plan A buffer */
  29. static char const **i_ptr; /* pointers to lines in plan A buffer */
  30. static size_t tibufsize; /* size of plan b buffers */
  31. #ifndef TIBUFSIZE_MINIMUM
  32. #define TIBUFSIZE_MINIMUM (8 * 1024) /* minimum value for tibufsize */
  33. #endif
  34. static int tifd = -1; /* plan b virtual string array */
  35. static char *tibuf[2]; /* plan b buffers */
  36. static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */
  37. static LINENUM lines_per_buf; /* how many lines per buffer */
  38. static size_t tireclen; /* length of records in tmp file */
  39. static size_t last_line_size; /* size of last input line */
  40. static bool plan_a PARAMS ((char const *));/* yield FALSE if memory runs out */
  41. static void plan_b PARAMS ((char const *));
  42. static void report_revision PARAMS ((int));
  43. static void too_many_lines PARAMS ((char const *)) __attribute__((noreturn));
  44. /* New patch--prepare to edit another file. */
  45. void
  46. re_input()
  47. {
  48. if (using_plan_a) {
  49. free (i_buffer);
  50. free (i_ptr);
  51. }
  52. else {
  53. close (tifd);
  54. tifd = -1;
  55. free(tibuf[0]);
  56. tibuf[0] = 0;
  57. tiline[0] = tiline[1] = -1;
  58. tireclen = 0;
  59. }
  60. }
  61. /* Construct the line index, somehow or other. */
  62. void
  63. scan_input(filename)
  64. char *filename;
  65. {
  66. using_plan_a = ! (debug & 16) && plan_a (filename);
  67. if (!using_plan_a)
  68. plan_b(filename);
  69. switch (verbosity)
  70. {
  71. case SILENT:
  72. break;
  73. case VERBOSE:
  74. say ("Patching file `%s' using Plan %s...\n",
  75. filename, using_plan_a ? "A" : "B");
  76. break;
  77. case DEFAULT_VERBOSITY:
  78. say ("patching file `%s'\n", filename);
  79. break;
  80. }
  81. }
  82. /* Report whether a desired revision was found. */
  83. static void
  84. report_revision (found_revision)
  85. int found_revision;
  86. {
  87. if (found_revision)
  88. {
  89. if (verbosity == VERBOSE)
  90. say ("Good. This file appears to be the %s version.\n", revision);
  91. }
  92. else if (force)
  93. {
  94. if (verbosity != SILENT)
  95. say ("Warning: this file doesn't appear to be the %s version -- patching anyway.\n",
  96. revision);
  97. }
  98. else if (batch)
  99. {
  100. fatal ("This file doesn't appear to be the %s version -- aborting.",
  101. revision);
  102. }
  103. else
  104. {
  105. ask ("This file doesn't appear to be the %s version -- patch anyway? [n] ",
  106. revision);
  107. if (*buf != 'y')
  108. fatal ("aborted");
  109. }
  110. }
  111. static void
  112. too_many_lines (filename)
  113. char const *filename;
  114. {
  115. fatal ("File `%s' has too many lines.", filename);
  116. }
  117. void
  118. get_input_file (filename, outname)
  119. char const *filename;
  120. char const *outname;
  121. {
  122. int elsewhere = strcmp (filename, outname);
  123. char const *cs;
  124. char *diffbuf;
  125. char *getbuf;
  126. if (inerrno == -1)
  127. inerrno = stat (inname, &instat) == 0 ? 0 : errno;
  128. /* Perhaps look for RCS or SCCS versions. */
  129. if (patch_get
  130. && invc != 0
  131. && (inerrno
  132. || (! elsewhere
  133. && (/* No one can write to it. */
  134. (instat.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0
  135. /* Only the owner (who's not me) can write to it. */
  136. || ((instat.st_mode & (S_IWGRP|S_IWOTH)) == 0
  137. && instat.st_uid != geteuid ()))))
  138. && (invc = !! (cs = (version_controller
  139. (filename, elsewhere,
  140. inerrno ? (struct stat *) 0 : &instat,
  141. &getbuf, &diffbuf))))) {
  142. if (!inerrno) {
  143. if (!elsewhere
  144. && (instat.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) != 0)
  145. /* Somebody can write to it. */
  146. fatal ("file `%s' seems to be locked by somebody else under %s",
  147. filename, cs);
  148. /* It might be checked out unlocked. See if it's safe to
  149. check out the default version locked. */
  150. if (verbosity == VERBOSE)
  151. say ("Comparing file `%s' to default %s version...\n",
  152. filename, cs);
  153. if (systemic (diffbuf) != 0)
  154. {
  155. say ("warning: patching file `%s', which does not match default %s version\n",
  156. filename, cs);
  157. cs = 0;
  158. }
  159. }
  160. if (cs && version_get (filename, cs, ! inerrno, elsewhere, getbuf,
  161. &instat))
  162. inerrno = 0;
  163. free (getbuf);
  164. free (diffbuf);
  165. } else if (inerrno && !pch_says_nonexistent (reverse))
  166. {
  167. errno = inerrno;
  168. pfatal ("can't find file `%s'", filename);
  169. }
  170. if (inerrno)
  171. {
  172. instat.st_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
  173. instat.st_size = 0;
  174. }
  175. else if (! S_ISREG (instat.st_mode))
  176. fatal ("`%s' is not a regular file -- can't patch", filename);
  177. }
  178. /* Try keeping everything in memory. */
  179. static bool
  180. plan_a(filename)
  181. char const *filename;
  182. {
  183. register char const *s;
  184. register char const *lim;
  185. register char const **ptr;
  186. register char *buffer;
  187. register LINENUM iline;
  188. size_t size = instat.st_size;
  189. /* Fail if the file size doesn't fit in a size_t,
  190. or if storage isn't available. */
  191. if (! (size == instat.st_size
  192. && (buffer = malloc (size ? size : (size_t) 1))))
  193. return FALSE;
  194. /* Read the input file, but don't bother reading it if it's empty.
  195. When creating files, the files do not actually exist. */
  196. if (size)
  197. {
  198. int ifd = open (filename, O_RDONLY|binary_transput);
  199. size_t buffered = 0, n;
  200. if (ifd < 0)
  201. pfatal ("can't open file `%s'", filename);
  202. while (size - buffered != 0)
  203. {
  204. n = read (ifd, buffer + buffered, size - buffered);
  205. if (n == 0)
  206. {
  207. /* Some non-POSIX hosts exaggerate st_size in text mode;
  208. or the file may have shrunk! */
  209. size = buffered;
  210. break;
  211. }
  212. if (n == (size_t) -1)
  213. {
  214. /* Perhaps size is too large for this host. */
  215. close (ifd);
  216. free (buffer);
  217. return FALSE;
  218. }
  219. buffered += n;
  220. }
  221. if (close (ifd) != 0)
  222. read_fatal ();
  223. }
  224. /* Scan the buffer and build array of pointers to lines. */
  225. lim = buffer + size;
  226. iline = 3; /* 1 unused, 1 for SOF, 1 for EOF if last line is incomplete */
  227. for (s = buffer; (s = (char *) memchr (s, '\n', lim - s)); s++)
  228. if (++iline < 0)
  229. too_many_lines (filename);
  230. if (! (iline == (size_t) iline
  231. && (size_t) iline * sizeof *ptr / sizeof *ptr == (size_t) iline
  232. && (ptr = (char const **) malloc ((size_t) iline * sizeof *ptr))))
  233. {
  234. free (buffer);
  235. return FALSE;
  236. }
  237. iline = 0;
  238. for (s = buffer; ; s++)
  239. {
  240. ptr[++iline] = s;
  241. if (! (s = (char *) memchr (s, '\n', lim - s)))
  242. break;
  243. }
  244. if (size && lim[-1] != '\n')
  245. ptr[++iline] = lim;
  246. input_lines = iline - 1;
  247. if (revision)
  248. {
  249. char const *rev = revision;
  250. int rev0 = rev[0];
  251. int found_revision = 0;
  252. size_t revlen = strlen (rev);
  253. if (revlen <= size)
  254. {
  255. char const *limrev = lim - revlen;
  256. for (s = buffer; (s = (char *) memchr (s, rev0, limrev - s)); s++)
  257. if (memcmp (s, rev, revlen) == 0
  258. && (s == buffer || ISSPACE ((unsigned char) s[-1]))
  259. && (s + 1 == limrev || ISSPACE ((unsigned char) s[revlen])))
  260. {
  261. found_revision = 1;
  262. break;
  263. }
  264. }
  265. report_revision (found_revision);
  266. }
  267. /* Plan A will work. */
  268. i_buffer = buffer;
  269. i_ptr = ptr;
  270. return TRUE;
  271. }
  272. /* Keep (virtually) nothing in memory. */
  273. static void
  274. plan_b(filename)
  275. char const *filename;
  276. {
  277. register FILE *ifp;
  278. register int c;
  279. register size_t len;
  280. register size_t maxlen;
  281. register int found_revision;
  282. register size_t i;
  283. register char const *rev;
  284. register size_t revlen;
  285. register LINENUM line = 1;
  286. if (instat.st_size == 0)
  287. filename = NULL_DEVICE;
  288. if (! (ifp = fopen (filename, binary_transput ? "rb" : "r")))
  289. pfatal ("can't open file `%s'", filename);
  290. tifd = create_file (TMPINNAME, O_RDWR | O_BINARY, (mode_t) 0);
  291. i = 0;
  292. len = 0;
  293. maxlen = 1;
  294. rev = revision;
  295. found_revision = !rev;
  296. revlen = rev ? strlen (rev) : 0;
  297. while ((c = getc (ifp)) != EOF)
  298. {
  299. len++;
  300. if (c == '\n')
  301. {
  302. if (++line < 0)
  303. too_many_lines (filename);
  304. if (maxlen < len)
  305. maxlen = len;
  306. len = 0;
  307. }
  308. if (!found_revision)
  309. {
  310. if (i == revlen)
  311. {
  312. found_revision = ISSPACE ((unsigned char) c);
  313. i = (size_t) -1;
  314. }
  315. else if (i != (size_t) -1)
  316. i = rev[i]==c ? i + 1 : (size_t) -1;
  317. if (i == (size_t) -1 && ISSPACE ((unsigned char) c))
  318. i = 0;
  319. }
  320. }
  321. if (revision)
  322. report_revision (found_revision);
  323. Fseek (ifp, (off_t) 0, SEEK_SET); /* rewind file */
  324. for (tibufsize = TIBUFSIZE_MINIMUM; tibufsize < maxlen; tibufsize <<= 1)
  325. continue;
  326. lines_per_buf = tibufsize / maxlen;
  327. tireclen = maxlen;
  328. tibuf[0] = xmalloc (2 * tibufsize);
  329. tibuf[1] = tibuf[0] + tibufsize;
  330. for (line = 1; ; line++)
  331. {
  332. char *p = tibuf[0] + maxlen * (line % lines_per_buf);
  333. char const *p0 = p;
  334. if (! (line % lines_per_buf)) /* new block */
  335. if (write (tifd, tibuf[0], tibufsize) != tibufsize)
  336. write_fatal ();
  337. if ((c = getc (ifp)) == EOF)
  338. break;
  339. for (;;)
  340. {
  341. *p++ = c;
  342. if (c == '\n')
  343. {
  344. last_line_size = p - p0;
  345. break;
  346. }
  347. if ((c = getc (ifp)) == EOF)
  348. {
  349. last_line_size = p - p0;
  350. line++;
  351. goto EOF_reached;
  352. }
  353. }
  354. }
  355. EOF_reached:
  356. if (ferror (ifp) || fclose (ifp) != 0)
  357. read_fatal ();
  358. if (line % lines_per_buf != 0)
  359. if (write (tifd, tibuf[0], tibufsize) != tibufsize)
  360. write_fatal ();
  361. input_lines = line - 1;
  362. }
  363. /* Fetch a line from the input file. */
  364. char const *
  365. ifetch (line, whichbuf, psize)
  366. register LINENUM line;
  367. int whichbuf; /* ignored when file in memory */
  368. size_t *psize;
  369. {
  370. register char const *q;
  371. register char const *p;
  372. if (line < 1 || line > input_lines) {
  373. *psize = 0;
  374. return "";
  375. }
  376. if (using_plan_a) {
  377. p = i_ptr[line];
  378. *psize = i_ptr[line + 1] - p;
  379. return p;
  380. } else {
  381. LINENUM offline = line % lines_per_buf;
  382. LINENUM baseline = line - offline;
  383. if (tiline[0] == baseline)
  384. whichbuf = 0;
  385. else if (tiline[1] == baseline)
  386. whichbuf = 1;
  387. else {
  388. tiline[whichbuf] = baseline;
  389. if (lseek (tifd, (off_t) (baseline/lines_per_buf * tibufsize),
  390. SEEK_SET) == -1
  391. || read (tifd, tibuf[whichbuf], tibufsize) < 0)
  392. read_fatal ();
  393. }
  394. p = tibuf[whichbuf] + (tireclen*offline);
  395. if (line == input_lines)
  396. *psize = last_line_size;
  397. else {
  398. for (q = p; *q++ != '\n'; )
  399. continue;
  400. *psize = q - p;
  401. }
  402. return p;
  403. }
  404. }