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