Annotation of src/usr.bin/rcs/rcsutil.c, Revision 1.1
1.1 ! xsa 1: /* $OpenBSD$ */
! 2: /*
! 3: * Copyright (c) 2005, 2006 Joris Vink <joris@openbsd.org>
! 4: * Copyright (c) 2006 Xavier Santolaria <xsa@openbsd.org>
! 5: * Copyright (c) 2006 Niall O'Higgins <niallo@openbsd.org>
! 6: * Copyright (c) 2006 Ray Lai <ray@openbsd.org>
! 7: * All rights reserved.
! 8: *
! 9: * Redistribution and use in source and binary forms, with or without
! 10: * modification, are permitted provided that the following conditions
! 11: * are met:
! 12: *
! 13: * 1. Redistributions of source code must retain the above copyright
! 14: * notice, this list of conditions and the following disclaimer.
! 15: * 2. The name of the author may not be used to endorse or promote products
! 16: * derived from this software without specific prior written permission.
! 17: *
! 18: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
! 19: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
! 20: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
! 21: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
! 22: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
! 23: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
! 24: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
! 25: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
! 26: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
! 27: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 28: */
! 29:
! 30: #include "includes.h"
! 31:
! 32: #include "rcsprog.h"
! 33:
! 34: /*
! 35: * rcs_get_mtime()
! 36: *
! 37: * Get <filename> last modified time.
! 38: * Returns last modified time on success, or -1 on failure.
! 39: */
! 40: time_t
! 41: rcs_get_mtime(const char *filename)
! 42: {
! 43: struct stat st;
! 44: time_t mtime;
! 45:
! 46: if (stat(filename, &st) == -1) {
! 47: warn("%s", filename);
! 48: return (-1);
! 49: }
! 50: mtime = (time_t)st.st_mtimespec.tv_sec;
! 51:
! 52: return (mtime);
! 53: }
! 54:
! 55: /*
! 56: * rcs_set_mtime()
! 57: *
! 58: * Set <filename> last modified time to <mtime> if it's not set to -1.
! 59: */
! 60: void
! 61: rcs_set_mtime(const char *filename, time_t mtime)
! 62: {
! 63: static struct timeval tv[2];
! 64:
! 65: if (mtime == -1)
! 66: return;
! 67:
! 68: tv[0].tv_sec = mtime;
! 69: tv[1].tv_sec = tv[0].tv_sec;
! 70:
! 71: if (utimes(filename, tv) == -1)
! 72: fatal("error setting utimes: %s", strerror(errno));
! 73: }
! 74:
! 75: int
! 76: rcs_getopt(int argc, char **argv, const char *optstr)
! 77: {
! 78: char *a;
! 79: const char *c;
! 80: static int i = 1;
! 81: int opt, hasargument, ret;
! 82:
! 83: hasargument = 0;
! 84: rcs_optarg = NULL;
! 85:
! 86: if (i >= argc)
! 87: return (-1);
! 88:
! 89: a = argv[i++];
! 90: if (*a++ != '-')
! 91: return (-1);
! 92:
! 93: ret = 0;
! 94: opt = *a;
! 95: for (c = optstr; *c != '\0'; c++) {
! 96: if (*c == opt) {
! 97: a++;
! 98: ret = opt;
! 99:
! 100: if (*(c + 1) == ':') {
! 101: if (*(c + 2) == ':') {
! 102: if (*a != '\0')
! 103: hasargument = 1;
! 104: } else {
! 105: if (*a != '\0') {
! 106: hasargument = 1;
! 107: } else {
! 108: ret = 1;
! 109: break;
! 110: }
! 111: }
! 112: }
! 113:
! 114: if (hasargument == 1)
! 115: rcs_optarg = a;
! 116:
! 117: if (ret == opt)
! 118: rcs_optind++;
! 119: break;
! 120: }
! 121: }
! 122:
! 123: if (ret == 0)
! 124: warnx("unknown option -%c", opt);
! 125: else if (ret == 1)
! 126: warnx("missing argument for option -%c", opt);
! 127:
! 128: return (ret);
! 129: }
! 130:
! 131: /*
! 132: * rcs_choosefile()
! 133: *
! 134: * Given a relative filename, decide where the corresponding RCS file
! 135: * should be. Tries each extension until a file is found. If no file
! 136: * was found, returns a path with the first extension.
! 137: *
! 138: * Returns pointer to a char array on success, NULL on failure.
! 139: */
! 140: char *
! 141: rcs_choosefile(const char *filename)
! 142: {
! 143: struct stat sb;
! 144: char *p, *ext, name[MAXPATHLEN], *next, *ptr, rcsdir[MAXPATHLEN],
! 145: *ret, *suffixes, rcspath[MAXPATHLEN];
! 146:
! 147: /* If -x flag was not given, use default. */
! 148: if (rcs_suffixes == NULL)
! 149: rcs_suffixes = RCS_DEFAULT_SUFFIX;
! 150:
! 151: /*
! 152: * If `filename' contains a directory, `rcspath' contains that
! 153: * directory, including a trailing slash. Otherwise `rcspath'
! 154: * contains an empty string.
! 155: */
! 156: if (strlcpy(rcspath, filename, sizeof(rcspath)) >= sizeof(rcspath))
! 157: return (NULL);
! 158: /* If `/' is found, end string after `/'. */
! 159: if ((ptr = strrchr(rcspath, '/')) != NULL)
! 160: *(++ptr) = '\0';
! 161: else
! 162: rcspath[0] = '\0';
! 163:
! 164: /* Append RCS/ to `rcspath' if it exists. */
! 165: if (strlcpy(rcsdir, rcspath, sizeof(rcsdir)) >= sizeof(rcsdir) ||
! 166: strlcat(rcsdir, RCSDIR, sizeof(rcsdir)) >= sizeof(rcsdir))
! 167: return (NULL);
! 168: if (stat(rcsdir, &sb) == 0 && (sb.st_mode & S_IFDIR))
! 169: if (strlcpy(rcspath, rcsdir, sizeof(rcspath)) >= sizeof(rcspath) ||
! 170: strlcat(rcspath, "/", sizeof(rcspath)) >= sizeof(rcspath))
! 171: return (NULL);
! 172:
! 173: /* Name of file without path. */
! 174: if ((ptr = strrchr(filename, '/')) == NULL) {
! 175: if (strlcpy(name, filename, sizeof(name)) >= sizeof(name))
! 176: return (NULL);
! 177: } else {
! 178: /* Skip `/'. */
! 179: if (strlcpy(name, ptr + 1, sizeof(name)) >= sizeof(name))
! 180: return (NULL);
! 181: }
! 182:
! 183: /* Name of RCS file without an extension. */
! 184: if (strlcat(rcspath, name, sizeof(rcspath)) >= sizeof(rcspath))
! 185: return (NULL);
! 186:
! 187: /*
! 188: * If only the empty suffix was given, use existing rcspath.
! 189: * This ensures that there is at least one suffix for strsep().
! 190: */
! 191: if (strcmp(rcs_suffixes, "") == 0) {
! 192: ret = xstrdup(rcspath);
! 193: return (ret);
! 194: }
! 195:
! 196: /*
! 197: * Cycle through slash-separated `rcs_suffixes', appending each
! 198: * extension to `rcspath' and testing if the file exists. If it
! 199: * does, return that string. Otherwise return path with first
! 200: * extension.
! 201: */
! 202: suffixes = xstrdup(rcs_suffixes);
! 203: for (ret = NULL, next = suffixes; (ext = strsep(&next, "/")) != NULL;) {
! 204: char fpath[MAXPATHLEN];
! 205:
! 206: if ((p = strrchr(rcspath, ',')) != NULL) {
! 207: if (!strcmp(p, ext)) {
! 208: if (stat(rcspath, &sb) == 0) {
! 209: ret = xstrdup(rcspath);
! 210: goto out;
! 211: }
! 212: }
! 213:
! 214: continue;
! 215: }
! 216:
! 217: /* Construct RCS file path. */
! 218: if (strlcpy(fpath, rcspath, sizeof(fpath)) >= sizeof(fpath) ||
! 219: strlcat(fpath, ext, sizeof(fpath)) >= sizeof(fpath))
! 220: goto out;
! 221:
! 222: /* Don't use `filename' as RCS file. */
! 223: if (strcmp(fpath, filename) == 0)
! 224: continue;
! 225:
! 226: if (stat(fpath, &sb) == 0) {
! 227: ret = xstrdup(fpath);
! 228: goto out;
! 229: }
! 230: }
! 231:
! 232: /*
! 233: * `ret' is still NULL. No RCS file with any extension exists
! 234: * so we use the first extension.
! 235: *
! 236: * `suffixes' should now be NUL separated, so the first
! 237: * extension can be read just by reading `suffixes'.
! 238: */
! 239: if (strlcat(rcspath, suffixes, sizeof(rcspath)) >=
! 240: sizeof(rcspath))
! 241: goto out;
! 242: ret = xstrdup(rcspath);
! 243:
! 244: out:
! 245: /* `ret' may be NULL, which indicates an error. */
! 246: xfree(suffixes);
! 247: return (ret);
! 248: }
! 249:
! 250: /*
! 251: * Find the name of an RCS file, given a file name `fname'. If an RCS
! 252: * file is found, the name is copied to the `len' sized buffer `out'.
! 253: * Returns 0 if RCS file was found, -1 otherwise.
! 254: */
! 255: int
! 256: rcs_statfile(char *fname, char *out, size_t len, int flags)
! 257: {
! 258: struct stat st;
! 259: char *rcspath;
! 260:
! 261: if ((rcspath = rcs_choosefile(fname)) == NULL)
! 262: fatal("rcs_statfile: path truncation");
! 263:
! 264: /* Error out if file not found and we are not creating one. */
! 265: if (stat(rcspath, &st) == -1 && !(flags & RCS_CREATE)) {
! 266: if (strcmp(__progname, "rcsclean") != 0 &&
! 267: strcmp(__progname, "ci") != 0)
! 268: warn("%s", rcspath);
! 269: xfree(rcspath);
! 270: return (-1);
! 271: }
! 272:
! 273: if (strlcpy(out, rcspath, len) >= len)
! 274: fatal("rcs_statfile: path truncation");
! 275:
! 276: xfree(rcspath);
! 277:
! 278: return (0);
! 279: }
! 280:
! 281: /*
! 282: * Allocate an RCSNUM and store in <rev>.
! 283: */
! 284: void
! 285: rcs_set_rev(const char *str, RCSNUM **rev)
! 286: {
! 287: if (str == NULL || (*rev = rcsnum_parse(str)) == NULL)
! 288: fatal("bad revision number '%s'", str);
! 289: }
! 290:
! 291: /*
! 292: * Set <str> to <new_str>. Print warning if <str> is redefined.
! 293: */
! 294: void
! 295: rcs_setrevstr(char **str, char *new_str)
! 296: {
! 297: if (new_str == NULL)
! 298: return;
! 299: if (*str != NULL)
! 300: warnx("redefinition of revision number");
! 301: *str = new_str;
! 302: }
! 303:
! 304: /*
! 305: * Set <str1> or <str2> to <new_str>, depending on which is not set.
! 306: * If both are set, error out.
! 307: */
! 308: void
! 309: rcs_setrevstr2(char **str1, char **str2, char *new_str)
! 310: {
! 311: if (new_str == NULL)
! 312: return;
! 313: if (*str1 == NULL)
! 314: *str1 = new_str;
! 315: else if (*str2 == NULL)
! 316: *str2 = new_str;
! 317: else
! 318: fatal("too many revision numbers");
! 319: }
! 320:
! 321: /*
! 322: * Get revision from file. The revision can be specified as a symbol or
! 323: * a revision number.
! 324: */
! 325: RCSNUM *
! 326: rcs_getrevnum(const char *rev_str, RCSFILE *file)
! 327: {
! 328: RCSNUM *rev;
! 329:
! 330: /* Search for symbol. */
! 331: rev = rcs_sym_getrev(file, rev_str);
! 332:
! 333: /* Search for revision number. */
! 334: if (rev == NULL)
! 335: rev = rcsnum_parse(rev_str);
! 336:
! 337: return (rev);
! 338: }
! 339:
! 340: /*
! 341: * Prompt for and store user's input in an allocated string.
! 342: *
! 343: * Returns the string's pointer.
! 344: */
! 345: char *
! 346: rcs_prompt(const char *prompt)
! 347: {
! 348: BUF *bp;
! 349: size_t len;
! 350: char *buf;
! 351:
! 352: bp = cvs_buf_alloc(0, BUF_AUTOEXT);
! 353: if (isatty(STDIN_FILENO))
! 354: (void)fprintf(stderr, "%s", prompt);
! 355: if (isatty(STDIN_FILENO))
! 356: (void)fprintf(stderr, ">> ");
! 357: while ((buf = fgetln(stdin, &len)) != NULL) {
! 358: /* The last line may not be EOL terminated. */
! 359: if (buf[0] == '.' && (len == 1 || buf[1] == '\n'))
! 360: break;
! 361: else
! 362: cvs_buf_append(bp, buf, len);
! 363:
! 364: if (isatty(STDIN_FILENO))
! 365: (void)fprintf(stderr, ">> ");
! 366: }
! 367: cvs_buf_putc(bp, '\0');
! 368:
! 369: return (cvs_buf_release(bp));
! 370: }
! 371:
! 372: u_int
! 373: rcs_rev_select(RCSFILE *file, char *range)
! 374: {
! 375: int i;
! 376: u_int nrev;
! 377: char *ep;
! 378: char *lstr, *rstr;
! 379: struct rcs_delta *rdp;
! 380: struct cvs_argvector *revargv, *revrange;
! 381: RCSNUM lnum, rnum;
! 382:
! 383: nrev = 0;
! 384: (void)memset(&lnum, 0, sizeof(lnum));
! 385: (void)memset(&rnum, 0, sizeof(rnum));
! 386:
! 387: if (range == NULL) {
! 388: TAILQ_FOREACH(rdp, &file->rf_delta, rd_list)
! 389: if (rcsnum_cmp(rdp->rd_num, file->rf_head, 0) == 0) {
! 390: rdp->rd_flags |= RCS_RD_SELECT;
! 391: return (1);
! 392: }
! 393: return (0);
! 394: }
! 395:
! 396: revargv = cvs_strsplit(range, ",");
! 397: for (i = 0; revargv->argv[i] != NULL; i++) {
! 398: revrange = cvs_strsplit(revargv->argv[i], ":");
! 399: if (revrange->argv[0] == NULL)
! 400: /* should not happen */
! 401: fatal("invalid revision range: %s", revargv->argv[i]);
! 402: else if (revrange->argv[1] == NULL)
! 403: lstr = rstr = revrange->argv[0];
! 404: else {
! 405: if (revrange->argv[2] != NULL)
! 406: fatal("invalid revision range: %s",
! 407: revargv->argv[i]);
! 408: lstr = revrange->argv[0];
! 409: rstr = revrange->argv[1];
! 410: if (strcmp(lstr, "") == 0)
! 411: lstr = NULL;
! 412: if (strcmp(rstr, "") == 0)
! 413: rstr = NULL;
! 414: }
! 415:
! 416: if (lstr == NULL)
! 417: lstr = RCS_HEAD_INIT;
! 418: if (rcsnum_aton(lstr, &ep, &lnum) == 0 || (*ep != '\0'))
! 419: fatal("invalid revision: %s", lstr);
! 420:
! 421: if (rstr != NULL) {
! 422: if (rcsnum_aton(rstr, &ep, &rnum) == 0 || (*ep != '\0'))
! 423: fatal("invalid revision: %s", rstr);
! 424: } else
! 425: rcsnum_cpy(file->rf_head, &rnum, 0);
! 426:
! 427: cvs_argv_destroy(revrange);
! 428:
! 429: TAILQ_FOREACH(rdp, &file->rf_delta, rd_list)
! 430: if (rcsnum_cmp(rdp->rd_num, &lnum, 0) <= 0 &&
! 431: rcsnum_cmp(rdp->rd_num, &rnum, 0) >= 0 &&
! 432: !(rdp->rd_flags & RCS_RD_SELECT)) {
! 433: rdp->rd_flags |= RCS_RD_SELECT;
! 434: nrev++;
! 435: }
! 436: }
! 437: cvs_argv_destroy(revargv);
! 438:
! 439: if (lnum.rn_id != NULL)
! 440: xfree(lnum.rn_id);
! 441: if (rnum.rn_id != NULL)
! 442: xfree(rnum.rn_id);
! 443:
! 444: return (nrev);
! 445: }