xref: /dragonfly/contrib/cvs-1.12/src/classify.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
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
Classify_File(struct file_info * finfo,char * tag,char * date,char * options,int force_tag_match,int aflag,Vers_TS ** versp,int pipeout)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)
240                          && No_Difference (finfo, vers))
241               {
242                     /* they are different -> conflict */
243                     if (!really_quiet)
244                         error (0, 0,
245                            "conflict: `%s' is modified but no longer in the"
246                                  " repository",
247                                  finfo->fullname);
248                     ret = T_CONFLICT;
249               }
250               else
251               {
252 
253                     /*
254                      * The user file is still unmodified, so just remove it from
255                      * the entry list
256                      */
257                     if (!really_quiet)
258                         error (0, 0, "`%s' is no longer in the repository",
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_MODIFIED 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 != 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
sticky_ck(struct file_info * finfo,int aflag,Vers_TS * vers)408 sticky_ck (struct file_info *finfo, int aflag, Vers_TS *vers)
409 {
410     if (aflag || vers->tag || vers->date)
411     {
412           char *enttag = vers->entdata->tag;
413           char *entdate = vers->entdata->date;
414 
415           if ((enttag && vers->tag && strcmp (enttag, vers->tag)) ||
416               ((enttag && !vers->tag) || (!enttag && vers->tag)) ||
417               (entdate && vers->date && strcmp (entdate, vers->date)) ||
418               ((entdate && !vers->date) || (!entdate && vers->date)))
419           {
420               Register (finfo->entries, finfo->file, vers->vn_user, vers->ts_rcs,
421                           vers->options, vers->tag, vers->date, vers->ts_conflict);
422 
423 #ifdef SERVER_SUPPORT
424               if (server_active)
425               {
426                     /* We need to update the entries line on the client side.
427                        It is possible we will later update it again via
428                        server_updated or some such, but that is OK.  */
429                     server_update_entries
430                       (finfo->file, finfo->update_dir, finfo->repository,
431                        strcmp (vers->ts_rcs, vers->ts_user) == 0 ?
432                        SERVER_UPDATED : SERVER_MERGED);
433               }
434 #endif
435           }
436     }
437 }
438