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 optind = 0; 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 sstat = "Classify Error"; 131 switch (status) 132 { 133 case T_UNKNOWN: 134 sstat = "Unknown"; 135 break; 136 case T_CHECKOUT: 137 sstat = "Needs Checkout"; 138 break; 139 case T_PATCH: 140 sstat = "Needs Patch"; 141 break; 142 case T_CONFLICT: 143 /* FIXME - This message could be clearer. It comes up 144 * when a file exists or has been added in the local sandbox 145 * and a file of the same name has been committed indepenently to 146 * the repository from a different sandbox, as well as when a 147 * timestamp hasn't changed since a merge resulted in conflicts. 148 * It also comes up whether an update has been attempted or not, so 149 * technically, I think the double-add case is not actually a 150 * conflict yet. 151 */ 152 sstat = "Unresolved Conflict"; 153 break; 154 case T_ADDED: 155 sstat = "Locally Added"; 156 break; 157 case T_REMOVED: 158 sstat = "Locally Removed"; 159 break; 160 case T_MODIFIED: 161 if (file_has_markers (finfo)) 162 sstat = "File had conflicts on merge"; 163 else 164 /* Note that we do not re Register() the file when we spot 165 * a resolved conflict like update_fileproc() does on the 166 * premise that status should not alter the sandbox. 167 */ 168 sstat = "Locally Modified"; 169 break; 170 case T_REMOVE_ENTRY: 171 sstat = "Entry Invalid"; 172 break; 173 case T_UPTODATE: 174 sstat = "Up-to-date"; 175 break; 176 case T_NEEDS_MERGE: 177 sstat = "Needs Merge"; 178 break; 179 case T_TITLE: 180 /* I don't think this case can occur here. Just print 181 "Classify Error". */ 182 break; 183 } 184 185 cvs_output ("\ 186 ===================================================================\n", 0); 187 if (vers->ts_user == NULL) 188 { 189 cvs_output ("File: no file ", 0); 190 cvs_output (finfo->file, 0); 191 cvs_output ("\t\tStatus: ", 0); 192 cvs_output (sstat, 0); 193 cvs_output ("\n\n", 0); 194 } 195 else 196 { 197 char *buf; 198 buf = Xasprintf ("File: %-17s\tStatus: %s\n\n", finfo->file, sstat); 199 cvs_output (buf, 0); 200 free (buf); 201 } 202 203 if (vers->vn_user == NULL) 204 { 205 cvs_output (" Working revision:\tNo entry for ", 0); 206 cvs_output (finfo->file, 0); 207 cvs_output ("\n", 0); 208 } 209 else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0') 210 cvs_output (" Working revision:\tNew file!\n", 0); 211 else 212 { 213 cvs_output (" Working revision:\t", 0); 214 cvs_output (vers->vn_user, 0); 215 216 /* Only add the UTC timezone if there is a time to use. */ 217 if (!server_active && strlen (vers->ts_rcs) > 0) 218 { 219 /* Convert from the asctime() format to ISO 8601 */ 220 char *buf; 221 222 cvs_output ("\t", 0); 223 224 /* Allow conversion from CVS/Entries asctime() to ISO 8601 */ 225 buf = Xasprintf ("%s UTC", vers->ts_rcs); 226 cvs_output_tagged ("date", buf); 227 free (buf); 228 } 229 cvs_output ("\n", 0); 230 } 231 232 if (vers->vn_rcs == NULL) 233 cvs_output (" Repository revision:\tNo revision control file\n", 0); 234 else 235 { 236 cvs_output (" Repository revision:\t", 0); 237 cvs_output (vers->vn_rcs, 0); 238 cvs_output ("\t", 0); 239 cvs_output (vers->srcfile->print_path, 0); 240 cvs_output ("\n", 0); 241 242 node = findnode(vers->srcfile->versions,vers->vn_rcs); 243 if (node) 244 { 245 RCSVers *v; 246 v=(RCSVers*)node->data; 247 node = findnode(v->other_delta,"commitid"); 248 cvs_output(" Commit Identifier:\t", 0); 249 if(node && node->data) 250 cvs_output(node->data, 0); 251 else 252 cvs_output("(none)",0); 253 cvs_output("\n",0); 254 } 255 } 256 257 if (vers->entdata) 258 { 259 Entnode *edata; 260 261 edata = vers->entdata; 262 if (edata->tag) 263 { 264 if (vers->vn_rcs == NULL) 265 { 266 cvs_output (" Sticky Tag:\t\t", 0); 267 cvs_output (edata->tag, 0); 268 cvs_output (" - MISSING from RCS file!\n", 0); 269 } 270 else 271 { 272 if (isdigit ((unsigned char) edata->tag[0])) 273 { 274 cvs_output (" Sticky Tag:\t\t", 0); 275 cvs_output (edata->tag, 0); 276 cvs_output ("\n", 0); 277 } 278 else 279 { 280 char *branch = NULL; 281 282 if (RCS_nodeisbranch (finfo->rcs, edata->tag)) 283 branch = RCS_whatbranch(finfo->rcs, edata->tag); 284 285 cvs_output (" Sticky Tag:\t\t", 0); 286 cvs_output (edata->tag, 0); 287 cvs_output (" (", 0); 288 cvs_output (branch ? "branch" : "revision", 0); 289 cvs_output (": ", 0); 290 cvs_output (branch ? branch : vers->vn_rcs, 0); 291 cvs_output (")\n", 0); 292 293 if (branch) 294 free (branch); 295 } 296 } 297 } 298 else if (!really_quiet) 299 cvs_output (" Sticky Tag:\t\t(none)\n", 0); 300 301 if (edata->date) 302 { 303 cvs_output (" Sticky Date:\t\t", 0); 304 cvs_output (edata->date, 0); 305 cvs_output ("\n", 0); 306 } 307 else if (!really_quiet) 308 cvs_output (" Sticky Date:\t\t(none)\n", 0); 309 310 if (edata->options && edata->options[0]) 311 { 312 cvs_output (" Sticky Options:\t", 0); 313 cvs_output (edata->options, 0); 314 cvs_output ("\n", 0); 315 } 316 else if (!really_quiet) 317 cvs_output (" Sticky Options:\t(none)\n", 0); 318 } 319 320 if (long_format && vers->srcfile) 321 { 322 List *symbols = RCS_symbols(vers->srcfile); 323 324 cvs_output ("\n Existing Tags:\n", 0); 325 if (symbols) 326 { 327 xrcsnode = finfo->rcs; 328 (void) walklist (symbols, tag_list_proc, NULL); 329 } 330 else 331 cvs_output ("\tNo Tags Exist\n", 0); 332 } 333 334 cvs_output ("\n", 0); 335 freevers_ts (&vers); 336 return (0); 337 } 338 339 340 341 /* 342 * Print a warm fuzzy message 343 */ 344 /* ARGSUSED */ 345 static Dtype 346 status_dirproc (void *callerdat, const char *dir, const char *repos, 347 const char *update_dir, List *entries) 348 { 349 if (!quiet) 350 error (0, 0, "Examining %s", update_dir); 351 return (R_PROCESS); 352 } 353 354 355 356 /* 357 * Print out a tag and its type 358 */ 359 static int 360 tag_list_proc (Node *p, void *closure) 361 { 362 char *branch = NULL; 363 char *buf; 364 365 if (RCS_nodeisbranch (xrcsnode, p->key)) 366 branch = RCS_whatbranch(xrcsnode, p->key) ; 367 368 buf = Xasprintf ("\t%-25s\t(%s: %s)\n", p->key, 369 branch ? "branch" : "revision", 370 branch ? branch : (char *)p->data); 371 cvs_output (buf, 0); 372 free (buf); 373 374 if (branch) 375 free (branch); 376 377 return (0); 378 } 379