Implement CLOCK_MONOTONIC using getnanouptime(), which in DragonFly is
[dragonfly.git] / contrib / cvs-1.12.9 / src / classify.c
1 /*
2  * Copyright (c) 1992, Brian Berliner and Jeff Polk
3  * Copyright (c) 1989-1992, Brian Berliner
4  * 
5  * You may distribute under the terms of the GNU General Public License as
6  * specified in the README file that comes with the CVS source distribution.
7  * 
8  */
9
10 #include "cvs.h"
11
12 static void sticky_ck (struct file_info *finfo, int aflag,
13                               Vers_TS * vers);
14
15 /*
16  * Classify the state of a file.
17  *
18  * INPUTS
19  *   finfo              Information about the file to be classified.
20  *   tag
21  *   date
22  *   options            Keyword expansion options.  Can be either NULL or "" to
23  *                      indicate none are specified here.
24  *   force_tag_match
25  *   aflag
26  *   versp
27  *   pipeout            Did the user pass the "pipeout" flag to request that
28  *                      all output go to STDOUT rather than to a file or files?
29  *
30  * RETURNS
31  *   A Ctype (defined as an enum) describing the state of the file relative to
32  *   the repository.  See the definition of Ctype for more.
33  */
34 Ctype
35 Classify_File (struct file_info *finfo, char *tag, char *date, char *options,
36                int force_tag_match, int aflag, Vers_TS **versp, int pipeout)
37 {
38     Vers_TS *vers;
39     Ctype ret;
40
41     /* get all kinds of good data about the file */
42     vers = Version_TS (finfo, options, tag, date,
43                        force_tag_match, 0);
44
45     if (vers->vn_user == NULL)
46     {
47         /* No entry available, ts_rcs is invalid */
48         if (vers->vn_rcs == NULL)
49         {
50             /* there is no RCS file either */
51             if (vers->ts_user == NULL)
52             {
53                 /* there is no user file */
54                 /* FIXME: Why do we skip this message if vers->tag or
55                    vers->date is set?  It causes "cvs update -r tag98 foo"
56                    to silently do nothing, which is seriously confusing
57                    behavior.  "cvs update foo" gives this message, which
58                    is what I would expect.  */
59                 if (!force_tag_match || !(vers->tag || vers->date))
60                     if (!really_quiet)
61                         error (0, 0, "nothing known about `%s'",
62                                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"
158                                " second party",
159                                finfo->fullname);
160                 }
161                 ret = T_CONFLICT;
162             }
163         }
164     }
165     else if (vers->vn_user[0] == '-')
166     {
167         /* An entry for a removed file, ts_rcs is invalid */
168
169         if (vers->ts_user == NULL)
170         {
171             /* There is no user file (as it should be) */
172
173             if (vers->vn_rcs == NULL
174                 || RCS_isdead (vers->srcfile, vers->vn_rcs))
175             {
176
177                 /*
178                  * There is no RCS file; this is all-right, but it has been
179                  * removed independently by a second party; remove the entry
180                  */
181                 ret = T_REMOVE_ENTRY;
182             }
183             else if (strcmp (vers->vn_rcs, vers->vn_user + 1) == 0)
184                 /*
185                  * The RCS file is the same version as the user file was, and
186                  * that's OK; remove it
187                  */
188                 ret = T_REMOVED;
189             else if (pipeout)
190                 /*
191                  * The RCS file doesn't match the user's file, but it doesn't
192                  * matter in this case
193                  */
194                 ret = T_NEEDS_MERGE;
195             else
196             {
197
198                 /*
199                  * The RCS file is a newer version than the removed user file
200                  * and this is definitely not OK; make it a conflict.
201                  */
202                 if (!really_quiet)
203                     error (0, 0,
204                            "conflict: removed `%s' was modified by"
205                            " second party",
206                            finfo->fullname);
207                 ret = T_CONFLICT;
208             }
209         }
210         else
211         {
212             /* The user file shouldn't be there */
213             if (!really_quiet)
214                 error (0, 0, "`%s' should be removed and is still there",
215                        finfo->fullname);
216             ret = T_REMOVED;
217         }
218     }
219     else
220     {
221         /* A normal entry, TS_Rcs is valid */
222         if (vers->vn_rcs == NULL || RCS_isdead (vers->srcfile, vers->vn_rcs))
223         {
224             /* There is no RCS file */
225
226             if (vers->ts_user == NULL)
227             {
228                 /* There is no user file, so just remove the entry */
229                 if (!really_quiet)
230                     error (0, 0, "warning: `%s' is not (any longer) pertinent",
231                            finfo->fullname);
232                 ret = T_REMOVE_ENTRY;
233             }
234             else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
235             {
236
237                 /*
238                  * The user file is still unmodified, so just remove it from
239                  * the entry list
240                  */
241                 if (!really_quiet)
242                     error (0, 0, "`%s' is no longer in the repository",
243                            finfo->fullname);
244                 ret = T_REMOVE_ENTRY;
245             }
246             else if (No_Difference (finfo, vers))
247             {
248                 /* they are different -> conflict */
249                 if (!really_quiet)
250                     error (0, 0,
251                            "conflict: `%s' is modified but no longer in the"
252                            " repository",
253                            finfo->fullname);
254                 ret = T_CONFLICT;
255             }
256             else
257             {
258                 /* they weren't really different */
259                 if (!really_quiet)
260                     error (0, 0,
261                            "warning: `%s' is not (any longer) pertinent",
262                            finfo->fullname);
263                 ret = T_REMOVE_ENTRY;
264             }
265         }
266         else if (strcmp (vers->vn_rcs, vers->vn_user) == 0)
267         {
268             /* The RCS file is the same version as the user file */
269
270             if (vers->ts_user == NULL)
271             {
272
273                 /*
274                  * There is no user file, so note that it was lost and
275                  * extract a new version
276                  */
277                 /* Comparing the cvs_cmd_name against "update", in
278                    addition to being an ugly way to operate, means
279                    that this message does not get printed by the
280                    server.  That might be considered just a straight
281                    bug, although there is one subtlety: that case also
282                    gets hit when a patch fails and the client fetches
283                    a file.  I'm not sure there is currently any way
284                    for the server to distinguish those two cases.  */
285                 if (strcmp (cvs_cmd_name, "update") == 0)
286                     if (!really_quiet)
287                         error (0, 0, "warning: `%s' was lost", finfo->fullname);
288                 ret = T_CHECKOUT;
289             }
290             else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
291             {
292
293                 /*
294                  * The user file is still unmodified, so nothing special at
295                  * all to do -- no lists updated, unless the sticky -k option
296                  * has changed.  If the sticky tag has changed, we just need
297                  * to re-register the entry
298                  */
299                 /* TODO: decide whether we need to check file permissions
300                    for a mismatch, and return T_CONFLICT if so. */
301                 if (vers->entdata->options &&
302                     strcmp (vers->entdata->options, vers->options) != 0)
303                     ret = T_CHECKOUT;
304                 else
305                 {
306                     sticky_ck (finfo, aflag, vers);
307                     ret = T_UPTODATE;
308                 }
309             }
310             else if (No_Difference (finfo, vers))
311             {
312
313                 /*
314                  * they really are different; modified if we aren't
315                  * changing any sticky -k options, else needs merge
316                  */
317 #ifdef XXX_FIXME_WHEN_RCSMERGE_IS_FIXED
318                 if (strcmp (vers->entdata->options ?
319                        vers->entdata->options : "", vers->options) == 0)
320                     ret = T_MODIFIED;
321                 else
322                     ret = T_NEEDS_MERGE;
323 #else
324                 ret = T_MODIFIED;
325                 sticky_ck (finfo, aflag, vers);
326 #endif
327             }
328             else if (strcmp (vers->entdata->options ?
329                        vers->entdata->options : "", vers->options) != 0)
330             {
331                 /* file has not changed; check out if -k changed */
332                 ret = T_CHECKOUT;
333             }
334             else
335             {
336
337                 /*
338                  * else -> note that No_Difference will Register the
339                  * file already for us, using the new tag/date. This
340                  * is the desired behaviour
341                  */
342                 ret = T_UPTODATE;
343             }
344         }
345         else
346         {
347             /* The RCS file is a newer version than the user file */
348
349             if (vers->ts_user == NULL)
350             {
351                 /* There is no user file, so just get it */
352
353                 /* See comment at other "update" compare, for more
354                    thoughts on this comparison.  */
355                 if (strcmp (cvs_cmd_name, "update") == 0)
356                     if (!really_quiet)
357                         error (0, 0, "warning: `%s' was lost", finfo->fullname);
358                 ret = T_CHECKOUT;
359             }
360             else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
361             {
362
363                 /*
364                  * The user file is still unmodified, so just get it as well
365                  */
366                 if (strcmp (vers->entdata->options ?
367                             vers->entdata->options : "", vers->options) != 0
368                     || (vers->srcfile != NULL
369                         && (vers->srcfile->flags & INATTIC) != 0))
370                     ret = T_CHECKOUT;
371                 else
372                     ret = T_PATCH;
373             }
374             else if (No_Difference (finfo, vers))
375                 /* really modified, needs to merge */
376                 ret = T_NEEDS_MERGE;
377             else if ((strcmp (vers->entdata->options ?
378                               vers->entdata->options : "", vers->options)
379                       != 0)
380                      || (vers->srcfile != NULL
381                          && (vers->srcfile->flags & INATTIC) != 0))
382                 /* not really modified, check it out */
383                 ret = T_CHECKOUT;
384             else
385                 ret = T_PATCH;
386         }
387     }
388
389     /* free up the vers struct, or just return it */
390     if (versp != (Vers_TS **) NULL)
391         *versp = vers;
392     else
393         freevers_ts (&vers);
394
395     /* return the status of the file */
396     return (ret);
397 }
398
399 static void
400 sticky_ck (struct file_info *finfo, int aflag, Vers_TS *vers)
401 {
402     if (aflag || vers->tag || vers->date)
403     {
404         char *enttag = vers->entdata->tag;
405         char *entdate = vers->entdata->date;
406
407         if ((enttag && vers->tag && strcmp (enttag, vers->tag)) ||
408             ((enttag && !vers->tag) || (!enttag && vers->tag)) ||
409             (entdate && vers->date && strcmp (entdate, vers->date)) ||
410             ((entdate && !vers->date) || (!entdate && vers->date)))
411         {
412             Register (finfo->entries, finfo->file, vers->vn_user, vers->ts_rcs,
413                       vers->options, vers->tag, vers->date, vers->ts_conflict);
414
415 #ifdef SERVER_SUPPORT
416             if (server_active)
417             {
418                 /* We need to update the entries line on the client side.
419                    It is possible we will later update it again via
420                    server_updated or some such, but that is OK.  */
421                 server_update_entries
422                   (finfo->file, finfo->update_dir, finfo->repository,
423                    strcmp (vers->ts_rcs, vers->ts_user) == 0 ?
424                    SERVER_UPDATED : SERVER_MERGED);
425             }
426 #endif
427         }
428     }
429 }