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