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