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