1/*
2 * Copyright (c) 1992, Brian Berliner and Jeff Polk
3 * Copyright (c) 1989-1992, Brian Berliner
4 *
5 * You may distribute under the terms of the GNU General Public License as
6 * specified in the README file that comes with the CVS source distribution.
7 *
8 * Check In
9 *
10 * Does a very careful checkin of the file "user", and tries not to spoil its
11 * modification time (to avoid needless recompilations). When RCS ID keywords
12 * get expanded on checkout, however, the modification time is updated and
13 * there is no good way to get around this.
14 *
15 * Returns non-zero on error.
16 */
17
18#include "cvs.h"
19#include "fileattr.h"
20#include "edit.h"
21
22int
23Checkin (type, finfo, rcs, rev, tag, options, message)
24    int type;
25    struct file_info *finfo;
26    char *rcs;
27    char *rev;
28    char *tag;
29    char *options;
30    char *message;
31{
32    Vers_TS *vers;
33    int set_time;
34    char *tocvsPath = NULL;
35
36    /* Hmm.  This message goes to stdout and the "foo,v  <--  foo"
37       message from "ci" goes to stderr.  This doesn't make a whole
38       lot of sense, but making everything go to stdout can only be
39       gracefully achieved once RCS_checkin is librarified.  */
40    cvs_output ("Checking in ", 0);
41    cvs_output (finfo->fullname, 0);
42    cvs_output (";\n", 0);
43
44    tocvsPath = wrap_tocvs_process_file (finfo->file);
45    if (!noexec)
46    {
47        if (tocvsPath)
48	{
49	    if (unlink_file_dir (finfo->file) < 0)
50		if (! existence_error (errno))
51		    error (1, errno, "cannot remove %s", finfo->fullname);
52	    rename_file (tocvsPath, finfo->file);
53	}
54    }
55
56    if (finfo->rcs == NULL)
57	finfo->rcs = RCS_parse (finfo->file, finfo->repository);
58
59    switch (RCS_checkin (finfo->rcs, NULL, message, rev, RCS_FLAGS_KEEPFILE))
60    {
61	case 0:			/* everything normal */
62
63	    /* The checkin succeeded.  If checking the file out again
64               would not cause any changes, we are done.  Otherwise,
65               we need to check out the file, which will change the
66               modification time of the file.
67
68	       The only way checking out the file could cause any
69	       changes is if the file contains RCS keywords.  So we if
70	       we are not expanding RCS keywords, we are done.  */
71
72	    if (strcmp (options, "-V4") == 0) /* upgrade to V5 now */
73		options[0] = '\0';
74
75	    /* FIXME: If PreservePermissions is on, RCS_cmp_file is
76               going to call RCS_checkout into a temporary file
77               anyhow.  In that case, it would be more efficient to
78               call RCS_checkout here, compare the resulting files
79               using xcmp, and rename if necessary.  I think this
80               should be fixed in RCS_cmp_file.  */
81	    if ((! preserve_perms
82		 && options != NULL
83		 && (strcmp (options, "-ko") == 0
84		     || strcmp (options, "-kb") == 0))
85		|| RCS_cmp_file (finfo->rcs, rev, options, finfo->file) == 0)
86	    {
87		/* The existing file is correct.  We don't have to do
88                   anything.  */
89		set_time = 0;
90	    }
91	    else
92	    {
93		/* The existing file is incorrect.  We need to check
94                   out the correct file contents.  */
95		if (RCS_checkout (finfo->rcs, finfo->file, rev, (char *) NULL,
96				  options, RUN_TTY, (RCSCHECKOUTPROC) NULL,
97				  (void *) NULL) != 0)
98		    error (1, 0, "failed when checking out new copy of %s",
99			   finfo->fullname);
100		xchmod (finfo->file, 1);
101		set_time = 1;
102	    }
103
104	    wrap_fromcvs_process_file (finfo->file);
105
106	    /*
107	     * If we want read-only files, muck the permissions here, before
108	     * getting the file time-stamp.
109	     */
110	    if (!cvswrite || fileattr_get (finfo->file, "_watched"))
111		xchmod (finfo->file, 0);
112
113	    /* Re-register with the new data.  */
114	    vers = Version_TS (finfo, NULL, tag, NULL, 1, set_time);
115	    if (strcmp (vers->options, "-V4") == 0)
116		vers->options[0] = '\0';
117	    Register (finfo->entries, finfo->file, vers->vn_rcs, vers->ts_user,
118		      vers->options, vers->tag, vers->date, (char *) 0);
119	    history_write (type, NULL, vers->vn_rcs,
120			   finfo->file, finfo->repository);
121
122	    if (tocvsPath)
123		if (unlink_file_dir (tocvsPath) < 0)
124		    error (0, errno, "cannot remove %s", tocvsPath);
125
126	    break;
127
128	case -1:			/* fork failed */
129	    if (tocvsPath)
130		if (unlink_file_dir (tocvsPath) < 0)
131		    error (0, errno, "cannot remove %s", tocvsPath);
132
133	    if (!noexec)
134		error (1, errno, "could not check in %s -- fork failed",
135		       finfo->fullname);
136	    return (1);
137
138	default:			/* ci failed */
139
140	    /* The checkin failed, for some unknown reason, so we
141	       print an error, and return an error.  We assume that
142	       the original file has not been touched.  */
143	    if (tocvsPath)
144		if (unlink_file_dir (tocvsPath) < 0)
145		    error (0, errno, "cannot remove %s", tocvsPath);
146
147	    if (!noexec)
148		error (0, 0, "could not check in %s", finfo->fullname);
149	    return (1);
150    }
151
152    /*
153     * When checking in a specific revision, we may have locked the wrong
154     * branch, so to be sure, we do an extra unlock here before
155     * returning.
156     */
157    if (rev)
158    {
159	(void) RCS_unlock (finfo->rcs, NULL, 1);
160	RCS_rewrite (finfo->rcs, NULL, NULL);
161    }
162
163#ifdef SERVER_SUPPORT
164    if (server_active)
165    {
166	if (set_time)
167	    /* Need to update the checked out file on the client side.  */
168	    server_updated (finfo, vers, SERVER_UPDATED,
169			    (mode_t) -1, (unsigned char *) NULL,
170			    (struct buffer *) NULL);
171	else
172	    server_checked_in (finfo->file, finfo->update_dir, finfo->repository);
173    }
174    else
175#endif
176	mark_up_to_date (finfo->file);
177
178    freevers_ts (&vers);
179    return (0);
180}
181