release.c revision 17721
1/* 2 * Release: "cancel" a checkout in the history log. 3 * 4 * - Don't allow release if anything is active - Don't allow release if not 5 * above or inside repository. - Don't allow release if ./CVS/Repository is 6 * not the same as the directory specified in the module database. 7 * 8 * - Enter a line in the history log indicating the "release". - If asked to, 9 * delete the local working directory. 10 */ 11 12#include "cvs.h" 13 14static void release_delete PROTO((char *dir)); 15 16static const char *const release_usage[] = 17{ 18 "Usage: %s %s [-d] modules...\n", 19 "\t-d\tDelete the given directory.\n", 20 NULL 21}; 22 23static short delete_flag; 24 25/* FIXME: This implementation is cheezy in quite a few ways: 26 27 1. The whole "cvs update" junk could be checked locally with a 28 fairly simple start_recursion/classify_file loop--a win for 29 portability, performance, and cleanliness. 30 31 2. Should be like edit/unedit in terms of working well if disconnected 32 from the network, and then sending a delayed notification. 33 34 3. Way too many network turnarounds. More than one for each argument. 35 Puh-leeze. 36 37 4. Oh, and as a purely stylistic nit, break this out into separate 38 functions for client/local and for server. Those #ifdefs are a mess. */ 39 40int 41release (argc, argv) 42 int argc; 43 char **argv; 44{ 45 FILE *fp; 46 register int i, c; 47 char *repository, *srepos; 48 char line[PATH_MAX], update_cmd[PATH_MAX]; 49 char *thisarg; 50 int arg_start_idx; 51 int err = 0; 52 53#ifdef SERVER_SUPPORT 54 if (!server_active) 55 { 56#endif /* SERVER_SUPPORT */ 57 if (argc == -1) 58 usage (release_usage); 59 optind = 1; 60 while ((c = getopt (argc, argv, "Qdq")) != -1) 61 { 62 switch (c) 63 { 64 case 'Q': 65 case 'q': 66#ifdef SERVER_SUPPORT 67 /* The CVS 1.5 client sends these options (in addition to 68 Global_option requests), so we must ignore them. */ 69 if (!server_active) 70#endif 71 error (1, 0, 72 "-q or -Q must be specified before \"%s\"", 73 command_name); 74 break; 75 case 'd': 76 delete_flag++; 77 break; 78 case '?': 79 default: 80 usage (release_usage); 81 break; 82 } 83 } 84 argc -= optind; 85 argv += optind; 86#ifdef SERVER_SUPPORT 87 } 88#endif /* SERVER_SUPPORT */ 89 90 /* We're going to run "cvs -n -q update" and check its output; if 91 * the output is sufficiently unalarming, then we release with no 92 * questions asked. Else we prompt, then maybe release. 93 */ 94 /* Construct the update command. */ 95 sprintf (update_cmd, "%s -n -q -d %s update", 96 program_path, CVSroot); 97 98#ifdef CLIENT_SUPPORT 99 /* Start the server; we'll close it after looping. */ 100 if (client_active) 101 { 102 start_server (); 103 ign_setup (); 104 } 105#endif /* CLIENT_SUPPORT */ 106 107 /* If !server_active, we already skipped over argv[0] in the "argc 108 -= optind;" statement above. But if server_active, we need to 109 skip it now. */ 110#ifdef SERVER_SUPPORT 111 if (server_active) 112 arg_start_idx = 1; 113 else 114#endif /* SERVER_SUPPORT */ 115 arg_start_idx = 0; 116 117 for (i = arg_start_idx; i < argc; i++) 118 { 119 thisarg = argv[i]; 120 121#ifdef SERVER_SUPPORT 122 if (server_active) 123 { 124 /* Just log the release -- all the interesting stuff happened 125 * on the client. 126 */ 127 history_write ('F', thisarg, "", thisarg, ""); /* F == Free */ 128 } 129 else 130 { 131#endif /* SERVER_SUPPORT */ 132 133 /* 134 * If we are in a repository, do it. Else if we are in the parent of 135 * a directory with the same name as the module, "cd" into it and 136 * look for a repository there. 137 */ 138 if (isdir (thisarg)) 139 { 140 if (chdir (thisarg) < 0) 141 { 142 if (!really_quiet) 143 error (0, 0, "can't chdir to: %s", thisarg); 144 continue; 145 } 146 if (!isdir (CVSADM)) 147 { 148 if (!really_quiet) 149 error (0, 0, "no repository module: %s", thisarg); 150 continue; 151 } 152 } 153 else 154 { 155 if (!really_quiet) 156 error (0, 0, "no such directory: %s", thisarg); 157 continue; 158 } 159 160 repository = Name_Repository ((char *) NULL, (char *) NULL); 161 srepos = Short_Repository (repository); 162 163 if (!really_quiet) 164 { 165 /* The "release" command piggybacks on "update", which 166 * does the real work of finding out if anything is not 167 * up-to-date with the repository. Then "release" prompts 168 * the user, telling her how many files have been 169 * modified, and asking if she still wants to do the 170 * release. 171 */ 172 fp = run_popen (update_cmd, "r"); 173 c = 0; 174 175 while (fgets (line, sizeof (line), fp)) 176 { 177 if (strchr ("MARCZ", *line)) 178 c++; 179 (void) printf (line); 180 } 181 182 /* If the update exited with an error, then we just want to 183 * complain and go on to the next arg. Especially, we do 184 * not want to delete the local copy, since it's obviously 185 * not what the user thinks it is. 186 */ 187 if ((pclose (fp)) != 0) 188 { 189 error (0, 0, "unable to release `%s'", thisarg); 190 continue; 191 } 192 193 (void) printf ("You have [%d] altered files in this repository.\n", 194 c); 195 (void) printf ("Are you sure you want to release %smodule `%s': ", 196 delete_flag ? "(and delete) " : "", thisarg); 197 c = !yesno (); 198 if (c) /* "No" */ 199 { 200 (void) fprintf (stderr, "** `%s' aborted by user choice.\n", 201 command_name); 202 free (repository); 203 continue; 204 } 205 } 206 207 if (1 208#ifdef SERVER_SUPPORT 209 && !server_active 210#endif 211#ifdef CLIENT_SUPPORT 212 && !(client_active 213 && (!supported_request ("noop") 214 || !supported_request ("Notify"))) 215#endif 216 ) 217 { 218 /* We are chdir'ed into the directory in question. 219 So don't pass args to unedit. */ 220 int argc = 1; 221 char *argv[3]; 222 argv[0] = "dummy"; 223 argv[1] = NULL; 224 err += unedit (argc, argv); 225 } 226 227#ifdef CLIENT_SUPPORT 228 if (client_active) 229 { 230 send_to_server ("Argument ", 0); 231 send_to_server (thisarg, 0); 232 send_to_server ("\012", 1); 233 send_to_server ("release\012", 0); 234 } 235 else 236 { 237#endif /* CLIENT_SUPPORT */ 238 history_write ('F', thisarg, "", thisarg, ""); /* F == Free */ 239#ifdef CLIENT_SUPPORT 240 } /* else client not active */ 241#endif /* CLIENT_SUPPORT */ 242 243 free (repository); 244 if (delete_flag) release_delete (thisarg); 245 246#ifdef CLIENT_SUPPORT 247 if (client_active) 248 return get_responses_and_close (); 249 else 250#endif /* CLIENT_SUPPORT */ 251 return (0); 252 253#ifdef SERVER_SUPPORT 254 } /* else server not active */ 255#endif /* SERVER_SUPPORT */ 256 } /* `for' loop */ 257 return err; 258} 259 260 261/* We want to "rm -r" the working directory, but let us be a little 262 paranoid. */ 263static void 264release_delete (dir) 265 char *dir; 266{ 267 struct stat st; 268 ino_t ino; 269 270 (void) stat (".", &st); 271 ino = st.st_ino; 272 (void) chdir (".."); 273 (void) stat (dir, &st); 274 if (ino != st.st_ino) 275 { 276 error (0, 0, 277 "Parent dir on a different disk, delete of %s aborted", dir); 278 return; 279 } 280 /* 281 * XXX - shouldn't this just delete the CVS-controlled files and, perhaps, 282 * the files that would normally be ignored and leave everything else? 283 */ 284 if (unlink_file_dir (dir) < 0) 285 error (0, errno, "deletion of directory %s failed", dir); 286} 287