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