PageRenderTime 45ms CodeModel.GetById 15ms app.highlight 25ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/cvs/src/classify.c

https://bitbucket.org/freebsd/freebsd-head/
C | 474 lines | 304 code | 35 blank | 135 comment | 135 complexity | 9d42fd1d0edb83629ce8710a82fca9c5 MD5 | raw file
  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
 20static void sticky_ck PROTO ((struct file_info *finfo, int aflag,
 21			      Vers_TS * vers));
 22
 23
 24
 25static inline int keywords_may_change PROTO ((int aflag, Vers_TS * vers));
 26static inline int
 27keywords_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 */
 73Ctype
 74Classify_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
441static void
442sticky_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}