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