1a7c91847Schristos /*
2a7c91847Schristos * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
3a7c91847Schristos *
4a7c91847Schristos * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5a7c91847Schristos * and others.
6a7c91847Schristos *
7a7c91847Schristos * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8a7c91847Schristos * Portions Copyright (C) 1989-1992, Brian Berliner
9a7c91847Schristos *
10a7c91847Schristos * You may distribute under the terms of the GNU General Public License as
11a7c91847Schristos * specified in the README file that comes with the CVS source distribution.
12a7c91847Schristos *
13a7c91847Schristos * Commit Files
14a7c91847Schristos *
15a7c91847Schristos * "commit" commits the present version to the RCS repository, AFTER
16a7c91847Schristos * having done a test on conflicts.
17a7c91847Schristos *
18a7c91847Schristos * The call is: cvs commit [options] files...
19a7c91847Schristos *
20a7c91847Schristos */
21*3cd63638Schristos #include <sys/cdefs.h>
22*3cd63638Schristos __RCSID("$NetBSD: commit.c,v 1.5 2016/05/17 14:00:09 christos Exp $");
23a7c91847Schristos
24a7c91847Schristos #include "cvs.h"
25a7c91847Schristos #include "getline.h"
26a7c91847Schristos #include "edit.h"
27a7c91847Schristos #include "fileattr.h"
28a7c91847Schristos #include "hardlink.h"
29a7c91847Schristos
30a7c91847Schristos static Dtype check_direntproc (void *callerdat, const char *dir,
31a7c91847Schristos const char *repos, const char *update_dir,
32a7c91847Schristos List *entries);
33a7c91847Schristos static int check_fileproc (void *callerdat, struct file_info *finfo);
34a7c91847Schristos static int check_filesdoneproc (void *callerdat, int err, const char *repos,
35a7c91847Schristos const char *update_dir, List *entries);
36a7c91847Schristos static int checkaddfile (const char *file, const char *repository,
37a7c91847Schristos const char *tag, const char *options,
38a7c91847Schristos RCSNode **rcsnode);
39a7c91847Schristos static Dtype commit_direntproc (void *callerdat, const char *dir,
40a7c91847Schristos const char *repos, const char *update_dir,
41a7c91847Schristos List *entries);
42a7c91847Schristos static int commit_dirleaveproc (void *callerdat, const char *dir, int err,
43a7c91847Schristos const char *update_dir, List *entries);
44a7c91847Schristos static int commit_fileproc (void *callerdat, struct file_info *finfo);
45a7c91847Schristos static int commit_filesdoneproc (void *callerdat, int err,
46a7c91847Schristos const char *repository,
47a7c91847Schristos const char *update_dir, List *entries);
48a7c91847Schristos static int finaladd (struct file_info *finfo, char *revision, char *tag,
49a7c91847Schristos char *options);
50a7c91847Schristos static int findmaxrev (Node * p, void *closure);
51a7c91847Schristos static int lock_RCS (const char *user, RCSNode *rcs, const char *rev,
52a7c91847Schristos const char *repository);
53a7c91847Schristos static int precommit_list_to_args_proc (Node * p, void *closure);
54a7c91847Schristos static int precommit_proc (const char *repository, const char *filter,
55a7c91847Schristos void *closure);
56a7c91847Schristos static int remove_file (struct file_info *finfo, char *tag,
57a7c91847Schristos char *message);
58a7c91847Schristos static void fixaddfile (const char *rcs);
59a7c91847Schristos static void fixbranch (RCSNode *, char *branch);
60a7c91847Schristos static void unlockrcs (RCSNode *rcs);
61a7c91847Schristos static void ci_delproc (Node *p);
62a7c91847Schristos static void masterlist_delproc (Node *p);
63a7c91847Schristos
64a7c91847Schristos struct commit_info
65a7c91847Schristos {
66a7c91847Schristos Ctype status; /* as returned from Classify_File() */
67a7c91847Schristos char *rev; /* a numeric rev, if we know it */
68a7c91847Schristos char *tag; /* any sticky tag, or -r option */
69a7c91847Schristos char *options; /* Any sticky -k option */
70a7c91847Schristos };
71a7c91847Schristos struct master_lists
72a7c91847Schristos {
73a7c91847Schristos List *ulist; /* list for Update_Logfile */
74a7c91847Schristos List *cilist; /* list with commit_info structs */
75a7c91847Schristos };
76a7c91847Schristos
77a7c91847Schristos static int check_valid_edit = 0;
78a7c91847Schristos static int force_ci = 0;
79a7c91847Schristos static int got_message;
80a7c91847Schristos static int aflag;
81a7c91847Schristos static char *saved_tag;
82a7c91847Schristos static char *write_dirtag;
83a7c91847Schristos static int write_dirnonbranch;
84a7c91847Schristos static char *logfile;
85a7c91847Schristos static List *mulist;
86a7c91847Schristos static char *saved_message;
87a7c91847Schristos static time_t last_register_time;
88a7c91847Schristos
89a7c91847Schristos static const char *const commit_usage[] =
90a7c91847Schristos {
91a7c91847Schristos "Usage: %s %s [-cRlf] [-m msg | -F logfile] [-r rev] files...\n",
92a7c91847Schristos " -c Check for valid edits before committing.\n",
93a7c91847Schristos " -R Process directories recursively.\n",
94a7c91847Schristos " -l Local directory only (not recursive).\n",
95a7c91847Schristos " -f Force the file to be committed; disables recursion.\n",
96a7c91847Schristos " -F logfile Read the log message from file.\n",
97a7c91847Schristos " -m msg Log message.\n",
98a7c91847Schristos " -r rev Commit to this branch or trunk revision.\n",
99a7c91847Schristos "(Specify the --help global option for a list of other help options)\n",
100a7c91847Schristos NULL
101a7c91847Schristos };
102a7c91847Schristos
103a7c91847Schristos #ifdef CLIENT_SUPPORT
104a7c91847Schristos /* Identify a file which needs "? foo" or a Questionable request. */
105a7c91847Schristos struct question
106a7c91847Schristos {
107a7c91847Schristos /* The two fields for the Directory request. */
108a7c91847Schristos char *dir;
109a7c91847Schristos char *repos;
110a7c91847Schristos
111a7c91847Schristos /* The file name. */
112a7c91847Schristos char *file;
113a7c91847Schristos
114a7c91847Schristos struct question *next;
115a7c91847Schristos };
116a7c91847Schristos
117a7c91847Schristos struct find_data
118a7c91847Schristos {
119a7c91847Schristos List *ulist;
120a7c91847Schristos int argc;
121a7c91847Schristos char **argv;
122a7c91847Schristos
123a7c91847Schristos /* This is used from dirent to filesdone time, for each directory,
124a7c91847Schristos to make a list of files we have already seen. */
125a7c91847Schristos List *ignlist;
126a7c91847Schristos
127a7c91847Schristos /* Linked list of files which need "? foo" or a Questionable request. */
128a7c91847Schristos struct question *questionables;
129a7c91847Schristos
130a7c91847Schristos /* Only good within functions called from the filesdoneproc. Stores
131a7c91847Schristos the repository (pointer into storage managed by the recursion
132a7c91847Schristos processor. */
133a7c91847Schristos const char *repository;
134a7c91847Schristos
135a7c91847Schristos /* Non-zero if we should force the commit. This is enabled by
136a7c91847Schristos either -f or -r options, unlike force_ci which is just -f. */
137a7c91847Schristos int force;
138a7c91847Schristos };
139a7c91847Schristos
140a7c91847Schristos
141a7c91847Schristos
142a7c91847Schristos static Dtype
find_dirent_proc(void * callerdat,const char * dir,const char * repository,const char * update_dir,List * entries)143a7c91847Schristos find_dirent_proc (void *callerdat, const char *dir, const char *repository,
144a7c91847Schristos const char *update_dir, List *entries)
145a7c91847Schristos {
146a7c91847Schristos struct find_data *find_data = callerdat;
147a7c91847Schristos
148a7c91847Schristos /* This check seems to slowly be creeping throughout CVS (update
149a7c91847Schristos and send_dirent_proc by CVS 1.5, diff in 31 Oct 1995. My guess
150a7c91847Schristos is that it (or some variant thereof) should go in all the
151a7c91847Schristos dirent procs. Unless someone has some better idea... */
152a7c91847Schristos if (!isdir (dir))
153a7c91847Schristos return R_SKIP_ALL;
154a7c91847Schristos
155a7c91847Schristos /* initialize the ignore list for this directory */
156a7c91847Schristos find_data->ignlist = getlist ();
157a7c91847Schristos
158a7c91847Schristos /* Print the same warm fuzzy as in check_direntproc, since that
159a7c91847Schristos code will never be run during client/server operation and we
160a7c91847Schristos want the messages to match. */
161a7c91847Schristos if (!quiet)
162a7c91847Schristos error (0, 0, "Examining %s", update_dir);
163a7c91847Schristos
164a7c91847Schristos return R_PROCESS;
165a7c91847Schristos }
166a7c91847Schristos
167a7c91847Schristos
168a7c91847Schristos
169a7c91847Schristos /* Here as a static until we get around to fixing ignore_files to pass
170a7c91847Schristos it along as an argument. */
171a7c91847Schristos static struct find_data *find_data_static;
172a7c91847Schristos
173a7c91847Schristos
174a7c91847Schristos
175a7c91847Schristos static void
find_ignproc(const char * file,const char * dir)176a7c91847Schristos find_ignproc (const char *file, const char *dir)
177a7c91847Schristos {
178a7c91847Schristos struct question *p;
179a7c91847Schristos
180a7c91847Schristos p = xmalloc (sizeof (struct question));
181a7c91847Schristos p->dir = xstrdup (dir);
182a7c91847Schristos p->repos = xstrdup (find_data_static->repository);
183a7c91847Schristos p->file = xstrdup (file);
184a7c91847Schristos p->next = find_data_static->questionables;
185a7c91847Schristos find_data_static->questionables = p;
186a7c91847Schristos }
187a7c91847Schristos
188a7c91847Schristos
189a7c91847Schristos
190a7c91847Schristos static int
find_filesdoneproc(void * callerdat,int err,const char * repository,const char * update_dir,List * entries)191a7c91847Schristos find_filesdoneproc (void *callerdat, int err, const char *repository,
192a7c91847Schristos const char *update_dir, List *entries)
193a7c91847Schristos {
194a7c91847Schristos struct find_data *find_data = callerdat;
195a7c91847Schristos find_data->repository = repository;
196a7c91847Schristos
197a7c91847Schristos /* if this directory has an ignore list, process it then free it */
198a7c91847Schristos if (find_data->ignlist)
199a7c91847Schristos {
200a7c91847Schristos find_data_static = find_data;
201a7c91847Schristos ignore_files (find_data->ignlist, entries, update_dir, find_ignproc);
202a7c91847Schristos dellist (&find_data->ignlist);
203a7c91847Schristos }
204a7c91847Schristos
205a7c91847Schristos find_data->repository = NULL;
206a7c91847Schristos
207a7c91847Schristos return err;
208a7c91847Schristos }
209a7c91847Schristos
210a7c91847Schristos
211a7c91847Schristos
212a7c91847Schristos /* Machinery to find out what is modified, added, and removed. It is
213a7c91847Schristos possible this should be broken out into a new client_classify function;
214a7c91847Schristos merging it with classify_file is almost sure to be a mess, though,
215a7c91847Schristos because classify_file has all kinds of repository processing. */
216a7c91847Schristos static int
find_fileproc(void * callerdat,struct file_info * finfo)217a7c91847Schristos find_fileproc (void *callerdat, struct file_info *finfo)
218a7c91847Schristos {
219a7c91847Schristos Vers_TS *vers;
220a7c91847Schristos enum classify_type status;
221a7c91847Schristos Node *node;
222a7c91847Schristos struct find_data *args = callerdat;
223a7c91847Schristos struct logfile_info *data;
224a7c91847Schristos struct file_info xfinfo;
225a7c91847Schristos
226a7c91847Schristos /* if this directory has an ignore list, add this file to it */
227a7c91847Schristos if (args->ignlist)
228a7c91847Schristos {
229a7c91847Schristos Node *p;
230a7c91847Schristos
231a7c91847Schristos p = getnode ();
232a7c91847Schristos p->type = FILES;
233a7c91847Schristos p->key = xstrdup (finfo->file);
234a7c91847Schristos if (addnode (args->ignlist, p) != 0)
235a7c91847Schristos freenode (p);
236a7c91847Schristos }
237a7c91847Schristos
238a7c91847Schristos xfinfo = *finfo;
239a7c91847Schristos xfinfo.repository = NULL;
240a7c91847Schristos xfinfo.rcs = NULL;
241a7c91847Schristos
242a7c91847Schristos vers = Version_TS (&xfinfo, NULL, saved_tag, NULL, 0, 0);
243a7c91847Schristos if (vers->vn_user == NULL)
244a7c91847Schristos {
245a7c91847Schristos if (vers->ts_user == NULL)
246a7c91847Schristos error (0, 0, "nothing known about `%s'", finfo->fullname);
247a7c91847Schristos else
248a7c91847Schristos error (0, 0, "use `%s add' to create an entry for `%s'",
249a7c91847Schristos program_name, finfo->fullname);
250a7c91847Schristos freevers_ts (&vers);
251a7c91847Schristos return 1;
252a7c91847Schristos }
253a7c91847Schristos if (vers->vn_user[0] == '-')
254a7c91847Schristos {
255a7c91847Schristos if (vers->ts_user != NULL)
256a7c91847Schristos {
257a7c91847Schristos error (0, 0,
258a7c91847Schristos "`%s' should be removed and is still there (or is back"
259a7c91847Schristos " again)", finfo->fullname);
260a7c91847Schristos freevers_ts (&vers);
261a7c91847Schristos return 1;
262a7c91847Schristos }
263a7c91847Schristos /* else */
264a7c91847Schristos status = T_REMOVED;
265a7c91847Schristos }
266a7c91847Schristos else if (strcmp (vers->vn_user, "0") == 0)
267a7c91847Schristos {
268a7c91847Schristos if (vers->ts_user == NULL)
269a7c91847Schristos {
270a7c91847Schristos /* This happens when one has `cvs add'ed a file, but it no
271a7c91847Schristos longer exists in the working directory at commit time.
272a7c91847Schristos FIXME: What classify_file does in this case is print
273a7c91847Schristos "new-born %s has disappeared" and removes the entry.
274a7c91847Schristos We probably should do the same. */
275a7c91847Schristos if (!really_quiet)
276a7c91847Schristos error (0, 0, "warning: new-born %s has disappeared",
277a7c91847Schristos finfo->fullname);
278a7c91847Schristos status = T_REMOVE_ENTRY;
279a7c91847Schristos }
280a7c91847Schristos else
281a7c91847Schristos status = T_ADDED;
282a7c91847Schristos }
283a7c91847Schristos else if (vers->ts_user == NULL)
284a7c91847Schristos {
285a7c91847Schristos /* FIXME: What classify_file does in this case is print
286a7c91847Schristos "%s was lost". We probably should do the same. */
287a7c91847Schristos freevers_ts (&vers);
288a7c91847Schristos return 0;
289a7c91847Schristos }
290a7c91847Schristos else if (vers->ts_rcs != NULL
291a7c91847Schristos && (args->force || strcmp (vers->ts_user, vers->ts_rcs) != 0))
292a7c91847Schristos /* If we are forcing commits, pretend that the file is
293a7c91847Schristos modified. */
294a7c91847Schristos status = T_MODIFIED;
295a7c91847Schristos else
296a7c91847Schristos {
297a7c91847Schristos /* This covers unmodified files, as well as a variety of other
298a7c91847Schristos cases. FIXME: we probably should be printing a message and
299a7c91847Schristos returning 1 for many of those cases (but I'm not sure
300a7c91847Schristos exactly which ones). */
301a7c91847Schristos freevers_ts (&vers);
302a7c91847Schristos return 0;
303a7c91847Schristos }
304a7c91847Schristos
305a7c91847Schristos node = getnode ();
306a7c91847Schristos node->key = xstrdup (finfo->fullname);
307a7c91847Schristos
308a7c91847Schristos data = xmalloc (sizeof (struct logfile_info));
309a7c91847Schristos data->type = status;
310a7c91847Schristos data->tag = xstrdup (vers->tag);
311a7c91847Schristos data->rev_old = data->rev_new = NULL;
312a7c91847Schristos
313a7c91847Schristos node->type = UPDATE;
314a7c91847Schristos node->delproc = update_delproc;
315a7c91847Schristos node->data = data;
316a7c91847Schristos (void)addnode (args->ulist, node);
317a7c91847Schristos
318a7c91847Schristos ++args->argc;
319a7c91847Schristos
320a7c91847Schristos freevers_ts (&vers);
321a7c91847Schristos return 0;
322a7c91847Schristos }
323a7c91847Schristos
324a7c91847Schristos
325a7c91847Schristos
326a7c91847Schristos static int
copy_ulist(Node * node,void * data)327a7c91847Schristos copy_ulist (Node *node, void *data)
328a7c91847Schristos {
329a7c91847Schristos struct find_data *args = data;
330a7c91847Schristos args->argv[args->argc++] = node->key;
331a7c91847Schristos return 0;
332a7c91847Schristos }
333a7c91847Schristos #endif /* CLIENT_SUPPORT */
334a7c91847Schristos
335a7c91847Schristos
336a7c91847Schristos
337a7c91847Schristos #ifdef SERVER_SUPPORT
338a7c91847Schristos # define COMMIT_OPTIONS "+cnlRm:fF:r:"
339a7c91847Schristos #else /* !SERVER_SUPPORT */
340a7c91847Schristos # define COMMIT_OPTIONS "+clRm:fF:r:"
341a7c91847Schristos #endif /* SERVER_SUPPORT */
342a7c91847Schristos int
commit(int argc,char ** argv)343a7c91847Schristos commit (int argc, char **argv)
344a7c91847Schristos {
345a7c91847Schristos int c;
346a7c91847Schristos int err = 0;
347a7c91847Schristos int local = 0;
348a7c91847Schristos
349a7c91847Schristos if (argc == -1)
350a7c91847Schristos usage (commit_usage);
351a7c91847Schristos
352a7c91847Schristos #ifdef CVS_BADROOT
353a7c91847Schristos /*
354a7c91847Schristos * For log purposes, do not allow "root" to commit files. If you look
355a7c91847Schristos * like root, but are really logged in as a non-root user, it's OK.
356a7c91847Schristos */
357a7c91847Schristos /* FIXME: Shouldn't this check be much more closely related to the
358a7c91847Schristos readonly user stuff (CVSROOT/readers, &c). That is, why should
359a7c91847Schristos root be able to "cvs init", "cvs import", &c, but not "cvs ci"? */
360a7c91847Schristos /* Who we are on the client side doesn't affect logging. */
361a7c91847Schristos if (geteuid () == (uid_t) 0 && !current_parsed_root->isremote)
362a7c91847Schristos {
363a7c91847Schristos struct passwd *pw;
364a7c91847Schristos
365a7c91847Schristos if ((pw = getpwnam (getcaller ())) == NULL)
366a7c91847Schristos error (1, 0,
367a7c91847Schristos "your apparent username (%s) is unknown to this system",
368a7c91847Schristos getcaller ());
369a7c91847Schristos if (pw->pw_uid == (uid_t) 0)
370a7c91847Schristos error (1, 0, "'root' is not allowed to commit files");
371a7c91847Schristos }
372a7c91847Schristos #endif /* CVS_BADROOT */
373a7c91847Schristos
374889c434eSchristos getoptreset ();
375a7c91847Schristos while ((c = getopt (argc, argv, COMMIT_OPTIONS)) != -1)
376a7c91847Schristos {
377a7c91847Schristos switch (c)
378a7c91847Schristos {
379a7c91847Schristos case 'c':
380a7c91847Schristos check_valid_edit = 1;
381a7c91847Schristos break;
382a7c91847Schristos #ifdef SERVER_SUPPORT
383a7c91847Schristos case 'n':
384a7c91847Schristos /* Silently ignore -n for compatibility with old
385a7c91847Schristos * clients.
386a7c91847Schristos */
387a7c91847Schristos break;
388a7c91847Schristos #endif /* SERVER_SUPPORT */
389a7c91847Schristos case 'm':
390a7c91847Schristos #ifdef FORCE_USE_EDITOR
391a7c91847Schristos use_editor = 1;
392a7c91847Schristos #else
393a7c91847Schristos use_editor = 0;
394a7c91847Schristos #endif
395a7c91847Schristos if (saved_message)
396a7c91847Schristos {
397a7c91847Schristos free (saved_message);
398a7c91847Schristos saved_message = NULL;
399a7c91847Schristos }
400a7c91847Schristos
401a7c91847Schristos saved_message = xstrdup (optarg);
402a7c91847Schristos break;
403a7c91847Schristos case 'r':
404a7c91847Schristos if (saved_tag)
405a7c91847Schristos free (saved_tag);
406a7c91847Schristos saved_tag = xstrdup (optarg);
407a7c91847Schristos break;
408a7c91847Schristos case 'l':
409a7c91847Schristos local = 1;
410a7c91847Schristos break;
411a7c91847Schristos case 'R':
412a7c91847Schristos local = 0;
413a7c91847Schristos break;
414a7c91847Schristos case 'f':
415a7c91847Schristos force_ci = 1;
416a7c91847Schristos check_valid_edit = 0;
417a7c91847Schristos local = 1; /* also disable recursion */
418a7c91847Schristos break;
419a7c91847Schristos case 'F':
420a7c91847Schristos #ifdef FORCE_USE_EDITOR
421a7c91847Schristos use_editor = 1;
422a7c91847Schristos #else
423a7c91847Schristos use_editor = 0;
424a7c91847Schristos #endif
425a7c91847Schristos logfile = optarg;
426a7c91847Schristos break;
427a7c91847Schristos case '?':
428a7c91847Schristos default:
429a7c91847Schristos usage (commit_usage);
430a7c91847Schristos break;
431a7c91847Schristos }
432a7c91847Schristos }
433a7c91847Schristos argc -= optind;
434a7c91847Schristos argv += optind;
435a7c91847Schristos
436a7c91847Schristos /* numeric specified revision means we ignore sticky tags... */
437a7c91847Schristos if (saved_tag && isdigit ((unsigned char) *saved_tag))
438a7c91847Schristos {
439a7c91847Schristos char *p = saved_tag + strlen (saved_tag);
440a7c91847Schristos aflag = 1;
441a7c91847Schristos /* strip trailing dots and leading zeros */
442a7c91847Schristos while (*--p == '.') ;
443a7c91847Schristos p[1] = '\0';
444a7c91847Schristos while (saved_tag[0] == '0' && isdigit ((unsigned char) saved_tag[1]))
445a7c91847Schristos ++saved_tag;
446a7c91847Schristos }
447a7c91847Schristos
448a7c91847Schristos /* some checks related to the "-F logfile" option */
449a7c91847Schristos if (logfile)
450a7c91847Schristos {
451a7c91847Schristos size_t size = 0, len;
452a7c91847Schristos
453a7c91847Schristos if (saved_message)
454a7c91847Schristos error (1, 0, "cannot specify both a message and a log file");
455a7c91847Schristos
456a7c91847Schristos get_file (logfile, logfile, "r", &saved_message, &size, &len);
457a7c91847Schristos }
458a7c91847Schristos
459a7c91847Schristos #ifdef CLIENT_SUPPORT
460a7c91847Schristos if (current_parsed_root->isremote)
461a7c91847Schristos {
462a7c91847Schristos struct find_data find_args;
463a7c91847Schristos
464a7c91847Schristos ign_setup ();
465a7c91847Schristos
466a7c91847Schristos find_args.ulist = getlist ();
467a7c91847Schristos find_args.argc = 0;
468a7c91847Schristos find_args.questionables = NULL;
469a7c91847Schristos find_args.ignlist = NULL;
470a7c91847Schristos find_args.repository = NULL;
471a7c91847Schristos
472a7c91847Schristos /* It is possible that only a numeric tag should set this.
473a7c91847Schristos I haven't really thought about it much.
474a7c91847Schristos Anyway, I suspect that setting it unnecessarily only causes
475a7c91847Schristos a little unneeded network traffic. */
476a7c91847Schristos find_args.force = force_ci || saved_tag != NULL;
477a7c91847Schristos
478a7c91847Schristos err = start_recursion
479a7c91847Schristos (find_fileproc, find_filesdoneproc, find_dirent_proc, NULL,
480a7c91847Schristos &find_args, argc, argv, local, W_LOCAL, 0, CVS_LOCK_NONE,
481a7c91847Schristos NULL, 0, NULL );
482a7c91847Schristos if (err)
483a7c91847Schristos error (1, 0, "correct above errors first!");
484a7c91847Schristos
485a7c91847Schristos if (find_args.argc == 0)
486a7c91847Schristos {
487a7c91847Schristos /* Nothing to commit. Exit now without contacting the
488a7c91847Schristos server (note that this means that we won't print "?
489a7c91847Schristos foo" for files which merit it, because we don't know
490a7c91847Schristos what is in the CVSROOT/cvsignore file). */
491a7c91847Schristos dellist (&find_args.ulist);
492a7c91847Schristos return 0;
493a7c91847Schristos }
494a7c91847Schristos
495a7c91847Schristos /* Now we keep track of which files we actually are going to
496a7c91847Schristos operate on, and only work with those files in the future.
497a7c91847Schristos This saves time--we don't want to search the file system
498a7c91847Schristos of the working directory twice. */
499a7c91847Schristos if (size_overflow_p (xtimes (find_args.argc, sizeof (char **))))
500a7c91847Schristos {
501a7c91847Schristos find_args.argc = 0;
502a7c91847Schristos return 0;
503a7c91847Schristos }
504a7c91847Schristos find_args.argv = xnmalloc (find_args.argc, sizeof (char **));
505a7c91847Schristos find_args.argc = 0;
506a7c91847Schristos walklist (find_args.ulist, copy_ulist, &find_args);
507a7c91847Schristos
508a7c91847Schristos /* Do this before calling do_editor; don't ask for a log
509a7c91847Schristos message if we can't talk to the server. But do it after we
510a7c91847Schristos have made the checks that we can locally (to more quickly
511a7c91847Schristos catch syntax errors, the case where no files are modified,
512a7c91847Schristos added or removed, etc.).
513a7c91847Schristos
514a7c91847Schristos On the other hand, calling start_server before do_editor
515a7c91847Schristos means that we chew up server resources the whole time that
516a7c91847Schristos the user has the editor open (hours or days if the user
517a7c91847Schristos forgets about it), which seems dubious. */
518a7c91847Schristos start_server ();
519a7c91847Schristos
520a7c91847Schristos /*
521a7c91847Schristos * We do this once, not once for each directory as in normal CVS.
522a7c91847Schristos * The protocol is designed this way. This is a feature.
523a7c91847Schristos */
524a7c91847Schristos if (use_editor)
525a7c91847Schristos do_editor (".", &saved_message, NULL, find_args.ulist);
526a7c91847Schristos
527a7c91847Schristos /* We always send some sort of message, even if empty. */
528a7c91847Schristos option_with_arg ("-m", saved_message ? saved_message : "");
529a7c91847Schristos
530a7c91847Schristos /* OK, now process all the questionable files we have been saving
531a7c91847Schristos up. */
532a7c91847Schristos {
533a7c91847Schristos struct question *p;
534a7c91847Schristos struct question *q;
535a7c91847Schristos
536a7c91847Schristos p = find_args.questionables;
537a7c91847Schristos while (p != NULL)
538a7c91847Schristos {
539a7c91847Schristos if (ign_inhibit_server || !supported_request ("Questionable"))
540a7c91847Schristos {
541a7c91847Schristos cvs_output ("? ", 2);
542a7c91847Schristos if (p->dir[0] != '\0')
543a7c91847Schristos {
544a7c91847Schristos cvs_output (p->dir, 0);
545a7c91847Schristos cvs_output ("/", 1);
546a7c91847Schristos }
547a7c91847Schristos cvs_output (p->file, 0);
548a7c91847Schristos cvs_output ("\n", 1);
549a7c91847Schristos }
550a7c91847Schristos else
551a7c91847Schristos {
552a7c91847Schristos /* This used to send the Directory line of its own accord,
553a7c91847Schristos * but skipped some of the other processing like checking
554a7c91847Schristos * for whether the server would accept "Relative-directory"
555a7c91847Schristos * requests. Relying on send_a_repository() to do this
556a7c91847Schristos * picks up these checks but also:
557a7c91847Schristos *
558a7c91847Schristos * 1. Causes the "Directory" request to be sent only once
559a7c91847Schristos * per directory.
560a7c91847Schristos * 2. Causes the global TOPLEVEL_REPOS to be set.
561a7c91847Schristos * 3. Causes "Static-directory" and "Sticky" requests
562a7c91847Schristos * to sometimes be sent.
563a7c91847Schristos *
564a7c91847Schristos * (1) is almost certainly a plus. (2) & (3) may or may
565a7c91847Schristos * not be useful sometimes, and will ocassionally cause a
566a7c91847Schristos * little extra network traffic. The additional network
567a7c91847Schristos * traffic is probably already saved several times over and
568a7c91847Schristos * certainly cancelled out via the multiple "Directory"
569a7c91847Schristos * request suppression of (1).
570a7c91847Schristos */
571a7c91847Schristos send_a_repository (p->dir, p->repos, p->dir);
572a7c91847Schristos
573a7c91847Schristos send_to_server ("Questionable ", 0);
574a7c91847Schristos send_to_server (p->file, 0);
575a7c91847Schristos send_to_server ("\012", 1);
576a7c91847Schristos }
577a7c91847Schristos free (p->dir);
578a7c91847Schristos free (p->repos);
579a7c91847Schristos free (p->file);
580a7c91847Schristos q = p->next;
581a7c91847Schristos free (p);
582a7c91847Schristos p = q;
583a7c91847Schristos }
584a7c91847Schristos }
585a7c91847Schristos
586a7c91847Schristos if (local)
587a7c91847Schristos send_arg ("-l");
588a7c91847Schristos if (check_valid_edit)
589a7c91847Schristos send_arg ("-c");
590a7c91847Schristos if (force_ci)
591a7c91847Schristos send_arg ("-f");
592a7c91847Schristos option_with_arg ("-r", saved_tag);
593a7c91847Schristos send_arg ("--");
594a7c91847Schristos
595a7c91847Schristos /* FIXME: This whole find_args.force/SEND_FORCE business is a
596a7c91847Schristos kludge. It would seem to be a server bug that we have to
597a7c91847Schristos say that files are modified when they are not. This makes
598a7c91847Schristos "cvs commit -r 2" across a whole bunch of files a very slow
599a7c91847Schristos operation (and it isn't documented in cvsclient.texi). I
600a7c91847Schristos haven't looked at the server code carefully enough to be
601a7c91847Schristos _sure_ why this is needed, but if it is because the "ci"
602a7c91847Schristos program, which we used to call, wanted the file to exist,
603a7c91847Schristos then it would be relatively simple to fix in the server. */
604a7c91847Schristos send_files (find_args.argc, find_args.argv, local, 0,
605a7c91847Schristos find_args.force ? SEND_FORCE : 0);
606a7c91847Schristos
607a7c91847Schristos /* Sending only the names of the files which were modified, added,
608a7c91847Schristos or removed means that the server will only do an up-to-date
609a7c91847Schristos check on those files. This is different from local CVS and
610a7c91847Schristos previous versions of client/server CVS, but it probably is a Good
611a7c91847Schristos Thing, or at least Not Such A Bad Thing. */
612a7c91847Schristos send_file_names (find_args.argc, find_args.argv, 0);
613a7c91847Schristos free (find_args.argv);
614a7c91847Schristos dellist (&find_args.ulist);
615a7c91847Schristos
616a7c91847Schristos send_to_server ("ci\012", 0);
617a7c91847Schristos err = get_responses_and_close ();
618a7c91847Schristos if (err != 0 && use_editor && saved_message != NULL)
619a7c91847Schristos {
620a7c91847Schristos /* If there was an error, don't nuke the user's carefully
621a7c91847Schristos constructed prose. This is something of a kludge; a better
622a7c91847Schristos solution is probably more along the lines of #150 in TODO
623a7c91847Schristos (doing a second up-to-date check before accepting the
624a7c91847Schristos log message has also been suggested, but that seems kind of
625a7c91847Schristos iffy because the real up-to-date check could still fail,
626a7c91847Schristos another error could occur, &c. Also, a second check would
627a7c91847Schristos slow things down). */
628a7c91847Schristos
629a7c91847Schristos char *fname;
630a7c91847Schristos FILE *fp;
631a7c91847Schristos
632a7c91847Schristos fp = cvs_temp_file (&fname);
633a7c91847Schristos if (fp == NULL)
634a7c91847Schristos error (1, 0, "cannot create temporary file %s", fname);
635a7c91847Schristos if (fwrite (saved_message, 1, strlen (saved_message), fp)
636a7c91847Schristos != strlen (saved_message))
637a7c91847Schristos error (1, errno, "cannot write temporary file %s", fname);
638a7c91847Schristos if (fclose (fp) < 0)
639a7c91847Schristos error (0, errno, "cannot close temporary file %s", fname);
640a7c91847Schristos error (0, 0, "saving log message in %s", fname);
641a7c91847Schristos free (fname);
642a7c91847Schristos }
643a7c91847Schristos return err;
644a7c91847Schristos }
645a7c91847Schristos #endif
646a7c91847Schristos
647a7c91847Schristos if (saved_tag != NULL)
648a7c91847Schristos tag_check_valid (saved_tag, argc, argv, local, aflag, "", false);
649a7c91847Schristos
650a7c91847Schristos /* XXX - this is not the perfect check for this */
651a7c91847Schristos if (argc <= 0)
652a7c91847Schristos write_dirtag = saved_tag;
653a7c91847Schristos
654a7c91847Schristos wrap_setup ();
655a7c91847Schristos
656a7c91847Schristos lock_tree_promotably (argc, argv, local, W_LOCAL, aflag);
657a7c91847Schristos
658a7c91847Schristos /*
659a7c91847Schristos * Set up the master update list and hard link list
660a7c91847Schristos */
661a7c91847Schristos mulist = getlist ();
662a7c91847Schristos
663a7c91847Schristos #ifdef PRESERVE_PERMISSIONS_SUPPORT
664a7c91847Schristos if (preserve_perms)
665a7c91847Schristos {
666a7c91847Schristos hardlist = getlist ();
667a7c91847Schristos
668a7c91847Schristos /*
669a7c91847Schristos * We need to save the working directory so that
670a7c91847Schristos * check_fileproc can construct a full pathname for each file.
671a7c91847Schristos */
672a7c91847Schristos working_dir = xgetcwd ();
673a7c91847Schristos }
674a7c91847Schristos #endif
675a7c91847Schristos
676a7c91847Schristos /*
677a7c91847Schristos * Run the recursion processor to verify the files are all up-to-date
678a7c91847Schristos */
679a7c91847Schristos err = start_recursion (check_fileproc, check_filesdoneproc,
680a7c91847Schristos check_direntproc, NULL, NULL, argc, argv, local,
681a7c91847Schristos W_LOCAL, aflag, CVS_LOCK_NONE, NULL, 1, NULL);
682a7c91847Schristos if (err)
683a7c91847Schristos error (1, 0, "correct above errors first!");
684a7c91847Schristos
685a7c91847Schristos /*
686a7c91847Schristos * Run the recursion processor to commit the files
687a7c91847Schristos */
688a7c91847Schristos write_dirnonbranch = 0;
689a7c91847Schristos if (noexec == 0)
690a7c91847Schristos err = start_recursion (commit_fileproc, commit_filesdoneproc,
691a7c91847Schristos commit_direntproc, commit_dirleaveproc, NULL,
692a7c91847Schristos argc, argv, local, W_LOCAL, aflag,
693a7c91847Schristos CVS_LOCK_WRITE, NULL, 1, NULL);
694a7c91847Schristos
695a7c91847Schristos /*
696a7c91847Schristos * Unlock all the dirs and clean up
697a7c91847Schristos */
698a7c91847Schristos Lock_Cleanup ();
699a7c91847Schristos dellist (&mulist);
700a7c91847Schristos
701a7c91847Schristos /* see if we need to sleep before returning to avoid time-stamp races */
702a7c91847Schristos if (!server_active && last_register_time)
703a7c91847Schristos {
704a7c91847Schristos sleep_past (last_register_time);
705a7c91847Schristos }
706a7c91847Schristos
707a7c91847Schristos return err;
708a7c91847Schristos }
709a7c91847Schristos
710a7c91847Schristos
711a7c91847Schristos
712a7c91847Schristos /* This routine determines the status of a given file and retrieves
713a7c91847Schristos the version information that is associated with that file. */
714a7c91847Schristos
715a7c91847Schristos static
716a7c91847Schristos Ctype
classify_file_internal(struct file_info * finfo,Vers_TS ** vers)717a7c91847Schristos classify_file_internal (struct file_info *finfo, Vers_TS **vers)
718a7c91847Schristos {
719a7c91847Schristos int save_noexec, save_quiet, save_really_quiet;
720a7c91847Schristos Ctype status;
721a7c91847Schristos
722a7c91847Schristos /* FIXME: Do we need to save quiet as well as really_quiet? Last
723a7c91847Schristos time I glanced at Classify_File I only saw it looking at really_quiet
724a7c91847Schristos not quiet. */
725a7c91847Schristos save_noexec = noexec;
726a7c91847Schristos save_quiet = quiet;
727a7c91847Schristos save_really_quiet = really_quiet;
728a7c91847Schristos noexec = quiet = really_quiet = 1;
729a7c91847Schristos
730a7c91847Schristos /* handle specified numeric revision specially */
731a7c91847Schristos if (saved_tag && isdigit ((unsigned char) *saved_tag))
732a7c91847Schristos {
733a7c91847Schristos /* If the tag is for the trunk, make sure we're at the head */
734a7c91847Schristos if (numdots (saved_tag) < 2)
735a7c91847Schristos {
736a7c91847Schristos status = Classify_File (finfo, NULL, NULL,
737a7c91847Schristos NULL, 1, aflag, vers, 0);
738a7c91847Schristos if (status == T_UPTODATE || status == T_MODIFIED ||
739a7c91847Schristos status == T_ADDED)
740a7c91847Schristos {
741a7c91847Schristos Ctype xstatus;
742a7c91847Schristos
743a7c91847Schristos freevers_ts (vers);
744a7c91847Schristos xstatus = Classify_File (finfo, saved_tag, NULL,
745a7c91847Schristos NULL, 1, aflag, vers, 0);
746a7c91847Schristos if (xstatus == T_REMOVE_ENTRY)
747a7c91847Schristos status = T_MODIFIED;
748a7c91847Schristos else if (status == T_MODIFIED && xstatus == T_CONFLICT)
749a7c91847Schristos status = T_MODIFIED;
750a7c91847Schristos else
751a7c91847Schristos status = xstatus;
752a7c91847Schristos }
753a7c91847Schristos }
754a7c91847Schristos else
755a7c91847Schristos {
756a7c91847Schristos char *xtag, *cp;
757a7c91847Schristos
758a7c91847Schristos /*
759a7c91847Schristos * The revision is off the main trunk; make sure we're
760a7c91847Schristos * up-to-date with the head of the specified branch.
761a7c91847Schristos */
762a7c91847Schristos xtag = xstrdup (saved_tag);
763a7c91847Schristos if ((numdots (xtag) & 1) != 0)
764a7c91847Schristos {
765a7c91847Schristos cp = strrchr (xtag, '.');
766a7c91847Schristos *cp = '\0';
767a7c91847Schristos }
768a7c91847Schristos status = Classify_File (finfo, xtag, NULL,
769a7c91847Schristos NULL, 1, aflag, vers, 0);
770a7c91847Schristos if ((status == T_REMOVE_ENTRY || status == T_CONFLICT)
771a7c91847Schristos && (cp = strrchr (xtag, '.')) != NULL)
772a7c91847Schristos {
773a7c91847Schristos /* pluck one more dot off the revision */
774a7c91847Schristos *cp = '\0';
775a7c91847Schristos freevers_ts (vers);
776a7c91847Schristos status = Classify_File (finfo, xtag, NULL,
777a7c91847Schristos NULL, 1, aflag, vers, 0);
778a7c91847Schristos if (status == T_UPTODATE || status == T_REMOVE_ENTRY)
779a7c91847Schristos status = T_MODIFIED;
780a7c91847Schristos }
781a7c91847Schristos /* now, muck with vers to make the tag correct */
782a7c91847Schristos free ((*vers)->tag);
783a7c91847Schristos (*vers)->tag = xstrdup (saved_tag);
784a7c91847Schristos free (xtag);
785a7c91847Schristos }
786a7c91847Schristos }
787a7c91847Schristos else
788a7c91847Schristos status = Classify_File (finfo, saved_tag, NULL, NULL, 1, 0, vers, 0);
789a7c91847Schristos noexec = save_noexec;
790a7c91847Schristos quiet = save_quiet;
791a7c91847Schristos really_quiet = save_really_quiet;
792a7c91847Schristos
793a7c91847Schristos return status;
794a7c91847Schristos }
795a7c91847Schristos
796a7c91847Schristos
797a7c91847Schristos
798a7c91847Schristos /*
799a7c91847Schristos * Check to see if a file is ok to commit and make sure all files are
800a7c91847Schristos * up-to-date
801a7c91847Schristos */
802a7c91847Schristos /* ARGSUSED */
803a7c91847Schristos static int
check_fileproc(void * callerdat,struct file_info * finfo)804a7c91847Schristos check_fileproc (void *callerdat, struct file_info *finfo)
805a7c91847Schristos {
806a7c91847Schristos Ctype status;
807a7c91847Schristos const char *xdir;
808a7c91847Schristos Node *p;
809a7c91847Schristos List *ulist, *cilist;
810a7c91847Schristos Vers_TS *vers;
811a7c91847Schristos struct commit_info *ci;
812a7c91847Schristos struct logfile_info *li;
813a7c91847Schristos int retval = 1;
814a7c91847Schristos
815a7c91847Schristos size_t cvsroot_len = strlen (current_parsed_root->directory);
816a7c91847Schristos
817a7c91847Schristos if (!finfo->repository)
818a7c91847Schristos {
819a7c91847Schristos error (0, 0, "nothing known about `%s'", finfo->fullname);
820a7c91847Schristos return 1;
821a7c91847Schristos }
822a7c91847Schristos
823a7c91847Schristos if (strncmp (finfo->repository, current_parsed_root->directory,
824a7c91847Schristos cvsroot_len) == 0
825a7c91847Schristos && ISSLASH (finfo->repository[cvsroot_len])
826a7c91847Schristos && strncmp (finfo->repository + cvsroot_len + 1,
827a7c91847Schristos CVSROOTADM,
828a7c91847Schristos sizeof (CVSROOTADM) - 1) == 0
829a7c91847Schristos && ISSLASH (finfo->repository[cvsroot_len + sizeof (CVSROOTADM)])
830a7c91847Schristos && strcmp (finfo->repository + cvsroot_len + sizeof (CVSROOTADM) + 1,
831a7c91847Schristos CVSNULLREPOS) == 0
832a7c91847Schristos )
833a7c91847Schristos error (1, 0, "cannot check in to %s", finfo->repository);
834a7c91847Schristos
835a7c91847Schristos status = classify_file_internal (finfo, &vers);
836a7c91847Schristos
837a7c91847Schristos /*
838a7c91847Schristos * If the force-commit option is enabled, and the file in question
839a7c91847Schristos * appears to be up-to-date, just make it look modified so that
840a7c91847Schristos * it will be committed.
841a7c91847Schristos */
842a7c91847Schristos if (force_ci && status == T_UPTODATE)
843a7c91847Schristos status = T_MODIFIED;
844a7c91847Schristos
845a7c91847Schristos switch (status)
846a7c91847Schristos {
847a7c91847Schristos case T_CHECKOUT:
848a7c91847Schristos case T_PATCH:
849a7c91847Schristos case T_NEEDS_MERGE:
850a7c91847Schristos case T_REMOVE_ENTRY:
851a7c91847Schristos error (0, 0, "Up-to-date check failed for `%s'", finfo->fullname);
852a7c91847Schristos goto out;
853a7c91847Schristos case T_CONFLICT:
854a7c91847Schristos case T_MODIFIED:
855a7c91847Schristos case T_ADDED:
856a7c91847Schristos case T_REMOVED:
857a7c91847Schristos {
858a7c91847Schristos char *editor;
859a7c91847Schristos
860a7c91847Schristos /*
861a7c91847Schristos * some quick sanity checks; if no numeric -r option specified:
862a7c91847Schristos * - can't have a sticky date
863a7c91847Schristos * - can't have a sticky tag that is not a branch
864a7c91847Schristos * Also,
865a7c91847Schristos * - if status is T_REMOVED, file must not exist and its entry
866a7c91847Schristos * can't have a numeric sticky tag.
867a7c91847Schristos * - if status is T_ADDED, rcs file must not exist unless on
868a7c91847Schristos * a branch or head is dead
869a7c91847Schristos * - if status is T_ADDED, can't have a non-trunk numeric rev
870a7c91847Schristos * - if status is T_MODIFIED and a Conflict marker exists, don't
871a7c91847Schristos * allow the commit if timestamp is identical or if we find
872a7c91847Schristos * an RCS_MERGE_PAT in the file.
873a7c91847Schristos */
874a7c91847Schristos if (!saved_tag || !isdigit ((unsigned char) *saved_tag))
875a7c91847Schristos {
876a7c91847Schristos if (vers->date)
877a7c91847Schristos {
878a7c91847Schristos error (0, 0,
879a7c91847Schristos "cannot commit with sticky date for file `%s'",
880a7c91847Schristos finfo->fullname);
881a7c91847Schristos goto out;
882a7c91847Schristos }
883a7c91847Schristos if (status == T_MODIFIED && vers->tag &&
884a7c91847Schristos !RCS_isbranch (finfo->rcs, vers->tag))
885a7c91847Schristos {
886a7c91847Schristos error (0, 0,
887a7c91847Schristos "sticky tag `%s' for file `%s' is not a branch",
888a7c91847Schristos vers->tag, finfo->fullname);
889a7c91847Schristos goto out;
890a7c91847Schristos }
891a7c91847Schristos }
892a7c91847Schristos if (status == T_CONFLICT && !force_ci)
893a7c91847Schristos {
894a7c91847Schristos error (0, 0,
895a7c91847Schristos "file `%s' had a conflict and has not been modified",
896a7c91847Schristos finfo->fullname);
897a7c91847Schristos goto out;
898a7c91847Schristos }
899a7c91847Schristos if (status == T_MODIFIED && !force_ci && file_has_markers (finfo))
900a7c91847Schristos {
901a7c91847Schristos /* Make this a warning, not an error, because we have
902a7c91847Schristos no way of knowing whether the "conflict indicators"
903a7c91847Schristos are really from a conflict or whether they are part
904a7c91847Schristos of the document itself (cvs.texinfo and sanity.sh in
905a7c91847Schristos CVS itself, for example, tend to want to have strings
906a7c91847Schristos like ">>>>>>>" at the start of a line). Making people
907a7c91847Schristos kludge this the way they need to kludge keyword
908a7c91847Schristos expansion seems undesirable. And it is worse than
909a7c91847Schristos keyword expansion, because there is no -ko
910a7c91847Schristos analogue. */
911a7c91847Schristos error (0, 0,
912a7c91847Schristos "\
913a7c91847Schristos warning: file `%s' seems to still contain conflict indicators",
914a7c91847Schristos finfo->fullname);
915a7c91847Schristos }
916a7c91847Schristos
917a7c91847Schristos if (status == T_REMOVED)
918a7c91847Schristos {
919a7c91847Schristos if (vers->ts_user != NULL)
920a7c91847Schristos {
921a7c91847Schristos error (0, 0,
922a7c91847Schristos "`%s' should be removed and is still there (or is"
923a7c91847Schristos " back again)", finfo->fullname);
924a7c91847Schristos goto out;
925a7c91847Schristos }
926a7c91847Schristos
927a7c91847Schristos if (vers->tag && isdigit ((unsigned char) *vers->tag))
928a7c91847Schristos {
929a7c91847Schristos /* Remove also tries to forbid this, but we should check
930a7c91847Schristos here. I'm only _sure_ about somewhat obscure cases
931a7c91847Schristos (hacking the Entries file, using an old version of
932a7c91847Schristos CVS for the remove and a new one for the commit), but
933a7c91847Schristos there might be other cases. */
934a7c91847Schristos error (0, 0,
935a7c91847Schristos "cannot remove file `%s' which has a numeric sticky"
936a7c91847Schristos " tag of `%s'", finfo->fullname, vers->tag);
937a7c91847Schristos freevers_ts (&vers);
938a7c91847Schristos goto out;
939a7c91847Schristos }
940a7c91847Schristos }
941a7c91847Schristos if (status == T_ADDED)
942a7c91847Schristos {
943a7c91847Schristos if (vers->tag == NULL)
944a7c91847Schristos {
945a7c91847Schristos if (finfo->rcs != NULL &&
946a7c91847Schristos !RCS_isdead (finfo->rcs, finfo->rcs->head))
947a7c91847Schristos {
948a7c91847Schristos error (0, 0,
949a7c91847Schristos "cannot add file `%s' when RCS file `%s' already exists",
950a7c91847Schristos finfo->fullname, finfo->rcs->path);
951a7c91847Schristos goto out;
952a7c91847Schristos }
953a7c91847Schristos }
954a7c91847Schristos else if (isdigit ((unsigned char) *vers->tag) &&
955a7c91847Schristos numdots (vers->tag) > 1)
956a7c91847Schristos {
957a7c91847Schristos error (0, 0,
958a7c91847Schristos "cannot add file `%s' with revision `%s'; must be on trunk",
959a7c91847Schristos finfo->fullname, vers->tag);
960a7c91847Schristos goto out;
961a7c91847Schristos }
962a7c91847Schristos }
963a7c91847Schristos
964a7c91847Schristos /* done with consistency checks; now, to get on with the commit */
965a7c91847Schristos if (finfo->update_dir[0] == '\0')
966a7c91847Schristos xdir = ".";
967a7c91847Schristos else
968a7c91847Schristos xdir = finfo->update_dir;
969a7c91847Schristos if ((p = findnode (mulist, xdir)) != NULL)
970a7c91847Schristos {
971a7c91847Schristos ulist = ((struct master_lists *) p->data)->ulist;
972a7c91847Schristos cilist = ((struct master_lists *) p->data)->cilist;
973a7c91847Schristos }
974a7c91847Schristos else
975a7c91847Schristos {
976a7c91847Schristos struct master_lists *ml;
977a7c91847Schristos
978a7c91847Schristos ml = xmalloc (sizeof (struct master_lists));
979a7c91847Schristos ulist = ml->ulist = getlist ();
980a7c91847Schristos cilist = ml->cilist = getlist ();
981a7c91847Schristos
982a7c91847Schristos p = getnode ();
983a7c91847Schristos p->key = xstrdup (xdir);
984a7c91847Schristos p->type = UPDATE;
985a7c91847Schristos p->data = ml;
986a7c91847Schristos p->delproc = masterlist_delproc;
987a7c91847Schristos (void) addnode (mulist, p);
988a7c91847Schristos }
989a7c91847Schristos
990a7c91847Schristos /* first do ulist, then cilist */
991a7c91847Schristos p = getnode ();
992a7c91847Schristos p->key = xstrdup (finfo->file);
993a7c91847Schristos p->type = UPDATE;
994a7c91847Schristos p->delproc = update_delproc;
995a7c91847Schristos li = xmalloc (sizeof (struct logfile_info));
996a7c91847Schristos li->type = status;
997a7c91847Schristos
998a7c91847Schristos if (check_valid_edit)
999a7c91847Schristos {
1000a7c91847Schristos char *editors = NULL;
1001a7c91847Schristos
1002a7c91847Schristos editor = NULL;
1003a7c91847Schristos editors = fileattr_get0 (finfo->file, "_editors");
1004a7c91847Schristos if (editors != NULL)
1005a7c91847Schristos {
1006a7c91847Schristos char *caller = getcaller ();
1007a7c91847Schristos char *p = NULL;
1008a7c91847Schristos char *p0 = NULL;
1009a7c91847Schristos
1010a7c91847Schristos p = editors;
1011a7c91847Schristos p0 = p;
1012a7c91847Schristos while (*p != '\0')
1013a7c91847Schristos {
1014a7c91847Schristos p = strchr (p, '>');
1015a7c91847Schristos if (p == NULL)
1016a7c91847Schristos {
1017a7c91847Schristos break;
1018a7c91847Schristos }
1019a7c91847Schristos *p = '\0';
1020a7c91847Schristos if (strcmp (caller, p0) == 0)
1021a7c91847Schristos {
1022a7c91847Schristos break;
1023a7c91847Schristos }
1024a7c91847Schristos p = strchr (p + 1, ',');
1025a7c91847Schristos if (p == NULL)
1026a7c91847Schristos {
1027a7c91847Schristos break;
1028a7c91847Schristos }
1029a7c91847Schristos ++p;
1030a7c91847Schristos p0 = p;
1031a7c91847Schristos }
1032a7c91847Schristos
1033a7c91847Schristos if (strcmp (caller, p0) == 0)
1034a7c91847Schristos {
1035a7c91847Schristos editor = caller;
1036a7c91847Schristos }
1037a7c91847Schristos
1038a7c91847Schristos free (editors);
1039a7c91847Schristos }
1040a7c91847Schristos }
1041a7c91847Schristos
1042a7c91847Schristos if (check_valid_edit && editor == NULL)
1043a7c91847Schristos {
1044a7c91847Schristos error (0, 0, "Valid edit does not exist for %s",
1045a7c91847Schristos finfo->fullname);
1046a7c91847Schristos freevers_ts (&vers);
1047a7c91847Schristos return 1;
1048a7c91847Schristos }
1049a7c91847Schristos
1050a7c91847Schristos li->tag = xstrdup (vers->tag);
1051274254cdSchristos /* If the file was re-added, we want the revision in the commitlog
1052274254cdSchristos to be NONE, not the previous dead revision. */
1053274254cdSchristos li->rev_old = status == T_ADDED ? NULL : xstrdup (vers->vn_rcs);
1054a7c91847Schristos li->rev_new = NULL;
1055a7c91847Schristos p->data = li;
1056a7c91847Schristos (void) addnode (ulist, p);
1057a7c91847Schristos
1058a7c91847Schristos p = getnode ();
1059a7c91847Schristos p->key = xstrdup (finfo->file);
1060a7c91847Schristos p->type = UPDATE;
1061a7c91847Schristos p->delproc = ci_delproc;
1062a7c91847Schristos ci = xmalloc (sizeof (struct commit_info));
1063a7c91847Schristos ci->status = status;
1064a7c91847Schristos if (vers->tag)
1065a7c91847Schristos if (isdigit ((unsigned char) *vers->tag))
1066a7c91847Schristos ci->rev = xstrdup (vers->tag);
1067a7c91847Schristos else
1068a7c91847Schristos ci->rev = RCS_whatbranch (finfo->rcs, vers->tag);
1069a7c91847Schristos else
1070a7c91847Schristos ci->rev = NULL;
1071a7c91847Schristos ci->tag = xstrdup (vers->tag);
1072a7c91847Schristos ci->options = xstrdup (vers->options);
1073a7c91847Schristos p->data = ci;
1074a7c91847Schristos (void) addnode (cilist, p);
1075a7c91847Schristos
1076a7c91847Schristos #ifdef PRESERVE_PERMISSIONS_SUPPORT
1077a7c91847Schristos if (preserve_perms)
1078a7c91847Schristos {
1079a7c91847Schristos /* Add this file to hardlist, indexed on its inode. When
1080a7c91847Schristos we are done, we can find out what files are hardlinked
1081a7c91847Schristos to a given file by looking up its inode in hardlist. */
1082a7c91847Schristos char *fullpath;
1083a7c91847Schristos Node *linkp;
1084a7c91847Schristos struct hardlink_info *hlinfo;
1085a7c91847Schristos
1086a7c91847Schristos /* Get the full pathname of the current file. */
1087a7c91847Schristos fullpath = Xasprintf ("%s/%s", working_dir, finfo->fullname);
1088a7c91847Schristos
1089a7c91847Schristos /* To permit following links in subdirectories, files
1090a7c91847Schristos are keyed on finfo->fullname, not on finfo->name. */
1091a7c91847Schristos linkp = lookup_file_by_inode (fullpath);
1092a7c91847Schristos
1093a7c91847Schristos /* If linkp is NULL, the file doesn't exist... maybe
1094a7c91847Schristos we're doing a remove operation? */
1095a7c91847Schristos if (linkp != NULL)
1096a7c91847Schristos {
1097a7c91847Schristos /* Create a new hardlink_info node, which will record
1098a7c91847Schristos the current file's status and the links listed in its
1099a7c91847Schristos `hardlinks' delta field. We will append this
1100a7c91847Schristos hardlink_info node to the appropriate hardlist entry. */
1101a7c91847Schristos hlinfo = xmalloc (sizeof (struct hardlink_info));
1102a7c91847Schristos hlinfo->status = status;
1103a7c91847Schristos linkp->data = hlinfo;
1104a7c91847Schristos }
1105a7c91847Schristos }
1106a7c91847Schristos #endif
1107a7c91847Schristos
1108a7c91847Schristos break;
1109a7c91847Schristos }
1110a7c91847Schristos
1111a7c91847Schristos case T_UNKNOWN:
1112a7c91847Schristos error (0, 0, "nothing known about `%s'", finfo->fullname);
1113a7c91847Schristos goto out;
1114a7c91847Schristos case T_UPTODATE:
1115a7c91847Schristos break;
1116a7c91847Schristos default:
1117a7c91847Schristos error (0, 0, "CVS internal error: unknown status %d", status);
1118a7c91847Schristos break;
1119a7c91847Schristos }
1120a7c91847Schristos
1121a7c91847Schristos retval = 0;
1122a7c91847Schristos
1123a7c91847Schristos out:
1124a7c91847Schristos
1125a7c91847Schristos freevers_ts (&vers);
1126a7c91847Schristos return retval;
1127a7c91847Schristos }
1128a7c91847Schristos
1129a7c91847Schristos
1130a7c91847Schristos
1131a7c91847Schristos /*
1132a7c91847Schristos * By default, return the code that tells do_recursion to examine all
1133a7c91847Schristos * directories
1134a7c91847Schristos */
1135a7c91847Schristos /* ARGSUSED */
1136a7c91847Schristos static Dtype
check_direntproc(void * callerdat,const char * dir,const char * repos,const char * update_dir,List * entries)1137a7c91847Schristos check_direntproc (void *callerdat, const char *dir, const char *repos,
1138a7c91847Schristos const char *update_dir, List *entries)
1139a7c91847Schristos {
1140a7c91847Schristos if (!isdir (dir))
1141a7c91847Schristos return R_SKIP_ALL;
1142a7c91847Schristos
1143a7c91847Schristos if (!quiet)
1144a7c91847Schristos error (0, 0, "Examining %s", update_dir);
1145a7c91847Schristos
1146a7c91847Schristos return R_PROCESS;
1147a7c91847Schristos }
1148a7c91847Schristos
1149a7c91847Schristos
1150a7c91847Schristos
1151a7c91847Schristos /*
1152a7c91847Schristos * Walklist proc to generate an arg list from the line in commitinfo
1153a7c91847Schristos */
1154a7c91847Schristos static int
precommit_list_to_args_proc(p,closure)1155a7c91847Schristos precommit_list_to_args_proc (p, closure)
1156a7c91847Schristos Node *p;
1157a7c91847Schristos void *closure;
1158a7c91847Schristos {
1159a7c91847Schristos struct format_cmdline_walklist_closure *c = closure;
1160a7c91847Schristos struct logfile_info *li;
1161a7c91847Schristos char *arg = NULL;
1162a7c91847Schristos const char *f;
1163a7c91847Schristos char *d;
1164a7c91847Schristos size_t doff;
1165a7c91847Schristos
1166a7c91847Schristos if (p->data == NULL) return 1;
1167a7c91847Schristos
1168a7c91847Schristos f = c->format;
1169a7c91847Schristos d = *c->d;
1170a7c91847Schristos /* foreach requested attribute */
1171a7c91847Schristos while (*f)
1172a7c91847Schristos {
1173a7c91847Schristos switch (*f++)
1174a7c91847Schristos {
1175a7c91847Schristos case 's':
1176a7c91847Schristos li = p->data;
1177a7c91847Schristos if (li->type == T_ADDED
1178a7c91847Schristos || li->type == T_MODIFIED
1179a7c91847Schristos || li->type == T_REMOVED)
1180a7c91847Schristos {
1181a7c91847Schristos arg = p->key;
1182a7c91847Schristos }
1183a7c91847Schristos break;
1184a7c91847Schristos default:
1185a7c91847Schristos error (1, 0,
1186a7c91847Schristos "Unknown format character or not a list attribute: %c",
1187a7c91847Schristos f[-1]);
1188a7c91847Schristos /* NOTREACHED */
1189a7c91847Schristos break;
1190a7c91847Schristos }
1191a7c91847Schristos /* copy the attribute into an argument */
1192a7c91847Schristos if (c->quotes)
1193a7c91847Schristos {
1194a7c91847Schristos arg = cmdlineescape (c->quotes, arg);
1195a7c91847Schristos }
1196a7c91847Schristos else
1197a7c91847Schristos {
1198a7c91847Schristos arg = cmdlinequote ('"', arg);
1199a7c91847Schristos }
1200a7c91847Schristos doff = d - *c->buf;
1201a7c91847Schristos expand_string (c->buf, c->length, doff + strlen (arg));
1202a7c91847Schristos d = *c->buf + doff;
1203a7c91847Schristos strncpy (d, arg, strlen (arg));
1204a7c91847Schristos d += strlen (arg);
1205a7c91847Schristos free (arg);
1206a7c91847Schristos
1207a7c91847Schristos /* and always put the extra space on. we'll have to back up a char
1208a7c91847Schristos * when we're done, but that seems most efficient
1209a7c91847Schristos */
1210a7c91847Schristos doff = d - *c->buf;
1211a7c91847Schristos expand_string (c->buf, c->length, doff + 1);
1212a7c91847Schristos d = *c->buf + doff;
1213a7c91847Schristos *d++ = ' ';
1214a7c91847Schristos }
1215a7c91847Schristos /* correct our original pointer into the buff */
1216a7c91847Schristos *c->d = d;
1217a7c91847Schristos return 0;
1218a7c91847Schristos }
1219a7c91847Schristos
1220a7c91847Schristos
1221a7c91847Schristos
1222a7c91847Schristos /*
1223a7c91847Schristos * Callback proc for pre-commit checking
1224a7c91847Schristos */
1225a7c91847Schristos static int
precommit_proc(const char * repository,const char * filter,void * closure)1226a7c91847Schristos precommit_proc (const char *repository, const char *filter, void *closure)
1227a7c91847Schristos {
1228a7c91847Schristos char *newfilter = NULL;
1229a7c91847Schristos char *cmdline;
1230a7c91847Schristos const char *srepos = Short_Repository (repository);
1231a7c91847Schristos List *ulist = closure;
1232a7c91847Schristos
1233a7c91847Schristos #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1234a7c91847Schristos if (!strchr (filter, '%'))
1235a7c91847Schristos {
1236a7c91847Schristos error (0, 0,
1237a7c91847Schristos "warning: commitinfo line contains no format strings:\n"
1238a7c91847Schristos " \"%s\"\n"
1239a7c91847Schristos "Appending defaults (\" %%r/%%p %%s\"), but please be aware that this usage is\n"
1240a7c91847Schristos "deprecated.", filter);
1241a7c91847Schristos newfilter = Xasprintf ("%s %%r/%%p %%s", filter);
1242a7c91847Schristos filter = newfilter;
1243a7c91847Schristos }
1244a7c91847Schristos #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1245a7c91847Schristos
1246a7c91847Schristos /*
1247a7c91847Schristos * Cast any NULL arguments as appropriate pointers as this is an
1248a7c91847Schristos * stdarg function and we need to be certain the caller gets what
1249a7c91847Schristos * is expected.
1250a7c91847Schristos */
1251a7c91847Schristos cmdline = format_cmdline (
1252a7c91847Schristos #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1253a7c91847Schristos false, srepos,
1254a7c91847Schristos #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1255a7c91847Schristos filter,
1256a7c91847Schristos "c", "s", cvs_cmd_name,
1257a7c91847Schristos #ifdef SERVER_SUPPORT
1258a7c91847Schristos "R", "s", referrer ? referrer->original : "NONE",
1259a7c91847Schristos #endif /* SERVER_SUPPORT */
1260a7c91847Schristos "p", "s", srepos,
1261a7c91847Schristos "r", "s", current_parsed_root->directory,
1262a7c91847Schristos "s", ",", ulist, precommit_list_to_args_proc,
1263a7c91847Schristos (void *) NULL,
1264a7c91847Schristos (char *) NULL);
1265a7c91847Schristos
1266a7c91847Schristos if (newfilter) free (newfilter);
1267a7c91847Schristos
1268a7c91847Schristos if (!cmdline || !strlen (cmdline))
1269a7c91847Schristos {
1270a7c91847Schristos if (cmdline) free (cmdline);
1271a7c91847Schristos error (0, 0, "precommit proc resolved to the empty string!");
1272a7c91847Schristos return 1;
1273a7c91847Schristos }
1274a7c91847Schristos
1275a7c91847Schristos run_setup (cmdline);
1276a7c91847Schristos free (cmdline);
1277a7c91847Schristos
1278274254cdSchristos return run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY|
1279274254cdSchristos (server_active ? 0 : RUN_UNSETXID));
1280a7c91847Schristos }
1281a7c91847Schristos
1282a7c91847Schristos
1283a7c91847Schristos
1284a7c91847Schristos /*
1285a7c91847Schristos * Run the pre-commit checks for the dir
1286a7c91847Schristos */
1287a7c91847Schristos /* ARGSUSED */
1288a7c91847Schristos static int
check_filesdoneproc(void * callerdat,int err,const char * repos,const char * update_dir,List * entries)1289a7c91847Schristos check_filesdoneproc (void *callerdat, int err, const char *repos,
1290a7c91847Schristos const char *update_dir, List *entries)
1291a7c91847Schristos {
1292a7c91847Schristos int n;
1293a7c91847Schristos Node *p;
1294a7c91847Schristos List *saved_ulist;
1295a7c91847Schristos
1296a7c91847Schristos /* find the update list for this dir */
1297a7c91847Schristos p = findnode (mulist, update_dir);
1298a7c91847Schristos if (p != NULL)
1299a7c91847Schristos saved_ulist = ((struct master_lists *) p->data)->ulist;
1300a7c91847Schristos else
1301a7c91847Schristos saved_ulist = NULL;
1302a7c91847Schristos
1303a7c91847Schristos /* skip the checks if there's nothing to do */
1304a7c91847Schristos if (saved_ulist == NULL || saved_ulist->list->next == saved_ulist->list)
1305a7c91847Schristos return err;
1306a7c91847Schristos
1307a7c91847Schristos /* run any pre-commit checks */
1308a7c91847Schristos n = Parse_Info (CVSROOTADM_COMMITINFO, repos, precommit_proc, PIOPT_ALL,
1309a7c91847Schristos saved_ulist);
1310a7c91847Schristos if (n > 0)
1311a7c91847Schristos {
1312a7c91847Schristos error (0, 0, "Pre-commit check failed");
1313a7c91847Schristos err += n;
1314a7c91847Schristos }
1315a7c91847Schristos
1316a7c91847Schristos return err;
1317a7c91847Schristos }
1318a7c91847Schristos
1319a7c91847Schristos
1320a7c91847Schristos
1321a7c91847Schristos /*
1322a7c91847Schristos * Do the work of committing a file
1323a7c91847Schristos */
1324a7c91847Schristos static int maxrev;
1325a7c91847Schristos static char *sbranch;
1326a7c91847Schristos
1327a7c91847Schristos /* ARGSUSED */
1328a7c91847Schristos static int
commit_fileproc(void * callerdat,struct file_info * finfo)1329a7c91847Schristos commit_fileproc (void *callerdat, struct file_info *finfo)
1330a7c91847Schristos {
1331a7c91847Schristos Node *p;
1332a7c91847Schristos int err = 0;
1333a7c91847Schristos List *ulist, *cilist;
1334a7c91847Schristos struct commit_info *ci;
1335a7c91847Schristos
1336a7c91847Schristos /* Keep track of whether write_dirtag is a branch tag.
1337a7c91847Schristos Note that if it is a branch tag in some files and a nonbranch tag
1338a7c91847Schristos in others, treat it as a nonbranch tag. It is possible that case
1339a7c91847Schristos should elicit a warning or an error. */
1340a7c91847Schristos if (write_dirtag != NULL
1341a7c91847Schristos && finfo->rcs != NULL)
1342a7c91847Schristos {
1343a7c91847Schristos char *rev = RCS_getversion (finfo->rcs, write_dirtag, NULL, 1, NULL);
1344a7c91847Schristos if (rev != NULL
1345a7c91847Schristos && !RCS_nodeisbranch (finfo->rcs, write_dirtag))
1346a7c91847Schristos write_dirnonbranch = 1;
1347a7c91847Schristos if (rev != NULL)
1348a7c91847Schristos free (rev);
1349a7c91847Schristos }
1350a7c91847Schristos
1351a7c91847Schristos if (finfo->update_dir[0] == '\0')
1352a7c91847Schristos p = findnode (mulist, ".");
1353a7c91847Schristos else
1354a7c91847Schristos p = findnode (mulist, finfo->update_dir);
1355a7c91847Schristos
1356a7c91847Schristos /*
1357a7c91847Schristos * if p is null, there were file type command line args which were
1358a7c91847Schristos * all up-to-date so nothing really needs to be done
1359a7c91847Schristos */
1360a7c91847Schristos if (p == NULL)
1361a7c91847Schristos return 0;
1362a7c91847Schristos ulist = ((struct master_lists *) p->data)->ulist;
1363a7c91847Schristos cilist = ((struct master_lists *) p->data)->cilist;
1364a7c91847Schristos
1365a7c91847Schristos /*
1366a7c91847Schristos * At this point, we should have the commit message unless we were called
1367a7c91847Schristos * with files as args from the command line. In that latter case, we
1368a7c91847Schristos * need to get the commit message ourselves
1369a7c91847Schristos */
1370a7c91847Schristos if (!got_message)
1371a7c91847Schristos {
1372a7c91847Schristos got_message = 1;
1373a7c91847Schristos if (!server_active && use_editor)
1374a7c91847Schristos do_editor (finfo->update_dir, &saved_message,
1375a7c91847Schristos finfo->repository, ulist);
1376a7c91847Schristos do_verify (&saved_message, finfo->repository, ulist);
1377a7c91847Schristos }
1378a7c91847Schristos
1379a7c91847Schristos p = findnode (cilist, finfo->file);
1380a7c91847Schristos if (p == NULL)
1381a7c91847Schristos return 0;
1382a7c91847Schristos
1383a7c91847Schristos ci = p->data;
1384a2b2f673Schristos
1385a2b2f673Schristos /* cvsacl patch */
1386a2b2f673Schristos #ifdef SERVER_SUPPORT
1387a2b2f673Schristos if (use_cvs_acl /* && server_active */)
1388a2b2f673Schristos {
1389a2b2f673Schristos int whichperm;
1390a2b2f673Schristos if (ci->status == T_MODIFIED)
1391a2b2f673Schristos whichperm = 3;
1392a2b2f673Schristos else if (ci->status == T_ADDED)
1393a2b2f673Schristos whichperm = 6;
1394a2b2f673Schristos else if (ci->status == T_REMOVED)
1395a2b2f673Schristos whichperm = 7;
1396a2b2f673Schristos
1397a2b2f673Schristos if (!access_allowed (finfo->file, finfo->repository, ci->tag, whichperm,
1398a2b2f673Schristos NULL, NULL, 1))
1399a2b2f673Schristos {
1400a2b2f673Schristos if (stop_at_first_permission_denied)
1401a2b2f673Schristos error (1, 0, "permission denied for %s",
1402a2b2f673Schristos Short_Repository (finfo->repository));
1403a2b2f673Schristos else
1404a2b2f673Schristos error (0, 0, "permission denied for %s/%s",
1405a2b2f673Schristos Short_Repository (finfo->repository), finfo->file);
1406a2b2f673Schristos
1407a2b2f673Schristos return (0);
1408a2b2f673Schristos }
1409a2b2f673Schristos }
1410a2b2f673Schristos #endif
1411a2b2f673Schristos
1412a7c91847Schristos if (ci->status == T_MODIFIED)
1413a7c91847Schristos {
1414a7c91847Schristos if (finfo->rcs == NULL)
1415a7c91847Schristos error (1, 0, "internal error: no parsed RCS file");
1416a7c91847Schristos if (lock_RCS (finfo->file, finfo->rcs, ci->rev,
1417a7c91847Schristos finfo->repository) != 0)
1418a7c91847Schristos {
1419a7c91847Schristos unlockrcs (finfo->rcs);
1420a7c91847Schristos err = 1;
1421a7c91847Schristos goto out;
1422a7c91847Schristos }
1423a7c91847Schristos }
1424a7c91847Schristos else if (ci->status == T_ADDED)
1425a7c91847Schristos {
1426a7c91847Schristos if (checkaddfile (finfo->file, finfo->repository, ci->tag, ci->options,
1427a7c91847Schristos &finfo->rcs) != 0)
1428a7c91847Schristos {
1429a7c91847Schristos if (finfo->rcs != NULL)
1430a7c91847Schristos fixaddfile (finfo->rcs->path);
1431a7c91847Schristos err = 1;
1432a7c91847Schristos goto out;
1433a7c91847Schristos }
1434a7c91847Schristos
1435a7c91847Schristos /* adding files with a tag, now means adding them on a branch.
1436a7c91847Schristos Since the branch test was done in check_fileproc for
1437a7c91847Schristos modified files, we need to stub it in again here. */
1438a7c91847Schristos
1439a7c91847Schristos if (ci->tag
1440a7c91847Schristos
1441a7c91847Schristos /* If numeric, it is on the trunk; check_fileproc enforced
1442a7c91847Schristos this. */
1443a7c91847Schristos && !isdigit ((unsigned char) ci->tag[0]))
1444a7c91847Schristos {
1445a7c91847Schristos if (finfo->rcs == NULL)
1446a7c91847Schristos error (1, 0, "internal error: no parsed RCS file");
1447a7c91847Schristos if (ci->rev)
1448a7c91847Schristos free (ci->rev);
1449a7c91847Schristos ci->rev = RCS_whatbranch (finfo->rcs, ci->tag);
1450a7c91847Schristos err = Checkin ('A', finfo, ci->rev,
1451a7c91847Schristos ci->tag, ci->options, saved_message);
1452a7c91847Schristos if (err != 0)
1453a7c91847Schristos {
1454a7c91847Schristos unlockrcs (finfo->rcs);
1455a7c91847Schristos fixbranch (finfo->rcs, sbranch);
1456a7c91847Schristos }
1457a7c91847Schristos
1458a7c91847Schristos (void) time (&last_register_time);
1459a7c91847Schristos
1460a7c91847Schristos ci->status = T_UPTODATE;
1461a7c91847Schristos }
1462a7c91847Schristos }
1463a7c91847Schristos
1464a7c91847Schristos /*
1465a7c91847Schristos * Add the file for real
1466a7c91847Schristos */
1467a7c91847Schristos if (ci->status == T_ADDED)
1468a7c91847Schristos {
1469a7c91847Schristos char *xrev = NULL;
1470a7c91847Schristos
1471a7c91847Schristos if (ci->rev == NULL)
1472a7c91847Schristos {
1473a7c91847Schristos /* find the max major rev number in this directory */
1474a7c91847Schristos maxrev = 0;
1475a7c91847Schristos (void) walklist (finfo->entries, findmaxrev, NULL);
1476a7c91847Schristos if (finfo->rcs->head)
1477a7c91847Schristos {
1478a7c91847Schristos /* resurrecting: include dead revision */
1479a7c91847Schristos int thisrev = atoi (finfo->rcs->head);
1480a7c91847Schristos if (thisrev > maxrev)
1481a7c91847Schristos maxrev = thisrev;
1482a7c91847Schristos }
1483a7c91847Schristos if (maxrev == 0)
1484a7c91847Schristos maxrev = 1;
1485a7c91847Schristos xrev = Xasprintf ("%d", maxrev);
1486a7c91847Schristos }
1487a7c91847Schristos
1488a7c91847Schristos /* XXX - an added file with symbolic -r should add tag as well */
1489a7c91847Schristos err = finaladd (finfo, ci->rev ? ci->rev : xrev, ci->tag, ci->options);
1490a7c91847Schristos if (xrev)
1491a7c91847Schristos free (xrev);
1492a7c91847Schristos }
1493a7c91847Schristos else if (ci->status == T_MODIFIED)
1494a7c91847Schristos {
1495a7c91847Schristos err = Checkin ('M', finfo, ci->rev, ci->tag,
1496a7c91847Schristos ci->options, saved_message);
1497a7c91847Schristos
1498a7c91847Schristos (void) time (&last_register_time);
1499a7c91847Schristos
1500a7c91847Schristos if (err != 0)
1501a7c91847Schristos {
1502a7c91847Schristos unlockrcs (finfo->rcs);
1503a7c91847Schristos fixbranch (finfo->rcs, sbranch);
1504a7c91847Schristos }
1505a7c91847Schristos }
1506a7c91847Schristos else if (ci->status == T_REMOVED)
1507a7c91847Schristos {
1508a7c91847Schristos err = remove_file (finfo, ci->tag, saved_message);
1509a7c91847Schristos #ifdef SERVER_SUPPORT
1510a7c91847Schristos if (server_active)
1511a7c91847Schristos {
1512a7c91847Schristos server_scratch_entry_only ();
1513a7c91847Schristos server_updated (finfo,
1514a7c91847Schristos NULL,
1515a7c91847Schristos
1516a7c91847Schristos /* Doesn't matter, it won't get checked. */
1517a7c91847Schristos SERVER_UPDATED,
1518a7c91847Schristos
1519a7c91847Schristos (mode_t) -1,
1520a7c91847Schristos NULL,
1521a7c91847Schristos NULL);
1522a7c91847Schristos }
1523a7c91847Schristos #endif
1524a7c91847Schristos }
1525a7c91847Schristos
1526a7c91847Schristos /* Clearly this is right for T_MODIFIED. I haven't thought so much
1527a7c91847Schristos about T_ADDED or T_REMOVED. */
1528a7c91847Schristos notify_do ('C', finfo->file, finfo->update_dir, getcaller (), NULL, NULL,
1529a7c91847Schristos finfo->repository);
1530a7c91847Schristos
1531a7c91847Schristos out:
1532a7c91847Schristos if (err != 0)
1533a7c91847Schristos {
1534a7c91847Schristos /* on failure, remove the file from ulist */
1535a7c91847Schristos p = findnode (ulist, finfo->file);
1536a7c91847Schristos if (p)
1537a7c91847Schristos delnode (p);
1538a7c91847Schristos }
1539a7c91847Schristos else
1540a7c91847Schristos {
1541a7c91847Schristos /* On success, retrieve the new version number of the file and
1542a7c91847Schristos copy it into the log information (see logmsg.c
1543a7c91847Schristos (logfile_write) for more details). We should only update
1544a7c91847Schristos the version number for files that have been added or
1545a7c91847Schristos modified but not removed since classify_file_internal
1546a7c91847Schristos will return the version number of a file even after it has
1547a7c91847Schristos been removed from the archive, which is not the behavior we
1548a7c91847Schristos want for our commitlog messages; we want the old version
1549a7c91847Schristos number and then "NONE." */
1550a7c91847Schristos
1551a7c91847Schristos if (ci->status != T_REMOVED)
1552a7c91847Schristos {
1553a7c91847Schristos p = findnode (ulist, finfo->file);
1554a7c91847Schristos if (p)
1555a7c91847Schristos {
1556a7c91847Schristos Vers_TS *vers;
1557a7c91847Schristos struct logfile_info *li;
1558a7c91847Schristos
1559a7c91847Schristos (void) classify_file_internal (finfo, &vers);
1560a7c91847Schristos li = p->data;
1561a7c91847Schristos li->rev_new = xstrdup (vers->vn_rcs);
1562a7c91847Schristos freevers_ts (&vers);
1563a7c91847Schristos }
1564a7c91847Schristos }
1565a7c91847Schristos }
1566a7c91847Schristos if (SIG_inCrSect ())
1567a7c91847Schristos SIG_endCrSect ();
1568a7c91847Schristos
1569a7c91847Schristos return err;
1570a7c91847Schristos }
1571a7c91847Schristos
1572a7c91847Schristos
1573a7c91847Schristos
1574a7c91847Schristos /*
1575a7c91847Schristos * Log the commit and clean up the update list
1576a7c91847Schristos */
1577a7c91847Schristos /* ARGSUSED */
1578a7c91847Schristos static int
commit_filesdoneproc(void * callerdat,int err,const char * repository,const char * update_dir,List * entries)1579a7c91847Schristos commit_filesdoneproc (void *callerdat, int err, const char *repository,
1580a7c91847Schristos const char *update_dir, List *entries)
1581a7c91847Schristos {
1582a7c91847Schristos Node *p;
1583a7c91847Schristos List *ulist;
1584a7c91847Schristos
1585a7c91847Schristos assert (repository);
1586a7c91847Schristos
1587a7c91847Schristos p = findnode (mulist, update_dir);
1588a7c91847Schristos if (p == NULL)
1589a7c91847Schristos return err;
1590a7c91847Schristos
1591a7c91847Schristos ulist = ((struct master_lists *) p->data)->ulist;
1592a7c91847Schristos
1593a7c91847Schristos got_message = 0;
1594a7c91847Schristos
1595a7c91847Schristos /* Build the administrative files if necessary. */
1596a7c91847Schristos {
1597a7c91847Schristos const char *p;
1598a7c91847Schristos
1599a7c91847Schristos if (strncmp (current_parsed_root->directory, repository,
1600a7c91847Schristos strlen (current_parsed_root->directory)) != 0)
1601a7c91847Schristos error (0, 0,
1602a7c91847Schristos "internal error: repository (%s) doesn't begin with root (%s)",
1603a7c91847Schristos repository, current_parsed_root->directory);
1604a7c91847Schristos p = repository + strlen (current_parsed_root->directory);
1605a7c91847Schristos if (*p == '/')
1606a7c91847Schristos ++p;
1607a7c91847Schristos if (strcmp ("CVSROOT", p) == 0
1608a7c91847Schristos /* Check for subdirectories because people may want to create
1609a7c91847Schristos subdirectories and list files therein in checkoutlist. */
1610a7c91847Schristos || strncmp ("CVSROOT/", p, strlen ("CVSROOT/")) == 0
1611a7c91847Schristos )
1612a7c91847Schristos {
1613a7c91847Schristos /* "Database" might a little bit grandiose and/or vague,
1614a7c91847Schristos but "checked-out copies of administrative files, unless
1615a7c91847Schristos in the case of modules and you are using ndbm in which
1616a7c91847Schristos case modules.{pag,dir,db}" is verbose and excessively
1617a7c91847Schristos focused on how the database is implemented. */
1618a7c91847Schristos
1619a7c91847Schristos /* mkmodules requires the absolute name of the CVSROOT directory.
1620a7c91847Schristos Remove anything after the `CVSROOT' component -- this is
1621a7c91847Schristos necessary when committing in a subdirectory of CVSROOT. */
1622a7c91847Schristos char *admin_dir = xstrdup (repository);
1623a7c91847Schristos int cvsrootlen = strlen ("CVSROOT");
1624a7c91847Schristos assert (admin_dir[p - repository + cvsrootlen] == '\0'
1625a7c91847Schristos || admin_dir[p - repository + cvsrootlen] == '/');
1626a7c91847Schristos admin_dir[p - repository + cvsrootlen] = '\0';
1627a7c91847Schristos
1628a7c91847Schristos if (!really_quiet)
1629a7c91847Schristos {
1630a7c91847Schristos cvs_output (program_name, 0);
1631a7c91847Schristos cvs_output (" ", 1);
1632a7c91847Schristos cvs_output (cvs_cmd_name, 0);
1633a7c91847Schristos cvs_output (": Rebuilding administrative file database\n", 0);
1634a7c91847Schristos }
1635a7c91847Schristos mkmodules (admin_dir);
1636a7c91847Schristos free (admin_dir);
1637a7c91847Schristos WriteTemplate (".", 1, repository);
1638a7c91847Schristos }
1639a7c91847Schristos }
1640a7c91847Schristos
1641a7c91847Schristos /* FIXME: This used to be above the block above. The advantage of being
1642a7c91847Schristos * here is that it is not called until after all possible writes from this
1643a7c91847Schristos * process are complete. The disadvantage is that a fatal error during
1644a7c91847Schristos * update of CVSROOT can prevent the loginfo script from being called.
1645a7c91847Schristos *
1646a7c91847Schristos * A more general solution I have been considering is calling a generic
1647a7c91847Schristos * "postwrite" hook from the remove write lock routine.
1648a7c91847Schristos */
1649a7c91847Schristos Update_Logfile (repository, saved_message, NULL, ulist);
1650a7c91847Schristos
1651a7c91847Schristos return err;
1652a7c91847Schristos }
1653a7c91847Schristos
1654a7c91847Schristos
1655a7c91847Schristos
1656a7c91847Schristos /*
1657a7c91847Schristos * Get the log message for a dir
1658a7c91847Schristos */
1659a7c91847Schristos /* ARGSUSED */
1660a7c91847Schristos static Dtype
commit_direntproc(void * callerdat,const char * dir,const char * repos,const char * update_dir,List * entries)1661a7c91847Schristos commit_direntproc (void *callerdat, const char *dir, const char *repos,
1662a7c91847Schristos const char *update_dir, List *entries)
1663a7c91847Schristos {
1664a7c91847Schristos Node *p;
1665a7c91847Schristos List *ulist;
1666a7c91847Schristos char *real_repos;
1667a7c91847Schristos
1668a7c91847Schristos if (!isdir (dir))
1669a7c91847Schristos return R_SKIP_ALL;
1670a7c91847Schristos
1671a7c91847Schristos /* find the update list for this dir */
1672a7c91847Schristos p = findnode (mulist, update_dir);
1673a7c91847Schristos if (p != NULL)
1674a7c91847Schristos ulist = ((struct master_lists *) p->data)->ulist;
1675a7c91847Schristos else
1676a7c91847Schristos ulist = NULL;
1677a7c91847Schristos
1678a7c91847Schristos /* skip the files as an optimization */
1679a7c91847Schristos if (ulist == NULL || ulist->list->next == ulist->list)
1680a7c91847Schristos return R_SKIP_FILES;
1681a7c91847Schristos
1682a7c91847Schristos /* get commit message */
1683a7c91847Schristos got_message = 1;
1684a7c91847Schristos real_repos = Name_Repository (dir, update_dir);
1685a7c91847Schristos if (!server_active && use_editor)
1686a7c91847Schristos do_editor (update_dir, &saved_message, real_repos, ulist);
1687a7c91847Schristos do_verify (&saved_message, real_repos, ulist);
1688a7c91847Schristos free (real_repos);
1689a7c91847Schristos return R_PROCESS;
1690a7c91847Schristos }
1691a7c91847Schristos
1692a7c91847Schristos
1693a7c91847Schristos
1694a7c91847Schristos /*
1695a7c91847Schristos * Process the post-commit proc if necessary
1696a7c91847Schristos */
1697a7c91847Schristos /* ARGSUSED */
1698a7c91847Schristos static int
commit_dirleaveproc(void * callerdat,const char * dir,int err,const char * update_dir,List * entries)1699a7c91847Schristos commit_dirleaveproc (void *callerdat, const char *dir, int err,
1700a7c91847Schristos const char *update_dir, List *entries)
1701a7c91847Schristos {
1702a7c91847Schristos /* update the per-directory tag info */
1703a7c91847Schristos /* FIXME? Why? The "commit examples" node of cvs.texinfo briefly
1704a7c91847Schristos mentions commit -r being sticky, but apparently in the context of
1705a7c91847Schristos this being a confusing feature! */
1706a7c91847Schristos if (err == 0 && write_dirtag != NULL)
1707a7c91847Schristos {
1708a7c91847Schristos char *repos = Name_Repository (NULL, update_dir);
1709a7c91847Schristos WriteTag (NULL, write_dirtag, NULL, write_dirnonbranch,
1710a7c91847Schristos update_dir, repos);
1711a7c91847Schristos free (repos);
1712a7c91847Schristos }
1713a7c91847Schristos
1714a7c91847Schristos return err;
1715a7c91847Schristos }
1716a7c91847Schristos
1717a7c91847Schristos
1718a7c91847Schristos
1719a7c91847Schristos /*
1720a7c91847Schristos * find the maximum major rev number in an entries file
1721a7c91847Schristos */
1722a7c91847Schristos static int
findmaxrev(Node * p,void * closure)1723a7c91847Schristos findmaxrev (Node *p, void *closure)
1724a7c91847Schristos {
1725a7c91847Schristos int thisrev;
1726a7c91847Schristos Entnode *entdata = p->data;
1727a7c91847Schristos
1728a7c91847Schristos if (entdata->type != ENT_FILE)
1729a7c91847Schristos return 0;
1730a7c91847Schristos thisrev = atoi (entdata->version);
1731a7c91847Schristos if (thisrev > maxrev)
1732a7c91847Schristos maxrev = thisrev;
1733a7c91847Schristos return 0;
1734a7c91847Schristos }
1735a7c91847Schristos
1736a7c91847Schristos /*
1737a7c91847Schristos * Actually remove a file by moving it to the attic
1738a7c91847Schristos * XXX - if removing a ,v file that is a relative symbolic link to
1739a7c91847Schristos * another ,v file, we probably should add a ".." component to the
1740a7c91847Schristos * link to keep it relative after we move it into the attic.
1741a7c91847Schristos
1742a7c91847Schristos Return value is 0 on success, or >0 on error (in which case we have
1743a7c91847Schristos printed an error message). */
1744a7c91847Schristos static int
remove_file(struct file_info * finfo,char * tag,char * message)1745a7c91847Schristos remove_file (struct file_info *finfo, char *tag, char *message)
1746a7c91847Schristos {
1747a7c91847Schristos int retcode;
1748a7c91847Schristos
1749a7c91847Schristos int branch;
1750a7c91847Schristos int lockflag;
1751a7c91847Schristos char *corev;
1752a7c91847Schristos char *rev;
1753a7c91847Schristos char *prev_rev;
1754a7c91847Schristos char *old_path;
1755a7c91847Schristos
1756a7c91847Schristos corev = NULL;
1757a7c91847Schristos rev = NULL;
1758a7c91847Schristos prev_rev = NULL;
1759a7c91847Schristos
1760a7c91847Schristos retcode = 0;
1761a7c91847Schristos
1762a7c91847Schristos if (finfo->rcs == NULL)
1763a7c91847Schristos error (1, 0, "internal error: no parsed RCS file");
1764a7c91847Schristos
1765a7c91847Schristos branch = 0;
1766a7c91847Schristos if (tag && !(branch = RCS_nodeisbranch (finfo->rcs, tag)))
1767a7c91847Schristos {
1768a7c91847Schristos /* a symbolic tag is specified; just remove the tag from the file */
1769a7c91847Schristos if ((retcode = RCS_deltag (finfo->rcs, tag)) != 0)
1770a7c91847Schristos {
1771a7c91847Schristos if (!quiet)
1772a7c91847Schristos error (0, retcode == -1 ? errno : 0,
1773a7c91847Schristos "failed to remove tag `%s' from `%s'", tag,
1774a7c91847Schristos finfo->fullname);
1775a7c91847Schristos return 1;
1776a7c91847Schristos }
1777a7c91847Schristos RCS_rewrite (finfo->rcs, NULL, NULL);
1778a7c91847Schristos Scratch_Entry (finfo->entries, finfo->file);
1779a7c91847Schristos return 0;
1780a7c91847Schristos }
1781a7c91847Schristos
1782a7c91847Schristos /* we are removing the file from either the head or a branch */
1783a7c91847Schristos /* commit a new, dead revision. */
1784a7c91847Schristos
1785a7c91847Schristos rev = NULL;
1786a7c91847Schristos lockflag = 1;
1787a7c91847Schristos if (branch)
1788a7c91847Schristos {
1789a7c91847Schristos char *branchname;
1790a7c91847Schristos
1791a7c91847Schristos rev = RCS_whatbranch (finfo->rcs, tag);
1792a7c91847Schristos if (rev == NULL)
1793a7c91847Schristos {
1794a7c91847Schristos error (0, 0, "cannot find branch \"%s\".", tag);
1795a7c91847Schristos return 1;
1796a7c91847Schristos }
1797a7c91847Schristos
1798a7c91847Schristos branchname = RCS_getbranch (finfo->rcs, rev, 1);
1799a7c91847Schristos if (branchname == NULL)
1800a7c91847Schristos {
1801a7c91847Schristos /* no revision exists on this branch. use the previous
1802a7c91847Schristos revision but do not lock. */
1803a7c91847Schristos corev = RCS_gettag (finfo->rcs, tag, 1, NULL);
1804a7c91847Schristos prev_rev = xstrdup (corev);
1805a7c91847Schristos lockflag = 0;
1806a7c91847Schristos } else
1807a7c91847Schristos {
1808a7c91847Schristos corev = xstrdup (rev);
1809a7c91847Schristos prev_rev = xstrdup (branchname);
1810a7c91847Schristos free (branchname);
1811a7c91847Schristos }
1812a7c91847Schristos
1813a7c91847Schristos } else /* Not a branch */
1814a7c91847Schristos {
1815a7c91847Schristos /* Get current head revision of file. */
1816a7c91847Schristos prev_rev = RCS_head (finfo->rcs);
1817a7c91847Schristos }
1818a7c91847Schristos
1819a7c91847Schristos /* if removing without a tag or a branch, then make sure the default
1820a7c91847Schristos branch is the trunk. */
1821a7c91847Schristos if (!tag && !branch)
1822a7c91847Schristos {
1823a7c91847Schristos if (RCS_setbranch (finfo->rcs, NULL) != 0)
1824a7c91847Schristos {
1825a7c91847Schristos error (0, 0, "cannot change branch to default for %s",
1826a7c91847Schristos finfo->fullname);
1827a7c91847Schristos return 1;
1828a7c91847Schristos }
1829a7c91847Schristos RCS_rewrite (finfo->rcs, NULL, NULL);
1830a7c91847Schristos }
1831a7c91847Schristos
1832a7c91847Schristos /* check something out. Generally this is the head. If we have a
1833a7c91847Schristos particular rev, then name it. */
1834a7c91847Schristos retcode = RCS_checkout (finfo->rcs, finfo->file, rev ? corev : NULL,
1835a7c91847Schristos NULL, NULL, RUN_TTY, NULL, NULL);
1836a7c91847Schristos if (retcode != 0)
1837a7c91847Schristos {
1838a7c91847Schristos error (0, 0,
1839a7c91847Schristos "failed to check out `%s'", finfo->fullname);
1840a7c91847Schristos return 1;
1841a7c91847Schristos }
1842a7c91847Schristos
1843a7c91847Schristos /* Except when we are creating a branch, lock the revision so that
1844a7c91847Schristos we can check in the new revision. */
1845a7c91847Schristos if (lockflag)
1846a7c91847Schristos {
1847a7c91847Schristos if (RCS_lock (finfo->rcs, rev ? corev : NULL, 1) == 0)
1848a7c91847Schristos RCS_rewrite (finfo->rcs, NULL, NULL);
1849a7c91847Schristos }
1850a7c91847Schristos
1851a7c91847Schristos if (corev != NULL)
1852a7c91847Schristos free (corev);
1853a7c91847Schristos
1854a7c91847Schristos retcode = RCS_checkin (finfo->rcs, NULL, finfo->file, message,
1855a7c91847Schristos rev, 0, RCS_FLAGS_DEAD | RCS_FLAGS_QUIET);
1856a7c91847Schristos if (retcode != 0)
1857a7c91847Schristos {
1858a7c91847Schristos if (!quiet)
1859a7c91847Schristos error (0, retcode == -1 ? errno : 0,
1860a7c91847Schristos "failed to commit dead revision for `%s'", finfo->fullname);
1861a7c91847Schristos return 1;
1862a7c91847Schristos }
1863a7c91847Schristos /* At this point, the file has been committed as removed. We should
1864a7c91847Schristos probably tell the history file about it */
1865a7c91847Schristos history_write ('R', NULL, finfo->rcs->head, finfo->file, finfo->repository);
1866a7c91847Schristos
1867a7c91847Schristos if (rev != NULL)
1868a7c91847Schristos free (rev);
1869a7c91847Schristos
1870a7c91847Schristos old_path = xstrdup (finfo->rcs->path);
1871a7c91847Schristos if (!branch)
1872a7c91847Schristos RCS_setattic (finfo->rcs, 1);
1873a7c91847Schristos
1874a7c91847Schristos /* Print message that file was removed. */
1875a7c91847Schristos if (!really_quiet)
1876a7c91847Schristos {
1877a7c91847Schristos cvs_output (old_path, 0);
1878a7c91847Schristos cvs_output (" <-- ", 0);
1879a7c91847Schristos if (finfo->update_dir && strlen (finfo->update_dir))
1880a7c91847Schristos {
1881a7c91847Schristos cvs_output (finfo->update_dir, 0);
1882a7c91847Schristos cvs_output ("/", 1);
1883a7c91847Schristos }
1884a7c91847Schristos cvs_output (finfo->file, 0);
1885a7c91847Schristos cvs_output ("\nnew revision: delete; previous revision: ", 0);
1886a7c91847Schristos cvs_output (prev_rev, 0);
1887a7c91847Schristos cvs_output ("\n", 0);
1888a7c91847Schristos }
1889a7c91847Schristos
1890a7c91847Schristos free (prev_rev);
1891a7c91847Schristos
1892a7c91847Schristos free (old_path);
1893a7c91847Schristos
1894a7c91847Schristos Scratch_Entry (finfo->entries, finfo->file);
1895a7c91847Schristos return 0;
1896a7c91847Schristos }
1897a7c91847Schristos
1898a7c91847Schristos
1899a7c91847Schristos
1900a7c91847Schristos /*
1901a7c91847Schristos * Do the actual checkin for added files
1902a7c91847Schristos */
1903a7c91847Schristos static int
finaladd(struct file_info * finfo,char * rev,char * tag,char * options)1904a7c91847Schristos finaladd (struct file_info *finfo, char *rev, char *tag, char *options)
1905a7c91847Schristos {
1906a7c91847Schristos int ret;
1907a7c91847Schristos
1908a7c91847Schristos ret = Checkin ('A', finfo, rev, tag, options, saved_message);
1909a7c91847Schristos if (ret == 0)
1910a7c91847Schristos {
1911a7c91847Schristos char *tmp = Xasprintf ("%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG);
1912a7c91847Schristos if (unlink_file (tmp) < 0
1913a7c91847Schristos && !existence_error (errno))
1914a7c91847Schristos error (0, errno, "cannot remove %s", tmp);
1915a7c91847Schristos free (tmp);
1916a7c91847Schristos }
1917a7c91847Schristos else if (finfo->rcs != NULL)
1918a7c91847Schristos fixaddfile (finfo->rcs->path);
1919a7c91847Schristos
1920a7c91847Schristos (void) time (&last_register_time);
1921a7c91847Schristos
1922a7c91847Schristos return ret;
1923a7c91847Schristos }
1924a7c91847Schristos
1925a7c91847Schristos
1926a7c91847Schristos
1927a7c91847Schristos /*
1928a7c91847Schristos * Unlock an rcs file
1929a7c91847Schristos */
1930a7c91847Schristos static void
unlockrcs(RCSNode * rcs)1931a7c91847Schristos unlockrcs (RCSNode *rcs)
1932a7c91847Schristos {
1933a7c91847Schristos int retcode;
1934a7c91847Schristos
1935a7c91847Schristos if ((retcode = RCS_unlock (rcs, NULL, 1)) != 0)
1936a7c91847Schristos error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
1937a7c91847Schristos "could not unlock %s", rcs->path);
1938a7c91847Schristos else
1939a7c91847Schristos RCS_rewrite (rcs, NULL, NULL);
1940a7c91847Schristos }
1941a7c91847Schristos
1942a7c91847Schristos
1943a7c91847Schristos
1944a7c91847Schristos /*
1945a7c91847Schristos * remove a partially added file. if we can parse it, leave it alone.
1946a7c91847Schristos *
1947a7c91847Schristos * FIXME: Every caller that calls this function can access finfo->rcs (the
1948a7c91847Schristos * parsed RCSNode data), so we should be able to detect that the file needs
1949a7c91847Schristos * to be removed without reparsing the file as we do below.
1950a7c91847Schristos */
1951a7c91847Schristos static void
fixaddfile(const char * rcs)1952a7c91847Schristos fixaddfile (const char *rcs)
1953a7c91847Schristos {
1954a7c91847Schristos RCSNode *rcsfile;
1955a7c91847Schristos int save_really_quiet;
1956a7c91847Schristos
1957a7c91847Schristos save_really_quiet = really_quiet;
1958a7c91847Schristos really_quiet = 1;
1959a7c91847Schristos if ((rcsfile = RCS_parsercsfile (rcs)) == NULL)
1960a7c91847Schristos {
1961a7c91847Schristos if (unlink_file (rcs) < 0)
1962a7c91847Schristos error (0, errno, "cannot remove %s", rcs);
1963a7c91847Schristos }
1964a7c91847Schristos else
1965a7c91847Schristos freercsnode (&rcsfile);
1966a7c91847Schristos really_quiet = save_really_quiet;
1967a7c91847Schristos }
1968a7c91847Schristos
1969a7c91847Schristos
1970a7c91847Schristos
1971a7c91847Schristos /*
1972a7c91847Schristos * put the branch back on an rcs file
1973a7c91847Schristos */
1974a7c91847Schristos static void
fixbranch(RCSNode * rcs,char * branch)1975a7c91847Schristos fixbranch (RCSNode *rcs, char *branch)
1976a7c91847Schristos {
1977a7c91847Schristos int retcode;
1978a7c91847Schristos
1979a7c91847Schristos if (branch != NULL)
1980a7c91847Schristos {
1981a7c91847Schristos if ((retcode = RCS_setbranch (rcs, branch)) != 0)
1982a7c91847Schristos error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
1983a7c91847Schristos "cannot restore branch to %s for %s", branch, rcs->path);
1984a7c91847Schristos RCS_rewrite (rcs, NULL, NULL);
1985a7c91847Schristos }
1986a7c91847Schristos }
1987a7c91847Schristos
1988a7c91847Schristos
1989a7c91847Schristos
1990a7c91847Schristos /*
1991a7c91847Schristos * do the initial part of a file add for the named file. if adding
1992a7c91847Schristos * with a tag, put the file in the Attic and point the symbolic tag
1993a7c91847Schristos * at the committed revision.
1994a7c91847Schristos *
1995a7c91847Schristos * INPUTS
1996a7c91847Schristos * file The name of the file in the workspace.
1997a7c91847Schristos * repository The repository directory to expect to find FILE,v in.
1998a7c91847Schristos * tag The name or rev num of the branch being added to, if any.
1999a7c91847Schristos * options Any RCS keyword expansion options specified by the user.
2000a7c91847Schristos * rcsnode A pointer to the pre-parsed RCSNode for this file, if the file
2001a7c91847Schristos * exists in the repository. If this is NULL, assume the file
2002a7c91847Schristos * does not yet exist.
2003a7c91847Schristos *
2004a7c91847Schristos * RETURNS
2005a7c91847Schristos * 0 on success.
2006a7c91847Schristos * 1 on errors, after printing any appropriate error messages.
2007a7c91847Schristos *
2008a7c91847Schristos * ERRORS
2009a7c91847Schristos * This function will return an error when any of the following functions do:
2010a7c91847Schristos * add_rcs_file
2011a7c91847Schristos * RCS_setattic
2012a7c91847Schristos * lock_RCS
2013a7c91847Schristos * RCS_checkin
2014a7c91847Schristos * RCS_parse (called to verify the newly created archive file)
2015a7c91847Schristos * RCS_settag
2016a7c91847Schristos */
2017a7c91847Schristos
2018a7c91847Schristos static int
checkaddfile(const char * file,const char * repository,const char * tag,const char * options,RCSNode ** rcsnode)2019a7c91847Schristos checkaddfile (const char *file, const char *repository, const char *tag,
2020a7c91847Schristos const char *options, RCSNode **rcsnode)
2021a7c91847Schristos {
2022a7c91847Schristos RCSNode *rcs;
2023a7c91847Schristos char *fname;
2024a7c91847Schristos int newfile = 0; /* Set to 1 if we created a new RCS archive. */
2025a7c91847Schristos int retval = 1;
2026a7c91847Schristos int adding_on_branch;
2027a7c91847Schristos
2028a7c91847Schristos assert (rcsnode != NULL);
2029a7c91847Schristos
2030a7c91847Schristos /* Callers expect to be able to use either "" or NULL to mean the
2031a7c91847Schristos default keyword expansion. */
2032a7c91847Schristos if (options != NULL && options[0] == '\0')
2033a7c91847Schristos options = NULL;
2034a7c91847Schristos if (options != NULL)
2035a7c91847Schristos assert (options[0] == '-' && options[1] == 'k');
2036a7c91847Schristos
2037a7c91847Schristos /* If numeric, it is on the trunk; check_fileproc enforced
2038a7c91847Schristos this. */
2039a7c91847Schristos adding_on_branch = tag != NULL && !isdigit ((unsigned char) tag[0]);
2040a7c91847Schristos
2041a7c91847Schristos if (*rcsnode == NULL)
2042a7c91847Schristos {
2043a7c91847Schristos char *rcsname;
2044a7c91847Schristos char *desc = NULL;
2045a7c91847Schristos size_t descalloc = 0;
2046a7c91847Schristos size_t desclen = 0;
2047a7c91847Schristos const char *opt;
2048a7c91847Schristos
2049a7c91847Schristos if (adding_on_branch)
2050a7c91847Schristos {
2051a7c91847Schristos mode_t omask;
2052a7c91847Schristos rcsname = xmalloc (strlen (repository)
2053a7c91847Schristos + sizeof (CVSATTIC)
2054a7c91847Schristos + strlen (file)
2055a7c91847Schristos + sizeof (RCSEXT)
2056a7c91847Schristos + 3);
2057a7c91847Schristos (void) sprintf (rcsname, "%s/%s", repository, CVSATTIC);
2058a7c91847Schristos omask = umask (cvsumask);
2059a7c91847Schristos if (CVS_MKDIR (rcsname, 0777) != 0 && errno != EEXIST)
2060a7c91847Schristos error (1, errno, "cannot make directory `%s'", rcsname);
2061a7c91847Schristos (void) umask (omask);
2062a7c91847Schristos (void) sprintf (rcsname,
2063a7c91847Schristos "%s/%s/%s%s",
2064a7c91847Schristos repository,
2065a7c91847Schristos CVSATTIC,
2066a7c91847Schristos file,
2067a7c91847Schristos RCSEXT);
2068a7c91847Schristos }
2069a7c91847Schristos else
2070a7c91847Schristos rcsname = Xasprintf ("%s/%s%s", repository, file, RCSEXT);
2071a7c91847Schristos
2072a7c91847Schristos /* this is the first time we have ever seen this file; create
2073a7c91847Schristos an RCS file. */
2074a7c91847Schristos fname = Xasprintf ("%s/%s%s", CVSADM, file, CVSEXT_LOG);
2075a7c91847Schristos /* If the file does not exist, no big deal. In particular, the
2076a7c91847Schristos server does not (yet at least) create CVSEXT_LOG files. */
2077a7c91847Schristos if (isfile (fname))
2078a7c91847Schristos /* FIXME: Should be including update_dir in the appropriate
2079a7c91847Schristos place here. */
2080a7c91847Schristos get_file (fname, fname, "r", &desc, &descalloc, &desclen);
2081a7c91847Schristos free (fname);
2082a7c91847Schristos
2083a7c91847Schristos /* From reading the RCS 5.7 source, "rcs -i" adds a newline to the
2084a7c91847Schristos end of the log message if the message is nonempty.
2085a7c91847Schristos Do it. RCS also deletes certain whitespace, in cleanlogmsg,
2086a7c91847Schristos which we don't try to do here. */
2087a7c91847Schristos if (desclen > 0)
2088a7c91847Schristos {
2089a7c91847Schristos expand_string (&desc, &descalloc, desclen + 1);
2090a7c91847Schristos desc[desclen++] = '\012';
2091a7c91847Schristos }
2092a7c91847Schristos
2093a7c91847Schristos /* Set RCS keyword expansion options. */
2094a7c91847Schristos if (options != NULL)
2095a7c91847Schristos opt = options + 2;
2096a7c91847Schristos else
2097a7c91847Schristos opt = NULL;
2098a7c91847Schristos
2099a7c91847Schristos if (add_rcs_file (NULL, rcsname, file, NULL, opt,
2100a7c91847Schristos NULL, NULL, 0, NULL,
2101a7c91847Schristos desc, desclen, NULL, 0) != 0)
2102a7c91847Schristos {
2103a7c91847Schristos if (rcsname != NULL)
2104a7c91847Schristos free (rcsname);
2105a7c91847Schristos goto out;
2106a7c91847Schristos }
2107a7c91847Schristos rcs = RCS_parsercsfile (rcsname);
2108a7c91847Schristos newfile = 1;
2109a7c91847Schristos if (rcsname != NULL)
2110a7c91847Schristos free (rcsname);
2111a7c91847Schristos if (desc != NULL)
2112a7c91847Schristos free (desc);
2113a7c91847Schristos *rcsnode = rcs;
2114a7c91847Schristos }
2115a7c91847Schristos else
2116a7c91847Schristos {
2117a7c91847Schristos /* file has existed in the past. Prepare to resurrect. */
2118a7c91847Schristos char *rev;
2119a7c91847Schristos char *oldexpand;
2120a7c91847Schristos
2121a7c91847Schristos rcs = *rcsnode;
2122a7c91847Schristos
2123a7c91847Schristos oldexpand = RCS_getexpand (rcs);
2124a7c91847Schristos if ((oldexpand != NULL
2125a7c91847Schristos && options != NULL
2126a7c91847Schristos && strcmp (options + 2, oldexpand) != 0)
2127a7c91847Schristos || (oldexpand == NULL && options != NULL))
2128a7c91847Schristos {
2129a7c91847Schristos /* We tell the user about this, because it means that the
2130a7c91847Schristos old revisions will no longer retrieve the way that they
2131a7c91847Schristos used to. */
2132a7c91847Schristos error (0, 0, "changing keyword expansion mode to %s", options);
2133a7c91847Schristos RCS_setexpand (rcs, options + 2);
2134a7c91847Schristos }
2135a7c91847Schristos
2136a7c91847Schristos if (!adding_on_branch)
2137a7c91847Schristos {
2138a7c91847Schristos /* We are adding on the trunk, so move the file out of the
2139a7c91847Schristos Attic. */
2140a7c91847Schristos if (!(rcs->flags & INATTIC))
2141a7c91847Schristos {
2142a7c91847Schristos error (0, 0, "warning: expected %s to be in Attic",
2143a7c91847Schristos rcs->path);
2144a7c91847Schristos }
2145a7c91847Schristos
2146a7c91847Schristos /* Begin a critical section around the code that spans the
2147a7c91847Schristos first commit on the trunk of a file that's already been
2148a7c91847Schristos committed on a branch. */
2149a7c91847Schristos SIG_beginCrSect ();
2150a7c91847Schristos
2151a7c91847Schristos if (RCS_setattic (rcs, 0))
2152a7c91847Schristos {
2153a7c91847Schristos goto out;
2154a7c91847Schristos }
2155a7c91847Schristos }
2156a7c91847Schristos
2157a7c91847Schristos rev = RCS_getversion (rcs, tag, NULL, 1, NULL);
2158a7c91847Schristos /* and lock it */
2159a7c91847Schristos if (lock_RCS (file, rcs, rev, repository))
2160a7c91847Schristos {
2161a7c91847Schristos error (0, 0, "cannot lock revision %s in `%s'.",
2162a7c91847Schristos rev ? rev : tag ? tag : "HEAD", rcs->path);
2163a7c91847Schristos if (rev != NULL)
2164a7c91847Schristos free (rev);
2165a7c91847Schristos goto out;
2166a7c91847Schristos }
2167a7c91847Schristos
2168a7c91847Schristos if (rev != NULL)
2169a7c91847Schristos free (rev);
2170a7c91847Schristos }
2171a7c91847Schristos
2172a7c91847Schristos /* when adding a file for the first time, and using a tag, we need
2173a7c91847Schristos to create a dead revision on the trunk. */
2174a7c91847Schristos if (adding_on_branch)
2175a7c91847Schristos {
2176a7c91847Schristos if (newfile)
2177a7c91847Schristos {
2178a7c91847Schristos char *tmp;
2179a7c91847Schristos FILE *fp;
2180a7c91847Schristos int retcode;
2181a7c91847Schristos
2182a7c91847Schristos /* move the new file out of the way. */
2183a7c91847Schristos fname = Xasprintf ("%s/%s%s", CVSADM, CVSPREFIX, file);
2184a7c91847Schristos rename_file (file, fname);
2185a7c91847Schristos
2186a7c91847Schristos /* Create empty FILE. Can't use copy_file with a DEVNULL
2187a7c91847Schristos argument -- copy_file now ignores device files. */
2188a7c91847Schristos fp = fopen (file, "w");
2189a7c91847Schristos if (fp == NULL)
2190a7c91847Schristos error (1, errno, "cannot open %s for writing", file);
2191a7c91847Schristos if (fclose (fp) < 0)
2192a7c91847Schristos error (0, errno, "cannot close %s", file);
2193a7c91847Schristos
2194a7c91847Schristos tmp = Xasprintf ("file %s was initially added on branch %s.",
2195a7c91847Schristos file, tag);
2196a7c91847Schristos /* commit a dead revision. */
2197a7c91847Schristos retcode = RCS_checkin (rcs, NULL, NULL, tmp, NULL, 0,
2198a7c91847Schristos RCS_FLAGS_DEAD | RCS_FLAGS_QUIET);
2199a7c91847Schristos free (tmp);
2200a7c91847Schristos if (retcode != 0)
2201a7c91847Schristos {
2202a7c91847Schristos error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
2203a7c91847Schristos "could not create initial dead revision %s", rcs->path);
2204a7c91847Schristos free (fname);
2205a7c91847Schristos goto out;
2206a7c91847Schristos }
2207a7c91847Schristos
2208a7c91847Schristos /* put the new file back where it was */
2209a7c91847Schristos rename_file (fname, file);
2210a7c91847Schristos free (fname);
2211a7c91847Schristos
2212a7c91847Schristos /* double-check that the file was written correctly */
2213a7c91847Schristos freercsnode (&rcs);
2214a7c91847Schristos rcs = RCS_parse (file, repository);
2215a7c91847Schristos if (rcs == NULL)
2216a7c91847Schristos {
2217a7c91847Schristos error (0, 0, "could not read %s", rcs->path);
2218a7c91847Schristos goto out;
2219a7c91847Schristos }
2220a7c91847Schristos *rcsnode = rcs;
2221a7c91847Schristos
2222a7c91847Schristos /* and lock it once again. */
2223a7c91847Schristos if (lock_RCS (file, rcs, NULL, repository))
2224a7c91847Schristos {
2225a7c91847Schristos error (0, 0, "cannot lock initial revision in `%s'.",
2226a7c91847Schristos rcs->path);
2227a7c91847Schristos goto out;
2228a7c91847Schristos }
2229a7c91847Schristos }
2230a7c91847Schristos
2231a7c91847Schristos /* when adding with a tag, we need to stub a branch, if it
2232a7c91847Schristos doesn't already exist. */
2233a7c91847Schristos if (!RCS_nodeisbranch (rcs, tag))
2234a7c91847Schristos {
2235a7c91847Schristos /* branch does not exist. Stub it. */
2236a7c91847Schristos char *head;
2237a7c91847Schristos char *magicrev;
2238a7c91847Schristos int retcode;
2239a7c91847Schristos time_t headtime = -1;
2240a7c91847Schristos char *revnum, *tmp;
2241a7c91847Schristos FILE *fp;
2242a7c91847Schristos time_t t = -1;
2243a7c91847Schristos struct tm *ct;
2244a7c91847Schristos
2245a7c91847Schristos fixbranch (rcs, sbranch);
2246a7c91847Schristos
2247a7c91847Schristos head = RCS_getversion (rcs, NULL, NULL, 0, NULL);
2248a7c91847Schristos if (!head)
2249a7c91847Schristos error (1, 0, "No head revision in archive file `%s'.",
2250a7c91847Schristos rcs->print_path);
2251a7c91847Schristos magicrev = RCS_magicrev (rcs, head);
2252a7c91847Schristos
2253a7c91847Schristos /* If this is not a new branch, then we will want a dead
2254a7c91847Schristos version created before this one. */
2255a7c91847Schristos if (!newfile)
2256a7c91847Schristos headtime = RCS_getrevtime (rcs, head, 0, 0);
2257a7c91847Schristos
2258a7c91847Schristos retcode = RCS_settag (rcs, tag, magicrev);
2259a7c91847Schristos RCS_rewrite (rcs, NULL, NULL);
2260a7c91847Schristos
2261a7c91847Schristos free (head);
2262a7c91847Schristos free (magicrev);
2263a7c91847Schristos
2264a7c91847Schristos if (retcode != 0)
2265a7c91847Schristos {
2266a7c91847Schristos error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
2267a7c91847Schristos "could not stub branch %s for %s", tag, rcs->path);
2268a7c91847Schristos goto out;
2269a7c91847Schristos }
2270a7c91847Schristos /* We need to add a dead version here to avoid -rtag -Dtime
2271a7c91847Schristos checkout problems between when the head version was
2272a7c91847Schristos created and now. */
2273a7c91847Schristos if (!newfile && headtime != -1)
2274a7c91847Schristos {
2275a7c91847Schristos /* move the new file out of the way. */
2276a7c91847Schristos fname = Xasprintf ("%s/%s%s", CVSADM, CVSPREFIX, file);
2277a7c91847Schristos rename_file (file, fname);
2278a7c91847Schristos
2279a7c91847Schristos /* Create empty FILE. Can't use copy_file with a DEVNULL
2280a7c91847Schristos argument -- copy_file now ignores device files. */
2281a7c91847Schristos fp = fopen (file, "w");
2282a7c91847Schristos if (fp == NULL)
2283a7c91847Schristos error (1, errno, "cannot open %s for writing", file);
2284a7c91847Schristos if (fclose (fp) < 0)
2285a7c91847Schristos error (0, errno, "cannot close %s", file);
2286a7c91847Schristos
2287a7c91847Schristos /* As we will be hacking the delta date, put the time
2288a7c91847Schristos this was added into the log message. */
2289a7c91847Schristos t = time (NULL);
2290a7c91847Schristos ct = gmtime (&t);
2291a7c91847Schristos tmp = Xasprintf ("file %s was added on branch %s on %d-%02d-%02d %02d:%02d:%02d +0000",
2292a7c91847Schristos file, tag,
2293a7c91847Schristos ct->tm_year + (ct->tm_year < 100 ? 0 : 1900),
2294a7c91847Schristos ct->tm_mon + 1, ct->tm_mday,
2295a7c91847Schristos ct->tm_hour, ct->tm_min, ct->tm_sec);
2296a7c91847Schristos
2297a7c91847Schristos /* commit a dead revision. */
2298a7c91847Schristos revnum = RCS_whatbranch (rcs, tag);
2299a7c91847Schristos retcode = RCS_checkin (rcs, NULL, NULL, tmp, revnum, headtime,
2300a7c91847Schristos RCS_FLAGS_DEAD |
2301a7c91847Schristos RCS_FLAGS_QUIET |
2302a7c91847Schristos RCS_FLAGS_USETIME);
2303a7c91847Schristos free (revnum);
2304a7c91847Schristos free (tmp);
2305a7c91847Schristos
2306a7c91847Schristos if (retcode != 0)
2307a7c91847Schristos {
2308a7c91847Schristos error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
2309a7c91847Schristos "could not created dead stub %s for %s", tag,
2310a7c91847Schristos rcs->path);
2311a7c91847Schristos goto out;
2312a7c91847Schristos }
2313a7c91847Schristos
2314a7c91847Schristos /* put the new file back where it was */
2315a7c91847Schristos rename_file (fname, file);
2316a7c91847Schristos free (fname);
2317a7c91847Schristos
2318a7c91847Schristos /* double-check that the file was written correctly */
2319a7c91847Schristos freercsnode (&rcs);
2320a7c91847Schristos rcs = RCS_parse (file, repository);
2321a7c91847Schristos if (rcs == NULL)
2322a7c91847Schristos {
2323a7c91847Schristos error (0, 0, "could not read %s", rcs->path);
2324a7c91847Schristos goto out;
2325a7c91847Schristos }
2326a7c91847Schristos *rcsnode = rcs;
2327a7c91847Schristos }
2328a7c91847Schristos }
2329a7c91847Schristos else
2330a7c91847Schristos {
2331a7c91847Schristos /* lock the branch. (stubbed branches need not be locked.) */
2332a7c91847Schristos if (lock_RCS (file, rcs, NULL, repository))
2333a7c91847Schristos {
2334a7c91847Schristos error (0, 0, "cannot lock head revision in `%s'.", rcs->path);
2335a7c91847Schristos goto out;
2336a7c91847Schristos }
2337a7c91847Schristos }
2338a7c91847Schristos
2339a7c91847Schristos if (*rcsnode != rcs)
2340a7c91847Schristos {
2341a7c91847Schristos freercsnode (rcsnode);
2342a7c91847Schristos *rcsnode = rcs;
2343a7c91847Schristos }
2344a7c91847Schristos }
2345a7c91847Schristos
2346a7c91847Schristos fileattr_newfile (file);
2347a7c91847Schristos
2348a7c91847Schristos /* At this point, we used to set the file mode of the RCS file
2349a7c91847Schristos based on the mode of the file in the working directory. If we
2350a7c91847Schristos are creating the RCS file for the first time, add_rcs_file does
2351a7c91847Schristos this already. If we are re-adding the file, then perhaps it is
2352a7c91847Schristos consistent to preserve the old file mode, just as we preserve
2353a7c91847Schristos the old keyword expansion mode.
2354a7c91847Schristos
2355a7c91847Schristos If we decide that we should change the modes, then we can't do
2356a7c91847Schristos it here anyhow. At this point, the RCS file may be owned by
2357a7c91847Schristos somebody else, so a chmod will fail. We need to instead do the
2358a7c91847Schristos chmod after rewriting it.
2359a7c91847Schristos
2360a7c91847Schristos FIXME: In general, I think the file mode (and the keyword
2361a7c91847Schristos expansion mode) should be associated with a particular revision
2362a7c91847Schristos of the file, so that it is possible to have different revisions
2363a7c91847Schristos of a file have different modes. */
2364a7c91847Schristos
2365a7c91847Schristos retval = 0;
2366a7c91847Schristos
2367a7c91847Schristos out:
2368a7c91847Schristos if (retval != 0 && SIG_inCrSect ())
2369a7c91847Schristos SIG_endCrSect ();
2370a7c91847Schristos return retval;
2371a7c91847Schristos }
2372a7c91847Schristos
2373a7c91847Schristos
2374a7c91847Schristos
2375a7c91847Schristos /*
2376a7c91847Schristos * Attempt to place a lock on the RCS file; returns 0 if it could and 1 if it
2377a7c91847Schristos * couldn't. If the RCS file currently has a branch as the head, we must
2378a7c91847Schristos * move the head back to the trunk before locking the file, and be sure to
2379a7c91847Schristos * put the branch back as the head if there are any errors.
2380a7c91847Schristos */
2381a7c91847Schristos static int
lock_RCS(const char * user,RCSNode * rcs,const char * rev,const char * repository)2382a7c91847Schristos lock_RCS (const char *user, RCSNode *rcs, const char *rev,
2383a7c91847Schristos const char *repository)
2384a7c91847Schristos {
2385a7c91847Schristos char *branch = NULL;
2386a7c91847Schristos int err = 0;
2387a7c91847Schristos
2388a7c91847Schristos /*
2389a7c91847Schristos * For a specified, numeric revision of the form "1" or "1.1", (or when
2390a7c91847Schristos * no revision is specified ""), definitely move the branch to the trunk
2391a7c91847Schristos * before locking the RCS file.
2392a7c91847Schristos *
2393a7c91847Schristos * The assumption is that if there is more than one revision on the trunk,
2394a7c91847Schristos * the head points to the trunk, not a branch... and as such, it's not
2395a7c91847Schristos * necessary to move the head in this case.
2396a7c91847Schristos */
2397a7c91847Schristos if (rev == NULL
2398a7c91847Schristos || (rev && isdigit ((unsigned char) *rev) && numdots (rev) < 2))
2399a7c91847Schristos {
2400a7c91847Schristos branch = xstrdup (rcs->branch);
2401a7c91847Schristos if (branch != NULL)
2402a7c91847Schristos {
2403a7c91847Schristos if (RCS_setbranch (rcs, NULL) != 0)
2404a7c91847Schristos {
2405a7c91847Schristos error (0, 0, "cannot change branch to default for %s",
2406a7c91847Schristos rcs->path);
2407a7c91847Schristos if (branch)
2408a7c91847Schristos free (branch);
2409a7c91847Schristos return 1;
2410a7c91847Schristos }
2411a7c91847Schristos }
2412a7c91847Schristos err = RCS_lock (rcs, NULL, 1);
2413a7c91847Schristos }
2414a7c91847Schristos else
2415a7c91847Schristos {
2416a7c91847Schristos RCS_lock (rcs, rev, 1);
2417a7c91847Schristos }
2418a7c91847Schristos
2419a7c91847Schristos /* We used to call RCS_rewrite here, and that might seem
2420a7c91847Schristos appropriate in order to write out the locked revision
2421a7c91847Schristos information. However, such a call would actually serve no
2422a7c91847Schristos purpose. CVS locks will prevent any interference from other
2423a7c91847Schristos CVS processes. The comment above rcs_internal_lockfile
2424a7c91847Schristos explains that it is already unsafe to use RCS and CVS
2425a7c91847Schristos simultaneously. It follows that writing out the locked
2426a7c91847Schristos revision information here would add no additional security.
2427a7c91847Schristos
2428a7c91847Schristos If we ever do care about it, the proper fix is to create the
2429a7c91847Schristos RCS lock file before calling this function, and maintain it
2430a7c91847Schristos until the checkin is complete.
2431a7c91847Schristos
2432a7c91847Schristos The call to RCS_lock is still required at present, since in
2433a7c91847Schristos some cases RCS_checkin will determine which revision to check
2434a7c91847Schristos in by looking for a lock. FIXME: This is rather roundabout,
2435a7c91847Schristos and a more straightforward approach would probably be easier to
2436a7c91847Schristos understand. */
2437a7c91847Schristos
2438a7c91847Schristos if (err == 0)
2439a7c91847Schristos {
2440a7c91847Schristos if (sbranch != NULL)
2441a7c91847Schristos free (sbranch);
2442a7c91847Schristos sbranch = branch;
2443a7c91847Schristos return 0;
2444a7c91847Schristos }
2445a7c91847Schristos
2446a7c91847Schristos /* try to restore the branch if we can on error */
2447a7c91847Schristos if (branch != NULL)
2448a7c91847Schristos fixbranch (rcs, branch);
2449a7c91847Schristos
2450a7c91847Schristos if (branch)
2451a7c91847Schristos free (branch);
2452a7c91847Schristos return 1;
2453a7c91847Schristos }
2454a7c91847Schristos
2455a7c91847Schristos
2456a7c91847Schristos
2457a7c91847Schristos /*
2458a7c91847Schristos * free an UPDATE node's data
2459a7c91847Schristos */
2460a7c91847Schristos void
update_delproc(Node * p)2461a7c91847Schristos update_delproc (Node *p)
2462a7c91847Schristos {
2463a7c91847Schristos struct logfile_info *li = p->data;
2464a7c91847Schristos
2465a7c91847Schristos if (li->tag)
2466a7c91847Schristos free (li->tag);
2467a7c91847Schristos if (li->rev_old)
2468a7c91847Schristos free (li->rev_old);
2469a7c91847Schristos if (li->rev_new)
2470a7c91847Schristos free (li->rev_new);
2471a7c91847Schristos free (li);
2472a7c91847Schristos }
2473a7c91847Schristos
2474a7c91847Schristos /*
2475a7c91847Schristos * Free the commit_info structure in p.
2476a7c91847Schristos */
2477a7c91847Schristos static void
ci_delproc(Node * p)2478a7c91847Schristos ci_delproc (Node *p)
2479a7c91847Schristos {
2480a7c91847Schristos struct commit_info *ci = p->data;
2481a7c91847Schristos
2482a7c91847Schristos if (ci->rev)
2483a7c91847Schristos free (ci->rev);
2484a7c91847Schristos if (ci->tag)
2485a7c91847Schristos free (ci->tag);
2486a7c91847Schristos if (ci->options)
2487a7c91847Schristos free (ci->options);
2488a7c91847Schristos free (ci);
2489a7c91847Schristos }
2490a7c91847Schristos
2491a7c91847Schristos /*
2492a7c91847Schristos * Free the commit_info structure in p.
2493a7c91847Schristos */
2494a7c91847Schristos static void
masterlist_delproc(Node * p)2495a7c91847Schristos masterlist_delproc (Node *p)
2496a7c91847Schristos {
2497a7c91847Schristos struct master_lists *ml = p->data;
2498a7c91847Schristos
2499a7c91847Schristos dellist (&ml->ulist);
2500a7c91847Schristos dellist (&ml->cilist);
2501a7c91847Schristos free (ml);
2502a7c91847Schristos }
2503