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