]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - gnu/usr.bin/cvs/cvs/patch.c
This is the Linux generic soundcard driver, version 1.0c. Supports
[FreeBSD/FreeBSD.git] / gnu / usr.bin / cvs / cvs / patch.c
1 /*
2  * Copyright (c) 1992, Brian Berliner and Jeff Polk
3  * Copyright (c) 1989-1992, Brian Berliner
4  * 
5  * You may distribute under the terms of the GNU General Public License as
6  * specified in the README file that comes with the CVS 1.3 kit.
7  * 
8  * Patch
9  * 
10  * Create a Larry Wall format "patch" file between a previous release and the
11  * current head of a module, or between two releases.  Can specify the
12  * release as either a date or a revision number.
13  */
14
15 #include "cvs.h"
16
17 #ifndef lint
18 static char rcsid[] = "@(#)patch.c 1.50 92/04/10";
19
20 #endif
21
22 #if __STDC__
23 static SIGTYPE patch_cleanup (void);
24 static Dtype patch_dirproc (char *dir, char *repos, char *update_dir);
25 static int patch_fileproc (char *file, char *update_dir, char *repository,
26                            List * entries, List * srcfiles);
27 static int patch_proc (int *pargc, char *argv[], char *xwhere,
28                        char *mwhere, char *mfile, int shorten,
29                        int local_specified, char *mname, char *msg);
30 #else
31 static int patch_proc ();
32 static int patch_fileproc ();
33 static Dtype patch_dirproc ();
34 static SIGTYPE patch_cleanup ();
35 #endif                          /* __STDC__ */
36
37 static int force_tag_match = 1;
38 static int patch_short = 0;
39 static int toptwo_diffs = 0;
40 static int local = 0;
41 static char *options = NULL;
42 static char *rev1 = NULL;
43 static char *rev2 = NULL;
44 static char *date1 = NULL;
45 static char *date2 = NULL;
46 static char tmpfile1[L_tmpnam+1], tmpfile2[L_tmpnam+1], tmpfile3[L_tmpnam+1];
47 static int unidiff = 0;
48
49 static char *patch_usage[] =
50 {
51     "Usage: %s %s [-Qflq] [-c|-u] [-s|-t] [-V %%d]\n",
52     "    -r rev|-D date [-r rev2 | -D date2] modules...\n",
53     "\t-Q\tReally quiet.\n",
54     "\t-f\tForce a head revision match if tag/date not found.\n",
55     "\t-l\tLocal directory only, not recursive\n",
56     "\t-c\tContext diffs (default)\n",
57     "\t-u\tUnidiff format.\n",
58     "\t-s\tShort patch - one liner per file.\n",
59     "\t-t\tTop two diffs - last change made to the file.\n",
60     "\t-D date\tDate.\n",
61     "\t-r rev\tRevision - symbolic or numeric.\n",
62     "\t-V vers\tUse RCS Version \"vers\" for keyword expansion.\n",
63     NULL
64 };
65
66 int
67 patch (argc, argv)
68     int argc;
69     char *argv[];
70 {
71     register int i;
72     int c;
73     int err = 0;
74     DBM *db;
75
76     if (argc == -1)
77         usage (patch_usage);
78
79     optind = 1;
80     while ((c = gnu_getopt (argc, argv, "V:k:cuftsQqlRD:r:")) != -1)
81     {
82         switch (c)
83         {
84             case 'Q':
85                 really_quiet = 1;
86                 /* FALL THROUGH */
87             case 'q':
88                 quiet = 1;
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                 if (atoi (optarg) <= 0)
130                     error (1, 0, "must specify a version number to -V");
131                 if (options)
132                     free (options);
133                 options = xmalloc (strlen (optarg) + 1 + 2);    /* for the -V */
134                 (void) sprintf (options, "-V%s", optarg);
135                 break;
136             case 'u':
137                 unidiff = 1;            /* Unidiff */
138                 break;
139             case 'c':                   /* Context diff */
140                 unidiff = 0;
141                 break;
142             case '?':
143             default:
144                 usage (patch_usage);
145                 break;
146         }
147     }
148     argc -= optind;
149     argv += optind;
150
151     /* Sanity checks */
152     if (argc < 1)
153         usage (patch_usage);
154
155     if (toptwo_diffs && patch_short)
156         error (1, 0, "-t and -s options are mutually exclusive");
157     if (toptwo_diffs && (date1 != NULL || date2 != NULL ||
158                          rev1 != NULL || rev2 != NULL))
159         error (1, 0, "must not specify revisions/dates with -t option!");
160
161     if (!toptwo_diffs && (date1 == NULL && date2 == NULL &&
162                           rev1 == NULL && rev2 == NULL))
163         error (1, 0, "must specify at least one revision/date!");
164     if (date1 != NULL && date2 != NULL)
165         if (RCS_datecmp (date1, date2) >= 0)
166             error (1, 0, "second date must come after first date!");
167
168     /* if options is NULL, make it a NULL string */
169     if (options == NULL)
170         options = xstrdup ("");
171
172     /* clean up if we get a signal */
173     (void) SIG_register (SIGHUP, patch_cleanup);
174     (void) SIG_register (SIGINT, patch_cleanup);
175     (void) SIG_register (SIGQUIT, patch_cleanup);
176     (void) SIG_register (SIGPIPE, patch_cleanup);
177     (void) SIG_register (SIGTERM, patch_cleanup);
178
179     db = open_module ();
180     for (i = 0; i < argc; i++)
181         err += do_module (db, argv[i], PATCH, "Patching", patch_proc,
182                           (char *) NULL, 0, 0, 0, (char *) NULL);
183     close_module (db);
184     free (options);
185     patch_cleanup ();
186     return (err);
187 }
188
189 /*
190  * callback proc for doing the real work of patching
191  */
192 /* ARGSUSED */
193 static char where[PATH_MAX];
194 static int
195 patch_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified,
196             mname, msg)
197     int *pargc;
198     char *argv[];
199     char *xwhere;
200     char *mwhere;
201     char *mfile;
202     int shorten;
203     int local_specified;
204     char *mname;
205     char *msg;
206 {
207     int err = 0;
208     int which;
209     char repository[PATH_MAX];
210
211     (void) sprintf (repository, "%s/%s", CVSroot, argv[0]);
212     (void) strcpy (where, argv[0]);
213
214     /* if mfile isn't null, we need to set up to do only part of the module */
215     if (mfile != NULL)
216     {
217         char *cp;
218         char path[PATH_MAX];
219
220         /* if the portion of the module is a path, put the dir part on repos */
221         if ((cp = rindex (mfile, '/')) != NULL)
222         {
223             *cp = '\0';
224             (void) strcat (repository, "/");
225             (void) strcat (repository, mfile);
226             (void) strcat (where, "/");
227             (void) strcat (where, mfile);
228             mfile = cp + 1;
229         }
230
231         /* take care of the rest */
232         (void) sprintf (path, "%s/%s", repository, mfile);
233         if (isdir (path))
234         {
235             /* directory means repository gets the dir tacked on */
236             (void) strcpy (repository, path);
237             (void) strcat (where, "/");
238             (void) strcat (where, mfile);
239         }
240         else
241         {
242             int i;
243
244             /* a file means muck argv */
245             for (i = 1; i < *pargc; i++)
246                 free (argv[i]);
247             argv[1] = xstrdup (mfile);
248             (*pargc) = 2;
249         }
250     }
251
252     /* cd to the starting repository */
253     if (chdir (repository) < 0)
254     {
255         error (0, errno, "cannot chdir to %s", repository);
256         return (1);
257     }
258
259     if (force_tag_match)
260         which = W_REPOS | W_ATTIC;
261     else
262         which = W_REPOS;
263
264     /* start the recursion processor */
265     err = start_recursion (patch_fileproc, (int (*) ()) NULL, patch_dirproc,
266                            (int (*) ()) NULL, *pargc - 1, argv + 1, local,
267                            which, 0, 1, where, 1);
268
269     return (err);
270 }
271
272 /*
273  * Called to examine a particular RCS file, as appropriate with the options
274  * that were set above.
275  */
276 /* ARGSUSED */
277 static int
278 patch_fileproc (file, update_dir, repository, entries, srcfiles)
279     char *file;
280     char *update_dir;
281     char *repository;
282     List *entries;
283     List *srcfiles;
284 {
285     char *vers_tag, *vers_head;
286     char rcsspace[PATH_MAX];
287     char *rcs = rcsspace;
288     Node *p;
289     RCSNode *rcsfile;
290     FILE *fp1, *fp2, *fp3;
291     int ret = 0;
292     int isattic = 0;
293     int retcode = 0;
294     char file1[PATH_MAX], file2[PATH_MAX], strippath[PATH_MAX];
295     char line1[MAXLINELEN], line2[MAXLINELEN];
296     char *cp1, *cp2, *commap;
297     FILE *fp;
298
299
300     /* find the parsed rcs file */
301     p = findnode (srcfiles, file);
302     if (p == NULL)
303         return (1);
304     rcsfile = (RCSNode *) p->data;
305     if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
306         isattic = 1;
307
308     (void) sprintf (rcs, "%s%s", file, RCSEXT);
309
310     /* if vers_head is NULL, may have been removed from the release */
311     if (isattic && rev2 == NULL && date2 == NULL)
312         vers_head = NULL;
313     else
314         vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match);
315
316     if (toptwo_diffs)
317     {
318         if (vers_head == NULL)
319             return (1);
320
321         if (!date1)
322             date1 = xmalloc (50);       /* plenty big :-) */
323         *date1 = '\0';
324         if (RCS_getrevtime (rcsfile, vers_head, date1, 1) == -1)
325         {
326             if (!really_quiet)
327                 error (0, 0, "cannot find date in rcs file %s revision %s",
328                        rcs, vers_head);
329             return (1);
330         }
331     }
332     vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match);
333
334     if (vers_tag == NULL && (vers_head == NULL || isattic))
335         return (0);                     /* nothing known about specified revs */
336
337     if (vers_tag && vers_head && strcmp (vers_head, vers_tag) == 0)
338         return (0);                     /* not changed between releases */
339
340     if (patch_short)
341     {
342         (void) printf ("File ");
343         if (vers_tag == NULL)
344             (void) printf ("%s is new; current revision %s\n", rcs, vers_head);
345         else if (vers_head == NULL)
346             (void) printf ("%s is removed; not included in release %s\n",
347                            rcs, rev2 ? rev2 : date2);
348         else
349             (void) printf ("%s changed from revision %s to %s\n",
350                            rcs, vers_tag, vers_head);
351         return (0);
352     }
353     if ((fp1 = fopen (tmpnam (tmpfile1), "w+")) != NULL)
354         (void) fclose (fp1);
355     if ((fp2 = fopen (tmpnam (tmpfile2), "w+")) != NULL)
356         (void) fclose (fp2);
357     if ((fp3 = fopen (tmpnam (tmpfile3), "w+")) != NULL)
358         (void) fclose (fp3);
359     if (fp1 == NULL || fp2 == NULL || fp3 == NULL)
360     {
361         error (0, 0, "cannot create temporary files");
362         ret = 1;
363         goto out;
364     }
365     if (vers_tag != NULL)
366     {
367         run_setup ("%s%s %s -p -q -r%s", Rcsbin, RCS_CO, options, vers_tag);
368         run_arg (rcsfile->path);
369         if ((retcode = run_exec (RUN_TTY, tmpfile1, RUN_TTY, RUN_NORMAL)) != 0)
370         {
371             if (!really_quiet)
372                 error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
373                        "co of revision %s in %s failed", vers_tag, rcs);
374             ret = 1;
375             goto out;
376         }
377     }
378     else if (toptwo_diffs)
379     {
380         ret = 1;
381         goto out;
382     }
383     if (vers_head != NULL)
384     {
385         run_setup ("%s%s %s -p -q -r%s", Rcsbin, RCS_CO, options, vers_head);
386         run_arg (rcsfile->path);
387         if ((retcode = run_exec (RUN_TTY, tmpfile2, RUN_TTY, RUN_NORMAL)) != 0)
388         {
389             if (!really_quiet)
390                 error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
391                        "co of revision %s in %s failed", vers_head, rcs);
392             ret = 1;
393             goto out;
394         }
395     }
396     run_setup ("%s -%c", DIFF, unidiff ? 'u' : 'c');
397     run_arg (tmpfile1);
398     run_arg (tmpfile2);
399     switch (run_exec (RUN_TTY, tmpfile3, RUN_TTY, RUN_NORMAL))
400     {
401         case -1:                        /* fork/wait failure */
402             error (1, errno, "fork for diff failed on %s", rcs);
403             break;
404         case 0:                         /* nothing to do */
405             break;
406         case 1:
407             /*
408              * The two revisions are really different, so read the first two
409              * lines of the diff output file, and munge them to include more
410              * reasonable file names that "patch" will understand.
411              */
412             fp = open_file (tmpfile3, "r");
413             if (fgets (line1, sizeof (line1), fp) == NULL ||
414                 fgets (line2, sizeof (line2), fp) == NULL)
415             {
416                 error (0, errno, "failed to read diff file header %s for %s",
417                        tmpfile3, rcs);
418                 ret = 1;
419                 (void) fclose (fp);
420                 goto out;
421             }
422             if (!unidiff)
423             {
424                 if (strncmp (line1, "*** ", 4) != 0 ||
425                     strncmp (line2, "--- ", 4) != 0 ||
426                     (cp1 = index (line1, '\t')) == NULL ||
427                     (cp2 = index (line2, '\t')) == NULL)
428                 {
429                     error (0, 0, "invalid diff header for %s", rcs);
430                     ret = 1;
431                     (void) fclose (fp);
432                     goto out;
433                 }
434             }
435             else
436             {
437                 if (strncmp (line1, "--- ", 4) != 0 ||
438                     strncmp (line2, "+++ ", 4) != 0 ||
439                     (cp1 = index (line1, '\t')) == NULL ||
440                     (cp2 = index (line2, '\t')) == NULL)
441                 {
442                     error (0, 0, "invalid unidiff header for %s", rcs);
443                     ret = 1;
444                     (void) fclose (fp);
445                     goto out;
446                 }
447             }
448             if (CVSroot != NULL)
449                 (void) sprintf (strippath, "%s/", CVSroot);
450             else
451                 (void) strcpy (strippath, REPOS_STRIP);
452             if (strncmp (rcs, strippath, strlen (strippath)) == 0)
453                 rcs += strlen (strippath);
454             commap = rindex (rcs, ',');
455             *commap = '\0';
456             if (vers_tag != NULL)
457             {
458                 (void) sprintf (file1, "%s%s%s:%s", update_dir,
459                                 update_dir[0] ? "/" : "", rcs, vers_tag);
460             }
461             else
462             {
463                 (void) strcpy (file1, DEVNULL);
464             }
465             (void) sprintf (file2, "%s%s%s:%s", update_dir,
466                             update_dir[0] ? "/" : "", rcs,
467                             vers_head ? vers_head : "removed");
468             if (unidiff)
469             {
470                 (void) printf ("diff -u %s %s\n", file1, file2);
471                 (void) printf ("--- %s%s+++ ", file1, cp1);
472             }
473             else
474             {
475                 (void) printf ("diff -c %s %s\n", file1, file2);
476                 (void) printf ("*** %s%s--- ", file1, cp1);
477             }
478
479             if (update_dir[0] != '\0')
480                 (void) printf ("%s/", update_dir);
481             (void) printf ("%s%s", rcs, cp2);
482             while (fgets (line1, sizeof (line1), fp) != NULL)
483                 (void) printf ("%s", line1);
484             (void) fclose (fp);
485             break;
486         default:
487             error (0, 0, "diff failed for %s", rcs);
488     }
489   out:
490     (void) unlink_file (tmpfile1);
491     (void) unlink_file (tmpfile2);
492     (void) unlink_file (tmpfile3);
493     return (ret);
494 }
495
496 /*
497  * Print a warm fuzzy message
498  */
499 /* ARGSUSED */
500 static Dtype
501 patch_dirproc (dir, repos, update_dir)
502     char *dir;
503     char *repos;
504     char *update_dir;
505 {
506     if (!quiet)
507         error (0, 0, "Diffing %s", update_dir);
508     return (R_PROCESS);
509 }
510
511 /*
512  * Clean up temporary files
513  */
514 static SIGTYPE
515 patch_cleanup ()
516 {
517     if (tmpfile1[0] != '\0')
518         (void) unlink_file (tmpfile1);
519     if (tmpfile2[0] != '\0')
520         (void) unlink_file (tmpfile2);
521     if (tmpfile3[0] != '\0')
522         (void) unlink_file (tmpfile3);
523 }