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