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