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 */
14 #include <sys/cdefs.h>
15 __RCSID("$NetBSD: classify.c,v 1.2 2016/05/17 14:00:09 christos Exp $");
16
17 #include "cvs.h"
18
19 static void sticky_ck (struct file_info *finfo, int aflag,
20 Vers_TS * vers);
21
22 /*
23 * Classify the state of a file.
24 *
25 * INPUTS
26 * finfo Information about the file to be classified.
27 * tag
28 * date
29 * options Keyword expansion options. Can be either NULL or "" to
30 * indicate none are specified here.
31 * force_tag_match
32 * aflag
33 * versp
34 * pipeout Did the user pass the "pipeout" flag to request that
35 * all output go to STDOUT rather than to a file or files?
36 *
37 * RETURNS
38 * A Ctype (defined as an enum) describing the state of the file relative to
39 * the repository. See the definition of Ctype for more.
40 */
41 Ctype
Classify_File(struct file_info * finfo,char * tag,char * date,char * options,int force_tag_match,int aflag,Vers_TS ** versp,int pipeout)42 Classify_File (struct file_info *finfo, char *tag, char *date, char *options,
43 int force_tag_match, int aflag, Vers_TS **versp, int pipeout)
44 {
45 Vers_TS *vers;
46 Ctype ret;
47
48 /* get all kinds of good data about the file */
49 vers = Version_TS (finfo, options, tag, date,
50 force_tag_match, 0);
51
52 if (vers->vn_user == NULL)
53 {
54 /* No entry available, ts_rcs is invalid */
55 if (vers->vn_rcs == NULL)
56 {
57 /* there is no RCS file either */
58 if (vers->ts_user == NULL)
59 {
60 /* there is no user file */
61 /* FIXME: Why do we skip this message if vers->tag or
62 vers->date is set? It causes "cvs update -r tag98 foo"
63 to silently do nothing, which is seriously confusing
64 behavior. "cvs update foo" gives this message, which
65 is what I would expect. */
66 if (!force_tag_match || !(vers->tag || vers->date))
67 if (!really_quiet)
68 error (0, 0, "nothing known about `%s'",
69 finfo->fullname);
70 ret = T_UNKNOWN;
71 }
72 else
73 {
74 /* there is a user file */
75 /* FIXME: Why do we skip this message if vers->tag or
76 vers->date is set? It causes "cvs update -r tag98 foo"
77 to silently do nothing, which is seriously confusing
78 behavior. "cvs update foo" gives this message, which
79 is what I would expect. */
80 if (!force_tag_match || !(vers->tag || vers->date))
81 if (!really_quiet)
82 error (0, 0, "use `%s add' to create an entry for `%s'",
83 program_name, finfo->fullname);
84 ret = T_UNKNOWN;
85 }
86 }
87 else if (RCS_isdead (vers->srcfile, vers->vn_rcs))
88 {
89 /* there is an RCS file, but it's dead */
90 if (vers->ts_user == NULL)
91 ret = T_UPTODATE;
92 else
93 {
94 error (0, 0, "use `%s add' to create an entry for `%s'",
95 program_name, finfo->fullname);
96 ret = T_UNKNOWN;
97 }
98 }
99 else if (!pipeout && vers->ts_user && No_Difference (finfo, vers))
100 {
101 /* the files were different so it is a conflict */
102 if (!really_quiet)
103 error (0, 0, "move away `%s'; it is in the way",
104 finfo->fullname);
105 ret = T_CONFLICT;
106 }
107 else
108 /* no user file or no difference, just checkout */
109 ret = T_CHECKOUT;
110 }
111 else if (strcmp (vers->vn_user, "0") == 0)
112 {
113 /* An entry for a new-born file; ts_rcs is dummy */
114
115 if (vers->ts_user == NULL)
116 {
117 if (pipeout)
118 {
119 ret = T_CHECKOUT;
120 }
121 else
122 {
123 /*
124 * There is no user file, but there should be one; remove the
125 * entry
126 */
127 if (!really_quiet)
128 error (0, 0, "warning: new-born `%s' has disappeared",
129 finfo->fullname);
130 ret = T_REMOVE_ENTRY;
131 }
132 }
133 else if (vers->vn_rcs == NULL ||
134 RCS_isdead (vers->srcfile, vers->vn_rcs))
135 /* No RCS file or RCS file revision is dead */
136 ret = T_ADDED;
137 else
138 {
139 if (pipeout)
140 {
141 ret = T_CHECKOUT;
142 }
143 else
144 {
145 if (vers->srcfile->flags & INATTIC
146 && vers->srcfile->flags & VALID)
147 {
148 /* This file has been added on some branch other than
149 the one we are looking at. In the branch we are
150 looking at, the file was already valid. */
151 if (!really_quiet)
152 error (0, 0,
153 "conflict: `%s' has been added, but already exists",
154 finfo->fullname);
155 }
156 else
157 {
158 /*
159 * There is an RCS file, so someone else must have checked
160 * one in behind our back; conflict
161 */
162 if (!really_quiet)
163 error (0, 0,
164 "conflict: `%s' created independently by"
165 " second party",
166 finfo->fullname);
167 }
168 ret = T_CONFLICT;
169 }
170 }
171 }
172 else if (vers->vn_user[0] == '-')
173 {
174 /* An entry for a removed file, ts_rcs is invalid */
175
176 if (vers->ts_user == NULL)
177 {
178 /* There is no user file (as it should be) */
179
180 if (vers->vn_rcs == NULL
181 || RCS_isdead (vers->srcfile, vers->vn_rcs))
182 {
183
184 /*
185 * There is no RCS file; this is all-right, but it has been
186 * removed independently by a second party; remove the entry
187 */
188 ret = T_REMOVE_ENTRY;
189 }
190 else if (strcmp (vers->vn_rcs, vers->vn_user + 1) == 0)
191 /*
192 * The RCS file is the same version as the user file was, and
193 * that's OK; remove it
194 */
195 ret = T_REMOVED;
196 else if (pipeout)
197 /*
198 * The RCS file doesn't match the user's file, but it doesn't
199 * matter in this case
200 */
201 ret = T_NEEDS_MERGE;
202 else
203 {
204
205 /*
206 * The RCS file is a newer version than the removed user file
207 * and this is definitely not OK; make it a conflict.
208 */
209 if (!really_quiet)
210 error (0, 0,
211 "conflict: removed `%s' was modified by"
212 " second party",
213 finfo->fullname);
214 ret = T_CONFLICT;
215 }
216 }
217 else
218 {
219 /* The user file shouldn't be there */
220 if (!really_quiet)
221 error (0, 0, "`%s' should be removed and is still there",
222 finfo->fullname);
223 ret = T_REMOVED;
224 }
225 }
226 else
227 {
228 /* A normal entry, TS_Rcs is valid */
229 if (vers->vn_rcs == NULL || RCS_isdead (vers->srcfile, vers->vn_rcs))
230 {
231 /* There is no RCS file */
232
233 if (vers->ts_user == NULL)
234 {
235 /* There is no user file, so just remove the entry */
236 if (!really_quiet)
237 error (0, 0, "warning: `%s' is not (any longer) pertinent",
238 finfo->fullname);
239 ret = T_REMOVE_ENTRY;
240 }
241 else if (strcmp (vers->ts_user, vers->ts_rcs)
242 && No_Difference (finfo, vers))
243 {
244 /* they are different -> conflict */
245 if (!really_quiet)
246 error (0, 0,
247 "conflict: `%s' is modified but no longer in the"
248 " repository",
249 finfo->fullname);
250 ret = T_CONFLICT;
251 }
252 else
253 {
254
255 /*
256 * The user file is still unmodified, so just remove it from
257 * the entry list
258 */
259 if (!really_quiet)
260 error (0, 0, "`%s' is no longer in the repository",
261 finfo->fullname);
262 ret = T_REMOVE_ENTRY;
263 }
264 }
265 else if (strcmp (vers->vn_rcs, vers->vn_user) == 0)
266 {
267 /* The RCS file is the same version as the user file */
268
269 if (vers->ts_user == NULL)
270 {
271
272 /*
273 * There is no user file, so note that it was lost and
274 * extract a new version
275 */
276 /* Comparing the cvs_cmd_name against "update", in
277 addition to being an ugly way to operate, means
278 that this message does not get printed by the
279 server. That might be considered just a straight
280 bug, although there is one subtlety: that case also
281 gets hit when a patch fails and the client fetches
282 a file. I'm not sure there is currently any way
283 for the server to distinguish those two cases. */
284 if (strcmp (cvs_cmd_name, "update") == 0)
285 if (!really_quiet)
286 error (0, 0, "warning: `%s' was lost", finfo->fullname);
287 ret = T_CHECKOUT;
288 }
289 else if (!strcmp (vers->ts_user,
290 vers->ts_conflict
291 ? vers->ts_conflict : vers->ts_rcs))
292 {
293
294 /*
295 * The user file is still unmodified, so nothing special at
296 * all to do -- no lists updated, unless the sticky -k option
297 * has changed. If the sticky tag has changed, we just need
298 * to re-register the entry
299 */
300 /* TODO: decide whether we need to check file permissions
301 for a mismatch, and return T_CONFLICT if so. */
302 if (vers->entdata->options &&
303 strcmp (vers->entdata->options, vers->options) != 0)
304 ret = T_CHECKOUT;
305 else if (vers->ts_conflict)
306 ret = T_CONFLICT;
307 else
308 {
309 sticky_ck (finfo, aflag, vers);
310 ret = T_UPTODATE;
311 }
312 }
313 else if (No_Difference (finfo, vers))
314 {
315
316 /*
317 * they really are different; modified if we aren't
318 * changing any sticky -k options, else needs merge
319 */
320 #ifdef XXX_FIXME_WHEN_RCSMERGE_IS_FIXED
321 if (strcmp (vers->entdata->options ?
322 vers->entdata->options : "", vers->options) == 0)
323 ret = T_MODIFIED;
324 else
325 ret = T_NEEDS_MERGE;
326 #else
327 /* Files with conflict markers and new timestamps fall through
328 * here, but they need to. T_CONFLICT is an error in
329 * commit_fileproc, whereas T_MODIFIED with conflict markers
330 * is caught but only warned about. Similarly, update_fileproc
331 * currently reregisters a file that was conflicted but lost
332 * its markers.
333 */
334 ret = T_MODIFIED;
335 sticky_ck (finfo, aflag, vers);
336 #endif
337 }
338 else if (strcmp (vers->entdata->options ?
339 vers->entdata->options : "", vers->options) != 0)
340 {
341 /* file has not changed; check out if -k changed */
342 ret = T_CHECKOUT;
343 }
344 else
345 {
346
347 /*
348 * else -> note that No_Difference will Register the
349 * file already for us, using the new tag/date. This
350 * is the desired behaviour
351 */
352 ret = T_UPTODATE;
353 }
354 }
355 else
356 {
357 /* The RCS file is a newer version than the user file */
358
359 if (vers->ts_user == NULL)
360 {
361 /* There is no user file, so just get it */
362
363 /* See comment at other "update" compare, for more
364 thoughts on this comparison. */
365 if (strcmp (cvs_cmd_name, "update") == 0)
366 if (!really_quiet)
367 error (0, 0, "warning: `%s' was lost", finfo->fullname);
368 ret = T_CHECKOUT;
369 }
370 else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
371 {
372
373 /*
374 * The user file is still unmodified, so just get it as well
375 */
376 if (strcmp (vers->entdata->options ?
377 vers->entdata->options : "", vers->options) != 0
378 || (vers->srcfile != NULL
379 && (vers->srcfile->flags & INATTIC) != 0))
380 ret = T_CHECKOUT;
381 else
382 ret = T_PATCH;
383 }
384 else if (No_Difference (finfo, vers))
385 /* really modified, needs to merge */
386 ret = T_NEEDS_MERGE;
387 else if ((strcmp (vers->entdata->options ?
388 vers->entdata->options : "", vers->options)
389 != 0)
390 || (vers->srcfile != NULL
391 && (vers->srcfile->flags & INATTIC) != 0))
392 /* not really modified, check it out */
393 ret = T_CHECKOUT;
394 else
395 ret = T_PATCH;
396 }
397 }
398
399 /* free up the vers struct, or just return it */
400 if (versp != NULL)
401 *versp = vers;
402 else
403 freevers_ts (&vers);
404
405 /* return the status of the file */
406 return (ret);
407 }
408
409 static void
sticky_ck(struct file_info * finfo,int aflag,Vers_TS * vers)410 sticky_ck (struct file_info *finfo, int aflag, Vers_TS *vers)
411 {
412 if (aflag || vers->tag || vers->date)
413 {
414 char *enttag = vers->entdata->tag;
415 char *entdate = vers->entdata->date;
416
417 if ((enttag && vers->tag && strcmp (enttag, vers->tag)) ||
418 ((enttag && !vers->tag) || (!enttag && vers->tag)) ||
419 (entdate && vers->date && strcmp (entdate, vers->date)) ||
420 ((entdate && !vers->date) || (!entdate && vers->date)))
421 {
422 Register (finfo->entries, finfo->file, vers->vn_user, vers->ts_rcs,
423 vers->options, vers->tag, vers->date, vers->ts_conflict);
424
425 #ifdef SERVER_SUPPORT
426 if (server_active)
427 {
428 /* We need to update the entries line on the client side.
429 It is possible we will later update it again via
430 server_updated or some such, but that is OK. */
431 server_update_entries
432 (finfo->file, finfo->update_dir, finfo->repository,
433 strcmp (vers->ts_rcs, vers->ts_user) == 0 ?
434 SERVER_UPDATED : SERVER_MERGED);
435 }
436 #endif
437 }
438 }
439 }
440