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