xref: /openbsd-src/gnu/usr.bin/cvs/src/classify.c (revision a4afd6dad3fba28f80e70208181c06c482259988)
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 1.4 kit.
7  *
8  */
9 
10 #include "cvs.h"
11 
12 #ifdef SERVER_SUPPORT
13 static void sticky_ck PROTO((char *file, int aflag, Vers_TS * vers,
14 			     List * entries,
15 			     char *repository, char *update_dir));
16 #else
17 static void sticky_ck PROTO((char *file, int aflag, Vers_TS * vers, List * entries));
18 #endif
19 
20 /*
21  * Classify the state of a file
22  */
23 Ctype
24 Classify_File (finfo, tag, date, options, force_tag_match, aflag, versp,
25 	       pipeout)
26     struct file_info *finfo;
27     char *tag;
28     char *date;
29     char *options;
30     int force_tag_match;
31     int aflag;
32     Vers_TS **versp;
33     int pipeout;
34 {
35     Vers_TS *vers;
36     Ctype ret;
37 
38     /* get all kinds of good data about the file */
39     vers = Version_TS (finfo, options, tag, date,
40 		       force_tag_match, 0);
41 
42     if (vers->vn_user == NULL)
43     {
44 	/* No entry available, ts_rcs is invalid */
45 	if (vers->vn_rcs == NULL)
46 	{
47 	    /* there is no RCS file either */
48 	    if (vers->ts_user == NULL)
49 	    {
50 		/* there is no user file */
51 		if (!force_tag_match || !(vers->tag || vers->date))
52 		    if (!really_quiet)
53 			error (0, 0, "nothing known about %s", finfo->fullname);
54 		ret = T_UNKNOWN;
55 	    }
56 	    else
57 	    {
58 		/* there is a user file */
59 		if (!force_tag_match || !(vers->tag || vers->date))
60 		    if (!really_quiet)
61 			error (0, 0, "use `cvs add' to create an entry for %s",
62 			       finfo->fullname);
63 		ret = T_UNKNOWN;
64 	    }
65 	}
66 	else if (RCS_isdead (vers->srcfile, vers->vn_rcs))
67 	{
68 	    if (vers->ts_user == NULL)
69 		ret = T_UPTODATE;
70 	    else
71 	    {
72 		error (0, 0, "use `cvs add' to create an entry for %s",
73 		       finfo->fullname);
74 		ret = T_UNKNOWN;
75 	    }
76 	}
77 	else
78 	{
79 	    /* there is an rcs file */
80 
81 	    if (vers->ts_user == NULL)
82 	    {
83 		/* There is no user file; needs checkout */
84 		ret = T_CHECKOUT;
85 	    }
86 	    else
87 	    {
88 		if (pipeout)
89 		{
90 		    /*
91 		     * The user file doesn't necessarily have anything
92 		     * to do with this.
93 		     */
94 		    ret = T_CHECKOUT;
95 		}
96 		/*
97 		 * There is a user file; print a warning and add it to the
98 		 * conflict list, only if it is indeed different from what we
99 		 * plan to extract
100 		 */
101 		else if (No_Difference (finfo, vers))
102 		{
103 		    /* the files were different so it is a conflict */
104 		    if (!really_quiet)
105 			error (0, 0, "move away %s; it is in the way",
106 			       finfo->fullname);
107 		    ret = T_CONFLICT;
108 		}
109 		else
110 		    /* since there was no difference, still needs checkout */
111 		    ret = T_CHECKOUT;
112 	    }
113 	}
114     }
115     else if (strcmp (vers->vn_user, "0") == 0)
116     {
117 	/* An entry for a new-born file; ts_rcs is dummy */
118 
119 	if (vers->ts_user == NULL)
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", finfo->fullname);
127 	    ret = T_REMOVE_ENTRY;
128 	}
129 	else
130 	{
131 	    /* There is a user file */
132 
133 	    if (vers->vn_rcs == NULL)
134 		/* There is no RCS file, added file */
135 		ret = T_ADDED;
136 	    else if (RCS_isdead (vers->srcfile, vers->vn_rcs))
137 		/* we are resurrecting. */
138 		ret = T_ADDED;
139 	    else
140 	    {
141 		if (vers->srcfile->flags & INATTIC
142 		    && vers->srcfile->flags & VALID)
143 		{
144 		    /* This file has been added on some branch other than
145 		       the one we are looking at.  In the branch we are
146 		       looking at, the file was already valid.  */
147 		    if (!really_quiet)
148 			error (0, 0,
149 			       "\
150 conflict: %s has been added, but already exists",
151 			       finfo->fullname);
152 		}
153 		else
154 		{
155 		    /*
156 		     * There is an RCS file, so someone else must have checked
157 		     * one in behind our back; conflict
158 		     */
159 		    if (!really_quiet)
160 			error (0, 0,
161 			       "\
162 conflict: %s created independently by second party",
163 			       finfo->fullname);
164 		}
165 		ret = T_CONFLICT;
166 	    }
167 	}
168     }
169     else if (vers->vn_user[0] == '-')
170     {
171 	/* An entry for a removed file, ts_rcs is invalid */
172 
173 	if (vers->ts_user == NULL)
174 	{
175 	    char tmp[PATH_MAX];
176 
177 	    /* There is no user file (as it should be) */
178 
179 	    (void) sprintf (tmp, "-%s", vers->vn_rcs ? vers->vn_rcs : "");
180 
181 	    if (vers->vn_rcs == NULL
182 		|| RCS_isdead (vers->srcfile, vers->vn_rcs))
183 	    {
184 
185 		/*
186 		 * There is no RCS file; this is all-right, but it has been
187 		 * removed independently by a second party; remove the entry
188 		 */
189 		ret = T_REMOVE_ENTRY;
190 	    }
191 	    else if (strcmp (tmp, vers->vn_user) == 0)
192 
193 		/*
194 		 * The RCS file is the same version as the user file was, and
195 		 * that's OK; remove it
196 		 */
197 		ret = T_REMOVED;
198 	    else
199 	    {
200 
201 		/*
202 		 * The RCS file is a newer version than the removed user file
203 		 * and this is definitely not OK; make it a conflict.
204 		 */
205 		if (!really_quiet)
206 		    error (0, 0,
207 			   "conflict: removed %s was modified by second party",
208 			   finfo->fullname);
209 		ret = T_CONFLICT;
210 	    }
211 	}
212 	else
213 	{
214 	    /* The user file shouldn't be there */
215 	    if (!really_quiet)
216 		error (0, 0, "%s should be removed and is still there",
217 		       finfo->fullname);
218 	    ret = T_REMOVED;
219 	}
220     }
221     else
222     {
223 	/* A normal entry, TS_Rcs is valid */
224 	if (vers->vn_rcs == NULL)
225 	{
226 	    /* There is no RCS file */
227 
228 	    if (vers->ts_user == NULL)
229 	    {
230 		/* There is no user file, so just remove the entry */
231 		if (!really_quiet)
232 		    error (0, 0, "warning: %s is not (any longer) pertinent",
233 			   finfo->fullname);
234 		ret = T_REMOVE_ENTRY;
235 	    }
236 	    else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
237 	    {
238 
239 		/*
240 		 * The user file is still unmodified, so just remove it from
241 		 * the entry list
242 		 */
243 		if (!really_quiet)
244 		    error (0, 0, "%s is no longer in the repository",
245 			   finfo->fullname);
246 		ret = T_REMOVE_ENTRY;
247 	    }
248 	    else
249 	    {
250 		/*
251 		 * The user file has been modified and since it is no longer
252 		 * in the repository, a conflict is raised
253 		 */
254 		if (No_Difference (finfo, vers))
255 		{
256 		    /* they are different -> conflict */
257 		    if (!really_quiet)
258 			error (0, 0,
259 	       "conflict: %s is modified but no longer in the repository",
260 			   finfo->fullname);
261 		    ret = T_CONFLICT;
262 		}
263 		else
264 		{
265 		    /* they weren't really different */
266 		    if (!really_quiet)
267 			error (0, 0,
268 			       "warning: %s is not (any longer) pertinent",
269 			       finfo->fullname);
270 		    ret = T_REMOVE_ENTRY;
271 		}
272 	    }
273 	}
274 	else if (strcmp (vers->vn_rcs, vers->vn_user) == 0)
275 	{
276 	    /* The RCS file is the same version as the user file */
277 
278 	    if (vers->ts_user == NULL)
279 	    {
280 
281 		/*
282 		 * There is no user file, so note that it was lost and
283 		 * extract a new version
284 		 */
285 		if (strcmp (command_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 		if (vers->entdata->options &&
300 		    strcmp (vers->entdata->options, vers->options) != 0)
301 		    ret = T_CHECKOUT;
302 		else
303 		{
304 #ifdef SERVER_SUPPORT
305 		    sticky_ck (finfo->file, aflag, vers, finfo->entries,
306 			       finfo->repository, finfo->update_dir);
307 #else
308 		    sticky_ck (finfo->file, aflag, vers, finfo->entries);
309 #endif
310 		    ret = T_UPTODATE;
311 		}
312 	    }
313 	    else
314 	    {
315 
316 		/*
317 		 * The user file appears to have been modified, but we call
318 		 * No_Difference to verify that it really has been modified
319 		 */
320 		if (No_Difference (finfo, vers))
321 		{
322 
323 		    /*
324 		     * they really are different; modified if we aren't
325 		     * changing any sticky -k options, else needs merge
326 		     */
327 #ifdef XXX_FIXME_WHEN_RCSMERGE_IS_FIXED
328 		    if (strcmp (vers->entdata->options ?
329 			   vers->entdata->options : "", vers->options) == 0)
330 			ret = T_MODIFIED;
331 		    else
332 			ret = T_NEEDS_MERGE;
333 #else
334 		    ret = T_MODIFIED;
335 #ifdef SERVER_SUPPORT
336 		    sticky_ck (finfo->file, aflag, vers, finfo->entries,
337 			       finfo->repository, finfo->update_dir);
338 #else
339 		    sticky_ck (finfo->file, aflag, vers, finfo->entries);
340 #endif /* SERVER_SUPPORT */
341 #endif
342 		}
343 		else
344 		{
345 		    /* file has not changed; check out if -k changed */
346 		    if (strcmp (vers->entdata->options ?
347 			   vers->entdata->options : "", vers->options) != 0)
348 		    {
349 			ret = T_CHECKOUT;
350 		    }
351 		    else
352 		    {
353 
354 			/*
355 			 * else -> note that No_Difference will Register the
356 			 * file already for us, using the new tag/date. This
357 			 * is the desired behaviour
358 			 */
359 			ret = T_UPTODATE;
360 		    }
361 		}
362 	    }
363 	}
364 	else
365 	{
366 	    /* The RCS file is a newer version than the user file */
367 
368 	    if (vers->ts_user == NULL)
369 	    {
370 		/* There is no user file, so just get it */
371 
372 		if (strcmp (command_name, "update") == 0)
373 		    if (!really_quiet)
374 			error (0, 0, "warning: %s was lost", finfo->fullname);
375 		ret = T_CHECKOUT;
376 	    }
377 	    else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
378 	    {
379 
380 		/*
381 		 * The user file is still unmodified, so just get it as well
382 		 */
383 #ifdef SERVER_SUPPORT
384 	        if (strcmp (vers->entdata->options ?
385 			    vers->entdata->options : "", vers->options) != 0
386 		    || (vers->srcfile != NULL
387 			&& (vers->srcfile->flags & INATTIC) != 0))
388 		    ret = T_CHECKOUT;
389 		else
390 		    ret = T_PATCH;
391 #else
392 		ret = T_CHECKOUT;
393 #endif
394 	    }
395 	    else
396 	    {
397 		if (No_Difference (finfo, vers))
398 		    /* really modified, needs to merge */
399 		    ret = T_NEEDS_MERGE;
400 #ifdef SERVER_SUPPORT
401 	        else if ((strcmp (vers->entdata->options ?
402 				  vers->entdata->options : "", vers->options)
403 			  != 0)
404 			 || (vers->srcfile != NULL
405 			     && (vers->srcfile->flags & INATTIC) != 0))
406 		    /* not really modified, check it out */
407 		    ret = T_CHECKOUT;
408 		else
409 		    ret = T_PATCH;
410 #else
411 		else
412 		    /* not really modified, check it out */
413 		    ret = T_CHECKOUT;
414 #endif
415 	    }
416 	}
417     }
418 
419     /* free up the vers struct, or just return it */
420     if (versp != (Vers_TS **) NULL)
421 	*versp = vers;
422     else
423 	freevers_ts (&vers);
424 
425     /* return the status of the file */
426     return (ret);
427 }
428 
429 static void
430 #ifdef SERVER_SUPPORT
431 sticky_ck (file, aflag, vers, entries, repository, update_dir)
432 #else
433 sticky_ck (file, aflag, vers, entries)
434 #endif
435     char *file;
436     int aflag;
437     Vers_TS *vers;
438     List *entries;
439 #ifdef SERVER_SUPPORT
440     char *repository;
441     char *update_dir;
442 #endif
443 {
444     if (aflag || vers->tag || vers->date)
445     {
446 	char *enttag = vers->entdata->tag;
447 	char *entdate = vers->entdata->date;
448 
449 	if ((enttag && vers->tag && strcmp (enttag, vers->tag)) ||
450 	    ((enttag && !vers->tag) || (!enttag && vers->tag)) ||
451 	    (entdate && vers->date && strcmp (entdate, vers->date)) ||
452 	    ((entdate && !vers->date) || (!entdate && vers->date)))
453 	{
454 	    Register (entries, file, vers->vn_user, vers->ts_rcs,
455 		      vers->options, vers->tag, vers->date, vers->ts_conflict);
456 
457 #ifdef SERVER_SUPPORT
458 	    if (server_active)
459 	    {
460 		/* We need to update the entries line on the client side.
461 		   It is possible we will later update it again via
462 		   server_updated or some such, but that is OK.  */
463 		server_update_entries
464 		  (file, update_dir, repository,
465 		   strcmp (vers->ts_rcs, vers->ts_user) == 0 ?
466 		   SERVER_UPDATED : SERVER_MERGED);
467 	    }
468 #endif
469 	}
470     }
471 }
472