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