1 /*
2  * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
3  *
4  * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5  *                                  and others.
6  *
7  * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8  * Portions Copyright (C) 1989-1992, Brian Berliner
9  *
10  * You may distribute under the terms of the GNU General Public License as
11  * specified in the README file that comes with the CVS source distribution.
12  *
13  * Check In
14  *
15  * Does a very careful checkin of the file "user", and tries not to spoil its
16  * modification time (to avoid needless recompilations). When RCS ID keywords
17  * get expanded on checkout, however, the modification time is updated and
18  * there is no good way to get around this.
19  *
20  * Returns non-zero on error.
21  */
22 #include <sys/cdefs.h>
23 __RCSID("$NetBSD: checkin.c,v 1.2 2016/05/17 14:00:09 christos Exp $");
24 
25 #include "cvs.h"
26 #include "fileattr.h"
27 #include "edit.h"
28 
29 int
Checkin(int type,struct file_info * finfo,char * rev,char * tag,char * options,char * message)30 Checkin (int type, struct file_info *finfo, char *rev, char *tag,
31            char *options, char *message)
32 {
33     Vers_TS *vers;
34     int set_time;
35     char *tocvsPath = NULL;
36 
37     tocvsPath = wrap_tocvs_process_file (finfo->file);
38     if (!noexec)
39     {
40         if (tocvsPath)
41           {
42               if (unlink_file_dir (finfo->file) < 0)
43                     if (! existence_error (errno))
44                         error (1, errno, "cannot remove %s", finfo->fullname);
45               rename_file (tocvsPath, finfo->file);
46           }
47     }
48 
49     /* There use to be a check for finfo->rcs == NULL here and then a
50      * call to RCS_parse when necessary, but Checkin() isn't called
51      * if the RCS file hasn't already been parsed in one of the
52      * check functions.
53      */
54     assert (finfo->rcs != NULL);
55 
56     switch (RCS_checkin (finfo->rcs, finfo->update_dir, finfo->file, message,
57                                rev, 0, RCS_FLAGS_KEEPFILE))
58     {
59           case 0:                       /* everything normal */
60 
61               /* The checkin succeeded.  If checking the file out again
62                would not cause any changes, we are done.  Otherwise,
63                we need to check out the file, which will change the
64                modification time of the file.
65 
66                  The only way checking out the file could cause any
67                  changes is if the file contains RCS keywords.  So we if
68                  we are not expanding RCS keywords, we are done.  */
69 
70               if (strcmp (options, "-V4") == 0) /* upgrade to V5 now */
71                     options[0] = '\0';
72 
73               /* FIXME: If PreservePermissions is on, RCS_cmp_file is
74                going to call RCS_checkout into a temporary file
75                anyhow.  In that case, it would be more efficient to
76                call RCS_checkout here, compare the resulting files
77                using xcmp, and rename if necessary.  I think this
78                should be fixed in RCS_cmp_file.  */
79               if ((1
80 #ifdef PRESERVE_PERMISSIONS_SUPPORT
81                      !config->preserve_perms
82 #endif /* PRESERVE_PERMISSIONS_SUPPORT */
83                      && options
84                      && (!strcmp (options, "-ko") || !strcmp (options, "-kb")))
85                     || !RCS_cmp_file (finfo->rcs, rev, NULL, NULL,
86                                     options, finfo->file))
87               {
88                     /* The existing file is correct.  We don't have to do
89                    anything.  */
90                     set_time = 0;
91               }
92               else
93               {
94                     /* The existing file is incorrect.  We need to check
95                    out the correct file contents.  */
96                     if (RCS_checkout (finfo->rcs, finfo->file, rev, NULL,
97                                           options, RUN_TTY, NULL, 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, NULL);
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, NULL, NULL);
170           else
171               server_checked_in (finfo->file, finfo->update_dir,
172                                      finfo->repository);
173     }
174     else
175 #endif
176           mark_up_to_date (finfo->file);
177 
178     freevers_ts (&vers);
179     return 0;
180 }
181