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