1 /* $OpenBSD: status.c,v 1.100 2017/06/01 08:08:24 joris Exp $ */ 2 /* 3 * Copyright (c) 2006 Joris Vink <joris@openbsd.org> 4 * Copyright (c) 2005-2008 Xavier Santolaria <xsa@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <stdlib.h> 20 #include <string.h> 21 #include <time.h> 22 #include <unistd.h> 23 24 #include "cvs.h" 25 #include "remote.h" 26 27 void cvs_status_local(struct cvs_file *); 28 29 static int show_sym = 0; 30 31 struct cvs_cmd cvs_cmd_status = { 32 CVS_OP_STATUS, CVS_USE_WDIR, "status", 33 { "st", "stat" }, 34 "Display status information on checked out files", 35 "[-lRv]", 36 "lRv", 37 NULL, 38 cvs_status 39 }; 40 41 #define CVS_STATUS_SEP \ 42 "===================================================================" 43 44 const char *status_tab[] = { 45 "Unknown", 46 "Locally Added", 47 "Locally Removed", 48 "Locally Modified", 49 "Up-to-date", 50 "Needs Checkout", 51 "Needs Checkout", 52 "Needs Merge", 53 "Needs Patch", 54 "Entry Invalid", 55 "Unresolved Conflict", 56 "Classifying error", 57 }; 58 59 int 60 cvs_status(int argc, char **argv) 61 { 62 int ch, flags; 63 char *arg = "."; 64 struct cvs_recursion cr; 65 66 flags = CR_RECURSE_DIRS; 67 68 while ((ch = getopt(argc, argv, cvs_cmd_status.cmd_opts)) != -1) { 69 switch (ch) { 70 case 'l': 71 flags &= ~CR_RECURSE_DIRS; 72 break; 73 case 'R': 74 flags |= CR_RECURSE_DIRS; 75 break; 76 case 'v': 77 show_sym = 1; 78 break; 79 default: 80 fatal("%s", cvs_cmd_status.cmd_synopsis); 81 } 82 } 83 84 argc -= optind; 85 argv += optind; 86 87 cr.enterdir = NULL; 88 cr.leavedir = NULL; 89 90 if (cvsroot_is_local()) { 91 flags |= CR_REPO; 92 cr.fileproc = cvs_status_local; 93 } else { 94 cvs_client_connect_to_server(); 95 if (!(flags & CR_RECURSE_DIRS)) 96 cvs_client_send_request("Argument -l"); 97 if (show_sym) 98 cvs_client_send_request("Argument -v"); 99 cr.fileproc = cvs_client_sendfile; 100 } 101 102 cr.flags = flags; 103 104 if (argc > 0) 105 cvs_file_run(argc, argv, &cr); 106 else 107 cvs_file_run(1, &arg, &cr); 108 109 if (cvsroot_is_remote()) { 110 cvs_client_send_files(argv, argc); 111 cvs_client_senddir("."); 112 cvs_client_send_request("status"); 113 cvs_client_get_responses(); 114 } 115 116 return (0); 117 } 118 119 void 120 cvs_status_local(struct cvs_file *cf) 121 { 122 size_t len; 123 RCSNUM *head, *brev; 124 const char *status; 125 struct rcs_delta *rdp; 126 char buf[PATH_MAX + CVS_REV_BUFSZ + 128]; 127 char timebuf[CVS_TIME_BUFSZ], revbuf[CVS_REV_BUFSZ]; 128 struct rcs_sym *sym; 129 130 cvs_log(LP_TRACE, "cvs_status_local(%s)", cf->file_path); 131 132 cvs_file_classify(cf, cvs_directory_tag); 133 134 if (cf->file_type == CVS_DIR) { 135 if (verbosity > 1) 136 cvs_log(LP_NOTICE, "Examining %s", cf->file_path); 137 return; 138 } 139 140 if (cf->file_status == FILE_UPTODATE && 141 !(cf->file_flags & FILE_ON_DISK) && 142 !(cf->file_flags & FILE_USER_SUPPLIED)) 143 return; 144 145 if (cf->file_rcs != NULL) 146 head = cf->file_rcsrev; 147 else 148 head = NULL; 149 150 cvs_printf("%s\n", CVS_STATUS_SEP); 151 152 if (cf->file_rcs != NULL && head == NULL) 153 status = status_tab[FILE_UNKNOWN]; 154 else 155 status = status_tab[cf->file_status]; 156 157 if (cf->file_status == FILE_MODIFIED && 158 cf->file_ent->ce_conflict != NULL) 159 status = "File had conflicts on merge"; 160 161 if (!(cf->file_flags & FILE_ON_DISK)) { 162 (void)xsnprintf(buf, sizeof(buf), "no file %s\t", 163 cf->file_name); 164 } else 165 if (strlcpy(buf, cf->file_name, sizeof(buf)) >= sizeof(buf)) 166 fatal("cvs_status_local: overflow"); 167 168 cvs_printf("File: %-17s\tStatus: %s\n\n", buf, status); 169 170 if (cf->file_ent == NULL) { 171 (void)xsnprintf(buf, sizeof(buf), 172 "No entry for %s", cf->file_name); 173 } else if (cf->file_ent->ce_status == CVS_ENT_ADDED) { 174 len = strlcpy(buf, "New file!", sizeof(buf)); 175 if (len >= sizeof(buf)) 176 fatal("cvs_status_local: truncation"); 177 } else { 178 rcsnum_tostr(cf->file_ent->ce_rev, revbuf, sizeof(revbuf)); 179 180 if (cf->file_ent->ce_conflict == NULL) { 181 if (cvs_server_active == 0) { 182 (void)strlcpy(timebuf, cf->file_ent->ce_time, 183 sizeof(timebuf)); 184 } else { 185 timebuf[0] = '\0'; 186 } 187 } else { 188 len = strlcpy(timebuf, cf->file_ent->ce_conflict, 189 sizeof(timebuf)); 190 if (len >= sizeof(timebuf)) 191 fatal("cvs_status_local: truncation"); 192 } 193 194 (void)strlcpy(buf, revbuf, sizeof(buf)); 195 if (cvs_server_active == 0) { 196 (void)strlcat(buf, "\t", sizeof(buf)); 197 (void)strlcat(buf, timebuf, sizeof(buf)); 198 } 199 } 200 201 cvs_printf(" Working revision:\t%s\n", buf); 202 203 buf[0] = '\0'; 204 if (cf->file_rcs == NULL) { 205 len = strlcat(buf, "No revision control file", sizeof(buf)); 206 if (len >= sizeof(buf)) 207 fatal("cvs_status_local: truncation"); 208 } else if (head == NULL) { 209 len = strlcat(buf, "No head revision", sizeof(buf)); 210 if (len >= sizeof(buf)) 211 fatal("cvs_status_local: truncation"); 212 } else { 213 rcsnum_tostr(head, revbuf, sizeof(revbuf)); 214 (void)xsnprintf(buf, sizeof(buf), "%s\t%s", revbuf, 215 cf->file_rpath); 216 } 217 218 cvs_printf(" Repository revision:\t%s\n", buf); 219 220 if (cf->file_rcs != NULL && head != NULL) { 221 rdp = rcs_findrev(cf->file_rcs, head); 222 if (rdp == NULL) { 223 fatal("cvs_status_local: No head revision delta"); 224 } 225 226 cvs_printf(" Commit Identifier:\t%s\n", 227 (rdp->rd_commitid != NULL) ? rdp->rd_commitid : "(none)"); 228 } 229 230 if (cf->file_ent != NULL) { 231 if (cf->file_ent->ce_tag != NULL) { 232 if ((brev = rcs_sym_getrev(cf->file_rcs, 233 cf->file_ent->ce_tag)) == NULL) { 234 (void)strlcpy(buf, "- MISSING from RCS file!", 235 sizeof(buf)); 236 } else { 237 rcsnum_tostr(brev, revbuf, sizeof(revbuf)); 238 if (RCSNUM_ISBRANCH(brev)) { 239 xsnprintf(buf, sizeof(buf), 240 "(branch: %s)", revbuf); 241 } else { 242 xsnprintf(buf, sizeof(buf), 243 "(revision: %s)", revbuf); 244 } 245 free(brev); 246 } 247 248 cvs_printf(" Sticky Tag:\t\t%s %s\n", 249 cf->file_ent->ce_tag, buf); 250 } else if (verbosity > 0) { 251 cvs_printf(" Sticky Tag:\t\t(none)\n"); 252 } 253 254 if (cf->file_ent->ce_date != -1) { 255 struct tm datetm; 256 char datetmp[CVS_TIME_BUFSZ]; 257 258 gmtime_r(&(cf->file_ent->ce_date), &datetm); 259 (void)strftime(datetmp, sizeof(datetmp), 260 CVS_DATE_FMT, &datetm); 261 262 cvs_printf(" Sticky Date:\t\t%s\n", datetmp); 263 } else if (verbosity > 0) 264 cvs_printf(" Sticky Date:\t\t(none)\n"); 265 266 if (cf->file_ent->ce_opts != NULL) 267 cvs_printf(" Sticky Options:\t%s\n", 268 cf->file_ent->ce_opts); 269 else if (verbosity > 0) 270 cvs_printf(" Sticky Options:\t(none)\n"); 271 } 272 273 if (cf->file_rcs != NULL && show_sym == 1) { 274 cvs_printf("\n"); 275 cvs_printf(" Existing Tags:\n"); 276 277 if (!TAILQ_EMPTY(&(cf->file_rcs->rf_symbols))) { 278 TAILQ_FOREACH(sym, 279 &(cf->file_rcs->rf_symbols), rs_list) { 280 (void)rcsnum_tostr(sym->rs_num, revbuf, 281 sizeof(revbuf)); 282 283 cvs_printf("\t%-25s\t(%s: %s)\n", sym->rs_name, 284 RCSNUM_ISBRANCH(sym->rs_num) ? "branch" : 285 "revision", revbuf); 286 } 287 } else 288 cvs_printf("\tNo Tags Exist\n"); 289 } 290 291 cvs_printf("\n"); 292 } 293