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