File: | src/gnu/usr.bin/cvs/src/tag.c |
Warning: | line 1003, column 9 Attempt to free released memory |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | * Tag and Rtag | |||
9 | * | |||
10 | * Add or delete a symbolic name to an RCS file, or a collection of RCS files. | |||
11 | * Tag uses the checked out revision in the current directory, rtag uses | |||
12 | * the modules database, if necessary. | |||
13 | */ | |||
14 | ||||
15 | #include "cvs.h" | |||
16 | #include "savecwd.h" | |||
17 | ||||
18 | static int rtag_proc PROTO((int argc, char **argv, char *xwhere,(int argc, char **argv, char *xwhere, char *mwhere, char *mfile , int shorten, int local_specified, char *mname, char *msg) | |||
19 | char *mwhere, char *mfile, int shorten,(int argc, char **argv, char *xwhere, char *mwhere, char *mfile , int shorten, int local_specified, char *mname, char *msg) | |||
20 | int local_specified, char *mname, char *msg))(int argc, char **argv, char *xwhere, char *mwhere, char *mfile , int shorten, int local_specified, char *mname, char *msg); | |||
21 | static int check_fileproc PROTO ((void *callerdat, struct file_info *finfo))(void *callerdat, struct file_info *finfo); | |||
22 | static int check_filesdoneproc PROTO ((void *callerdat, int err,(void *callerdat, int err, char *repos, char *update_dir, List *entries) | |||
23 | char *repos, char *update_dir,(void *callerdat, int err, char *repos, char *update_dir, List *entries) | |||
24 | List *entries))(void *callerdat, int err, char *repos, char *update_dir, List *entries); | |||
25 | static int pretag_proc PROTO((char *repository, char *filter))(char *repository, char *filter); | |||
26 | static void masterlist_delproc PROTO((Node *p))(Node *p); | |||
27 | static void tag_delproc PROTO((Node *p))(Node *p); | |||
28 | static int pretag_list_proc PROTO((Node *p, void *closure))(Node *p, void *closure); | |||
29 | ||||
30 | static Dtype tag_dirproc PROTO ((void *callerdat, char *dir,(void *callerdat, char *dir, char *repos, char *update_dir, List *entries) | |||
31 | char *repos, char *update_dir,(void *callerdat, char *dir, char *repos, char *update_dir, List *entries) | |||
32 | List *entries))(void *callerdat, char *dir, char *repos, char *update_dir, List *entries); | |||
33 | static int rtag_fileproc PROTO ((void *callerdat, struct file_info *finfo))(void *callerdat, struct file_info *finfo); | |||
34 | static int rtag_delete PROTO((RCSNode *rcsfile))(RCSNode *rcsfile); | |||
35 | static int tag_fileproc PROTO ((void *callerdat, struct file_info *finfo))(void *callerdat, struct file_info *finfo); | |||
36 | ||||
37 | static char *numtag; /* specific revision to tag */ | |||
38 | static int numtag_validated = 0; | |||
39 | static char *date = NULL((void*)0); | |||
40 | static char *symtag; /* tag to add or delete */ | |||
41 | static int delete_flag; /* adding a tag by default */ | |||
42 | static int branch_mode; /* make an automagic "branch" tag */ | |||
43 | static int local; /* recursive by default */ | |||
44 | static int force_tag_match = 1; /* force tag to match by default */ | |||
45 | static int force_tag_move; /* don't force tag to move by default */ | |||
46 | static int check_uptodate; /* no uptodate-check by default */ | |||
47 | static int attic_too; /* remove tag from Attic files */ | |||
48 | static int is_rtag; | |||
49 | ||||
50 | struct tag_info | |||
51 | { | |||
52 | Ctype status; | |||
53 | char *rev; | |||
54 | char *tag; | |||
55 | char *options; | |||
56 | }; | |||
57 | ||||
58 | struct master_lists | |||
59 | { | |||
60 | List *tlist; | |||
61 | }; | |||
62 | ||||
63 | static List *mtlist; | |||
64 | static List *tlist; | |||
65 | ||||
66 | static const char rtag_opts[] = "+abdFflnQqRr:D:"; | |||
67 | static const char *const rtag_usage[] = | |||
68 | { | |||
69 | "Usage: %s %s [-abdFflnR] [-r rev|-D date] tag modules...\n", | |||
70 | "\t-a\tClear tag from removed files that would not otherwise be tagged.\n", | |||
71 | "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n", | |||
72 | "\t-d\tDelete the given tag.\n", | |||
73 | "\t-F\tMove tag if it already exists.\n", | |||
74 | "\t-f\tForce a head revision match if tag/date not found.\n", | |||
75 | "\t-l\tLocal directory only, not recursive.\n", | |||
76 | "\t-n\tNo execution of 'tag program'.\n", | |||
77 | "\t-R\tProcess directories recursively.\n", | |||
78 | "\t-r rev\tExisting revision/tag.\n", | |||
79 | "\t-D\tExisting date.\n", | |||
80 | "(Specify the --help global option for a list of other help options)\n", | |||
81 | NULL((void*)0) | |||
82 | }; | |||
83 | ||||
84 | static const char tag_opts[] = "+bcdFflQqRr:D:"; | |||
85 | static const char *const tag_usage[] = | |||
86 | { | |||
87 | "Usage: %s %s [-bcdFflR] [-r rev|-D date] tag [files...]\n", | |||
88 | "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n", | |||
89 | "\t-c\tCheck that working files are unmodified.\n", | |||
90 | "\t-d\tDelete the given tag.\n", | |||
91 | "\t-F\tMove tag if it already exists.\n", | |||
92 | "\t-f\tForce a head revision match if tag/date not found.\n", | |||
93 | "\t-l\tLocal directory only, not recursive.\n", | |||
94 | "\t-R\tProcess directories recursively.\n", | |||
95 | "\t-r rev\tExisting revision/tag.\n", | |||
96 | "\t-D\tExisting date.\n", | |||
97 | "(Specify the --help global option for a list of other help options)\n", | |||
98 | NULL((void*)0) | |||
99 | }; | |||
100 | ||||
101 | int | |||
102 | cvstag (argc, argv) | |||
103 | int argc; | |||
104 | char **argv; | |||
105 | { | |||
106 | int c; | |||
107 | int err = 0; | |||
108 | int run_module_prog = 1; | |||
109 | ||||
110 | is_rtag = (strcmp (command_name, "rtag") == 0); | |||
111 | ||||
112 | if (argc == -1) | |||
113 | usage (is_rtag ? rtag_usage : tag_usage); | |||
114 | ||||
115 | optind = 0; | |||
116 | while ((c = getopt (argc, argv, is_rtag ? rtag_opts : tag_opts)) != -1) | |||
117 | { | |||
118 | switch (c) | |||
119 | { | |||
120 | case 'a': | |||
121 | attic_too = 1; | |||
122 | break; | |||
123 | case 'b': | |||
124 | branch_mode = 1; | |||
125 | break; | |||
126 | case 'c': | |||
127 | check_uptodate = 1; | |||
128 | break; | |||
129 | case 'd': | |||
130 | delete_flag = 1; | |||
131 | break; | |||
132 | case 'F': | |||
133 | force_tag_move = 1; | |||
134 | break; | |||
135 | case 'f': | |||
136 | force_tag_match = 0; | |||
137 | break; | |||
138 | case 'l': | |||
139 | local = 1; | |||
140 | break; | |||
141 | case 'n': | |||
142 | run_module_prog = 0; | |||
143 | break; | |||
144 | case 'Q': | |||
145 | case 'q': | |||
146 | #ifdef SERVER_SUPPORT1 | |||
147 | /* The CVS 1.5 client sends these options (in addition to | |||
148 | Global_option requests), so we must ignore them. */ | |||
149 | if (!server_active) | |||
150 | #endif | |||
151 | error (1, 0, | |||
152 | "-q or -Q must be specified before \"%s\"", | |||
153 | command_name); | |||
154 | break; | |||
155 | case 'R': | |||
156 | local = 0; | |||
157 | break; | |||
158 | case 'r': | |||
159 | numtag = optarg; | |||
160 | break; | |||
161 | case 'D': | |||
162 | if (date) | |||
163 | free (date); | |||
164 | date = Make_Date (optarg); | |||
165 | break; | |||
166 | case '?': | |||
167 | default: | |||
168 | usage (is_rtag ? rtag_usage : tag_usage); | |||
169 | break; | |||
170 | } | |||
171 | } | |||
172 | argc -= optind; | |||
173 | argv += optind; | |||
174 | ||||
175 | if (argc < (is_rtag ? 2 : 1)) | |||
176 | usage (is_rtag ? rtag_usage : tag_usage); | |||
177 | symtag = argv[0]; | |||
178 | argc--; | |||
179 | argv++; | |||
180 | ||||
181 | if (date && numtag) | |||
182 | error (1, 0, "-r and -D options are mutually exclusive"); | |||
183 | if (delete_flag && branch_mode) | |||
184 | error (0, 0, "warning: -b ignored with -d options"); | |||
185 | RCS_check_tag (symtag); | |||
186 | ||||
187 | #ifdef CLIENT_SUPPORT1 | |||
188 | if (current_parsed_root->isremote) | |||
189 | { | |||
190 | /* We're the client side. Fire up the remote server. */ | |||
191 | start_server (); | |||
192 | ||||
193 | ign_setup (); | |||
194 | ||||
195 | if (attic_too) | |||
196 | send_arg("-a"); | |||
197 | if (branch_mode) | |||
198 | send_arg("-b"); | |||
199 | if (check_uptodate) | |||
200 | send_arg("-c"); | |||
201 | if (delete_flag) | |||
202 | send_arg("-d"); | |||
203 | if (force_tag_move) | |||
204 | send_arg("-F"); | |||
205 | if (!force_tag_match) | |||
206 | send_arg ("-f"); | |||
207 | if (local) | |||
208 | send_arg("-l"); | |||
209 | if (!run_module_prog) | |||
210 | send_arg("-n"); | |||
211 | ||||
212 | if (numtag) | |||
213 | option_with_arg ("-r", numtag); | |||
214 | if (date) | |||
215 | client_senddate (date); | |||
216 | ||||
217 | send_arg (symtag); | |||
218 | ||||
219 | if (is_rtag) | |||
220 | { | |||
221 | int i; | |||
222 | for (i = 0; i < argc; ++i) | |||
223 | send_arg (argv[i]); | |||
224 | send_to_server ("rtag\012", 0); | |||
225 | } | |||
226 | else | |||
227 | { | |||
228 | ||||
229 | send_files (argc, argv, local, 0, | |||
230 | ||||
231 | /* I think the -c case is like "cvs status", in | |||
232 | which we really better be correct rather than | |||
233 | being fast; it is just too confusing otherwise. */ | |||
234 | check_uptodate ? 0 : SEND_NO_CONTENTS4); | |||
235 | send_file_names (argc, argv, SEND_EXPAND_WILD1); | |||
236 | send_to_server ("tag\012", 0); | |||
237 | } | |||
238 | ||||
239 | return get_responses_and_close (); | |||
240 | } | |||
241 | #endif | |||
242 | ||||
243 | if (is_rtag) | |||
244 | { | |||
245 | DBM *db; | |||
246 | int i; | |||
247 | db = open_module (); | |||
248 | for (i = 0; i < argc; i++) | |||
249 | { | |||
250 | /* XXX last arg should be repository, but doesn't make sense here */ | |||
251 | history_write ('T', (delete_flag ? "D" : (numtag ? numtag : | |||
252 | (date ? date : "A"))), symtag, argv[i], ""); | |||
253 | err += do_module (db, argv[i], TAG, | |||
254 | delete_flag ? "Untagging" : "Tagging", | |||
255 | rtag_proc, (char *) NULL((void*)0), 0, 0, run_module_prog, | |||
256 | 0, symtag); | |||
257 | } | |||
258 | close_module (db); | |||
259 | } | |||
260 | else | |||
261 | { | |||
262 | err = rtag_proc (argc + 1, argv - 1, NULL((void*)0), NULL((void*)0), NULL((void*)0), 0, 0, NULL((void*)0), | |||
263 | NULL((void*)0)); | |||
264 | } | |||
265 | ||||
266 | return (err); | |||
267 | } | |||
268 | ||||
269 | /* | |||
270 | * callback proc for doing the real work of tagging | |||
271 | */ | |||
272 | /* ARGSUSED */ | |||
273 | static int | |||
274 | rtag_proc (argc, argv, xwhere, mwhere, mfile, shorten, local_specified, | |||
275 | mname, msg) | |||
276 | int argc; | |||
277 | char **argv; | |||
278 | char *xwhere; | |||
279 | char *mwhere; | |||
280 | char *mfile; | |||
281 | int shorten; | |||
282 | int local_specified; | |||
283 | char *mname; | |||
284 | char *msg; | |||
285 | { | |||
286 | /* Begin section which is identical to patch_proc--should this | |||
287 | be abstracted out somehow? */ | |||
288 | char *myargv[2]; | |||
289 | int err = 0; | |||
290 | int which; | |||
291 | char *repository; | |||
292 | char *where; | |||
293 | ||||
294 | if (is_rtag) | |||
295 | { | |||
296 | repository = xmalloc (strlen (current_parsed_root->directory) + strlen (argv[0]) | |||
297 | + (mfile == NULL((void*)0) ? 0 : strlen (mfile) + 1) + 2); | |||
298 | (void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]); | |||
299 | where = xmalloc (strlen (argv[0]) + (mfile == NULL((void*)0) ? 0 : strlen (mfile) + 1) | |||
300 | + 1); | |||
301 | (void) strcpy (where, argv[0]); | |||
302 | ||||
303 | /* if mfile isn't null, we need to set up to do only part of the module */ | |||
304 | if (mfile != NULL((void*)0)) | |||
305 | { | |||
306 | char *cp; | |||
307 | char *path; | |||
308 | ||||
309 | /* if the portion of the module is a path, put the dir part on repos */ | |||
310 | if ((cp = strrchr (mfile, '/')) != NULL((void*)0)) | |||
311 | { | |||
312 | *cp = '\0'; | |||
313 | (void) strcat (repository, "/"); | |||
314 | (void) strcat (repository, mfile); | |||
315 | (void) strcat (where, "/"); | |||
316 | (void) strcat (where, mfile); | |||
317 | mfile = cp + 1; | |||
318 | } | |||
319 | ||||
320 | /* take care of the rest */ | |||
321 | path = xmalloc (strlen (repository) + strlen (mfile) + 5); | |||
322 | (void) sprintf (path, "%s/%s", repository, mfile); | |||
323 | if (isdir (path)) | |||
324 | { | |||
325 | /* directory means repository gets the dir tacked on */ | |||
326 | (void) strcpy (repository, path); | |||
327 | (void) strcat (where, "/"); | |||
328 | (void) strcat (where, mfile); | |||
329 | } | |||
330 | else | |||
331 | { | |||
332 | myargv[0] = argv[0]; | |||
333 | myargv[1] = mfile; | |||
334 | argc = 2; | |||
335 | argv = myargv; | |||
336 | } | |||
337 | free (path); | |||
338 | } | |||
339 | ||||
340 | /* cd to the starting repository */ | |||
341 | if ( CVS_CHDIRchdir (repository) < 0) | |||
342 | { | |||
343 | error (0, errno(*__errno()), "cannot chdir to %s", repository); | |||
344 | free (repository); | |||
345 | return (1); | |||
346 | } | |||
347 | free (repository); | |||
348 | /* End section which is identical to patch_proc. */ | |||
349 | ||||
350 | if (delete_flag || attic_too || (force_tag_match && numtag)) | |||
351 | which = W_REPOS0x02 | W_ATTIC0x04; | |||
352 | else | |||
353 | which = W_REPOS0x02; | |||
354 | repository = NULL((void*)0); | |||
355 | } | |||
356 | else | |||
357 | { | |||
358 | where = NULL((void*)0); | |||
359 | which = W_LOCAL0x01; | |||
360 | repository = ""; | |||
361 | } | |||
362 | ||||
363 | if (numtag != NULL((void*)0) && !numtag_validated) | |||
364 | { | |||
365 | tag_check_valid (numtag, argc - 1, argv + 1, local, 0, repository); | |||
366 | numtag_validated = 1; | |||
367 | } | |||
368 | ||||
369 | /* check to make sure they are authorized to tag all the | |||
370 | specified files in the repository */ | |||
371 | ||||
372 | mtlist = getlist(); | |||
373 | err = start_recursion (check_fileproc, check_filesdoneproc, | |||
374 | (DIRENTPROC) NULL((void*)0), (DIRLEAVEPROC) NULL((void*)0), NULL((void*)0), | |||
375 | argc - 1, argv + 1, local, which, 0, 1, | |||
376 | where, 1); | |||
377 | ||||
378 | if (err) | |||
379 | { | |||
380 | error (1, 0, "correct the above errors first!"); | |||
381 | } | |||
382 | ||||
383 | /* It would be nice to provide consistency with respect to | |||
384 | commits; however CVS lacks the infrastructure to do that (see | |||
385 | Concurrency in cvs.texinfo and comment in do_recursion). We | |||
386 | do need to ensure that the RCS file info that gets read and | |||
387 | cached in do_recursion isn't stale by the time we get around | |||
388 | to using it to rewrite the RCS file in the callback, and this | |||
389 | is the easiest way to accomplish that. */ | |||
390 | lock_tree_for_write (argc - 1, argv + 1, local, which, 0); | |||
391 | ||||
392 | /* start the recursion processor */ | |||
393 | err = start_recursion (is_rtag ? rtag_fileproc : tag_fileproc, | |||
394 | (FILESDONEPROC) NULL((void*)0), tag_dirproc, | |||
395 | (DIRLEAVEPROC) NULL((void*)0), NULL((void*)0), argc - 1, argv + 1, | |||
396 | local, which, 0, 0, where, 1); | |||
397 | Lock_Cleanup (); | |||
398 | dellist (&mtlist); | |||
399 | if (where != NULL((void*)0)) | |||
400 | free (where); | |||
401 | return (err); | |||
402 | } | |||
403 | ||||
404 | /* check file that is to be tagged */ | |||
405 | /* All we do here is add it to our list */ | |||
406 | ||||
407 | static int | |||
408 | check_fileproc (callerdat, finfo) | |||
409 | void *callerdat; | |||
410 | struct file_info *finfo; | |||
411 | { | |||
412 | char *xdir; | |||
413 | Node *p; | |||
414 | Vers_TS *vers; | |||
415 | ||||
416 | if (check_uptodate) | |||
417 | { | |||
418 | Ctype status = Classify_File (finfo, (char *) NULL((void*)0), (char *) NULL((void*)0), | |||
419 | (char *) NULL((void*)0), 1, 0, &vers, 0); | |||
420 | if ((status != T_UPTODATE) && (status != T_CHECKOUT) && | |||
421 | (status != T_PATCH)) | |||
422 | { | |||
423 | error (0, 0, "%s is locally modified", finfo->fullname); | |||
424 | freevers_ts (&vers); | |||
425 | return (1); | |||
426 | } | |||
427 | } | |||
428 | else | |||
429 | vers = Version_TS (finfo, NULL((void*)0), NULL((void*)0), NULL((void*)0), 0, 0); | |||
430 | ||||
431 | if (finfo->update_dir[0] == '\0') | |||
432 | xdir = "."; | |||
433 | else | |||
434 | xdir = finfo->update_dir; | |||
435 | if ((p = findnode (mtlist, xdir)) != NULL((void*)0)) | |||
436 | { | |||
437 | tlist = ((struct master_lists *) p->data)->tlist; | |||
438 | } | |||
439 | else | |||
440 | { | |||
441 | struct master_lists *ml; | |||
442 | ||||
443 | tlist = getlist (); | |||
444 | p = getnode (); | |||
445 | p->key = xstrdup (xdir); | |||
446 | p->type = UPDATE; | |||
447 | ml = (struct master_lists *) | |||
448 | xmalloc (sizeof (struct master_lists)); | |||
449 | ml->tlist = tlist; | |||
450 | p->data = (char *) ml; | |||
451 | p->delproc = masterlist_delproc; | |||
452 | (void) addnode (mtlist, p); | |||
453 | } | |||
454 | /* do tlist */ | |||
455 | p = getnode (); | |||
456 | p->key = xstrdup (finfo->file); | |||
457 | p->type = UPDATE; | |||
458 | p->delproc = tag_delproc; | |||
459 | if (vers->srcfile == NULL((void*)0)) | |||
460 | { | |||
461 | if (!really_quiet) | |||
462 | error (0, 0, "nothing known about %s", finfo->file); | |||
463 | freevers_ts (&vers); | |||
464 | freenode (p); | |||
465 | return (1); | |||
466 | } | |||
467 | ||||
468 | /* Here we duplicate the calculation in tag_fileproc about which | |||
469 | version we are going to tag. There probably are some subtle races | |||
470 | (e.g. numtag is "foo" which gets moved between here and | |||
471 | tag_fileproc). */ | |||
472 | if (!is_rtag && numtag == NULL((void*)0) && date == NULL((void*)0)) | |||
473 | p->data = xstrdup (vers->vn_user); | |||
474 | else | |||
475 | p->data = RCS_getversion (vers->srcfile, numtag, date, | |||
476 | force_tag_match, NULL((void*)0)); | |||
477 | ||||
478 | if (p->data != NULL((void*)0)) | |||
479 | { | |||
480 | int addit = 1; | |||
481 | char *oversion; | |||
482 | ||||
483 | oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL((void*)0), 1, | |||
484 | (int *) NULL((void*)0)); | |||
485 | if (oversion == NULL((void*)0)) | |||
486 | { | |||
487 | if (delete_flag) | |||
488 | { | |||
489 | /* Deleting a tag which did not exist is a noop and | |||
490 | should not be logged. */ | |||
491 | addit = 0; | |||
492 | } | |||
493 | } | |||
494 | else if (delete_flag) | |||
495 | { | |||
496 | free (p->data); | |||
497 | p->data = xstrdup (oversion); | |||
498 | } | |||
499 | else if (strcmp(oversion, p->data) == 0) | |||
500 | { | |||
501 | addit = 0; | |||
502 | } | |||
503 | else if (!force_tag_move) | |||
504 | { | |||
505 | addit = 0; | |||
506 | } | |||
507 | if (oversion != NULL((void*)0)) | |||
508 | { | |||
509 | free(oversion); | |||
510 | } | |||
511 | if (!addit) | |||
512 | { | |||
513 | free(p->data); | |||
514 | p->data = NULL((void*)0); | |||
515 | } | |||
516 | } | |||
517 | freevers_ts (&vers); | |||
518 | (void) addnode (tlist, p); | |||
519 | return (0); | |||
520 | } | |||
521 | ||||
522 | static int | |||
523 | check_filesdoneproc (callerdat, err, repos, update_dir, entries) | |||
524 | void *callerdat; | |||
525 | int err; | |||
526 | char *repos; | |||
527 | char *update_dir; | |||
528 | List *entries; | |||
529 | { | |||
530 | int n; | |||
531 | Node *p; | |||
532 | ||||
533 | p = findnode(mtlist, update_dir); | |||
534 | if (p != NULL((void*)0)) | |||
535 | { | |||
536 | tlist = ((struct master_lists *) p->data)->tlist; | |||
537 | } | |||
538 | else | |||
539 | { | |||
540 | tlist = (List *) NULL((void*)0); | |||
541 | } | |||
542 | if ((tlist == NULL((void*)0)) || (tlist->list->next == tlist->list)) | |||
543 | { | |||
544 | return (err); | |||
545 | } | |||
546 | if ((n = Parse_Info(CVSROOTADM_TAGINFO"taginfo", repos, pretag_proc, 1)) > 0) | |||
547 | { | |||
548 | error (0, 0, "Pre-tag check failed"); | |||
549 | err += n; | |||
550 | } | |||
551 | return (err); | |||
552 | } | |||
553 | ||||
554 | static int | |||
555 | pretag_proc(repository, filter) | |||
556 | char *repository; | |||
557 | char *filter; | |||
558 | { | |||
559 | if (filter[0] == '/') | |||
560 | { | |||
561 | char *s, *cp; | |||
562 | ||||
563 | s = xstrdup(filter); | |||
564 | for (cp=s; *cp; cp++) | |||
565 | { | |||
566 | if (isspace ((unsigned char) *cp)) | |||
567 | { | |||
568 | *cp = '\0'; | |||
569 | break; | |||
570 | } | |||
571 | } | |||
572 | if (!isfile(s)) | |||
573 | { | |||
574 | error (0, errno(*__errno()), "cannot find pre-tag filter '%s'", s); | |||
575 | free(s); | |||
576 | return (1); | |||
577 | } | |||
578 | free(s); | |||
579 | } | |||
580 | run_setup (filter); | |||
581 | run_arg (symtag); | |||
582 | run_arg (delete_flag ? "del" : force_tag_move ? "mov" : "add"); | |||
583 | run_arg (repository); | |||
584 | walklist(tlist, pretag_list_proc, NULL((void*)0)); | |||
585 | return (run_exec (RUN_TTY(char *)0, RUN_TTY(char *)0, RUN_TTY(char *)0, RUN_NORMAL0x0000)); | |||
586 | } | |||
587 | ||||
588 | static void | |||
589 | masterlist_delproc(p) | |||
590 | Node *p; | |||
591 | { | |||
592 | struct master_lists *ml; | |||
593 | ||||
594 | ml = (struct master_lists *)p->data; | |||
595 | dellist(&ml->tlist); | |||
596 | free(ml); | |||
597 | return; | |||
598 | } | |||
599 | ||||
600 | static void | |||
601 | tag_delproc(p) | |||
602 | Node *p; | |||
603 | { | |||
604 | if (p->data != NULL((void*)0)) | |||
605 | { | |||
606 | free(p->data); | |||
607 | p->data = NULL((void*)0); | |||
608 | } | |||
609 | return; | |||
610 | } | |||
611 | ||||
612 | static int | |||
613 | pretag_list_proc(p, closure) | |||
614 | Node *p; | |||
615 | void *closure; | |||
616 | { | |||
617 | if (p->data != NULL((void*)0)) | |||
618 | { | |||
619 | run_arg(p->key); | |||
620 | run_arg(p->data); | |||
621 | } | |||
622 | return (0); | |||
623 | } | |||
624 | ||||
625 | ||||
626 | /* | |||
627 | * Called to rtag a particular file, as appropriate with the options that were | |||
628 | * set above. | |||
629 | */ | |||
630 | /* ARGSUSED */ | |||
631 | static int | |||
632 | rtag_fileproc (callerdat, finfo) | |||
633 | void *callerdat; | |||
634 | struct file_info *finfo; | |||
635 | { | |||
636 | RCSNode *rcsfile; | |||
637 | char *version, *rev; | |||
638 | int retcode = 0; | |||
639 | ||||
640 | /* find the parsed RCS data */ | |||
641 | if ((rcsfile = finfo->rcs) == NULL((void*)0)) | |||
642 | return (1); | |||
643 | ||||
644 | /* | |||
645 | * For tagging an RCS file which is a symbolic link, you'd best be | |||
646 | * running with RCS 5.6, since it knows how to handle symbolic links | |||
647 | * correctly without breaking your link! | |||
648 | */ | |||
649 | ||||
650 | if (delete_flag) | |||
651 | return (rtag_delete (rcsfile)); | |||
652 | ||||
653 | /* | |||
654 | * If we get here, we are adding a tag. But, if -a was specified, we | |||
655 | * need to check to see if a -r or -D option was specified. If neither | |||
656 | * was specified and the file is in the Attic, remove the tag. | |||
657 | */ | |||
658 | if (attic_too && (!numtag && !date)) | |||
659 | { | |||
660 | if ((rcsfile->flags & VALID0x1) && (rcsfile->flags & INATTIC0x2)) | |||
661 | return (rtag_delete (rcsfile)); | |||
662 | } | |||
663 | ||||
664 | version = RCS_getversion (rcsfile, numtag, date, force_tag_match, | |||
665 | (int *) NULL((void*)0)); | |||
666 | if (version == NULL((void*)0)) | |||
667 | { | |||
668 | /* If -a specified, clean up any old tags */ | |||
669 | if (attic_too) | |||
670 | (void) rtag_delete (rcsfile); | |||
671 | ||||
672 | if (!quiet && !force_tag_match) | |||
673 | { | |||
674 | error (0, 0, "cannot find tag `%s' in `%s'", | |||
675 | numtag ? numtag : "head", rcsfile->path); | |||
676 | return (1); | |||
677 | } | |||
678 | return (0); | |||
679 | } | |||
680 | if (numtag | |||
681 | && isdigit ((unsigned char) *numtag) | |||
682 | && strcmp (numtag, version) != 0) | |||
683 | { | |||
684 | ||||
685 | /* | |||
686 | * We didn't find a match for the numeric tag that was specified, but | |||
687 | * that's OK. just pass the numeric tag on to rcs, to be tagged as | |||
688 | * specified. Could get here if one tried to tag "1.1.1" and there | |||
689 | * was a 1.1.1 branch with some head revision. In this case, we want | |||
690 | * the tag to reference "1.1.1" and not the revision at the head of | |||
691 | * the branch. Use a symbolic tag for that. | |||
692 | */ | |||
693 | rev = branch_mode ? RCS_magicrev (rcsfile, version) : numtag; | |||
694 | retcode = RCS_settag(rcsfile, symtag, numtag); | |||
695 | if (retcode == 0) | |||
696 | RCS_rewrite (rcsfile, NULL((void*)0), NULL((void*)0)); | |||
697 | } | |||
698 | else | |||
699 | { | |||
700 | char *oversion; | |||
701 | ||||
702 | /* | |||
703 | * As an enhancement for the case where a tag is being re-applied to | |||
704 | * a large body of a module, make one extra call to RCS_getversion to | |||
705 | * see if the tag is already set in the RCS file. If so, check to | |||
706 | * see if it needs to be moved. If not, do nothing. This will | |||
707 | * likely save a lot of time when simply moving the tag to the | |||
708 | * "current" head revisions of a module -- which I have found to be a | |||
709 | * typical tagging operation. | |||
710 | */ | |||
711 | rev = branch_mode ? RCS_magicrev (rcsfile, version) : version; | |||
712 | oversion = RCS_getversion (rcsfile, symtag, (char *) NULL((void*)0), 1, | |||
713 | (int *) NULL((void*)0)); | |||
714 | if (oversion != NULL((void*)0)) | |||
715 | { | |||
716 | int isbranch = RCS_nodeisbranch (finfo->rcs, symtag); | |||
717 | ||||
718 | /* | |||
719 | * if versions the same and neither old or new are branches don't | |||
720 | * have to do anything | |||
721 | */ | |||
722 | if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch) | |||
723 | { | |||
724 | free (oversion); | |||
725 | free (version); | |||
726 | return (0); | |||
727 | } | |||
728 | ||||
729 | if (!force_tag_move) | |||
730 | { | |||
731 | /* we're NOT going to move the tag */ | |||
732 | (void) printf ("W %s", finfo->fullname); | |||
733 | ||||
734 | (void) printf (" : %s already exists on %s %s", | |||
735 | symtag, isbranch ? "branch" : "version", | |||
736 | oversion); | |||
737 | (void) printf (" : NOT MOVING tag to %s %s\n", | |||
738 | branch_mode ? "branch" : "version", rev); | |||
739 | free (oversion); | |||
740 | free (version); | |||
741 | return (0); | |||
742 | } | |||
743 | free (oversion); | |||
744 | } | |||
745 | retcode = RCS_settag(rcsfile, symtag, rev); | |||
746 | if (retcode == 0) | |||
747 | RCS_rewrite (rcsfile, NULL((void*)0), NULL((void*)0)); | |||
748 | } | |||
749 | ||||
750 | if (retcode != 0) | |||
751 | { | |||
752 | error (1, retcode == -1 ? errno(*__errno()) : 0, | |||
753 | "failed to set tag `%s' to revision `%s' in `%s'", | |||
754 | symtag, rev, rcsfile->path); | |||
755 | if (branch_mode) | |||
756 | free (rev); | |||
757 | free (version); | |||
758 | return (1); | |||
759 | } | |||
760 | if (branch_mode) | |||
761 | free (rev); | |||
762 | free (version); | |||
763 | return (0); | |||
764 | } | |||
765 | ||||
766 | /* | |||
767 | * If -d is specified, "force_tag_match" is set, so that this call to | |||
768 | * RCS_getversion() will return a NULL version string if the symbolic | |||
769 | * tag does not exist in the RCS file. | |||
770 | * | |||
771 | * If the -r flag was used, numtag is set, and we only delete the | |||
772 | * symtag from files that have numtag. | |||
773 | * | |||
774 | * This is done here because it's MUCH faster than just blindly calling | |||
775 | * "rcs" to remove the tag... trust me. | |||
776 | */ | |||
777 | static int | |||
778 | rtag_delete (rcsfile) | |||
779 | RCSNode *rcsfile; | |||
780 | { | |||
781 | char *version; | |||
782 | int retcode; | |||
783 | ||||
784 | if (numtag) | |||
785 | { | |||
786 | version = RCS_getversion (rcsfile, numtag, (char *) NULL((void*)0), 1, | |||
787 | (int *) NULL((void*)0)); | |||
788 | if (version == NULL((void*)0)) | |||
789 | return (0); | |||
790 | free (version); | |||
791 | } | |||
792 | ||||
793 | version = RCS_getversion (rcsfile, symtag, (char *) NULL((void*)0), 1, | |||
794 | (int *) NULL((void*)0)); | |||
795 | if (version == NULL((void*)0)) | |||
796 | return (0); | |||
797 | free (version); | |||
798 | ||||
799 | if ((retcode = RCS_deltag(rcsfile, symtag)) != 0) | |||
800 | { | |||
801 | if (!quiet) | |||
802 | error (0, retcode == -1 ? errno(*__errno()) : 0, | |||
803 | "failed to remove tag `%s' from `%s'", symtag, | |||
804 | rcsfile->path); | |||
805 | return (1); | |||
806 | } | |||
807 | RCS_rewrite (rcsfile, NULL((void*)0), NULL((void*)0)); | |||
808 | return (0); | |||
809 | } | |||
810 | ||||
811 | ||||
812 | /* | |||
813 | * Called to tag a particular file (the currently checked out version is | |||
814 | * tagged with the specified tag - or the specified tag is deleted). | |||
815 | */ | |||
816 | /* ARGSUSED */ | |||
817 | static int | |||
818 | tag_fileproc (callerdat, finfo) | |||
819 | void *callerdat; | |||
820 | struct file_info *finfo; | |||
821 | { | |||
822 | char *version, *oversion; | |||
823 | char *nversion = NULL((void*)0); | |||
824 | char *rev; | |||
825 | Vers_TS *vers; | |||
826 | int retcode = 0; | |||
827 | ||||
828 | vers = Version_TS (finfo, NULL((void*)0), NULL((void*)0), NULL((void*)0), 0, 0); | |||
829 | ||||
830 | if ((numtag != NULL((void*)0)) || (date != NULL((void*)0))) | |||
| ||||
831 | { | |||
832 | nversion = RCS_getversion(vers->srcfile, | |||
833 | numtag, | |||
834 | date, | |||
835 | force_tag_match, | |||
836 | (int *) NULL((void*)0)); | |||
837 | if (nversion == NULL((void*)0)) | |||
838 | { | |||
839 | freevers_ts (&vers); | |||
840 | return (0); | |||
841 | } | |||
842 | } | |||
843 | if (delete_flag) | |||
844 | { | |||
845 | ||||
846 | /* | |||
847 | * If -d is specified, "force_tag_match" is set, so that this call to | |||
848 | * RCS_getversion() will return a NULL version string if the symbolic | |||
849 | * tag does not exist in the RCS file. | |||
850 | * | |||
851 | * This is done here because it's MUCH faster than just blindly calling | |||
852 | * "rcs" to remove the tag... trust me. | |||
853 | */ | |||
854 | ||||
855 | version = RCS_getversion (vers->srcfile, symtag, (char *) NULL((void*)0), 1, | |||
856 | (int *) NULL((void*)0)); | |||
857 | if (version == NULL((void*)0) || vers->srcfile == NULL((void*)0)) | |||
858 | { | |||
859 | freevers_ts (&vers); | |||
860 | return (0); | |||
861 | } | |||
862 | free (version); | |||
863 | ||||
864 | if ((retcode = RCS_deltag(vers->srcfile, symtag)) != 0) | |||
865 | { | |||
866 | if (!quiet) | |||
867 | error (0, retcode == -1 ? errno(*__errno()) : 0, | |||
868 | "failed to remove tag %s from %s", symtag, | |||
869 | vers->srcfile->path); | |||
870 | freevers_ts (&vers); | |||
871 | return (1); | |||
872 | } | |||
873 | RCS_rewrite (vers->srcfile, NULL((void*)0), NULL((void*)0)); | |||
874 | ||||
875 | /* warm fuzzies */ | |||
876 | if (!really_quiet) | |||
877 | { | |||
878 | cvs_output ("D ", 2); | |||
879 | cvs_output (finfo->fullname, 0); | |||
880 | cvs_output ("\n", 1); | |||
881 | } | |||
882 | ||||
883 | freevers_ts (&vers); | |||
884 | return (0); | |||
885 | } | |||
886 | ||||
887 | /* | |||
888 | * If we are adding a tag, we need to know which version we have checked | |||
889 | * out and we'll tag that version. | |||
890 | */ | |||
891 | if (nversion
| |||
892 | { | |||
893 | version = vers->vn_user; | |||
894 | } | |||
895 | else | |||
896 | { | |||
897 | version = nversion; | |||
898 | } | |||
899 | if (version
| |||
900 | { | |||
901 | freevers_ts (&vers); | |||
902 | return (0); | |||
903 | } | |||
904 | else if (strcmp (version, "0") == 0) | |||
905 | { | |||
906 | if (!quiet) | |||
907 | error (0, 0, "couldn't tag added but un-commited file `%s'", finfo->file); | |||
908 | freevers_ts (&vers); | |||
909 | return (0); | |||
910 | } | |||
911 | else if (version[0] == '-') | |||
912 | { | |||
913 | if (!quiet) | |||
914 | error (0, 0, "skipping removed but un-commited file `%s'", finfo->file); | |||
915 | freevers_ts (&vers); | |||
916 | return (0); | |||
917 | } | |||
918 | else if (vers->srcfile == NULL((void*)0)) | |||
919 | { | |||
920 | if (!quiet) | |||
921 | error (0, 0, "cannot find revision control file for `%s'", finfo->file); | |||
922 | freevers_ts (&vers); | |||
923 | return (0); | |||
924 | } | |||
925 | ||||
926 | /* | |||
927 | * As an enhancement for the case where a tag is being re-applied to a | |||
928 | * large number of files, make one extra call to RCS_getversion to see | |||
929 | * if the tag is already set in the RCS file. If so, check to see if it | |||
930 | * needs to be moved. If not, do nothing. This will likely save a lot of | |||
931 | * time when simply moving the tag to the "current" head revisions of a | |||
932 | * module -- which I have found to be a typical tagging operation. | |||
933 | */ | |||
934 | rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version; | |||
935 | oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL((void*)0), 1, | |||
936 | (int *) NULL((void*)0)); | |||
937 | if (oversion != NULL((void*)0)) | |||
938 | { | |||
939 | int isbranch = RCS_nodeisbranch (finfo->rcs, symtag); | |||
940 | ||||
941 | /* | |||
942 | * if versions the same and neither old or new are branches don't have | |||
943 | * to do anything | |||
944 | */ | |||
945 | if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch) | |||
946 | { | |||
947 | free (oversion); | |||
948 | if (branch_mode) | |||
949 | free (rev); | |||
950 | freevers_ts (&vers); | |||
951 | return (0); | |||
952 | } | |||
953 | ||||
954 | if (!force_tag_move) | |||
955 | { | |||
956 | /* we're NOT going to move the tag */ | |||
957 | cvs_output ("W ", 2); | |||
958 | cvs_output (finfo->fullname, 0); | |||
959 | cvs_output (" : ", 0); | |||
960 | cvs_output (symtag, 0); | |||
961 | cvs_output (" already exists on ", 0); | |||
962 | cvs_output (isbranch ? "branch" : "version", 0); | |||
963 | cvs_output (" ", 0); | |||
964 | cvs_output (oversion, 0); | |||
965 | cvs_output (" : NOT MOVING tag to ", 0); | |||
966 | cvs_output (branch_mode ? "branch" : "version", 0); | |||
967 | cvs_output (" ", 0); | |||
968 | cvs_output (rev, 0); | |||
969 | cvs_output ("\n", 1); | |||
970 | free (oversion); | |||
971 | if (branch_mode) | |||
972 | free (rev); | |||
973 | freevers_ts (&vers); | |||
974 | return (0); | |||
975 | } | |||
976 | free (oversion); | |||
977 | } | |||
978 | ||||
979 | if ((retcode = RCS_settag(vers->srcfile, symtag, rev)) != 0) | |||
980 | { | |||
981 | error (1, retcode == -1 ? errno(*__errno()) : 0, | |||
982 | "failed to set tag %s to revision %s in %s", | |||
983 | symtag, rev, vers->srcfile->path); | |||
984 | if (branch_mode) | |||
985 | free (rev); | |||
986 | freevers_ts (&vers); | |||
987 | return (1); | |||
988 | } | |||
989 | if (branch_mode) | |||
990 | free (rev); | |||
991 | RCS_rewrite (vers->srcfile, NULL((void*)0), NULL((void*)0)); | |||
992 | ||||
993 | /* more warm fuzzies */ | |||
994 | if (!really_quiet) | |||
995 | { | |||
996 | cvs_output ("T ", 2); | |||
997 | cvs_output (finfo->fullname, 0); | |||
998 | cvs_output ("\n", 1); | |||
999 | } | |||
1000 | ||||
1001 | if (nversion
| |||
1002 | { | |||
1003 | free (nversion); | |||
| ||||
1004 | } | |||
1005 | freevers_ts (&vers); | |||
1006 | return (0); | |||
1007 | } | |||
1008 | ||||
1009 | /* | |||
1010 | * Print a warm fuzzy message | |||
1011 | */ | |||
1012 | /* ARGSUSED */ | |||
1013 | static Dtype | |||
1014 | tag_dirproc (callerdat, dir, repos, update_dir, entries) | |||
1015 | void *callerdat; | |||
1016 | char *dir; | |||
1017 | char *repos; | |||
1018 | char *update_dir; | |||
1019 | List *entries; | |||
1020 | { | |||
1021 | ||||
1022 | if (ignore_directory (update_dir)) | |||
1023 | { | |||
1024 | /* print the warm fuzzy message */ | |||
1025 | if (!quiet) | |||
1026 | error (0, 0, "Ignoring %s", update_dir); | |||
1027 | return R_SKIP_ALL; | |||
1028 | } | |||
1029 | ||||
1030 | if (!quiet) | |||
1031 | error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging", update_dir); | |||
1032 | return (R_PROCESS); | |||
1033 | } | |||
1034 | ||||
1035 | /* Code relating to the val-tags file. Note that this file has no way | |||
1036 | of knowing when a tag has been deleted. The problem is that there | |||
1037 | is no way of knowing whether a tag still exists somewhere, when we | |||
1038 | delete it some places. Using per-directory val-tags files (in | |||
1039 | CVSREP) might be better, but that might slow down the process of | |||
1040 | verifying that a tag is correct (maybe not, for the likely cases, | |||
1041 | if carefully done), and/or be harder to implement correctly. */ | |||
1042 | ||||
1043 | struct val_args { | |||
1044 | char *name; | |||
1045 | int found; | |||
1046 | }; | |||
1047 | ||||
1048 | static int val_fileproc PROTO ((void *callerdat, struct file_info *finfo))(void *callerdat, struct file_info *finfo); | |||
1049 | ||||
1050 | static int | |||
1051 | val_fileproc (callerdat, finfo) | |||
1052 | void *callerdat; | |||
1053 | struct file_info *finfo; | |||
1054 | { | |||
1055 | RCSNode *rcsdata; | |||
1056 | struct val_args *args = (struct val_args *)callerdat; | |||
1057 | char *tag; | |||
1058 | ||||
1059 | if ((rcsdata = finfo->rcs) == NULL((void*)0)) | |||
1060 | /* Not sure this can happen, after all we passed only | |||
1061 | W_REPOS | W_ATTIC. */ | |||
1062 | return 0; | |||
1063 | ||||
1064 | tag = RCS_gettag (rcsdata, args->name, 1, (int *) NULL((void*)0)); | |||
1065 | if (tag != NULL((void*)0)) | |||
1066 | { | |||
1067 | /* FIXME: should find out a way to stop the search at this point. */ | |||
1068 | args->found = 1; | |||
1069 | free (tag); | |||
1070 | } | |||
1071 | return 0; | |||
1072 | } | |||
1073 | ||||
1074 | static Dtype val_direntproc PROTO ((void *, char *, char *, char *, List *))(void *, char *, char *, char *, List *); | |||
1075 | ||||
1076 | static Dtype | |||
1077 | val_direntproc (callerdat, dir, repository, update_dir, entries) | |||
1078 | void *callerdat; | |||
1079 | char *dir; | |||
1080 | char *repository; | |||
1081 | char *update_dir; | |||
1082 | List *entries; | |||
1083 | { | |||
1084 | /* This is not quite right--it doesn't get right the case of "cvs | |||
1085 | update -d -r foobar" where foobar is a tag which exists only in | |||
1086 | files in a directory which does not exist yet, but which is | |||
1087 | about to be created. */ | |||
1088 | if (isdir (dir)) | |||
1089 | return R_PROCESS; | |||
1090 | return R_SKIP_ALL; | |||
1091 | } | |||
1092 | ||||
1093 | /* Check to see whether NAME is a valid tag. If so, return. If not | |||
1094 | print an error message and exit. ARGC, ARGV, LOCAL, and AFLAG specify | |||
1095 | which files we will be operating on. | |||
1096 | ||||
1097 | REPOSITORY is the repository if we need to cd into it, or NULL if | |||
1098 | we are already there, or "" if we should do a W_LOCAL recursion. | |||
1099 | Sorry for three cases, but the "" case is needed in case the | |||
1100 | working directories come from diverse parts of the repository, the | |||
1101 | NULL case avoids an unneccesary chdir, and the non-NULL, non-"" | |||
1102 | case is needed for checkout, where we don't want to chdir if the | |||
1103 | tag is found in CVSROOTADM_VALTAGS, but there is not (yet) any | |||
1104 | local directory. */ | |||
1105 | void | |||
1106 | tag_check_valid (name, argc, argv, local, aflag, repository) | |||
1107 | char *name; | |||
1108 | int argc; | |||
1109 | char **argv; | |||
1110 | int local; | |||
1111 | int aflag; | |||
1112 | char *repository; | |||
1113 | { | |||
1114 | DBM *db; | |||
1115 | char *valtags_filename; | |||
1116 | int err; | |||
1117 | int nowrite = 0; | |||
1118 | datum mytag; | |||
1119 | struct val_args the_val_args; | |||
1120 | struct saved_cwd cwd; | |||
1121 | int which; | |||
1122 | ||||
1123 | /* Numeric tags require only a syntactic check. */ | |||
1124 | if (isdigit ((unsigned char) name[0])) | |||
1125 | { | |||
1126 | char *p; | |||
1127 | for (p = name; *p != '\0'; ++p) | |||
1128 | { | |||
1129 | if (!(isdigit ((unsigned char) *p) || *p == '.')) | |||
1130 | error (1, 0, "\ | |||
1131 | Numeric tag %s contains characters other than digits and '.'", name); | |||
1132 | } | |||
1133 | return; | |||
1134 | } | |||
1135 | ||||
1136 | /* Special tags are always valid. */ | |||
1137 | if (strcmp (name, TAG_BASE"BASE") == 0 | |||
1138 | || strcmp (name, TAG_HEAD"HEAD") == 0) | |||
1139 | return; | |||
1140 | ||||
1141 | /* FIXME: This routine doesn't seem to do any locking whatsoever | |||
1142 | (and it is called from places which don't have locks in place). | |||
1143 | If two processes try to write val-tags at the same time, it would | |||
1144 | seem like we are in trouble. */ | |||
1145 | ||||
1146 | mytag.dptr = name; | |||
1147 | mytag.dsize = strlen (name); | |||
1148 | ||||
1149 | valtags_filename = xmalloc (strlen (current_parsed_root->directory) | |||
1150 | + sizeof CVSROOTADM"CVSROOT" | |||
1151 | + sizeof CVSROOTADM_VALTAGS"val-tags" + 3); | |||
1152 | sprintf (valtags_filename, "%s/%s/%s", current_parsed_root->directory, | |||
1153 | CVSROOTADM"CVSROOT", CVSROOTADM_VALTAGS"val-tags"); | |||
1154 | db = dbm_openmydbm_open (valtags_filename, O_RDWR0x0002, 0666); | |||
1155 | if (db == NULL((void*)0)) | |||
1156 | { | |||
1157 | if (!existence_error (errno)(((*__errno())) == 2)) | |||
1158 | { | |||
1159 | error (0, errno(*__errno()), "warning: cannot open %s read/write", | |||
1160 | valtags_filename); | |||
1161 | db = dbm_openmydbm_open (valtags_filename, O_RDONLY0x0000, 0666); | |||
1162 | if (db != NULL((void*)0)) | |||
1163 | nowrite = 1; | |||
1164 | else if (!existence_error (errno)(((*__errno())) == 2)) | |||
1165 | error (1, errno(*__errno()), "cannot read %s", valtags_filename); | |||
1166 | } | |||
1167 | /* If the file merely fails to exist, we just keep going and create | |||
1168 | it later if need be. */ | |||
1169 | } | |||
1170 | if (db != NULL((void*)0)) | |||
1171 | { | |||
1172 | datum val; | |||
1173 | ||||
1174 | val = dbm_fetchmydbm_fetch (db, mytag); | |||
1175 | if (val.dptr != NULL((void*)0)) | |||
1176 | { | |||
1177 | /* Found. The tag is valid. */ | |||
1178 | dbm_closemydbm_close (db); | |||
1179 | free (valtags_filename); | |||
1180 | return; | |||
1181 | } | |||
1182 | /* FIXME: should check errors somehow (add dbm_error to myndbm.c?). */ | |||
1183 | } | |||
1184 | ||||
1185 | /* We didn't find the tag in val-tags, so look through all the RCS files | |||
1186 | to see whether it exists there. Yes, this is expensive, but there | |||
1187 | is no other way to cope with a tag which might have been created | |||
1188 | by an old version of CVS, from before val-tags was invented. | |||
1189 | ||||
1190 | Since we need this code anyway, we also use it to create | |||
1191 | entries in val-tags in general (that is, the val-tags entry | |||
1192 | will get created the first time the tag is used, not when the | |||
1193 | tag is created). */ | |||
1194 | ||||
1195 | the_val_args.name = name; | |||
1196 | the_val_args.found = 0; | |||
1197 | ||||
1198 | which = W_REPOS0x02 | W_ATTIC0x04; | |||
1199 | ||||
1200 | if (repository != NULL((void*)0)) | |||
1201 | { | |||
1202 | if (repository[0] == '\0') | |||
1203 | which |= W_LOCAL0x01; | |||
1204 | else | |||
1205 | { | |||
1206 | if (save_cwd (&cwd)) | |||
1207 | error_exit (); | |||
1208 | if ( CVS_CHDIRchdir (repository) < 0) | |||
1209 | error (1, errno(*__errno()), "cannot change to %s directory", repository); | |||
1210 | } | |||
1211 | } | |||
1212 | ||||
1213 | err = start_recursion (val_fileproc, (FILESDONEPROC) NULL((void*)0), | |||
1214 | val_direntproc, (DIRLEAVEPROC) NULL((void*)0), | |||
1215 | (void *)&the_val_args, | |||
1216 | argc, argv, local, which, aflag, | |||
1217 | 1, NULL((void*)0), 1); | |||
1218 | if (repository != NULL((void*)0) && repository[0] != '\0') | |||
1219 | { | |||
1220 | if (restore_cwd (&cwd, NULL((void*)0))) | |||
1221 | exit (EXIT_FAILURE1); | |||
1222 | free_cwd (&cwd); | |||
1223 | } | |||
1224 | ||||
1225 | if (!the_val_args.found) | |||
1226 | error (1, 0, "no such tag %s", name); | |||
1227 | else | |||
1228 | { | |||
1229 | /* The tags is valid but not mentioned in val-tags. Add it. */ | |||
1230 | datum value; | |||
1231 | ||||
1232 | if (noexec || nowrite) | |||
1233 | { | |||
1234 | if (db != NULL((void*)0)) | |||
1235 | dbm_closemydbm_close (db); | |||
1236 | free (valtags_filename); | |||
1237 | return; | |||
1238 | } | |||
1239 | ||||
1240 | if (db == NULL((void*)0)) | |||
1241 | { | |||
1242 | mode_t omask; | |||
1243 | omask = umask (cvsumask); | |||
1244 | db = dbm_openmydbm_open (valtags_filename, O_RDWR0x0002 | O_CREAT0x0200 | O_TRUNC0x0400, 0666); | |||
1245 | (void) umask (omask); | |||
1246 | ||||
1247 | if (db == NULL((void*)0)) | |||
1248 | { | |||
1249 | error (0, errno(*__errno()), "warning: cannot create %s", valtags_filename); | |||
1250 | free (valtags_filename); | |||
1251 | return; | |||
1252 | } | |||
1253 | } | |||
1254 | value.dptr = "y"; | |||
1255 | value.dsize = 1; | |||
1256 | if (dbm_storemydbm_store (db, mytag, value, DBM_REPLACE1) < 0) | |||
1257 | error (0, errno(*__errno()), "cannot store %s into %s", name, | |||
1258 | valtags_filename); | |||
1259 | dbm_closemydbm_close (db); | |||
1260 | } | |||
1261 | free (valtags_filename); | |||
1262 | } | |||
1263 | ||||
1264 | /* | |||
1265 | * Check whether a join tag is valid. This is just like | |||
1266 | * tag_check_valid, but we must stop before the colon if there is one. | |||
1267 | */ | |||
1268 | ||||
1269 | void | |||
1270 | tag_check_valid_join (join_tag, argc, argv, local, aflag, repository) | |||
1271 | char *join_tag; | |||
1272 | int argc; | |||
1273 | char **argv; | |||
1274 | int local; | |||
1275 | int aflag; | |||
1276 | char *repository; | |||
1277 | { | |||
1278 | char *c, *s; | |||
1279 | ||||
1280 | c = xstrdup (join_tag); | |||
1281 | s = strchr (c, ':'); | |||
1282 | if (s != NULL((void*)0)) | |||
1283 | { | |||
1284 | if (isdigit ((unsigned char) join_tag[0])) | |||
1285 | error (1, 0, | |||
1286 | "Numeric join tag %s may not contain a date specifier", | |||
1287 | join_tag); | |||
1288 | ||||
1289 | *s = '\0'; | |||
1290 | /* hmmm... I think it makes sense to allow -j:<date>, but | |||
1291 | * for now this fixes a bug where CVS just spins and spins (I | |||
1292 | * think in the RCS code) looking for a zero length tag. | |||
1293 | */ | |||
1294 | if (!*c) | |||
1295 | error (1, 0, | |||
1296 | "argument to join may not contain a date specifier without a tag"); | |||
1297 | } | |||
1298 | ||||
1299 | tag_check_valid (c, argc, argv, local, aflag, repository); | |||
1300 | ||||
1301 | free (c); | |||
1302 | } |