Deleted Added
sdiff udiff text old ( 102843 ) new ( 107487 )
full compact
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 * "import" checks in the vendor release located in the current directory into
9 * the CVS source repository. The CVS vendor branch support is utilized.
10 *
11 * At least three arguments are expected to follow the options:
12 * repository Where the source belongs relative to the CVSROOT
13 * VendorTag Vendor's major tag
14 * VendorReleTag Tag for this particular release
15 *
16 * Additional arguments specify more Vendor Release Tags.
17 *
18 * $FreeBSD: head/contrib/cvs/src/import.c 102843 2002-09-02 05:57:14Z peter $
19 */
20
21#include "cvs.h"
22#include "savecwd.h"
23#include <assert.h>
24
25static char *get_comment PROTO((char *user));
26static int add_rev PROTO((char *message, RCSNode *rcs, char *vfile,
27 char *vers));
28static int add_tags PROTO((RCSNode *rcs, char *vfile, char *vtag, int targc,
29 char *targv[]));
30static int import_descend PROTO((char *message, char *vtag, int targc, char *targv[]));
31static int import_descend_dir PROTO((char *message, char *dir, char *vtag,
32 int targc, char *targv[]));
33static int process_import_file PROTO((char *message, char *vfile, char *vtag,
34 int targc, char *targv[]));
35static int update_rcs_file PROTO((char *message, char *vfile, char *vtag, int targc,
36 char *targv[], int inattic));
37static void add_log PROTO((int ch, char *fname));
38
39static int repos_len;
40static char *vhead;
41static char *vbranch;
42static FILE *logfp;
43static char *repository;
44static int conflicts;
45static int use_file_modtime;
46static char *keyword_opt = NULL;
47
48static const char *const import_usage[] =
49{
50 "Usage: %s %s [-d] [-k subst] [-I ign] [-m msg] [-b branch]\n",
51 " [-W spec] repository vendor-tag release-tags...\n",
52 "\t-d\tUse the file's modification time as the time of import.\n",
53 "\t-k sub\tSet default RCS keyword substitution mode.\n",
54 "\t-I ign\tMore files to ignore (! to reset).\n",
55 "\t-b bra\tVendor branch id.\n",
56 "\t-m msg\tLog message.\n",
57 "\t-W spec\tWrappers specification line.\n",
58 "(Specify the --help global option for a list of other help options)\n",
59 NULL
60};
61
62int
63import (argc, argv)
64 int argc;
65 char **argv;
66{
67 char *message = NULL;
68 char *tmpfile;
69 char *cp;
70 int i, c, msglen, err;
71 List *ulist;
72 Node *p;
73 struct logfile_info *li;
74
75 if (argc == -1)
76 usage (import_usage);
77
78 ign_setup ();
79 wrap_setup ();
80
81 vbranch = xstrdup (CVSBRANCH);
82 optind = 0;
83 while ((c = getopt (argc, argv, "+Qqdb:m:I:k:W:")) != -1)
84 {
85 switch (c)
86 {
87 case 'Q':
88 case 'q':
89#ifdef SERVER_SUPPORT
90 /* The CVS 1.5 client sends these options (in addition to
91 Global_option requests), so we must ignore them. */
92 if (!server_active)
93#endif
94 error (1, 0,
95 "-q or -Q must be specified before \"%s\"",
96 command_name);
97 break;
98 case 'd':
99#ifdef SERVER_SUPPORT
100 if (server_active)
101 {
102 /* CVS 1.10 and older clients will send this, but it
103 doesn't do any good. So tell the user we can't
104 cope, rather than silently losing. */
105 error (0, 0,
106 "warning: not setting the time of import from the file");
107 error (0, 0, "due to client limitations");
108 }
109#endif
110 use_file_modtime = 1;
111 break;
112 case 'b':
113 free (vbranch);
114 vbranch = xstrdup (optarg);
115 break;
116 case 'm':
117#ifdef FORCE_USE_EDITOR
118 use_editor = 1;
119#else
120 use_editor = 0;
121#endif
122 message = xstrdup(optarg);
123 break;
124 case 'I':
125 ign_add (optarg, 0);
126 break;
127 case 'k':
128 /* RCS_check_kflag returns strings of the form -kxx. We
129 only use it for validation, so we can free the value
130 as soon as it is returned. */
131 free (RCS_check_kflag (optarg));
132 keyword_opt = optarg;
133 break;
134 case 'W':
135 wrap_add (optarg, 0);
136 break;
137 case '?':
138 default:
139 usage (import_usage);
140 break;
141 }
142 }
143 argc -= optind;
144 argv += optind;
145 if (argc < 3)
146 usage (import_usage);
147
148#ifdef SERVER_SUPPORT
149 /* This is for handling the Checkin-time request. It might seem a
150 bit odd to enable the use_file_modtime code even in the case
151 where Checkin-time was not sent for a particular file. The
152 effect is that we use the time of upload, rather than the time
153 when we call RCS_checkin. Since those times are both during
154 CVS's run, that seems OK, and it is easier to implement than
155 putting the "was Checkin-time sent" flag in CVS/Entries or some
156 such place. */
157
158 if (server_active)
159 use_file_modtime = 1;
160#endif
161
162 for (i = 1; i < argc; i++) /* check the tags for validity */
163 {
164 int j;
165
166 RCS_check_tag (argv[i]);
167 for (j = 1; j < i; j++)
168 if (strcmp (argv[j], argv[i]) == 0)
169 error (1, 0, "tag `%s' was specified more than once", argv[i]);
170 }
171
172 /* XXX - this should be a module, not just a pathname */
173 if (! isabsolute (argv[0])
174 && pathname_levels (argv[0]) == 0)
175 {
176 if (current_parsed_root == NULL)
177 {
178 error (0, 0, "missing CVSROOT environment variable\n");
179 error (1, 0, "Set it or specify the '-d' option to %s.",
180 program_name);
181 }
182 repository = xmalloc (strlen (current_parsed_root->directory)
183 + strlen (argv[0])
184 + 2);
185 (void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]);
186 repos_len = strlen (current_parsed_root->directory);
187 }
188 else
189 {
190 /* It is somewhere between a security hole and "unexpected" to
191 let the client start mucking around outside the cvsroot
192 (wouldn't get the right CVSROOT configuration, &c). */
193 error (1, 0, "directory %s not relative within the repository",
194 argv[0]);
195 }
196
197 /*
198 * Consistency checks on the specified vendor branch. It must be
199 * composed of only numbers and dots ('.'). Also, for now we only
200 * support branching to a single level, so the specified vendor branch
201 * must only have two dots in it (like "1.1.1").
202 */
203 for (cp = vbranch; *cp != '\0'; cp++)
204 if (!isdigit ((unsigned char) *cp) && *cp != '.')
205 error (1, 0, "%s is not a numeric branch", vbranch);
206 if (numdots (vbranch) != 2)
207 error (1, 0, "Only branches with two dots are supported: %s", vbranch);
208 vhead = xstrdup (vbranch);
209 cp = strrchr (vhead, '.');
210 *cp = '\0';
211
212#ifdef CLIENT_SUPPORT
213 if (current_parsed_root->isremote)
214 {
215 /* For rationale behind calling start_server before do_editor, see
216 commit.c */
217 start_server ();
218 }
219#endif
220
221 if (use_editor)
222 {
223 do_editor ((char *) NULL, &message,
224#ifdef CLIENT_SUPPORT
225 current_parsed_root->isremote ? (char *) NULL :
226#endif
227 repository,
228 (List *) NULL);
229 }
230 do_verify (&message, repository);
231 msglen = message == NULL ? 0 : strlen (message);
232 if (msglen == 0 || message[msglen - 1] != '\n')
233 {
234 char *nm = xmalloc (msglen + 2);
235 *nm = '\0';
236 if (message != NULL)
237 {
238 (void) strcpy (nm, message);
239 free (message);
240 }
241 (void) strcat (nm + msglen, "\n");
242 message = nm;
243 }
244
245#ifdef CLIENT_SUPPORT
246 if (current_parsed_root->isremote)
247 {
248 int err;
249
250 if (vbranch[0] != '\0')
251 option_with_arg ("-b", vbranch);
252 if (message)
253 option_with_arg ("-m", message);
254 if (keyword_opt != NULL)
255 option_with_arg ("-k", keyword_opt);
256 /* The only ignore processing which takes place on the server side
257 is the CVSROOT/cvsignore file. But if the user specified -I !,
258 the documented behavior is to not process said file. */
259 if (ign_inhibit_server)
260 {
261 send_arg ("-I");
262 send_arg ("!");
263 }
264 wrap_send ();
265
266 {
267 int i;
268 for (i = 0; i < argc; ++i)
269 send_arg (argv[i]);
270 }
271
272 logfp = stdin;
273 client_import_setup (repository);
274 err = import_descend (message, argv[1], argc - 2, argv + 2);
275 client_import_done ();
276 if (message)
277 free (message);
278 free (repository);
279 free (vbranch);
280 free (vhead);
281 send_to_server ("import\012", 0);
282 err += get_responses_and_close ();
283 return err;
284 }
285#endif
286
287 if (!safe_location ( NULL ))
288 {
289 error (1, 0, "attempt to import the repository");
290 }
291
292 /*
293 * Make all newly created directories writable. Should really use a more
294 * sophisticated security mechanism here.
295 */
296 (void) umask (cvsumask);
297 make_directories (repository);
298
299 /* Create the logfile that will be logged upon completion */
300 if ((logfp = cvs_temp_file (&tmpfile)) == NULL)
301 error (1, errno, "cannot create temporary file `%s'", tmpfile);
302 /* On systems where we can unlink an open file, do so, so it will go
303 away no matter how we exit. FIXME-maybe: Should be checking for
304 errors but I'm not sure which error(s) we get if we are on a system
305 where one can't unlink open files. */
306 (void) CVS_UNLINK (tmpfile);
307 (void) fprintf (logfp, "\nVendor Tag:\t%s\n", argv[1]);
308 (void) fprintf (logfp, "Release Tags:\t");
309 for (i = 2; i < argc; i++)
310 (void) fprintf (logfp, "%s\n\t\t", argv[i]);
311 (void) fprintf (logfp, "\n");
312
313 /* Just Do It. */
314 err = import_descend (message, argv[1], argc - 2, argv + 2);
315 if (conflicts)
316 {
317 if (!really_quiet)
318 {
319 char buf[20];
320
321 cvs_output_tagged ("+importmergecmd", NULL);
322 cvs_output_tagged ("newline", NULL);
323 sprintf (buf, "%d", conflicts);
324 cvs_output_tagged ("conflicts", buf);
325 cvs_output_tagged ("text", " conflicts created by this import.");
326 cvs_output_tagged ("newline", NULL);
327 cvs_output_tagged ("text",
328 "Use the following command to help the merge:");
329 cvs_output_tagged ("newline", NULL);
330 cvs_output_tagged ("newline", NULL);
331 cvs_output_tagged ("text", "\t");
332 cvs_output_tagged ("text", program_name);
333 if (CVSroot_cmdline != NULL)
334 {
335 cvs_output_tagged ("text", " -d ");
336 cvs_output_tagged ("text", CVSroot_cmdline);
337 }
338 cvs_output_tagged ("text", " checkout -j");
339 cvs_output_tagged ("mergetag1", "<prev_rel_tag>");
340 cvs_output_tagged ("text", " -j");
341 cvs_output_tagged ("mergetag2", argv[2]);
342 cvs_output_tagged ("text", " ");
343 cvs_output_tagged ("repository", argv[0]);
344 cvs_output_tagged ("newline", NULL);
345 cvs_output_tagged ("newline", NULL);
346 cvs_output_tagged ("-importmergecmd", NULL);
347 }
348
349 /* FIXME: I'm not sure whether we need to put this information
350 into the loginfo. If we do, then note that it does not
351 report any required -d option. There is no particularly
352 clean way to tell the server about the -d option used by
353 the client. */
354 (void) fprintf (logfp, "\n%d conflicts created by this import.\n",
355 conflicts);
356 (void) fprintf (logfp,
357 "Use the following command to help the merge:\n\n");
358 (void) fprintf (logfp, "\t%s checkout ", program_name);
359 (void) fprintf (logfp, "-j%s:yesterday -j%s %s\n\n",
360 argv[1], argv[1], argv[0]);
361 }
362 else
363 {
364 if (!really_quiet)
365 cvs_output ("\nNo conflicts created by this import\n\n", 0);
366 (void) fprintf (logfp, "\nNo conflicts created by this import\n\n");
367 }
368
369 /*
370 * Write out the logfile and clean up.
371 */
372 ulist = getlist ();
373 p = getnode ();
374 p->type = UPDATE;
375 p->delproc = update_delproc;
376 p->key = xstrdup ("- Imported sources");
377 li = (struct logfile_info *) xmalloc (sizeof (struct logfile_info));
378 li->type = T_TITLE;
379 li->tag = xstrdup (vbranch);
380 li->rev_old = li->rev_new = NULL;
381 p->data = (char *) li;
382 (void) addnode (ulist, p);
383 Update_Logfile (repository, message, logfp, ulist);
384 dellist (&ulist);
385 if (fclose (logfp) < 0)
386 error (0, errno, "error closing %s", tmpfile);
387
388 /* Make sure the temporary file goes away, even on systems that don't let
389 you delete a file that's in use. */
390 if (CVS_UNLINK (tmpfile) < 0 && !existence_error (errno))
391 error (0, errno, "cannot remove %s", tmpfile);
392 free (tmpfile);
393
394 if (message)
395 free (message);
396 free (repository);
397 free (vbranch);
398 free (vhead);
399
400 return (err);
401}
402
403/* Process all the files in ".", then descend into other directories.
404 Returns 0 for success, or >0 on error (in which case a message
405 will have been printed). */
406static int
407import_descend (message, vtag, targc, targv)
408 char *message;
409 char *vtag;
410 int targc;
411 char *targv[];
412{
413 DIR *dirp;
414 struct dirent *dp;
415 int err = 0;
416 List *dirlist = NULL;
417
418 /* first, load up any per-directory ignore lists */
419 ign_add_file (CVSDOTIGNORE, 1);
420 wrap_add_file (CVSDOTWRAPPER, 1);
421
422 if ((dirp = CVS_OPENDIR (".")) == NULL)
423 {
424 error (0, errno, "cannot open directory");
425 err++;
426 }
427 else
428 {
429 errno = 0;
430 while ((dp = CVS_READDIR (dirp)) != NULL)
431 {
432 if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
433 goto one_more_time_boys;
434#ifdef SERVER_SUPPORT
435 /* CVS directories are created in the temp directory by
436 server.c because it doesn't special-case import. So
437 don't print a message about them, regardless of -I!. */
438 if (server_active && strcmp (dp->d_name, CVSADM) == 0)
439 goto one_more_time_boys;
440#endif
441 if (ign_name (dp->d_name))
442 {
443 add_log ('I', dp->d_name);
444 goto one_more_time_boys;
445 }
446
447 if (
448#ifdef DT_DIR
449 (dp->d_type == DT_DIR
450 || (dp->d_type == DT_UNKNOWN && isdir (dp->d_name)))
451#else
452 isdir (dp->d_name)
453#endif
454 && !wrap_name_has (dp->d_name, WRAP_TOCVS)
455 )
456 {
457 Node *n;
458
459 if (dirlist == NULL)
460 dirlist = getlist();
461
462 n = getnode();
463 n->key = xstrdup (dp->d_name);
464 addnode(dirlist, n);
465 }
466 else if (
467#ifdef DT_DIR
468 dp->d_type == DT_LNK
469 || (dp->d_type == DT_UNKNOWN && islink (dp->d_name))
470#else
471 islink (dp->d_name)
472#endif
473 )
474 {
475 add_log ('L', dp->d_name);
476 err++;
477 }
478 else
479 {
480#ifdef CLIENT_SUPPORT
481 if (current_parsed_root->isremote)
482 err += client_process_import_file (message, dp->d_name,
483 vtag, targc, targv,
484 repository,
485 keyword_opt != NULL &&
486 keyword_opt[0] == 'b',
487 use_file_modtime);
488 else
489#endif
490 err += process_import_file (message, dp->d_name,
491 vtag, targc, targv);
492 }
493 one_more_time_boys:
494 errno = 0;
495 }
496 if (errno != 0)
497 {
498 error (0, errno, "cannot read directory");
499 ++err;
500 }
501 (void) CVS_CLOSEDIR (dirp);
502 }
503
504 if (dirlist != NULL)
505 {
506 Node *head, *p;
507
508 head = dirlist->list;
509 for (p = head->next; p != head; p = p->next)
510 {
511 err += import_descend_dir (message, p->key, vtag, targc, targv);
512 }
513
514 dellist(&dirlist);
515 }
516
517 return (err);
518}
519
520/*
521 * Process the argument import file.
522 */
523static int
524process_import_file (message, vfile, vtag, targc, targv)
525 char *message;
526 char *vfile;
527 char *vtag;
528 int targc;
529 char *targv[];
530{
531 char *rcs;
532 int inattic = 0;
533
534 rcs = xmalloc (strlen (repository) + strlen (vfile) + sizeof (RCSEXT)
535 + 5);
536 (void) sprintf (rcs, "%s/%s%s", repository, vfile, RCSEXT);
537 if (!isfile (rcs))
538 {
539 char *attic_name;
540
541 attic_name = xmalloc (strlen (repository) + strlen (vfile) +
542 sizeof (CVSATTIC) + sizeof (RCSEXT) + 10);
543 (void) sprintf (attic_name, "%s/%s/%s%s", repository, CVSATTIC,
544 vfile, RCSEXT);
545 if (!isfile (attic_name))
546 {
547 int retval;
548 char *free_opt = NULL;
549 char *our_opt = keyword_opt;
550
551 free (attic_name);
552 /*
553 * A new import source file; it doesn't exist as a ,v within the
554 * repository nor in the Attic -- create it anew.
555 */
556 add_log ('N', vfile);
557
558#ifdef SERVER_SUPPORT
559 /* The most reliable information on whether the file is binary
560 is what the client told us. That is because if the client had
561 the wrong idea about binaryness, it corrupted the file, so
562 we might as well believe the client. */
563 if (server_active)
564 {
565 Node *node;
566 List *entries;
567
568 /* Reading all the entries for each file is fairly silly, and
569 probably slow. But I am too lazy at the moment to do
570 anything else. */
571 entries = Entries_Open (0, NULL);
572 node = findnode_fn (entries, vfile);
573 if (node != NULL)
574 {
575 Entnode *entdata = (Entnode *) node->data;
576 if (entdata->type == ENT_FILE)
577 {
578 assert (entdata->options[0] == '-'
579 && entdata->options[1] == 'k');
580 our_opt = xstrdup (entdata->options + 2);
581 free_opt = our_opt;
582 }
583 }
584 Entries_Close (entries);
585 }
586#endif
587
588 retval = add_rcs_file (message, rcs, vfile, vhead, our_opt,
589 vbranch, vtag, targc, targv,
590 NULL, 0, logfp);
591 if (free_opt != NULL)
592 free (free_opt);
593 free (rcs);
594 return retval;
595 }
596 free (attic_name);
597 inattic = 1;
598 }
599
600 free (rcs);
601 /*
602 * an rcs file exists. have to do things the official, slow, way.
603 */
604 return (update_rcs_file (message, vfile, vtag, targc, targv, inattic));
605}
606
607/*
608 * The RCS file exists; update it by adding the new import file to the
609 * (possibly already existing) vendor branch.
610 */
611static int
612update_rcs_file (message, vfile, vtag, targc, targv, inattic)
613 char *message;
614 char *vfile;
615 char *vtag;
616 int targc;
617 char *targv[];
618 int inattic;
619{
620 Vers_TS *vers;
621 int letter;
622 char *tocvsPath;
623 char *expand;
624 struct file_info finfo;
625
626 memset (&finfo, 0, sizeof finfo);
627 finfo.file = vfile;
628 /* Not used, so don't worry about it. */
629 finfo.update_dir = NULL;
630 finfo.fullname = finfo.file;
631 finfo.repository = repository;
632 finfo.entries = NULL;
633 finfo.rcs = NULL;
634 vers = Version_TS (&finfo, (char *) NULL, vbranch, (char *) NULL,
635 1, 0);
636 if (vers->vn_rcs != NULL
637 && !RCS_isdead(vers->srcfile, vers->vn_rcs))
638 {
639 int different;
640
641 /*
642 * The rcs file does have a revision on the vendor branch. Compare
643 * this revision with the import file; if they match exactly, there
644 * is no need to install the new import file as a new revision to the
645 * branch. Just tag the revision with the new import tags.
646 *
647 * This is to try to cut down the number of "C" conflict messages for
648 * locally modified import source files.
649 */
650 tocvsPath = wrap_tocvs_process_file (vfile);
651 /* FIXME: Why don't we pass tocvsPath to RCS_cmp_file if it is
652 not NULL? */
653 expand = vers->srcfile->expand != NULL &&
654 vers->srcfile->expand[0] == 'b' ? "-kb" : "-ko";
655 different = RCS_cmp_file (vers->srcfile, vers->vn_rcs, expand, vfile);
656 if (tocvsPath)
657 if (unlink_file_dir (tocvsPath) < 0)
658 error (0, errno, "cannot remove %s", tocvsPath);
659
660 if (!different)
661 {
662 int retval = 0;
663
664 /*
665 * The two files are identical. Just update the tags, print the
666 * "U", signifying that the file has changed, but needs no
667 * attention, and we're done.
668 */
669 if (add_tags (vers->srcfile, vfile, vtag, targc, targv))
670 retval = 1;
671 add_log ('U', vfile);
672 freevers_ts (&vers);
673 return (retval);
674 }
675 }
676
677 /* We may have failed to parse the RCS file; check just in case */
678 if (vers->srcfile == NULL ||
679 add_rev (message, vers->srcfile, vfile, vers->vn_rcs) ||
680 add_tags (vers->srcfile, vfile, vtag, targc, targv))
681 {
682 freevers_ts (&vers);
683 return (1);
684 }
685
686 if (vers->srcfile->branch == NULL || inattic ||
687 strcmp (vers->srcfile->branch, vbranch) != 0)
688 {
689 conflicts++;
690 letter = 'C';
691 }
692 else
693 letter = 'U';
694 add_log (letter, vfile);
695
696 freevers_ts (&vers);
697 return (0);
698}
699
700/*
701 * Add the revision to the vendor branch
702 */
703static int
704add_rev (message, rcs, vfile, vers)
705 char *message;
706 RCSNode *rcs;
707 char *vfile;
708 char *vers;
709{
710 int locked, status, ierrno;
711 char *tocvsPath;
712
713 if (noexec)
714 return (0);
715
716 locked = 0;
717 if (vers != NULL)
718 {
719 /* Before RCS_lock existed, we were directing stdout, as well as
720 stderr, from the RCS command, to DEVNULL. I wouldn't guess that
721 was necessary, but I don't know for sure. */
722 /* Earlier versions of this function printed a `fork failed' error
723 when RCS_lock returned an error code. That's not appropriate
724 now that RCS_lock is librarified, but should the error text be
725 preserved? */
726 if (RCS_lock (rcs, vbranch, 1) != 0)
727 return 1;
728 locked = 1;
729 RCS_rewrite (rcs, NULL, NULL);
730 }
731 tocvsPath = wrap_tocvs_process_file (vfile);
732
733 status = RCS_checkin (rcs, tocvsPath == NULL ? vfile : tocvsPath,
734 message, vbranch,
735 (RCS_FLAGS_QUIET | RCS_FLAGS_KEEPFILE
736 | (use_file_modtime ? RCS_FLAGS_MODTIME : 0)));
737 ierrno = errno;
738
739 if ((tocvsPath != NULL) && (unlink_file_dir (tocvsPath) < 0))
740 error (0, errno, "cannot remove %s", tocvsPath);
741
742 if (status)
743 {
744 if (!noexec)
745 {
746 fperrmsg (logfp, 0, status == -1 ? ierrno : 0,
747 "ERROR: Check-in of %s failed", rcs->path);
748 error (0, status == -1 ? ierrno : 0,
749 "ERROR: Check-in of %s failed", rcs->path);
750 }
751 if (locked)
752 {
753 (void) RCS_unlock(rcs, vbranch, 0);
754 RCS_rewrite (rcs, NULL, NULL);
755 }
756 return (1);
757 }
758 return (0);
759}
760
761/*
762 * Add the vendor branch tag and all the specified import release tags to the
763 * RCS file. The vendor branch tag goes on the branch root (1.1.1) while the
764 * vendor release tags go on the newly added leaf of the branch (1.1.1.1,
765 * 1.1.1.2, ...).
766 */
767static int
768add_tags (rcs, vfile, vtag, targc, targv)
769 RCSNode *rcs;
770 char *vfile;
771 char *vtag;
772 int targc;
773 char *targv[];
774{
775 int i, ierrno;
776 Vers_TS *vers;
777 int retcode = 0;
778 struct file_info finfo;
779
780 if (noexec)
781 return (0);
782
783 if ((retcode = RCS_settag(rcs, vtag, vbranch)) != 0)
784 {
785 ierrno = errno;
786 fperrmsg (logfp, 0, retcode == -1 ? ierrno : 0,
787 "ERROR: Failed to set tag %s in %s", vtag, rcs->path);
788 error (0, retcode == -1 ? ierrno : 0,
789 "ERROR: Failed to set tag %s in %s", vtag, rcs->path);
790 return (1);
791 }
792 RCS_rewrite (rcs, NULL, NULL);
793
794 memset (&finfo, 0, sizeof finfo);
795 finfo.file = vfile;
796 /* Not used, so don't worry about it. */
797 finfo.update_dir = NULL;
798 finfo.fullname = finfo.file;
799 finfo.repository = repository;
800 finfo.entries = NULL;
801 finfo.rcs = NULL;
802 vers = Version_TS (&finfo, NULL, vtag, NULL, 1, 0);
803 for (i = 0; i < targc; i++)
804 {
805 if ((retcode = RCS_settag (rcs, targv[i], vers->vn_rcs)) == 0)
806 RCS_rewrite (rcs, NULL, NULL);
807 else
808 {
809 ierrno = errno;
810 fperrmsg (logfp, 0, retcode == -1 ? ierrno : 0,
811 "WARNING: Couldn't add tag %s to %s", targv[i],
812 rcs->path);
813 error (0, retcode == -1 ? ierrno : 0,
814 "WARNING: Couldn't add tag %s to %s", targv[i],
815 rcs->path);
816 }
817 }
818 freevers_ts (&vers);
819 return (0);
820}
821
822/*
823 * Stolen from rcs/src/rcsfnms.c, and adapted/extended.
824 */
825struct compair
826{
827 char *suffix, *comlead;
828};
829
830static const struct compair comtable[] =
831{
832
833/*
834 * comtable pairs each filename suffix with a comment leader. The comment
835 * leader is placed before each line generated by the $Log keyword. This
836 * table is used to guess the proper comment leader from the working file's
837 * suffix during initial ci (see InitAdmin()). Comment leaders are needed for
838 * languages without multiline comments; for others they are optional.
839 *
840 * I believe that the comment leader is unused if you are using RCS 5.7, which
841 * decides what leader to use based on the text surrounding the $Log keyword
842 * rather than a specified comment leader.
843 */
844 {"a", "-- "}, /* Ada */
845 {"ada", "-- "},
846 {"adb", "-- "},
847 {"asm", ";; "}, /* assembler (MS-DOS) */
848 {"ads", "-- "}, /* Ada */
849 {"bas", "' "}, /* Visual Basic code */
850 {"bat", ":: "}, /* batch (MS-DOS) */
851 {"body", "-- "}, /* Ada */
852 {"c", " * "}, /* C */
853 {"c++", "// "}, /* C++ in all its infinite guises */
854 {"cc", "// "},
855 {"cpp", "// "},
856 {"cxx", "// "},
857 {"m", "// "}, /* Objective-C */
858 {"cl", ";;; "}, /* Common Lisp */
859 {"cmd", ":: "}, /* command (OS/2) */
860 {"cmf", "c "}, /* CM Fortran */
861 {"cs", " * "}, /* C* */
862 {"csh", "# "}, /* shell */
863 {"dlg", " * "}, /* MS Windows dialog file */
864 {"e", "# "}, /* efl */
865 {"epsf", "% "}, /* encapsulated postscript */
866 {"epsi", "% "}, /* encapsulated postscript */
867 {"el", "; "}, /* Emacs Lisp */
868 {"f", "c "}, /* Fortran */
869 {"for", "c "},
870 {"frm", "' "}, /* Visual Basic form */
871 {"h", " * "}, /* C-header */
872 {"hh", "// "}, /* C++ header */
873 {"hpp", "// "},
874 {"hxx", "// "},
875 {"in", "# "}, /* for Makefile.in */
876 {"l", " * "}, /* lex (conflict between lex and
877 * franzlisp) */
878 {"mac", ";; "}, /* macro (DEC-10, MS-DOS, PDP-11,
879 * VMS, etc) */
880 {"mak", "# "}, /* makefile, e.g. Visual C++ */
881 {"me", ".\\\" "}, /* me-macros t/nroff */
882 {"ml", "; "}, /* mocklisp */
883 {"mm", ".\\\" "}, /* mm-macros t/nroff */
884 {"ms", ".\\\" "}, /* ms-macros t/nroff */
885 {"man", ".\\\" "}, /* man-macros t/nroff */
886 {"1", ".\\\" "}, /* feeble attempt at man pages... */
887 {"2", ".\\\" "},
888 {"3", ".\\\" "},
889 {"4", ".\\\" "},
890 {"5", ".\\\" "},
891 {"6", ".\\\" "},
892 {"7", ".\\\" "},
893 {"8", ".\\\" "},
894 {"9", ".\\\" "},
895 {"p", " * "}, /* pascal */
896 {"pas", " * "},
897 {"pl", "# "}, /* perl (conflict with Prolog) */
898 {"ps", "% "}, /* postscript */
899 {"psw", "% "}, /* postscript wrap */
900 {"pswm", "% "}, /* postscript wrap */
901 {"r", "# "}, /* ratfor */
902 {"rc", " * "}, /* Microsoft Windows resource file */
903 {"red", "% "}, /* psl/rlisp */
904#ifdef sparc
905 {"s", "! "}, /* assembler */
906#endif
907#ifdef mc68000
908 {"s", "| "}, /* assembler */
909#endif
910#ifdef pdp11
911 {"s", "/ "}, /* assembler */
912#endif
913#ifdef vax
914 {"s", "# "}, /* assembler */
915#endif
916#ifdef __ksr__
917 {"s", "# "}, /* assembler */
918 {"S", "# "}, /* Macro assembler */
919#endif
920 {"sh", "# "}, /* shell */
921 {"sl", "% "}, /* psl */
922 {"spec", "-- "}, /* Ada */
923 {"tex", "% "}, /* tex */
924 {"y", " * "}, /* yacc */
925 {"ye", " * "}, /* yacc-efl */
926 {"yr", " * "}, /* yacc-ratfor */
927 {"", "# "}, /* default for empty suffix */
928 {NULL, "# "} /* default for unknown suffix; */
929/* must always be last */
930};
931
932static char *
933get_comment (user)
934 char *user;
935{
936 char *cp, *suffix;
937 char *suffix_path;
938 int i;
939 char *retval;
940
941 suffix_path = xmalloc (strlen (user) + 5);
942 cp = strrchr (user, '.');
943 if (cp != NULL)
944 {
945 cp++;
946
947 /*
948 * Convert to lower-case, since we are not concerned about the
949 * case-ness of the suffix.
950 */
951 (void) strcpy (suffix_path, cp);
952 for (cp = suffix_path; *cp; cp++)
953 if (isupper ((unsigned char) *cp))
954 *cp = tolower (*cp);
955 suffix = suffix_path;
956 }
957 else
958 suffix = ""; /* will use the default */
959 for (i = 0;; i++)
960 {
961 if (comtable[i].suffix == NULL)
962 {
963 /* Default. Note we'll always hit this case before we
964 ever return NULL. */
965 retval = comtable[i].comlead;
966 break;
967 }
968 if (strcmp (suffix, comtable[i].suffix) == 0)
969 {
970 retval = comtable[i].comlead;
971 break;
972 }
973 }
974 free (suffix_path);
975 return retval;
976}
977
978/* Create a new RCS file from scratch.
979
980 This probably should be moved to rcs.c now that it is called from
981 places outside import.c.
982
983 Return value is 0 for success, or nonzero for failure (in which
984 case an error message will have already been printed). */
985int
986add_rcs_file (message, rcs, user, add_vhead, key_opt,
987 add_vbranch, vtag, targc, targv,
988 desctext, desclen, add_logfp)
989 /* Log message for the addition. Not used if add_vhead == NULL. */
990 char *message;
991 /* Filename of the RCS file to create. */
992 char *rcs;
993 /* Filename of the file to serve as the contents of the initial
994 revision. Even if add_vhead is NULL, we use this to determine
995 the modes to give the new RCS file. */
996 char *user;
997
998 /* Revision number of head that we are adding. Normally 1.1 but
999 could be another revision as long as ADD_VBRANCH is a branch
1000 from it. If NULL, then just add an empty file without any
1001 revisions (similar to the one created by "rcs -i"). */
1002 char *add_vhead;
1003
1004 /* Keyword expansion mode, e.g., "b" for binary. NULL means the
1005 default behavior. */
1006 char *key_opt;
1007
1008 /* Vendor branch to import to, or NULL if none. If non-NULL, then
1009 vtag should also be non-NULL. */
1010 char *add_vbranch;
1011 char *vtag;
1012 int targc;
1013 char *targv[];
1014
1015 /* If non-NULL, description for the file. If NULL, the description
1016 will be empty. */
1017 char *desctext;
1018 size_t desclen;
1019
1020 /* Write errors to here as well as via error (), or NULL if we should
1021 use only error (). */
1022 FILE *add_logfp;
1023{
1024 FILE *fprcs, *fpuser;
1025 struct stat sb;
1026 struct tm *ftm;
1027 time_t now;
1028 char altdate1[MAXDATELEN];
1029 char *author;
1030 int i, ierrno, err = 0;
1031 mode_t mode;
1032 char *tocvsPath;
1033 char *userfile;
1034 char *local_opt = key_opt;
1035 char *free_opt = NULL;
1036 mode_t file_type;
1037
1038 if (noexec)
1039 return (0);
1040
1041 /* Note that as the code stands now, the -k option overrides any
1042 settings in wrappers (whether CVSROOT/cvswrappers, -W, or
1043 whatever). Some have suggested this should be the other way
1044 around. As far as I know the documentation doesn't say one way
1045 or the other. Before making a change of this sort, should think
1046 about what is best, document it (in cvs.texinfo and NEWS), &c. */
1047
1048 if (local_opt == NULL)
1049 {
1050 if (wrap_name_has (user, WRAP_RCSOPTION))
1051 {
1052 local_opt = free_opt = wrap_rcsoption (user, 0);
1053 }
1054 }
1055
1056 tocvsPath = wrap_tocvs_process_file (user);
1057 userfile = (tocvsPath == NULL ? user : tocvsPath);
1058
1059 /* Opening in text mode is probably never the right thing for the
1060 server (because the protocol encodes text files in a fashion
1061 which does not depend on what the client or server OS is, as
1062 documented in cvsclient.texi), but as long as the server just
1063 runs on unix it is a moot point. */
1064
1065 /* If PreservePermissions is set, then make sure that the file
1066 is a plain file before trying to open it. Longstanding (although
1067 often unpopular) CVS behavior has been to follow symlinks, so we
1068 maintain that behavior if PreservePermissions is not on.
1069
1070 NOTE: this error message used to be `cannot fstat', but is now
1071 `cannot lstat'. I don't see a way around this, since we must
1072 stat the file before opening it. -twp */
1073
1074 if (CVS_LSTAT (userfile, &sb) < 0)
1075 {
1076 /* not fatal, continue import */
1077 if (add_logfp != NULL)
1078 fperrmsg (add_logfp, 0, errno,
1079 "ERROR: cannot lstat file %s", userfile);
1080 error (0, errno, "cannot lstat file %s", userfile);
1081 goto read_error;
1082 }
1083 file_type = sb.st_mode & S_IFMT;
1084
1085 fpuser = NULL;
1086 if (!preserve_perms || file_type == S_IFREG)
1087 {
1088 fpuser = CVS_FOPEN (userfile,
1089 ((local_opt != NULL && strcmp (local_opt, "b") == 0)
1090 ? "rb"
1091 : "r")
1092 );
1093 if (fpuser == NULL)
1094 {
1095 /* not fatal, continue import */
1096 if (add_logfp != NULL)
1097 fperrmsg (add_logfp, 0, errno,
1098 "ERROR: cannot read file %s", userfile);
1099 error (0, errno, "ERROR: cannot read file %s", userfile);
1100 goto read_error;
1101 }
1102 }
1103
1104 fprcs = CVS_FOPEN (rcs, "w+b");
1105 if (fprcs == NULL)
1106 {
1107 ierrno = errno;
1108 goto write_error_noclose;
1109 }
1110
1111 /*
1112 * putadmin()
1113 */
1114 if (add_vhead != NULL)
1115 {
1116 if (fprintf (fprcs, "head %s;\012", add_vhead) < 0)
1117 goto write_error;
1118 }
1119 else
1120 {
1121 if (fprintf (fprcs, "head ;\012") < 0)
1122 goto write_error;
1123 }
1124
1125 if (add_vbranch != NULL)
1126 {
1127 if (fprintf (fprcs, "branch %s;\012", add_vbranch) < 0)
1128 goto write_error;
1129 }
1130 if (fprintf (fprcs, "access ;\012") < 0 ||
1131 fprintf (fprcs, "symbols ") < 0)
1132 {
1133 goto write_error;
1134 }
1135
1136 for (i = targc - 1; i >= 0; i--)
1137 {
1138 /* RCS writes the symbols backwards */
1139 assert (add_vbranch != NULL);
1140 if (fprintf (fprcs, "%s:%s.1 ", targv[i], add_vbranch) < 0)
1141 goto write_error;
1142 }
1143
1144 if (add_vbranch != NULL)
1145 {
1146 if (fprintf (fprcs, "%s:%s", vtag, add_vbranch) < 0)
1147 goto write_error;
1148 }
1149 if (fprintf (fprcs, ";\012") < 0)
1150 goto write_error;
1151
1152 if (fprintf (fprcs, "locks ; strict;\012") < 0 ||
1153 /* XXX - make sure @@ processing works in the RCS file */
1154 fprintf (fprcs, "comment @%s@;\012", get_comment (user)) < 0)
1155 {
1156 goto write_error;
1157 }
1158
1159 if (local_opt != NULL && strcmp (local_opt, "kv") != 0)
1160 {
1161 if (fprintf (fprcs, "expand @%s@;\012", local_opt) < 0)
1162 {
1163 goto write_error;
1164 }
1165 }
1166
1167 if (fprintf (fprcs, "\012") < 0)
1168 goto write_error;
1169
1170 /* Write the revision(s), with the date and author and so on
1171 (that is "delta" rather than "deltatext" from rcsfile(5)). */
1172 if (add_vhead != NULL)
1173 {
1174 if (use_file_modtime)
1175 now = sb.st_mtime;
1176 else
1177 (void) time (&now);
1178 ftm = gmtime (&now);
1179 (void) sprintf (altdate1, DATEFORM,
1180 ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
1181 ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
1182 ftm->tm_min, ftm->tm_sec);
1183 author = getcaller ();
1184
1185 if (fprintf (fprcs, "\012%s\012", add_vhead) < 0 ||
1186 fprintf (fprcs, "date %s; author %s; state Exp;\012",
1187 altdate1, author) < 0)
1188 goto write_error;
1189
1190 if (fprintf (fprcs, "branches") < 0)
1191 goto write_error;
1192 if (add_vbranch != NULL)
1193 {
1194 if (fprintf (fprcs, " %s.1", add_vbranch) < 0)
1195 goto write_error;
1196 }
1197 if (fprintf (fprcs, ";\012") < 0)
1198 goto write_error;
1199
1200 if (fprintf (fprcs, "next ;\012") < 0)
1201 goto write_error;
1202
1203#ifdef PRESERVE_PERMISSIONS_SUPPORT
1204 /* Store initial permissions if necessary. */
1205 if (preserve_perms)
1206 {
1207 if (file_type == S_IFLNK)
1208 {
1209 char *link = xreadlink (userfile);
1210 if (fprintf (fprcs, "symlink\t@") < 0 ||
1211 expand_at_signs (link, strlen (link), fprcs) < 0 ||
1212 fprintf (fprcs, "@;\012") < 0)
1213 goto write_error;
1214 free (link);
1215 }
1216 else
1217 {
1218 if (fprintf (fprcs, "owner\t%u;\012", sb.st_uid) < 0)
1219 goto write_error;
1220 if (fprintf (fprcs, "group\t%u;\012", sb.st_gid) < 0)
1221 goto write_error;
1222 if (fprintf (fprcs, "permissions\t%o;\012",
1223 sb.st_mode & 07777) < 0)
1224 goto write_error;
1225 switch (file_type)
1226 {
1227 case S_IFREG: break;
1228 case S_IFCHR:
1229 case S_IFBLK:
1230#ifdef HAVE_ST_RDEV
1231 if (fprintf (fprcs, "special\t%s %lu;\012",
1232 (file_type == S_IFCHR
1233 ? "character"
1234 : "block"),
1235 (unsigned long) sb.st_rdev) < 0)
1236 goto write_error;
1237#else
1238 error (0, 0,
1239"can't import %s: unable to import device files on this system",
1240userfile);
1241#endif
1242 break;
1243 default:
1244 error (0, 0,
1245 "can't import %s: unknown kind of special file",
1246 userfile);
1247 }
1248 }
1249 }
1250#endif
1251
1252 if (add_vbranch != NULL)
1253 {
1254 if (fprintf (fprcs, "\012%s.1\012", add_vbranch) < 0 ||
1255 fprintf (fprcs, "date %s; author %s; state Exp;\012",
1256 altdate1, author) < 0 ||
1257 fprintf (fprcs, "branches ;\012") < 0 ||
1258 fprintf (fprcs, "next ;\012") < 0)
1259 goto write_error;
1260
1261#ifdef PRESERVE_PERMISSIONS_SUPPORT
1262 /* Store initial permissions if necessary. */
1263 if (preserve_perms)
1264 {
1265 if (file_type == S_IFLNK)
1266 {
1267 char *link = xreadlink (userfile);
1268 if (fprintf (fprcs, "symlink\t@") < 0 ||
1269 expand_at_signs (link, strlen (link), fprcs) < 0 ||
1270 fprintf (fprcs, "@;\012") < 0)
1271 goto write_error;
1272 free (link);
1273 }
1274 else
1275 {
1276 if (fprintf (fprcs, "owner\t%u;\012", sb.st_uid) < 0 ||
1277 fprintf (fprcs, "group\t%u;\012", sb.st_gid) < 0 ||
1278 fprintf (fprcs, "permissions\t%o;\012",
1279 sb.st_mode & 07777) < 0)
1280 goto write_error;
1281
1282 switch (file_type)
1283 {
1284 case S_IFREG: break;
1285 case S_IFCHR:
1286 case S_IFBLK:
1287#ifdef HAVE_ST_RDEV
1288 if (fprintf (fprcs, "special\t%s %lu;\012",
1289 (file_type == S_IFCHR
1290 ? "character"
1291 : "block"),
1292 (unsigned long) sb.st_rdev) < 0)
1293 goto write_error;
1294#else
1295 error (0, 0,
1296"can't import %s: unable to import device files on this system",
1297userfile);
1298#endif
1299 break;
1300 default:
1301 error (0, 0,
1302 "cannot import %s: special file of unknown type",
1303 userfile);
1304 }
1305 }
1306 }
1307#endif
1308
1309 if (fprintf (fprcs, "\012") < 0)
1310 goto write_error;
1311 }
1312 }
1313
1314 /* Now write the description (possibly empty). */
1315 if (fprintf (fprcs, "\012desc\012") < 0 ||
1316 fprintf (fprcs, "@") < 0)
1317 goto write_error;
1318 if (desctext != NULL)
1319 {
1320 /* The use of off_t not size_t for the second argument is very
1321 strange, since we are dealing with something which definitely
1322 fits in memory. */
1323 if (expand_at_signs (desctext, (off_t) desclen, fprcs) < 0)
1324 goto write_error;
1325 }
1326 if (fprintf (fprcs, "@\012\012\012") < 0)
1327 goto write_error;
1328
1329 /* Now write the log messages and contents for the revision(s) (that
1330 is, "deltatext" rather than "delta" from rcsfile(5)). */
1331 if (add_vhead != NULL)
1332 {
1333 if (fprintf (fprcs, "\012%s\012", add_vhead) < 0 ||
1334 fprintf (fprcs, "log\012@") < 0)
1335 goto write_error;
1336 if (add_vbranch != NULL)
1337 {
1338 /* We are going to put the log message in the revision on the
1339 branch. So putting it here too seems kind of redundant, I
1340 guess (and that is what CVS has always done, anyway). */
1341 if (fprintf (fprcs, "Initial revision\012") < 0)
1342 goto write_error;
1343 }
1344 else
1345 {
1346 if (expand_at_signs (message, (off_t) strlen (message), fprcs) < 0)
1347 goto write_error;
1348 }
1349 if (fprintf (fprcs, "@\012") < 0 ||
1350 fprintf (fprcs, "text\012@") < 0)
1351 {
1352 goto write_error;
1353 }
1354
1355 /* Now copy over the contents of the file, expanding at signs.
1356 If preserve_perms is set, do this only for regular files. */
1357 if (!preserve_perms || file_type == S_IFREG)
1358 {
1359 char buf[8192];
1360 unsigned int len;
1361
1362 while (1)
1363 {
1364 len = fread (buf, 1, sizeof buf, fpuser);
1365 if (len == 0)
1366 {
1367 if (ferror (fpuser))
1368 error (1, errno, "cannot read file %s for copying",
1369 user);
1370 break;
1371 }
1372 if (expand_at_signs (buf, len, fprcs) < 0)
1373 goto write_error;
1374 }
1375 }
1376 if (fprintf (fprcs, "@\012\012") < 0)
1377 goto write_error;
1378 if (add_vbranch != NULL)
1379 {
1380 if (fprintf (fprcs, "\012%s.1\012", add_vbranch) < 0 ||
1381 fprintf (fprcs, "log\012@") < 0 ||
1382 expand_at_signs (message,
1383 (off_t) strlen (message), fprcs) < 0 ||
1384 fprintf (fprcs, "@\012text\012") < 0 ||
1385 fprintf (fprcs, "@@\012") < 0)
1386 goto write_error;
1387 }
1388 }
1389
1390 if (fclose (fprcs) == EOF)
1391 {
1392 ierrno = errno;
1393 goto write_error_noclose;
1394 }
1395 /* Close fpuser only if we opened it to begin with. */
1396 if (fpuser != NULL)
1397 {
1398 if (fclose (fpuser) < 0)
1399 error (0, errno, "cannot close %s", user);
1400 }
1401
1402 /*
1403 * Fix the modes on the RCS files. The user modes of the original
1404 * user file are propagated to the group and other modes as allowed
1405 * by the repository umask, except that all write permissions are
1406 * turned off.
1407 */
1408 mode = (sb.st_mode |
1409 (sb.st_mode & S_IRWXU) >> 3 |
1410 (sb.st_mode & S_IRWXU) >> 6) &
1411 ~cvsumask &
1412 ~(S_IWRITE | S_IWGRP | S_IWOTH);
1413 if (chmod (rcs, mode) < 0)
1414 {
1415 ierrno = errno;
1416 if (add_logfp != NULL)
1417 fperrmsg (add_logfp, 0, ierrno,
1418 "WARNING: cannot change mode of file %s", rcs);
1419 error (0, ierrno, "WARNING: cannot change mode of file %s", rcs);
1420 err++;
1421 }
1422 if (tocvsPath)
1423 if (unlink_file_dir (tocvsPath) < 0)
1424 error (0, errno, "cannot remove %s", tocvsPath);
1425 if (free_opt != NULL)
1426 free (free_opt);
1427 return (err);
1428
1429write_error:
1430 ierrno = errno;
1431 if (fclose (fprcs) < 0)
1432 error (0, errno, "cannot close %s", rcs);
1433write_error_noclose:
1434 if (fclose (fpuser) < 0)
1435 error (0, errno, "cannot close %s", user);
1436 if (add_logfp != NULL)
1437 fperrmsg (add_logfp, 0, ierrno, "ERROR: cannot write file %s", rcs);
1438 error (0, ierrno, "ERROR: cannot write file %s", rcs);
1439 if (ierrno == ENOSPC)
1440 {
1441 if (CVS_UNLINK (rcs) < 0)
1442 error (0, errno, "cannot remove %s", rcs);
1443 if (add_logfp != NULL)
1444 fperrmsg (add_logfp, 0, 0, "ERROR: out of space - aborting");
1445 error (1, 0, "ERROR: out of space - aborting");
1446 }
1447read_error:
1448 if (tocvsPath)
1449 if (unlink_file_dir (tocvsPath) < 0)
1450 error (0, errno, "cannot remove %s", tocvsPath);
1451
1452 if (free_opt != NULL)
1453 free (free_opt);
1454
1455 return (err + 1);
1456}
1457
1458/*
1459 * Write SIZE bytes at BUF to FP, expanding @ signs into double @
1460 * signs. If an error occurs, return a negative value and set errno
1461 * to indicate the error. If not, return a nonnegative value.
1462 */
1463int
1464expand_at_signs (buf, size, fp)
1465 char *buf;
1466 off_t size;
1467 FILE *fp;
1468{
1469 register char *cp, *next;
1470
1471 cp = buf;
1472 while ((next = memchr (cp, '@', size)) != NULL)
1473 {
1474 int len;
1475
1476 ++next;
1477 len = next - cp;
1478 if (fwrite (cp, 1, len, fp) != len)
1479 return EOF;
1480 if (putc ('@', fp) == EOF)
1481 return EOF;
1482 cp = next;
1483 size -= len;
1484 }
1485
1486 if (fwrite (cp, 1, size, fp) != size)
1487 return EOF;
1488
1489 return 1;
1490}
1491
1492/*
1493 * Write an update message to (potentially) the screen and the log file.
1494 */
1495static void
1496add_log (ch, fname)
1497 int ch;
1498 char *fname;
1499{
1500 if (!really_quiet) /* write to terminal */
1501 {
1502 char buf[2];
1503 buf[0] = ch;
1504 buf[1] = ' ';
1505 cvs_output (buf, 2);
1506 if (repos_len)
1507 {
1508 cvs_output (repository + repos_len + 1, 0);
1509 cvs_output ("/", 1);
1510 }
1511 else if (repository[0] != '\0')
1512 {
1513 cvs_output (repository, 0);
1514 cvs_output ("/", 1);
1515 }
1516 cvs_output (fname, 0);
1517 cvs_output ("\n", 1);
1518 }
1519
1520 if (repos_len) /* write to logfile */
1521 (void) fprintf (logfp, "%c %s/%s\n", ch,
1522 repository + repos_len + 1, fname);
1523 else if (repository[0])
1524 (void) fprintf (logfp, "%c %s/%s\n", ch, repository, fname);
1525 else
1526 (void) fprintf (logfp, "%c %s\n", ch, fname);
1527}
1528
1529/*
1530 * This is the recursive function that walks the argument directory looking
1531 * for sub-directories that have CVS administration files in them and updates
1532 * them recursively.
1533 *
1534 * Note that we do not follow symbolic links here, which is a feature!
1535 */
1536static int
1537import_descend_dir (message, dir, vtag, targc, targv)
1538 char *message;
1539 char *dir;
1540 char *vtag;
1541 int targc;
1542 char *targv[];
1543{
1544 struct saved_cwd cwd;
1545 char *cp;
1546 int ierrno, err;
1547 char *rcs = NULL;
1548
1549 if (islink (dir))
1550 return (0);
1551 if (save_cwd (&cwd))
1552 {
1553 fperrmsg (logfp, 0, 0, "ERROR: cannot get working directory");
1554 return (1);
1555 }
1556
1557 /* Concatenate DIR to the end of REPOSITORY. */
1558 if (repository[0] == '\0')
1559 {
1560 char *new = xstrdup (dir);
1561 free (repository);
1562 repository = new;
1563 }
1564 else
1565 {
1566 char *new = xmalloc (strlen (repository) + strlen (dir) + 10);
1567 strcpy (new, repository);
1568 (void) strcat (new, "/");
1569 (void) strcat (new, dir);
1570 free (repository);
1571 repository = new;
1572 }
1573
1574#ifdef CLIENT_SUPPORT
1575 if (!quiet && !current_parsed_root->isremote)
1576#else
1577 if (!quiet)
1578#endif
1579 error (0, 0, "Importing %s", repository);
1580
1581 if ( CVS_CHDIR (dir) < 0)
1582 {
1583 ierrno = errno;
1584 fperrmsg (logfp, 0, ierrno, "ERROR: cannot chdir to %s", repository);
1585 error (0, ierrno, "ERROR: cannot chdir to %s", repository);
1586 err = 1;
1587 goto out;
1588 }
1589#ifdef CLIENT_SUPPORT
1590 if (!current_parsed_root->isremote && !isdir (repository))
1591#else
1592 if (!isdir (repository))
1593#endif
1594 {
1595 rcs = xmalloc (strlen (repository) + sizeof (RCSEXT) + 5);
1596 (void) sprintf (rcs, "%s%s", repository, RCSEXT);
1597 if (isfile (repository) || isfile(rcs))
1598 {
1599 fperrmsg (logfp, 0, 0,
1600 "ERROR: %s is a file, should be a directory!",
1601 repository);
1602 error (0, 0, "ERROR: %s is a file, should be a directory!",
1603 repository);
1604 err = 1;
1605 goto out;
1606 }
1607 if (noexec == 0 && CVS_MKDIR (repository, 0777) < 0)
1608 {
1609 ierrno = errno;
1610 fperrmsg (logfp, 0, ierrno,
1611 "ERROR: cannot mkdir %s -- not added", repository);
1612 error (0, ierrno,
1613 "ERROR: cannot mkdir %s -- not added", repository);
1614 err = 1;
1615 goto out;
1616 }
1617 }
1618 err = import_descend (message, vtag, targc, targv);
1619 out:
1620 if (rcs != NULL)
1621 free (rcs);
1622 if ((cp = strrchr (repository, '/')) != NULL)
1623 *cp = '\0';
1624 else
1625 repository[0] = '\0';
1626 if (restore_cwd (&cwd, NULL))
1627 error_exit ();
1628 free_cwd (&cwd);
1629 return (err);
1630}