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