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