| @@ -8,26 +8,27 @@ | | | @@ -8,26 +8,27 @@ |
8 | * Portions Copyright (C) 1989-1992, Brian Berliner | | 8 | * Portions Copyright (C) 1989-1992, Brian Berliner |
9 | * | | 9 | * |
10 | * You may distribute under the terms of the GNU General Public License as | | 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. | | 11 | * specified in the README file that comes with the CVS source distribution. |
12 | * | | 12 | * |
13 | * Tag and Rtag | | 13 | * Tag and Rtag |
14 | * | | 14 | * |
15 | * Add or delete a symbolic name to an RCS file, or a collection of RCS files. | | 15 | * Add or delete a symbolic name to an RCS file, or a collection of RCS files. |
16 | * Tag uses the checked out revision in the current directory, rtag uses | | 16 | * Tag uses the checked out revision in the current directory, rtag uses |
17 | * the modules database, if necessary. | | 17 | * the modules database, if necessary. |
18 | */ | | 18 | */ |
19 | | | 19 | |
20 | #include "cvs.h" | | 20 | #include "cvs.h" |
| | | 21 | #include <grp.h> |
21 | #include "save-cwd.h" | | 22 | #include "save-cwd.h" |
22 | | | 23 | |
23 | static int rtag_proc (int argc, char **argv, char *xwhere, | | 24 | static int rtag_proc (int argc, char **argv, char *xwhere, |
24 | char *mwhere, char *mfile, int shorten, | | 25 | char *mwhere, char *mfile, int shorten, |
25 | int local_specified, char *mname, char *msg); | | 26 | int local_specified, char *mname, char *msg); |
26 | static int check_fileproc (void *callerdat, struct file_info *finfo); | | 27 | static int check_fileproc (void *callerdat, struct file_info *finfo); |
27 | static int check_filesdoneproc (void *callerdat, int err, | | 28 | static int check_filesdoneproc (void *callerdat, int err, |
28 | const char *repos, const char *update_dir, | | 29 | const char *repos, const char *update_dir, |
29 | List *entries); | | 30 | List *entries); |
30 | static int pretag_proc (const char *_repository, const char *_filter, | | 31 | static int pretag_proc (const char *_repository, const char *_filter, |
31 | void *_closure); | | 32 | void *_closure); |
32 | static void masterlist_delproc (Node *_p); | | 33 | static void masterlist_delproc (Node *_p); |
33 | static void tag_delproc (Node *_p); | | 34 | static void tag_delproc (Node *_p); |
| @@ -96,44 +97,49 @@ static const char *const tag_usage[] = | | | @@ -96,44 +97,49 @@ static const char *const tag_usage[] = |
96 | "\t-B\tAllows -F and -d to disturb branch tags. Use with extreme care.\n", | | 97 | "\t-B\tAllows -F and -d to disturb branch tags. Use with extreme care.\n", |
97 | "\t-c\tCheck that working files are unmodified.\n", | | 98 | "\t-c\tCheck that working files are unmodified.\n", |
98 | "\t-d\tDelete the given tag.\n", | | 99 | "\t-d\tDelete the given tag.\n", |
99 | "\t-F\tMove tag if it already exists.\n", | | 100 | "\t-F\tMove tag if it already exists.\n", |
100 | "\t-f\tForce a head revision match if tag/date not found.\n", | | 101 | "\t-f\tForce a head revision match if tag/date not found.\n", |
101 | "\t-l\tLocal directory only, not recursive.\n", | | 102 | "\t-l\tLocal directory only, not recursive.\n", |
102 | "\t-R\tProcess directories recursively.\n", | | 103 | "\t-R\tProcess directories recursively.\n", |
103 | "\t-r rev\tExisting revision/tag.\n", | | 104 | "\t-r rev\tExisting revision/tag.\n", |
104 | "\t-D\tExisting date.\n", | | 105 | "\t-D\tExisting date.\n", |
105 | "(Specify the --help global option for a list of other help options)\n", | | 106 | "(Specify the --help global option for a list of other help options)\n", |
106 | NULL | | 107 | NULL |
107 | }; | | 108 | }; |
108 | | | 109 | |
109 | | | 110 | char *UserTagOptions = "bcflRrD"; |
110 | | | 111 | |
111 | int | | 112 | int |
112 | cvstag (int argc, char **argv) | | 113 | cvstag (int argc, char **argv) |
113 | { | | 114 | { |
| | | 115 | struct group *grp; |
114 | bool local = false; /* recursive by default */ | | 116 | bool local = false; /* recursive by default */ |
115 | int c; | | 117 | int c; |
116 | int err = 0; | | 118 | int err = 0; |
117 | bool run_module_prog = true; | | 119 | bool run_module_prog = true; |
| | | 120 | int only_allowed_options; |
118 | | | 121 | |
119 | is_rtag = (strcmp (cvs_cmd_name, "rtag") == 0); | | 122 | is_rtag = (strcmp (cvs_cmd_name, "rtag") == 0); |
120 | | | 123 | |
121 | if (argc == -1) | | 124 | if (argc == -1) |
122 | usage (is_rtag ? rtag_usage : tag_usage); | | 125 | usage (is_rtag ? rtag_usage : tag_usage); |
123 | | | 126 | |
124 | getoptreset (); | | 127 | getoptreset (); |
| | | 128 | only_allowed_options = 1; |
125 | while ((c = getopt (argc, argv, is_rtag ? rtag_opts : tag_opts)) != -1) | | 129 | while ((c = getopt (argc, argv, is_rtag ? rtag_opts : tag_opts)) != -1) |
126 | { | | 130 | { |
| | | 131 | if (!strchr(UserTagOptions, c)) |
| | | 132 | only_allowed_options = 0; |
127 | switch (c) | | 133 | switch (c) |
128 | { | | 134 | { |
129 | case 'a': | | 135 | case 'a': |
130 | attic_too = true; | | 136 | attic_too = true; |
131 | break; | | 137 | break; |
132 | case 'b': | | 138 | case 'b': |
133 | branch_mode = true; | | 139 | branch_mode = true; |
134 | break; | | 140 | break; |
135 | case 'B': | | 141 | case 'B': |
136 | disturb_branch_tags = true; | | 142 | disturb_branch_tags = true; |
137 | break; | | 143 | break; |
138 | case 'c': | | 144 | case 'c': |
139 | check_uptodate = true; | | 145 | check_uptodate = true; |
| @@ -183,26 +189,62 @@ cvstag (int argc, char **argv) | | | @@ -183,26 +189,62 @@ cvstag (int argc, char **argv) |
183 | | | 189 | |
184 | if (argc < (is_rtag ? 2 : 1)) | | 190 | if (argc < (is_rtag ? 2 : 1)) |
185 | usage (is_rtag ? rtag_usage : tag_usage); | | 191 | usage (is_rtag ? rtag_usage : tag_usage); |
186 | symtag = argv[0]; | | 192 | symtag = argv[0]; |
187 | argc--; | | 193 | argc--; |
188 | argv++; | | 194 | argv++; |
189 | | | 195 | |
190 | if (date && delete_flag) | | 196 | if (date && delete_flag) |
191 | error (1, 0, "-d makes no sense with a date specification."); | | 197 | error (1, 0, "-d makes no sense with a date specification."); |
192 | if (delete_flag && branch_mode) | | 198 | if (delete_flag && branch_mode) |
193 | error (0, 0, "warning: -b ignored with -d options"); | | 199 | error (0, 0, "warning: -b ignored with -d options"); |
194 | RCS_check_tag (symtag); | | 200 | RCS_check_tag (symtag); |
195 | | | 201 | |
| | | 202 | #ifdef CVS_ADMIN_GROUP |
| | | 203 | if (!only_allowed_options && |
| | | 204 | (grp = getgrnam(CVS_ADMIN_GROUP)) != NULL) |
| | | 205 | { |
| | | 206 | #ifdef HAVE_GETGROUPS |
| | | 207 | gid_t *grps; |
| | | 208 | int i, n; |
| | | 209 | |
| | | 210 | /* get number of auxiliary groups */ |
| | | 211 | n = getgroups (0, NULL); |
| | | 212 | if (n < 0) |
| | | 213 | error (1, errno, "unable to get number of auxiliary groups"); |
| | | 214 | grps = (gid_t *) xmalloc((n + 1) * sizeof *grps); |
| | | 215 | n = getgroups (n, grps); |
| | | 216 | if (n < 0) |
| | | 217 | error (1, errno, "unable to get list of auxiliary groups"); |
| | | 218 | grps[n] = getgid(); |
| | | 219 | for (i = 0; i <= n; i++) |
| | | 220 | if (grps[i] == grp->gr_gid) break; |
| | | 221 | free (grps); |
| | | 222 | if (i > n) |
| | | 223 | error (1, 0, "usage is restricted to members of the group %s", |
| | | 224 | CVS_ADMIN_GROUP); |
| | | 225 | #else |
| | | 226 | char *me = getcaller(); |
| | | 227 | char **grnam; |
| | | 228 | |
| | | 229 | for (grnam = grp->gr_mem; *grnam; grnam++) |
| | | 230 | if (strcmp (*grnam, me) == 0) break; |
| | | 231 | if (!*grnam && getgid() != grp->gr_gid) |
| | | 232 | error (1, 0, "usage is restricted to members of the group %s", |
| | | 233 | CVS_ADMIN_GROUP); |
| | | 234 | #endif |
| | | 235 | } |
| | | 236 | #endif /* defined CVS_ADMIN_GROUP */ |
| | | 237 | |
196 | #ifdef CLIENT_SUPPORT | | 238 | #ifdef CLIENT_SUPPORT |
197 | if (current_parsed_root->isremote) | | 239 | if (current_parsed_root->isremote) |
198 | { | | 240 | { |
199 | /* We're the client side. Fire up the remote server. */ | | 241 | /* We're the client side. Fire up the remote server. */ |
200 | start_server (); | | 242 | start_server (); |
201 | | | 243 | |
202 | ign_setup (); | | 244 | ign_setup (); |
203 | | | 245 | |
204 | if (attic_too) | | 246 | if (attic_too) |
205 | send_arg ("-a"); | | 247 | send_arg ("-a"); |
206 | if (branch_mode) | | 248 | if (branch_mode) |
207 | send_arg ("-b"); | | 249 | send_arg ("-b"); |
208 | if (disturb_branch_tags) | | 250 | if (disturb_branch_tags) |
| @@ -261,26 +303,33 @@ cvstag (int argc, char **argv) | | | @@ -261,26 +303,33 @@ cvstag (int argc, char **argv) |
261 | { | | 303 | { |
262 | /* XXX last arg should be repository, but doesn't make sense here */ | | 304 | /* XXX last arg should be repository, but doesn't make sense here */ |
263 | history_write ('T', (delete_flag ? "D" : (numtag ? numtag : | | 305 | history_write ('T', (delete_flag ? "D" : (numtag ? numtag : |
264 | (date ? date : "A"))), symtag, argv[i], ""); | | 306 | (date ? date : "A"))), symtag, argv[i], ""); |
265 | err += do_module (db, argv[i], TAG, | | 307 | err += do_module (db, argv[i], TAG, |
266 | delete_flag ? "Untagging" : "Tagging", | | 308 | delete_flag ? "Untagging" : "Tagging", |
267 | rtag_proc, NULL, 0, local, run_module_prog, | | 309 | rtag_proc, NULL, 0, local, run_module_prog, |
268 | 0, symtag); | | 310 | 0, symtag); |
269 | } | | 311 | } |
270 | close_module (db); | | 312 | close_module (db); |
271 | } | | 313 | } |
272 | else | | 314 | else |
273 | { | | 315 | { |
| | | 316 | int i; |
| | | 317 | for (i = 0; i < argc; i++) |
| | | 318 | { |
| | | 319 | /* XXX last arg should be repository, but doesn't make sense here */ |
| | | 320 | history_write ('T', (delete_flag ? "D" : (numtag ? numtag : |
| | | 321 | (date ? date : "A"))), symtag, argv[i], ""); |
| | | 322 | } |
274 | err = rtag_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, local, NULL, | | 323 | err = rtag_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, local, NULL, |
275 | NULL); | | 324 | NULL); |
276 | } | | 325 | } |
277 | | | 326 | |
278 | return err; | | 327 | return err; |
279 | } | | 328 | } |
280 | | | 329 | |
281 | | | 330 | |
282 | | | 331 | |
283 | struct pretag_proc_data { | | 332 | struct pretag_proc_data { |
284 | List *tlist; | | 333 | List *tlist; |
285 | bool delete_flag; | | 334 | bool delete_flag; |
286 | bool force_tag_move; | | 335 | bool force_tag_move; |
| @@ -938,26 +987,45 @@ rtag_fileproc (void *callerdat, struct f | | | @@ -938,26 +987,45 @@ rtag_fileproc (void *callerdat, struct f |
938 | /* find the parsed RCS data */ | | 987 | /* find the parsed RCS data */ |
939 | if ((rcsfile = finfo->rcs) == NULL) | | 988 | if ((rcsfile = finfo->rcs) == NULL) |
940 | { | | 989 | { |
941 | retval = 1; | | 990 | retval = 1; |
942 | goto free_vars_and_return; | | 991 | goto free_vars_and_return; |
943 | } | | 992 | } |
944 | | | 993 | |
945 | /* | | 994 | /* |
946 | * For tagging an RCS file which is a symbolic link, you'd best be | | 995 | * For tagging an RCS file which is a symbolic link, you'd best be |
947 | * running with RCS 5.6, since it knows how to handle symbolic links | | 996 | * running with RCS 5.6, since it knows how to handle symbolic links |
948 | * correctly without breaking your link! | | 997 | * correctly without breaking your link! |
949 | */ | | 998 | */ |
950 | | | 999 | |
| | | 1000 | /* cvsacl patch */ |
| | | 1001 | #ifdef SERVER_SUPPORT |
| | | 1002 | if (use_cvs_acl /* && server_active */) |
| | | 1003 | { |
| | | 1004 | if (!access_allowed (finfo->file, finfo->repository, numtag, 4, |
| | | 1005 | NULL, NULL, 1)) |
| | | 1006 | { |
| | | 1007 | if (stop_at_first_permission_denied) |
| | | 1008 | error (1, 0, "permission denied for %s", |
| | | 1009 | Short_Repository (finfo->repository)); |
| | | 1010 | else |
| | | 1011 | error (0, 0, "permission denied for %s/%s", |
| | | 1012 | Short_Repository (finfo->repository), finfo->file); |
| | | 1013 | |
| | | 1014 | return (0); |
| | | 1015 | } |
| | | 1016 | } |
| | | 1017 | #endif |
| | | 1018 | |
951 | if (delete_flag) | | 1019 | if (delete_flag) |
952 | { | | 1020 | { |
953 | retval = rtag_delete (rcsfile); | | 1021 | retval = rtag_delete (rcsfile); |
954 | goto free_vars_and_return; | | 1022 | goto free_vars_and_return; |
955 | } | | 1023 | } |
956 | | | 1024 | |
957 | /* | | 1025 | /* |
958 | * If we get here, we are adding a tag. But, if -a was specified, we | | 1026 | * If we get here, we are adding a tag. But, if -a was specified, we |
959 | * need to check to see if a -r or -D option was specified. If neither | | 1027 | * need to check to see if a -r or -D option was specified. If neither |
960 | * was specified and the file is in the Attic, remove the tag. | | 1028 | * was specified and the file is in the Attic, remove the tag. |
961 | */ | | 1029 | */ |
962 | if (attic_too && (!numtag && !date)) | | 1030 | if (attic_too && (!numtag && !date)) |
963 | { | | 1031 | { |
| @@ -1157,26 +1225,41 @@ tag_fileproc (void *callerdat, struct fi | | | @@ -1157,26 +1225,41 @@ tag_fileproc (void *callerdat, struct fi |
1157 | int retcode = 0; | | 1225 | int retcode = 0; |
1158 | int retval = 0; | | 1226 | int retval = 0; |
1159 | static bool valtagged = false; | | 1227 | static bool valtagged = false; |
1160 | | | 1228 | |
1161 | vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); | | 1229 | vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); |
1162 | | | 1230 | |
1163 | if (numtag || date) | | 1231 | if (numtag || date) |
1164 | { | | 1232 | { |
1165 | nversion = RCS_getversion (vers->srcfile, numtag, date, | | 1233 | nversion = RCS_getversion (vers->srcfile, numtag, date, |
1166 | force_tag_match, NULL); | | 1234 | force_tag_match, NULL); |
1167 | if (!nversion) | | 1235 | if (!nversion) |
1168 | goto free_vars_and_return; | | 1236 | goto free_vars_and_return; |
1169 | } | | 1237 | } |
| | | 1238 | |
| | | 1239 | /* cvsacl patch */ |
| | | 1240 | #ifdef SERVER_SUPPORT |
| | | 1241 | if (use_cvs_acl /* && server_active */) |
| | | 1242 | { |
| | | 1243 | if (!access_allowed (finfo->file, finfo->repository, vers->tag, 4, |
| | | 1244 | NULL, NULL, 1)) |
| | | 1245 | { |
| | | 1246 | error (0, 0, "permission denied for %s/%s", |
| | | 1247 | Short_Repository (finfo->repository), finfo->file); |
| | | 1248 | return (0); |
| | | 1249 | } |
| | | 1250 | } |
| | | 1251 | #endif |
| | | 1252 | |
1170 | if (delete_flag) | | 1253 | if (delete_flag) |
1171 | { | | 1254 | { |
1172 | | | 1255 | |
1173 | int isbranch; | | 1256 | int isbranch; |
1174 | /* | | 1257 | /* |
1175 | * If -d is specified, "force_tag_match" is set, so that this call to | | 1258 | * If -d is specified, "force_tag_match" is set, so that this call to |
1176 | * RCS_getversion() will return a NULL version string if the symbolic | | 1259 | * RCS_getversion() will return a NULL version string if the symbolic |
1177 | * tag does not exist in the RCS file. | | 1260 | * tag does not exist in the RCS file. |
1178 | * | | 1261 | * |
1179 | * This is done here because it's MUCH faster than just blindly calling | | 1262 | * This is done here because it's MUCH faster than just blindly calling |
1180 | * "rcs" to remove the tag... trust me. | | 1263 | * "rcs" to remove the tag... trust me. |
1181 | */ | | 1264 | */ |
1182 | | | 1265 | |