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