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 * Status Information 14 */ 15 16 #include "cvs.h" 17 18 static Dtype status_dirproc (void *callerdat, const char *dir, 19 const char *repos, const char *update_dir, 20 List *entries); 21 static int status_fileproc (void *callerdat, struct file_info *finfo); 22 static int tag_list_proc (Node * p, void *closure); 23 24 static int local = 0; 25 static int long_format = 0; 26 static RCSNode *xrcsnode; 27 28 static const char *const status_usage[] = 29 { 30 "Usage: %s %s [-vlR] [files...]\n", 31 "\t-v\tVerbose format; includes tag information for the file\n", 32 "\t-l\tProcess this directory only (not recursive).\n", 33 "\t-R\tProcess directories recursively.\n", 34 "(Specify the --help global option for a list of other help options)\n", 35 NULL 36 }; 37 38 int 39 cvsstatus (int argc, char **argv) 40 { 41 int c; 42 int err = 0; 43 44 if (argc == -1) 45 usage (status_usage); 46 47 getoptreset (); 48 while ((c = getopt (argc, argv, "+vlR")) != -1) 49 { 50 switch (c) 51 { 52 case 'v': 53 long_format = 1; 54 break; 55 case 'l': 56 local = 1; 57 break; 58 case 'R': 59 local = 0; 60 break; 61 case '?': 62 default: 63 usage (status_usage); 64 break; 65 } 66 } 67 argc -= optind; 68 argv += optind; 69 70 wrap_setup (); 71 72 #ifdef CLIENT_SUPPORT 73 if (current_parsed_root->isremote) 74 { 75 start_server (); 76 77 ign_setup (); 78 79 if (long_format) 80 send_arg("-v"); 81 if (local) 82 send_arg("-l"); 83 send_arg ("--"); 84 85 /* For a while, we tried setting SEND_NO_CONTENTS here so this 86 could be a fast operation. That prevents the 87 server from updating our timestamp if the timestamp is 88 changed but the file is unmodified. Worse, it is user-visible 89 (shows "locally modified" instead of "up to date" if 90 timestamp is changed but file is not). And there is no good 91 workaround (you might not want to run "cvs update"; "cvs -n 92 update" doesn't update CVS/Entries; "cvs diff --brief" or 93 something perhaps could be made to work but somehow that 94 seems nonintuitive to me even if so). Given that timestamps 95 seem to have the potential to get munged for any number of 96 reasons, it seems better to not rely too much on them. */ 97 98 send_files (argc, argv, local, 0, 0); 99 100 send_file_names (argc, argv, SEND_EXPAND_WILD); 101 102 send_to_server ("status\012", 0); 103 err = get_responses_and_close (); 104 105 return err; 106 } 107 #endif 108 109 /* start the recursion processor */ 110 err = start_recursion (status_fileproc, NULL, status_dirproc, 111 NULL, NULL, argc, argv, local, W_LOCAL, 112 0, CVS_LOCK_READ, NULL, 1, NULL); 113 114 return (err); 115 } 116 117 /* 118 * display the status of a file 119 */ 120 /* ARGSUSED */ 121 static int 122 status_fileproc (void *callerdat, struct file_info *finfo) 123 { 124 Ctype status; 125 char *sstat; 126 Vers_TS *vers; 127 Node *node; 128 129 status = Classify_File (finfo, NULL, NULL, NULL, 1, 0, &vers, 0); 130 131 /* cvsacl patch */ 132 #ifdef SERVER_SUPPORT 133 if (use_cvs_acl /* && server_active */) 134 { 135 if (!access_allowed (finfo->file, finfo->repository, vers->tag, 5, 136 NULL, NULL, 1)) 137 { 138 if (stop_at_first_permission_denied) 139 error (1, 0, "permission denied for %s", 140 Short_Repository (finfo->repository)); 141 else 142 error (0, 0, "permission denied for %s/%s", 143 Short_Repository (finfo->repository), finfo->file); 144 145 return (0); 146 } 147 } 148 #endif 149 150 sstat = "Classify Error"; 151 switch (status) 152 { 153 case T_UNKNOWN: 154 sstat = "Unknown"; 155 break; 156 case T_CHECKOUT: 157 sstat = "Needs Checkout"; 158 break; 159 case T_PATCH: 160 sstat = "Needs Patch"; 161 break; 162 case T_CONFLICT: 163 /* FIXME - This message could be clearer. It comes up 164 * when a file exists or has been added in the local sandbox 165 * and a file of the same name has been committed indepenently to 166 * the repository from a different sandbox, as well as when a 167 * timestamp hasn't changed since a merge resulted in conflicts. 168 * It also comes up whether an update has been attempted or not, so 169 * technically, I think the double-add case is not actually a 170 * conflict yet. 171 */ 172 sstat = "Unresolved Conflict"; 173 break; 174 case T_ADDED: 175 sstat = "Locally Added"; 176 break; 177 case T_REMOVED: 178 sstat = "Locally Removed"; 179 break; 180 case T_MODIFIED: 181 if (file_has_markers (finfo)) 182 sstat = "File had conflicts on merge"; 183 else 184 /* Note that we do not re Register() the file when we spot 185 * a resolved conflict like update_fileproc() does on the 186 * premise that status should not alter the sandbox. 187 */ 188 sstat = "Locally Modified"; 189 break; 190 case T_REMOVE_ENTRY: 191 sstat = "Entry Invalid"; 192 break; 193 case T_UPTODATE: 194 sstat = "Up-to-date"; 195 break; 196 case T_NEEDS_MERGE: 197 sstat = "Needs Merge"; 198 break; 199 case T_TITLE: 200 /* I don't think this case can occur here. Just print 201 "Classify Error". */ 202 break; 203 } 204 205 cvs_output ("\ 206 ===================================================================\n", 0); 207 if (vers->ts_user == NULL) 208 { 209 cvs_output ("File: no file ", 0); 210 cvs_output (finfo->file, 0); 211 cvs_output ("\t\tStatus: ", 0); 212 cvs_output (sstat, 0); 213 cvs_output ("\n\n", 0); 214 } 215 else 216 { 217 char *buf; 218 buf = Xasprintf ("File: %-17s\tStatus: %s\n\n", finfo->file, sstat); 219 cvs_output (buf, 0); 220 free (buf); 221 } 222 223 if (vers->vn_user == NULL) 224 { 225 cvs_output (" Working revision:\tNo entry for ", 0); 226 cvs_output (finfo->file, 0); 227 cvs_output ("\n", 0); 228 } 229 else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0') 230 cvs_output (" Working revision:\tNew file!\n", 0); 231 else 232 { 233 cvs_output (" Working revision:\t", 0); 234 cvs_output (vers->vn_user, 0); 235 236 /* Only add the UTC timezone if there is a time to use. */ 237 if (!server_active && strlen (vers->ts_rcs) > 0) 238 { 239 /* Convert from the asctime() format to ISO 8601 */ 240 char *buf; 241 242 cvs_output ("\t", 0); 243 244 /* Allow conversion from CVS/Entries asctime() to ISO 8601 */ 245 buf = Xasprintf ("%s UTC", vers->ts_rcs); 246 cvs_output_tagged ("date", buf); 247 free (buf); 248 } 249 cvs_output ("\n", 0); 250 } 251 252 if (vers->vn_rcs == NULL) 253 cvs_output (" Repository revision:\tNo revision control file\n", 0); 254 else 255 { 256 cvs_output (" Repository revision:\t", 0); 257 cvs_output (vers->vn_rcs, 0); 258 cvs_output ("\t", 0); 259 cvs_output (vers->srcfile->print_path, 0); 260 cvs_output ("\n", 0); 261 262 node = findnode(vers->srcfile->versions,vers->vn_rcs); 263 if (node) 264 { 265 RCSVers *v; 266 v=(RCSVers*)node->data; 267 node = findnode(v->other_delta,"commitid"); 268 cvs_output(" Commit Identifier:\t", 0); 269 if(node && node->data) 270 cvs_output(node->data, 0); 271 else 272 cvs_output("(none)",0); 273 cvs_output("\n",0); 274 } 275 } 276 277 if (vers->entdata) 278 { 279 Entnode *edata; 280 281 edata = vers->entdata; 282 if (edata->tag) 283 { 284 if (vers->vn_rcs == NULL) 285 { 286 cvs_output (" Sticky Tag:\t\t", 0); 287 cvs_output (edata->tag, 0); 288 cvs_output (" - MISSING from RCS file!\n", 0); 289 } 290 else 291 { 292 if (isdigit ((unsigned char) edata->tag[0])) 293 { 294 cvs_output (" Sticky Tag:\t\t", 0); 295 cvs_output (edata->tag, 0); 296 cvs_output ("\n", 0); 297 } 298 else 299 { 300 char *branch = NULL; 301 302 if (RCS_nodeisbranch (finfo->rcs, edata->tag)) 303 branch = RCS_whatbranch(finfo->rcs, edata->tag); 304 305 cvs_output (" Sticky Tag:\t\t", 0); 306 cvs_output (edata->tag, 0); 307 cvs_output (" (", 0); 308 cvs_output (branch ? "branch" : "revision", 0); 309 cvs_output (": ", 0); 310 cvs_output (branch ? branch : vers->vn_rcs, 0); 311 cvs_output (")\n", 0); 312 313 if (branch) 314 free (branch); 315 } 316 } 317 } 318 else if (!really_quiet) 319 cvs_output (" Sticky Tag:\t\t(none)\n", 0); 320 321 if (edata->date) 322 { 323 cvs_output (" Sticky Date:\t\t", 0); 324 cvs_output (edata->date, 0); 325 cvs_output ("\n", 0); 326 } 327 else if (!really_quiet) 328 cvs_output (" Sticky Date:\t\t(none)\n", 0); 329 330 if (edata->options && edata->options[0]) 331 { 332 cvs_output (" Sticky Options:\t", 0); 333 cvs_output (edata->options, 0); 334 cvs_output ("\n", 0); 335 } 336 else if (!really_quiet) 337 cvs_output (" Sticky Options:\t(none)\n", 0); 338 } 339 340 if (long_format && vers->srcfile) 341 { 342 List *symbols = RCS_symbols(vers->srcfile); 343 344 cvs_output ("\n Existing Tags:\n", 0); 345 if (symbols) 346 { 347 xrcsnode = finfo->rcs; 348 (void) walklist (symbols, tag_list_proc, NULL); 349 } 350 else 351 cvs_output ("\tNo Tags Exist\n", 0); 352 } 353 354 cvs_output ("\n", 0); 355 freevers_ts (&vers); 356 return (0); 357 } 358 359 360 361 /* 362 * Print a warm fuzzy message 363 */ 364 /* ARGSUSED */ 365 static Dtype 366 status_dirproc (void *callerdat, const char *dir, const char *repos, 367 const char *update_dir, List *entries) 368 { 369 if (!quiet) 370 error (0, 0, "Examining %s", update_dir); 371 return (R_PROCESS); 372 } 373 374 375 376 /* 377 * Print out a tag and its type 378 */ 379 static int 380 tag_list_proc (Node *p, void *closure) 381 { 382 char *branch = NULL; 383 char *buf; 384 385 if (RCS_nodeisbranch (xrcsnode, p->key)) 386 branch = RCS_whatbranch(xrcsnode, p->key) ; 387 388 buf = Xasprintf ("\t%-25s\t(%s: %s)\n", p->key, 389 branch ? "branch" : "revision", 390 branch ? branch : (char *)p->data); 391 cvs_output (buf, 0); 392 free (buf); 393 394 if (branch) 395 free (branch); 396 397 return (0); 398 } 399