1 /* Merge RCS revisions.
2 
3    Copyright (C) 2010-2020 Thien-Thi Nguyen
4    Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
5    Copyright (C) 1982, 1988, 1989 Walter Tichy
6 
7    This file is part of GNU RCS.
8 
9    GNU RCS is free software: you can redistribute it and/or modify it
10    under the terms of the GNU General Public License as published by
11    the Free Software Foundation, either version 3 of the License, or
12    (at your option) any later version.
13 
14    GNU RCS is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty
16    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17    See the GNU General Public License for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22 
23 #include "base.h"
24 #include <string.h>
25 #include "rcsmerge.help"
26 #include "b-complain.h"
27 #include "b-divvy.h"
28 #include "b-feph.h"
29 #include "b-fro.h"
30 #include "b-merger.h"
31 #include "b-peer.h"
32 
33 #define quietarg  "-q"
34 
35 DECLARE_PROGRAM (rcsmerge, BOG_DIFF);
36 
37 static int
rcsmerge_main(const char * cmd,int argc,char ** argv)38 rcsmerge_main (const char *cmd, int argc, char **argv)
39 {
40   register int i;
41   char *a, **newargv;
42   struct symdef three_manifestations[3];
43   char const *rev[3];                   /*revision numbers */
44   char const *edarg, *expandarg, *suffixarg, *versionarg, *zonearg;
45   bool tostdout;
46   int status, exitstatus;
47   struct fro *workptr;
48   struct delta *target;
49 
50   CHECK_HV (cmd);
51   gnurcs_init (&program);
52 
53   edarg = rev[1] = rev[2] = NULL;
54   status = 0;                           /* Keep lint happy.  */
55   tostdout = false;
56   expandarg = suffixarg = versionarg = zonearg = quietarg;      /* no-op */
57 
58   argc = getRCSINIT (argc, argv, &newargv);
59   argv = newargv;
60   while (a = *++argv, 0 < --argc && *a++ == '-')
61     {
62       switch (*a++)
63         {
64         case 'p':
65           tostdout = true;
66           goto revno;
67 
68         case 'q':
69           BE (quiet) = true;
70         revno:
71           if (!*a)
72             break;
73           /* falls into -r */
74         case 'r':
75           if (!rev[1])
76             rev[1] = a;
77           else if (!rev[2])
78             rev[2] = a;
79           else
80             PERR ("too many %ss", ks_revno);
81           break;
82 
83         case 'A':
84         case 'E':
85         case 'e':
86           if (*a)
87             goto unknown;
88           edarg = *argv;
89           break;
90 
91         case 'x':
92           suffixarg = *argv;
93           BE (pe) = a;
94           break;
95         case 'z':
96           zonearg = *argv;
97           zone_set (a);
98           break;
99         case 'T':
100           /* Ignore ‘-T’, so that env var ‘RCSINIT’ can contain ‘-T’.  */
101           if (*a)
102             goto unknown;
103           break;
104         case 'V':
105           versionarg = *argv;
106           setRCSversion (versionarg);
107           break;
108 
109         case 'k':
110           expandarg = *argv;
111           if (0 <= str2expmode (expandarg + 2))
112             break;
113           /* fall into */
114         default:
115         unknown:
116           bad_option (*argv);
117         };
118     }
119   /* (End of option processing.)  */
120 
121   if (!rev[1])
122     PFATAL ("no base %s given", ks_revno);
123 
124   /* Now handle all filenames.  */
125   if (!FLOW (erroneous))
126     {
127       if (argc < 1)
128         PFATAL ("no input file");
129       if (0 < pairnames (argc, argv, rcsreadopen, true, false))
130         {
131           struct cbuf numericrev;
132           char const *repo_filename = REPO (filename);
133           char const *mani_filename = MANI (filename);
134           char const *defbr = GROK (branch);
135           struct delta *tip = REPO (tip);
136 
137           if (argc > 2 || (argc == 2 && argv[1]))
138             PWARN ("excess arguments ignored");
139           if (BE (kws) == kwsub_b)
140             MERR ("merging binary files");
141           diagnose ("RCS file: %s", repo_filename);
142           if (!(workptr = fro_open (mani_filename, FOPEN_R_WORK, NULL)))
143             fatal_sys (mani_filename);
144 
145           if (!tip)
146             RFATAL ("no revisions present");
147 
148           if (!*rev[1])
149             rev[1] = defbr ? defbr : tip->num;
150           if (fully_numeric (&numericrev, rev[1], workptr)
151               && (target = delta_from_ref (numericrev.string)))
152             {
153               LABEL (1) = target->num;
154               if (!rev[2] || !*rev[2])
155                 rev[2] = defbr ? defbr : tip->num;
156               if (fully_numeric (&numericrev, rev[2], workptr)
157                   && (target = delta_from_ref (numericrev.string)))
158                 {
159                   LABEL (2) = target->num;
160 
161                   if (STR_SAME (LABEL (1), LABEL (2)))
162                     {
163                       if (tostdout)
164                         {
165                           fro_spew (workptr, stdout);
166                           fclose (stdout);
167                         }
168                     }
169                   else
170                     {
171                       fro_zclose (&workptr);
172 
173                       for (i = 1; i <= 2; i++)
174                         {
175                           struct cbuf commarg = minus_p (LABEL (i), rev[i]);
176 
177                           if (run (-1,
178                                    /* Don't collide with merger.c ‘maketemp’.  */
179                                    FNAME (i) = maketemp (i + 2),
180                                    PEER_SUPER (), "co", quietarg, commarg.string,
181                                    expandarg, suffixarg, versionarg, zonearg,
182                                    repo_filename, NULL))
183                             RFATAL ("co failed");
184                         }
185                       diagnose
186                         ("Merging differences between %s and %s into %s%s",
187                          LABEL (1), LABEL (2), mani_filename,
188                          tostdout ? "; result to stdout" : "");
189 
190                       FNAME (0) = LABEL (0) = mani_filename;
191                       status = merge (tostdout, edarg, three_manifestations);
192                     }
193                 }
194             }
195 
196           fro_zclose (&workptr);
197         }
198     }
199   tempunlink ();
200   exitstatus = FLOW (erroneous)
201     ? DIFF_TROUBLE
202     : status;
203   gnurcs_goodbye ();
204   return exitstatus;
205 }
206 
207 static const uint8_t rcsmerge_aka[16] =
208 {
209   2 /* count */,
210   5,'m','e','r','g','e',
211   8,'r','c','s','m','e','r','g','e'
212 };
213 
214 YET_ANOTHER_COMMAND (rcsmerge);
215 
216 /*:help
217 [options] file
218 Options:
219   -A            Passed to diff3(1).
220   -E            Passed to diff3(1); default if unspecified.
221   -e            Passed to diff3(1); do not warn on conflicts.
222   -p[REV]       Write to stdout instead of overwriting the working file.
223   -q[REV]       Quiet mode.
224   -rREV         (one or two times) specify a revision.
225   -kSUBST       Substitute using mode SUBST (see co(1)).
226   -T            No effect; included for compatibility with other commands.
227   -V            Obsolete; do not use.
228   -VN           Emulate RCS version N.
229   -xSUFF        Specify SUFF as a slash-separated list of suffixes
230                 used to identify RCS file names.
231   -zZONE        Specify date output format in keyword-substitution.
232 
233 One or two revisions must be specified (using -p, -q, or -r).
234 If only one is specified, use the latest revision on the default
235 branch to be the second revision.
236 */
237 
238 /* rcsmerge.c ends here */
239