/contrib/cvs/src/patch.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 849 lines · 685 code · 62 blank · 102 comment · 202 complexity · 5e7ca6f22ee10f7dfa09540bfcea3ec4 MD5 · raw file

  1. /*
  2. * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
  3. *
  4. * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
  5. * and others.
  6. *
  7. * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
  8. * Portions Copyright (C) 1989-1992, Brian Berliner
  9. *
  10. * You may distribute under the terms of the GNU General Public License as
  11. * specified in the README file that comes with the CVS source distribution.
  12. *
  13. * Patch
  14. *
  15. * Create a Larry Wall format "patch" file between a previous release and the
  16. * current head of a module, or between two releases. Can specify the
  17. * release as either a date or a revision number.
  18. *
  19. * $FreeBSD$
  20. */
  21. #include <assert.h>
  22. #include "cvs.h"
  23. #include "getline.h"
  24. static RETSIGTYPE patch_cleanup PROTO((void));
  25. static Dtype patch_dirproc PROTO ((void *callerdat, const char *dir,
  26. const char *repos, const char *update_dir,
  27. List *entries));
  28. static int patch_fileproc PROTO ((void *callerdat, struct file_info *finfo));
  29. static int patch_proc PROTO((int argc, char **argv, char *xwhere,
  30. char *mwhere, char *mfile, int shorten,
  31. int local_specified, char *mname, char *msg));
  32. static int force_tag_match = 1;
  33. static int patch_short = 0;
  34. static int toptwo_diffs = 0;
  35. static char *options = NULL;
  36. static char *rev1 = NULL;
  37. static int rev1_validated = 0;
  38. static char *rev2 = NULL;
  39. static int rev2_validated = 0;
  40. static char *date1 = NULL;
  41. static char *date2 = NULL;
  42. static char *tmpfile1 = NULL;
  43. static char *tmpfile2 = NULL;
  44. static char *tmpfile3 = NULL;
  45. static int unidiff = 0;
  46. static const char *const patch_usage[] =
  47. {
  48. "Usage: %s %s [-flR] [-c|-u] [-s|-t] [-V %%d] [-k kopt]\n",
  49. " -r rev|-D date [-r rev2 | -D date2] modules...\n",
  50. "\t-f\tForce a head revision match if tag/date not found.\n",
  51. "\t-l\tLocal directory only, not recursive\n",
  52. "\t-R\tProcess directories recursively.\n",
  53. "\t-c\tContext diffs (default)\n",
  54. "\t-u\tUnidiff format.\n",
  55. "\t-s\tShort patch - one liner per file.\n",
  56. "\t-t\tTop two diffs - last change made to the file.\n",
  57. "\t-V vers\tUse RCS Version \"vers\" for keyword expansion.\n",
  58. "\t-k kopt\tSpecify keyword expansion mode.\n",
  59. "\t-D date\tDate.\n",
  60. "\t-r rev\tRevision - symbolic or numeric.\n",
  61. "(Specify the --help global option for a list of other help options)\n",
  62. NULL
  63. };
  64. int
  65. patch (argc, argv)
  66. int argc;
  67. char **argv;
  68. {
  69. register int i;
  70. int local = 0;
  71. int c;
  72. int err = 0;
  73. DBM *db;
  74. if (argc == -1)
  75. usage (patch_usage);
  76. optind = 0;
  77. while ((c = getopt (argc, argv, "+V:k:cuftsQqlRD:r:")) != -1)
  78. {
  79. switch (c)
  80. {
  81. case 'Q':
  82. case 'q':
  83. /* The CVS 1.5 client sends these options (in addition to
  84. Global_option requests), so we must ignore them. */
  85. if (!server_active)
  86. error (1, 0,
  87. "-q or -Q must be specified before \"%s\"",
  88. cvs_cmd_name);
  89. break;
  90. case 'f':
  91. force_tag_match = 0;
  92. break;
  93. case 'l':
  94. local = 1;
  95. break;
  96. case 'R':
  97. local = 0;
  98. break;
  99. case 't':
  100. toptwo_diffs = 1;
  101. break;
  102. case 's':
  103. patch_short = 1;
  104. break;
  105. case 'D':
  106. if (rev2 != NULL || date2 != NULL)
  107. error (1, 0,
  108. "no more than two revisions/dates can be specified");
  109. if (rev1 != NULL || date1 != NULL)
  110. date2 = Make_Date (optarg);
  111. else
  112. date1 = Make_Date (optarg);
  113. break;
  114. case 'r':
  115. if (rev2 != NULL || date2 != NULL)
  116. error (1, 0,
  117. "no more than two revisions/dates can be specified");
  118. if (rev1 != NULL || date1 != NULL)
  119. rev2 = optarg;
  120. else
  121. rev1 = optarg;
  122. break;
  123. case 'k':
  124. if (options)
  125. free (options);
  126. options = RCS_check_kflag (optarg);
  127. break;
  128. case 'V':
  129. /* This option is pretty seriously broken:
  130. 1. It is not clear what it does (does it change keyword
  131. expansion behavior? If so, how? Or does it have
  132. something to do with what version of RCS we are using?
  133. Or the format we write RCS files in?).
  134. 2. Because both it and -k use the options variable,
  135. specifying both -V and -k doesn't work.
  136. 3. At least as of CVS 1.9, it doesn't work (failed
  137. assertion in RCS_checkout where it asserts that options
  138. starts with -k). Few people seem to be complaining.
  139. In the future (perhaps the near future), I have in mind
  140. removing it entirely, and updating NEWS and cvs.texinfo,
  141. but in case it is a good idea to give people more time
  142. to complain if they would miss it, I'll just add this
  143. quick and dirty error message for now. */
  144. error (1, 0,
  145. "the -V option is obsolete and should not be used");
  146. #if 0
  147. if (atoi (optarg) <= 0)
  148. error (1, 0, "must specify a version number to -V");
  149. if (options)
  150. free (options);
  151. options = xmalloc (strlen (optarg) + 1 + 2); /* for the -V */
  152. (void) sprintf (options, "-V%s", optarg);
  153. #endif
  154. break;
  155. case 'u':
  156. unidiff = 1; /* Unidiff */
  157. break;
  158. case 'c': /* Context diff */
  159. unidiff = 0;
  160. break;
  161. case '?':
  162. default:
  163. usage (patch_usage);
  164. break;
  165. }
  166. }
  167. argc -= optind;
  168. argv += optind;
  169. /* Sanity checks */
  170. if (argc < 1)
  171. usage (patch_usage);
  172. if (toptwo_diffs && patch_short)
  173. error (1, 0, "-t and -s options are mutually exclusive");
  174. if (toptwo_diffs && (date1 != NULL || date2 != NULL ||
  175. rev1 != NULL || rev2 != NULL))
  176. error (1, 0, "must not specify revisions/dates with -t option!");
  177. if (!toptwo_diffs && (date1 == NULL && date2 == NULL &&
  178. rev1 == NULL && rev2 == NULL))
  179. error (1, 0, "must specify at least one revision/date!");
  180. if (date1 != NULL && date2 != NULL)
  181. if (RCS_datecmp (date1, date2) >= 0)
  182. error (1, 0, "second date must come after first date!");
  183. /* if options is NULL, make it a NULL string */
  184. if (options == NULL)
  185. options = xstrdup ("");
  186. #ifdef CLIENT_SUPPORT
  187. if (current_parsed_root->isremote)
  188. {
  189. /* We're the client side. Fire up the remote server. */
  190. start_server ();
  191. ign_setup ();
  192. if (local)
  193. send_arg("-l");
  194. if (!force_tag_match)
  195. send_arg("-f");
  196. if (toptwo_diffs)
  197. send_arg("-t");
  198. if (patch_short)
  199. send_arg("-s");
  200. if (unidiff)
  201. send_arg("-u");
  202. if (rev1)
  203. option_with_arg ("-r", rev1);
  204. if (date1)
  205. client_senddate (date1);
  206. if (rev2)
  207. option_with_arg ("-r", rev2);
  208. if (date2)
  209. client_senddate (date2);
  210. if (options[0] != '\0')
  211. send_arg (options);
  212. {
  213. int i;
  214. for (i = 0; i < argc; ++i)
  215. send_arg (argv[i]);
  216. }
  217. send_to_server ("rdiff\012", 0);
  218. return get_responses_and_close ();
  219. }
  220. #endif
  221. /* clean up if we get a signal */
  222. #ifdef SIGABRT
  223. (void)SIG_register (SIGABRT, patch_cleanup);
  224. #endif
  225. #ifdef SIGHUP
  226. (void)SIG_register (SIGHUP, patch_cleanup);
  227. #endif
  228. #ifdef SIGINT
  229. (void)SIG_register (SIGINT, patch_cleanup);
  230. #endif
  231. #ifdef SIGQUIT
  232. (void)SIG_register (SIGQUIT, patch_cleanup);
  233. #endif
  234. #ifdef SIGPIPE
  235. (void)SIG_register (SIGPIPE, patch_cleanup);
  236. #endif
  237. #ifdef SIGTERM
  238. (void)SIG_register (SIGTERM, patch_cleanup);
  239. #endif
  240. db = open_module ();
  241. for (i = 0; i < argc; i++)
  242. err += do_module (db, argv[i], PATCH, "Patching", patch_proc,
  243. (char *)NULL, 0, local, 0, 0, (char *)NULL);
  244. close_module (db);
  245. free (options);
  246. patch_cleanup ();
  247. return err;
  248. }
  249. /*
  250. * callback proc for doing the real work of patching
  251. */
  252. /* ARGSUSED */
  253. static int
  254. patch_proc (argc, argv, xwhere, mwhere, mfile, shorten, local_specified,
  255. mname, msg)
  256. int argc;
  257. char **argv;
  258. char *xwhere;
  259. char *mwhere;
  260. char *mfile;
  261. int shorten;
  262. int local_specified;
  263. char *mname;
  264. char *msg;
  265. {
  266. char *myargv[2];
  267. int err = 0;
  268. int which;
  269. char *repository;
  270. char *where;
  271. repository = xmalloc (strlen (current_parsed_root->directory)
  272. + strlen (argv[0])
  273. + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2);
  274. (void)sprintf (repository, "%s/%s",
  275. current_parsed_root->directory, argv[0]);
  276. where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile) + 1)
  277. + 1);
  278. (void)strcpy (where, argv[0]);
  279. /* if mfile isn't null, we need to set up to do only part of the module */
  280. if (mfile != NULL)
  281. {
  282. char *cp;
  283. char *path;
  284. /* if the portion of the module is a path, put the dir part on repos */
  285. if ((cp = strrchr (mfile, '/')) != NULL)
  286. {
  287. *cp = '\0';
  288. (void)strcat (repository, "/");
  289. (void)strcat (repository, mfile);
  290. (void)strcat (where, "/");
  291. (void)strcat (where, mfile);
  292. mfile = cp + 1;
  293. }
  294. /* take care of the rest */
  295. path = xmalloc (strlen (repository) + strlen (mfile) + 2);
  296. (void)sprintf (path, "%s/%s", repository, mfile);
  297. if (isdir (path))
  298. {
  299. /* directory means repository gets the dir tacked on */
  300. (void)strcpy (repository, path);
  301. (void)strcat (where, "/");
  302. (void)strcat (where, mfile);
  303. }
  304. else
  305. {
  306. myargv[0] = argv[0];
  307. myargv[1] = mfile;
  308. argc = 2;
  309. argv = myargv;
  310. }
  311. free (path);
  312. }
  313. /* cd to the starting repository */
  314. if ( CVS_CHDIR (repository) < 0)
  315. {
  316. error (0, errno, "cannot chdir to %s", repository);
  317. free (repository);
  318. free (where);
  319. return 1;
  320. }
  321. if (force_tag_match)
  322. which = W_REPOS | W_ATTIC;
  323. else
  324. which = W_REPOS;
  325. if (rev1 != NULL && !rev1_validated)
  326. {
  327. tag_check_valid (rev1, argc - 1, argv + 1, local_specified, 0,
  328. repository);
  329. rev1_validated = 1;
  330. }
  331. if (rev2 != NULL && !rev2_validated)
  332. {
  333. tag_check_valid (rev2, argc - 1, argv + 1, local_specified, 0,
  334. repository);
  335. rev2_validated = 1;
  336. }
  337. /* start the recursion processor */
  338. err = start_recursion (patch_fileproc, (FILESDONEPROC)NULL, patch_dirproc,
  339. (DIRLEAVEPROC)NULL, NULL,
  340. argc - 1, argv + 1, local_specified,
  341. which, 0, CVS_LOCK_READ, where, 1, repository);
  342. free (repository);
  343. free (where);
  344. return err;
  345. }
  346. /*
  347. * Called to examine a particular RCS file, as appropriate with the options
  348. * that were set above.
  349. */
  350. /* ARGSUSED */
  351. static int
  352. patch_fileproc (callerdat, finfo)
  353. void *callerdat;
  354. struct file_info *finfo;
  355. {
  356. struct utimbuf t;
  357. char *vers_tag, *vers_head;
  358. char *rcs = NULL;
  359. char *rcs_orig = NULL;
  360. RCSNode *rcsfile;
  361. FILE *fp1, *fp2, *fp3;
  362. int ret = 0;
  363. int isattic = 0;
  364. int retcode = 0;
  365. char *file1;
  366. char *file2;
  367. char *strippath;
  368. char *line1, *line2;
  369. size_t line1_chars_allocated;
  370. size_t line2_chars_allocated;
  371. char *cp1, *cp2;
  372. FILE *fp;
  373. int line_length;
  374. int dargc = 0;
  375. size_t darg_allocated = 0;
  376. char **dargv = NULL;
  377. line1 = NULL;
  378. line1_chars_allocated = 0;
  379. line2 = NULL;
  380. line2_chars_allocated = 0;
  381. vers_tag = vers_head = NULL;
  382. /* find the parsed rcs file */
  383. if ((rcsfile = finfo->rcs) == NULL)
  384. {
  385. ret = 1;
  386. goto out2;
  387. }
  388. if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
  389. isattic = 1;
  390. rcs_orig = rcs = xmalloc (strlen (finfo->file) + sizeof (RCSEXT) + 5);
  391. (void) sprintf (rcs, "%s%s", finfo->file, RCSEXT);
  392. /* if vers_head is NULL, may have been removed from the release */
  393. if (isattic && rev2 == NULL && date2 == NULL)
  394. vers_head = NULL;
  395. else
  396. {
  397. vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match,
  398. (int *) NULL);
  399. if (vers_head != NULL && RCS_isdead (rcsfile, vers_head))
  400. {
  401. free (vers_head);
  402. vers_head = NULL;
  403. }
  404. }
  405. if (toptwo_diffs)
  406. {
  407. if (vers_head == NULL)
  408. {
  409. ret = 1;
  410. goto out2;
  411. }
  412. if (!date1)
  413. date1 = xmalloc (MAXDATELEN);
  414. *date1 = '\0';
  415. if (RCS_getrevtime (rcsfile, vers_head, date1, 1) == (time_t)-1)
  416. {
  417. if (!really_quiet)
  418. error (0, 0, "cannot find date in rcs file %s revision %s",
  419. rcs, vers_head);
  420. ret = 1;
  421. goto out2;
  422. }
  423. }
  424. vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match,
  425. (int *) NULL);
  426. if (vers_tag != NULL && RCS_isdead (rcsfile, vers_tag))
  427. {
  428. free (vers_tag);
  429. vers_tag = NULL;
  430. }
  431. if ((vers_tag == NULL && vers_head == NULL) ||
  432. (vers_tag != NULL && vers_head != NULL &&
  433. strcmp (vers_head, vers_tag) == 0))
  434. {
  435. /* Nothing known about specified revs or
  436. * not changed between releases.
  437. */
  438. ret = 0;
  439. goto out2;
  440. }
  441. if( patch_short && ( vers_tag == NULL || vers_head == NULL ) )
  442. {
  443. /* For adds & removes with a short patch requested, we can print our
  444. * error message now and get out.
  445. */
  446. cvs_output ("File ", 0);
  447. cvs_output (finfo->fullname, 0);
  448. if (vers_tag == NULL)
  449. {
  450. cvs_output( " is new; ", 0 );
  451. cvs_output( rev2 ? rev2 : date2 ? date2 : "current", 0 );
  452. cvs_output( " revision ", 0 );
  453. cvs_output (vers_head, 0);
  454. cvs_output ("\n", 1);
  455. }
  456. else
  457. {
  458. cvs_output( " is removed; ", 0 );
  459. cvs_output( rev1 ? rev1 : date1, 0 );
  460. cvs_output( " revision ", 0 );
  461. cvs_output( vers_tag, 0 );
  462. cvs_output ("\n", 1);
  463. }
  464. ret = 0;
  465. goto out2;
  466. }
  467. /* Create 3 empty files. I'm not really sure there is any advantage
  468. * to doing so now rather than just waiting until later.
  469. *
  470. * There is - cvs_temp_file opens the file so that it can guarantee that
  471. * we have exclusive write access to the file. Unfortunately we spoil that
  472. * by closing it and reopening it again. Of course any better solution
  473. * requires that the RCS functions accept open file pointers rather than
  474. * simple file names.
  475. */
  476. if ((fp1 = cvs_temp_file (&tmpfile1)) == NULL)
  477. {
  478. error (0, errno, "cannot create temporary file %s",
  479. tmpfile1 ? tmpfile1 : "(null)");
  480. ret = 1;
  481. goto out;
  482. }
  483. else
  484. if (fclose (fp1) < 0)
  485. error (0, errno, "warning: cannot close %s", tmpfile1);
  486. if ((fp2 = cvs_temp_file (&tmpfile2)) == NULL)
  487. {
  488. error (0, errno, "cannot create temporary file %s",
  489. tmpfile2 ? tmpfile2 : "(null)");
  490. ret = 1;
  491. goto out;
  492. }
  493. else
  494. if (fclose (fp2) < 0)
  495. error (0, errno, "warning: cannot close %s", tmpfile2);
  496. if ((fp3 = cvs_temp_file (&tmpfile3)) == NULL)
  497. {
  498. error (0, errno, "cannot create temporary file %s",
  499. tmpfile3 ? tmpfile3 : "(null)");
  500. ret = 1;
  501. goto out;
  502. }
  503. else
  504. if (fclose (fp3) < 0)
  505. error (0, errno, "warning: cannot close %s", tmpfile3);
  506. if (vers_tag != NULL)
  507. {
  508. retcode = RCS_checkout (rcsfile, (char *)NULL, vers_tag,
  509. rev1, options, tmpfile1,
  510. (RCSCHECKOUTPROC)NULL, (void *)NULL);
  511. if (retcode != 0)
  512. {
  513. error (0, 0,
  514. "cannot check out revision %s of %s", vers_tag, rcs);
  515. ret = 1;
  516. goto out;
  517. }
  518. memset ((char *) &t, 0, sizeof (t));
  519. if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_tag,
  520. (char *) 0, 0)) != -1)
  521. /* I believe this timestamp only affects the dates in our diffs,
  522. and therefore should be on the server, not the client. */
  523. (void) utime (tmpfile1, &t);
  524. }
  525. else if (toptwo_diffs)
  526. {
  527. ret = 1;
  528. goto out;
  529. }
  530. if (vers_head != NULL)
  531. {
  532. retcode = RCS_checkout (rcsfile, (char *)NULL, vers_head,
  533. rev2, options, tmpfile2,
  534. (RCSCHECKOUTPROC)NULL, (void *)NULL);
  535. if (retcode != 0)
  536. {
  537. error (0, 0,
  538. "cannot check out revision %s of %s", vers_head, rcs);
  539. ret = 1;
  540. goto out;
  541. }
  542. if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_head,
  543. (char *)0, 0)) != -1)
  544. /* I believe this timestamp only affects the dates in our diffs,
  545. and therefore should be on the server, not the client. */
  546. (void)utime (tmpfile2, &t);
  547. }
  548. if (unidiff) run_add_arg_p (&dargc, &darg_allocated, &dargv, "-u");
  549. else run_add_arg_p (&dargc, &darg_allocated, &dargv, "-c");
  550. switch (diff_exec (tmpfile1, tmpfile2, NULL, NULL, dargc, dargv,
  551. tmpfile3))
  552. {
  553. case -1: /* fork/wait failure */
  554. error (1, errno, "fork for diff failed on %s", rcs);
  555. break;
  556. case 0: /* nothing to do */
  557. break;
  558. case 1:
  559. /*
  560. * The two revisions are really different, so read the first two
  561. * lines of the diff output file, and munge them to include more
  562. * reasonable file names that "patch" will understand, unless the
  563. * user wanted a short patch. In that case, just output the short
  564. * message.
  565. */
  566. if( patch_short )
  567. {
  568. cvs_output ("File ", 0);
  569. cvs_output (finfo->fullname, 0);
  570. cvs_output (" changed from revision ", 0);
  571. cvs_output (vers_tag, 0);
  572. cvs_output (" to ", 0);
  573. cvs_output (vers_head, 0);
  574. cvs_output ("\n", 1);
  575. ret = 0;
  576. goto out;
  577. }
  578. /* Output an "Index:" line for patch to use */
  579. cvs_output ("Index: ", 0);
  580. cvs_output (finfo->fullname, 0);
  581. cvs_output ("\n", 1);
  582. /* Now the munging. */
  583. fp = open_file (tmpfile3, "r");
  584. if (getline (&line1, &line1_chars_allocated, fp) < 0 ||
  585. getline (&line2, &line2_chars_allocated, fp) < 0)
  586. {
  587. if (feof (fp))
  588. error (0, 0, "\
  589. failed to read diff file header %s for %s: end of file", tmpfile3, rcs);
  590. else
  591. error (0, errno,
  592. "failed to read diff file header %s for %s",
  593. tmpfile3, rcs);
  594. ret = 1;
  595. if (fclose (fp) < 0)
  596. error (0, errno, "error closing %s", tmpfile3);
  597. goto out;
  598. }
  599. if (!unidiff)
  600. {
  601. if (strncmp (line1, "*** ", 4) != 0 ||
  602. strncmp (line2, "--- ", 4) != 0 ||
  603. (cp1 = strchr (line1, '\t')) == NULL ||
  604. (cp2 = strchr (line2, '\t')) == NULL)
  605. {
  606. error (0, 0, "invalid diff header for %s", rcs);
  607. ret = 1;
  608. if (fclose (fp) < 0)
  609. error (0, errno, "error closing %s", tmpfile3);
  610. goto out;
  611. }
  612. }
  613. else
  614. {
  615. if (strncmp (line1, "--- ", 4) != 0 ||
  616. strncmp (line2, "+++ ", 4) != 0 ||
  617. (cp1 = strchr (line1, '\t')) == NULL ||
  618. (cp2 = strchr (line2, '\t')) == NULL)
  619. {
  620. error (0, 0, "invalid unidiff header for %s", rcs);
  621. ret = 1;
  622. if (fclose (fp) < 0)
  623. error (0, errno, "error closing %s", tmpfile3);
  624. goto out;
  625. }
  626. }
  627. assert (current_parsed_root != NULL);
  628. assert (current_parsed_root->directory != NULL);
  629. {
  630. strippath = xmalloc (strlen (current_parsed_root->directory)
  631. + 2);
  632. (void)sprintf (strippath, "%s/",
  633. current_parsed_root->directory);
  634. }
  635. /*else
  636. strippath = xstrdup (REPOS_STRIP); */
  637. if (strncmp (rcs, strippath, strlen (strippath)) == 0)
  638. rcs += strlen (strippath);
  639. free (strippath);
  640. if (vers_tag != NULL)
  641. {
  642. file1 = xmalloc (strlen (finfo->fullname)
  643. + strlen (vers_tag)
  644. + 10);
  645. (void)sprintf (file1, "%s:%s", finfo->fullname, vers_tag);
  646. }
  647. else
  648. {
  649. file1 = xstrdup (DEVNULL);
  650. }
  651. file2 = xmalloc (strlen (finfo->fullname)
  652. + (vers_head != NULL ? strlen (vers_head) : 10)
  653. + 10);
  654. (void)sprintf (file2, "%s:%s", finfo->fullname,
  655. vers_head ? vers_head : "removed");
  656. /* Note that the string "diff" is specified by POSIX (for -c)
  657. and is part of the diff output format, not the name of a
  658. program. */
  659. if (unidiff)
  660. {
  661. cvs_output ("diff -u ", 0);
  662. cvs_output (file1, 0);
  663. cvs_output (" ", 1);
  664. cvs_output (file2, 0);
  665. cvs_output ("\n", 1);
  666. cvs_output ("--- ", 0);
  667. cvs_output (file1, 0);
  668. cvs_output (cp1, 0);
  669. cvs_output ("+++ ", 0);
  670. }
  671. else
  672. {
  673. cvs_output ("diff -c ", 0);
  674. cvs_output (file1, 0);
  675. cvs_output (" ", 1);
  676. cvs_output (file2, 0);
  677. cvs_output ("\n", 1);
  678. cvs_output ("*** ", 0);
  679. cvs_output (file1, 0);
  680. cvs_output (cp1, 0);
  681. cvs_output ("--- ", 0);
  682. }
  683. cvs_output (finfo->fullname, 0);
  684. cvs_output (cp2, 0);
  685. /* spew the rest of the diff out */
  686. while ((line_length
  687. = getline (&line1, &line1_chars_allocated, fp))
  688. >= 0)
  689. cvs_output (line1, 0);
  690. if (line_length < 0 && !feof (fp))
  691. error (0, errno, "cannot read %s", tmpfile3);
  692. if (fclose (fp) < 0)
  693. error (0, errno, "cannot close %s", tmpfile3);
  694. free (file1);
  695. free (file2);
  696. break;
  697. default:
  698. error (0, 0, "diff failed for %s", finfo->fullname);
  699. }
  700. out:
  701. if (line1)
  702. free (line1);
  703. if (line2)
  704. free (line2);
  705. if (tmpfile1 != NULL)
  706. {
  707. if (CVS_UNLINK (tmpfile1) < 0)
  708. error (0, errno, "cannot unlink %s", tmpfile1);
  709. free (tmpfile1);
  710. tmpfile1 = NULL;
  711. }
  712. if (tmpfile2 != NULL)
  713. {
  714. if (CVS_UNLINK (tmpfile2) < 0)
  715. error (0, errno, "cannot unlink %s", tmpfile2);
  716. free (tmpfile2);
  717. tmpfile2 = NULL;
  718. }
  719. if (tmpfile3 != NULL)
  720. {
  721. if (CVS_UNLINK (tmpfile3) < 0)
  722. error (0, errno, "cannot unlink %s", tmpfile3);
  723. free (tmpfile3);
  724. tmpfile3 = NULL;
  725. }
  726. if (dargc)
  727. {
  728. run_arg_free_p (dargc, dargv);
  729. free (dargv);
  730. }
  731. out2:
  732. if (vers_tag != NULL)
  733. free (vers_tag);
  734. if (vers_head != NULL)
  735. free (vers_head);
  736. if (rcs_orig)
  737. free (rcs_orig);
  738. return ret;
  739. }
  740. /*
  741. * Print a warm fuzzy message
  742. */
  743. /* ARGSUSED */
  744. static Dtype
  745. patch_dirproc (callerdat, dir, repos, update_dir, entries)
  746. void *callerdat;
  747. const char *dir;
  748. const char *repos;
  749. const char *update_dir;
  750. List *entries;
  751. {
  752. if (!quiet)
  753. error (0, 0, "Diffing %s", update_dir);
  754. return (R_PROCESS);
  755. }
  756. /*
  757. * Clean up temporary files
  758. */
  759. static RETSIGTYPE
  760. patch_cleanup ()
  761. {
  762. /* Note that the checks for existence_error are because we are
  763. called from a signal handler, without SIG_begincrsect, so
  764. we don't know whether the files got created. */
  765. if (tmpfile1 != NULL)
  766. {
  767. if (unlink_file (tmpfile1) < 0
  768. && !existence_error (errno))
  769. error (0, errno, "cannot remove %s", tmpfile1);
  770. free (tmpfile1);
  771. }
  772. if (tmpfile2 != NULL)
  773. {
  774. if (unlink_file (tmpfile2) < 0
  775. && !existence_error (errno))
  776. error (0, errno, "cannot remove %s", tmpfile2);
  777. free (tmpfile2);
  778. }
  779. if (tmpfile3 != NULL)
  780. {
  781. if (unlink_file (tmpfile3) < 0
  782. && !existence_error (errno))
  783. error (0, errno, "cannot remove %s", tmpfile3);
  784. free (tmpfile3);
  785. }
  786. tmpfile1 = tmpfile2 = tmpfile3 = NULL;
  787. }