Annotation of src/usr.bin/cvs/rcsparse.c, Revision 1.1
1.1 ! tobias 1: /* $OpenBSD$ */
! 2: /*
! 3: * Copyright (c) 2010 Tobias Stoeckmann <tobias@openbsd.org>
! 4: *
! 5: * Permission to use, copy, modify, and distribute this software for any
! 6: * purpose with or without fee is hereby granted, provided that the above
! 7: * copyright notice and this permission notice appear in all copies.
! 8: *
! 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 16: */
! 17:
! 18: #include <sys/queue.h>
! 19:
! 20: #include <ctype.h>
! 21: #include <err.h>
! 22: #include <pwd.h>
! 23: #include <stdarg.h>
! 24: #include <stdio.h>
! 25: #include <stdlib.h>
! 26: #include <string.h>
! 27: #include <unistd.h>
! 28:
! 29: #include "log.h"
! 30: #include "rcs.h"
! 31: #include "rcsparse.h"
! 32: #include "xmalloc.h"
! 33:
! 34: #define RCS_BUFSIZE 16384
! 35: #define RCS_BUFEXTSIZE 8192
! 36:
! 37: /* RCS token types */
! 38: #define RCS_TOK_HEAD (1 << 0)
! 39: #define RCS_TOK_BRANCH (1 << 1)
! 40: #define RCS_TOK_ACCESS (1 << 2)
! 41: #define RCS_TOK_SYMBOLS (1 << 3)
! 42: #define RCS_TOK_LOCKS (1 << 4)
! 43: #define RCS_TOK_STRICT (1 << 5)
! 44: #define RCS_TOK_COMMENT (1 << 6)
! 45: #define RCS_TOK_COMMITID (1 << 7)
! 46: #define RCS_TOK_EXPAND (1 << 8)
! 47: #define RCS_TOK_DESC (1 << 9)
! 48: #define RCS_TOK_DATE (1 << 10)
! 49: #define RCS_TOK_AUTHOR (1 << 11)
! 50: #define RCS_TOK_STATE (1 << 12)
! 51: #define RCS_TOK_BRANCHES (1 << 13)
! 52: #define RCS_TOK_NEXT (1 << 14)
! 53: #define RCS_TOK_LOG (1 << 15)
! 54: #define RCS_TOK_TEXT (1 << 16)
! 55: #define RCS_TOK_COLON (1 << 17)
! 56: #define RCS_TOK_COMMA (1 << 18)
! 57: #define RCS_TOK_SCOLON (1 << 19)
! 58:
! 59: #define RCS_TYPE_STRING (1 << 20)
! 60: #define RCS_TYPE_NUMBER (1 << 21)
! 61: #define RCS_TYPE_BRANCH (1 << 22)
! 62: #define RCS_TYPE_REVISION (1 << 23)
! 63: #define RCS_TYPE_LOGIN (1 << 24)
! 64: #define RCS_TYPE_STATE (1 << 25)
! 65: #define RCS_TYPE_SYMBOL (1 << 26)
! 66: #define RCS_TYPE_DATE (1 << 27)
! 67: #define RCS_TYPE_KEYWORD (1 << 28)
! 68: #define RCS_TYPE_COMMITID (1 << 29)
! 69:
! 70: #define MANDATORY 0
! 71: #define OPTIONAL 1
! 72:
! 73: /* opaque parse data */
! 74: struct rcs_pdata {
! 75: char *rp_buf;
! 76: size_t rp_blen;
! 77: char *rp_bufend;
! 78: size_t rp_tlen;
! 79:
! 80: struct rcs_delta *rp_delta;
! 81: FILE *rp_file;
! 82: int rp_lineno;
! 83: int rp_msglineno;
! 84: int rp_token;
! 85:
! 86: union {
! 87: RCSNUM *rev;
! 88: char *str;
! 89: struct tm date;
! 90: } rp_value;
! 91: };
! 92:
! 93: struct rcs_keyword {
! 94: const char *k_name;
! 95: int k_val;
! 96: };
! 97:
! 98: struct rcs_section {
! 99: int token;
! 100: int (*parse)(RCSFILE *, struct rcs_pdata *);
! 101: int opt;
! 102: };
! 103:
! 104: /* this has to be sorted always */
! 105: static const struct rcs_keyword keywords[] = {
! 106: { "access", RCS_TOK_ACCESS},
! 107: { "author", RCS_TOK_AUTHOR},
! 108: { "branch", RCS_TOK_BRANCH},
! 109: { "branches", RCS_TOK_BRANCHES},
! 110: { "comment", RCS_TOK_COMMENT},
! 111: { "date", RCS_TOK_DATE},
! 112: { "desc", RCS_TOK_DESC},
! 113: { "expand", RCS_TOK_EXPAND},
! 114: { "head", RCS_TOK_HEAD},
! 115: { "locks", RCS_TOK_LOCKS},
! 116: { "log", RCS_TOK_LOG},
! 117: { "next", RCS_TOK_NEXT},
! 118: { "state", RCS_TOK_STATE},
! 119: { "strict", RCS_TOK_STRICT},
! 120: { "symbols", RCS_TOK_SYMBOLS},
! 121: { "text", RCS_TOK_TEXT}
! 122: };
! 123:
! 124: /* parser functions specified in rcs_section structs */
! 125: static int rcsparse_head(RCSFILE *, struct rcs_pdata *);
! 126: static int rcsparse_branch(RCSFILE *, struct rcs_pdata *);
! 127: static int rcsparse_access(RCSFILE *, struct rcs_pdata *);
! 128: static int rcsparse_symbols(RCSFILE *, struct rcs_pdata *);
! 129: static int rcsparse_locks(RCSFILE *, struct rcs_pdata *);
! 130: static int rcsparse_strict(RCSFILE *, struct rcs_pdata *);
! 131: static int rcsparse_comment(RCSFILE *, struct rcs_pdata *);
! 132: static int rcsparse_commitid(RCSFILE *, struct rcs_pdata *);
! 133: static int rcsparse_expand(RCSFILE *, struct rcs_pdata *);
! 134: static int rcsparse_deltarevision(RCSFILE *, struct rcs_pdata *);
! 135: static int rcsparse_date(RCSFILE *, struct rcs_pdata *);
! 136: static int rcsparse_author(RCSFILE *, struct rcs_pdata *);
! 137: static int rcsparse_state(RCSFILE *, struct rcs_pdata *);
! 138: static int rcsparse_branches(RCSFILE *, struct rcs_pdata *);
! 139: static int rcsparse_next(RCSFILE *, struct rcs_pdata *);
! 140: static int rcsparse_textrevision(RCSFILE *, struct rcs_pdata *);
! 141: static int rcsparse_log(RCSFILE *, struct rcs_pdata *);
! 142: static int rcsparse_text(RCSFILE *, struct rcs_pdata *);
! 143:
! 144: static int rcsparse_delta(RCSFILE *);
! 145: static int rcsparse_deltatext(RCSFILE *);
! 146: static int rcsparse_desc(RCSFILE *);
! 147:
! 148: static int kw_cmp(const void *, const void *);
! 149: static int rcsparse(RCSFILE *, struct rcs_section *);
! 150: static void rcsparse_growbuf(RCSFILE *);
! 151: static int rcsparse_string(RCSFILE *, int);
! 152: static int rcsparse_token(RCSFILE *, int);
! 153: static void rcsparse_warnx(RCSFILE *, char *, ...);
! 154: static int valid_login(char *);
! 155:
! 156: /*
! 157: * head [REVISION];
! 158: * [branch BRANCH];
! 159: * access [LOGIN ...];
! 160: * symbols [SYMBOL:REVISION ...];
! 161: * locks [LOGIN:REVISION ...];
! 162: * [strict;]
! 163: * [comment [@[...]@];]
! 164: * [expand [@[...]@];]
! 165: */
! 166: static struct rcs_section sec_admin[] = {
! 167: { RCS_TOK_HEAD, rcsparse_head, MANDATORY },
! 168: { RCS_TOK_BRANCH, rcsparse_branch, OPTIONAL },
! 169: { RCS_TOK_ACCESS, rcsparse_access, MANDATORY },
! 170: { RCS_TOK_SYMBOLS, rcsparse_symbols, MANDATORY },
! 171: { RCS_TOK_LOCKS, rcsparse_locks, MANDATORY },
! 172: { RCS_TOK_STRICT, rcsparse_strict, OPTIONAL },
! 173: { RCS_TOK_COMMENT, rcsparse_comment, OPTIONAL },
! 174: { RCS_TOK_EXPAND, rcsparse_expand, OPTIONAL },
! 175: { 0, NULL, 0 }
! 176: };
! 177:
! 178: /*
! 179: * REVISION
! 180: * date [YY]YY.MM.DD.HH.MM.SS;
! 181: * author LOGIN;
! 182: * state STATE;
! 183: * branches [REVISION ...];
! 184: * next [REVISION];
! 185: * [commitid ID;]
! 186: */
! 187: static struct rcs_section sec_delta[] = {
! 188: { RCS_TYPE_REVISION, rcsparse_deltarevision, MANDATORY },
! 189: { RCS_TOK_DATE, rcsparse_date, MANDATORY },
! 190: { RCS_TOK_AUTHOR, rcsparse_author, MANDATORY },
! 191: { RCS_TOK_STATE, rcsparse_state, MANDATORY },
! 192: { RCS_TOK_BRANCHES, rcsparse_branches, MANDATORY },
! 193: { RCS_TOK_NEXT, rcsparse_next, MANDATORY },
! 194: { RCS_TOK_COMMITID, rcsparse_commitid, OPTIONAL },
! 195: { 0, NULL, 0 }
! 196: };
! 197:
! 198: /*
! 199: * REVISION
! 200: * log @[...]@
! 201: * text @[...]@
! 202: */
! 203: static struct rcs_section sec_deltatext[] = {
! 204: { RCS_TYPE_REVISION, rcsparse_textrevision, MANDATORY },
! 205: { RCS_TOK_LOG, rcsparse_log, MANDATORY },
! 206: { RCS_TOK_TEXT, rcsparse_text, MANDATORY },
! 207: { 0, NULL, 0 }
! 208: };
! 209:
! 210: /*
! 211: * rcsparse_init()
! 212: *
! 213: * Initializes the parsing data structure and parses the admin section of
! 214: * RCS file <rfp>.
! 215: *
! 216: * Returns 0 on success or 1 on failure.
! 217: */
! 218: int
! 219: rcsparse_init(RCSFILE *rfp)
! 220: {
! 221: struct rcs_pdata *pdp;
! 222:
! 223: if (rfp->rf_flags & RCS_PARSED)
! 224: return (0);
! 225:
! 226: pdp = xmalloc(sizeof(*pdp));
! 227: pdp->rp_buf = xmalloc(RCS_BUFSIZE);
! 228: pdp->rp_blen = RCS_BUFSIZE;
! 229: pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
! 230: pdp->rp_token = -1;
! 231: pdp->rp_lineno = 1;
! 232: pdp->rp_msglineno = 1;
! 233: if ((pdp->rp_file = fdopen(rfp->rf_fd, "r")) == NULL) {
! 234: xfree(pdp);
! 235: return (1);
! 236: }
! 237: /* ditch the strict lock */
! 238: rfp->rf_flags &= ~RCS_SLOCK;
! 239: rfp->rf_pdata = pdp;
! 240:
! 241: if (rcsparse(rfp, sec_admin)) {
! 242: rcsparse_free(rfp);
! 243: return (1);
! 244: }
! 245:
! 246: if ((rfp->rf_flags & RCS_PARSE_FULLY) &&
! 247: rcsparse_deltatexts(rfp, NULL)) {
! 248: rcsparse_free(rfp);
! 249: return (1);
! 250: }
! 251:
! 252: rfp->rf_flags |= RCS_SYNCED;
! 253: return (0);
! 254: }
! 255:
! 256: /*
! 257: * rcsparse_deltas()
! 258: *
! 259: * Parse deltas. If <rev> is not NULL, parse only as far as that
! 260: * revision. If <rev> is NULL, parse all deltas.
! 261: *
! 262: * Returns 0 on success or 1 on error.
! 263: */
! 264: int
! 265: rcsparse_deltas(RCSFILE *rfp, RCSNUM *rev)
! 266: {
! 267: int ret;
! 268: struct rcs_delta *enddelta;
! 269:
! 270: if ((rfp->rf_flags & PARSED_DELTAS) || (rfp->rf_flags & RCS_CREATE))
! 271: return (0);
! 272:
! 273: for (;;) {
! 274: ret = rcsparse_delta(rfp);
! 275: if (rev != NULL) {
! 276: enddelta = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist);
! 277: if (enddelta == NULL)
! 278: return (1);
! 279:
! 280: if (rcsnum_cmp(enddelta->rd_num, rev, 0) == 0)
! 281: break;
! 282: }
! 283:
! 284: if (ret == 0) {
! 285: rfp->rf_flags |= PARSED_DELTAS;
! 286: break;
! 287: }
! 288: else if (ret == -1)
! 289: return (1);
! 290: }
! 291:
! 292: return (0);
! 293: }
! 294:
! 295: /*
! 296: * rcsparse_deltatexts()
! 297: *
! 298: * Parse deltatexts. If <rev> is not NULL, parse only as far as that
! 299: * revision. If <rev> is NULL, parse everything.
! 300: *
! 301: * Returns 0 on success or 1 on error.
! 302: */
! 303: int
! 304: rcsparse_deltatexts(RCSFILE *rfp, RCSNUM *rev)
! 305: {
! 306: int ret;
! 307: struct rcs_delta *rdp;
! 308:
! 309: if ((rfp->rf_flags & PARSED_DELTATEXTS) ||
! 310: (rfp->rf_flags & RCS_CREATE))
! 311: return (0);
! 312:
! 313: if (!(rfp->rf_flags & PARSED_DESC))
! 314: if (rcsparse_desc(rfp))
! 315: return (1);
! 316: for (;;) {
! 317: if (rev != NULL) {
! 318: rdp = rcs_findrev(rfp, rev);
! 319: if (rdp->rd_text != NULL)
! 320: break;
! 321: else
! 322: ret = rcsparse_deltatext(rfp);
! 323: } else
! 324: ret = rcsparse_deltatext(rfp);
! 325: if (ret == 0) {
! 326: rfp->rf_flags |= PARSED_DELTATEXTS;
! 327: break;
! 328: }
! 329: else if (ret == -1)
! 330: return (1);
! 331: }
! 332:
! 333: return (0);
! 334: }
! 335:
! 336: /*
! 337: * rcsparse_free()
! 338: *
! 339: * Free the contents of the <rfp>'s parser data structure.
! 340: */
! 341: void
! 342: rcsparse_free(RCSFILE *rfp)
! 343: {
! 344: struct rcs_pdata *pdp;
! 345:
! 346: pdp = rfp->rf_pdata;
! 347:
! 348: if (pdp->rp_file != NULL)
! 349: (void)fclose(pdp->rp_file);
! 350: if (pdp->rp_buf != NULL)
! 351: xfree(pdp->rp_buf);
! 352: if (pdp->rp_token == RCS_TYPE_REVISION)
! 353: rcsnum_free(pdp->rp_value.rev);
! 354: xfree(pdp);
! 355: }
! 356:
! 357: /*
! 358: * rcsparse_desc()
! 359: *
! 360: * Parse desc of the RCS file <rfp>. By calling rcsparse_desc, all deltas
! 361: * will be parsed in order to proceed the reading cursor to the desc keyword.
! 362: *
! 363: * desc @[...]@;
! 364: *
! 365: * Returns 0 on success or 1 on error.
! 366: */
! 367: static int
! 368: rcsparse_desc(RCSFILE *rfp)
! 369: {
! 370: struct rcs_pdata *pdp;
! 371:
! 372: if (rfp->rf_flags & PARSED_DESC)
! 373: return (0);
! 374:
! 375: if (!(rfp->rf_flags & PARSED_DELTAS) && rcsparse_deltas(rfp, NULL))
! 376: return (1);
! 377:
! 378: pdp = (struct rcs_pdata *)rfp->rf_pdata;
! 379:
! 380: if (rcsparse_token(rfp, RCS_TOK_DESC) != RCS_TOK_DESC ||
! 381: rcsparse_token(rfp, RCS_TYPE_STRING) != RCS_TYPE_STRING)
! 382: return (1);
! 383:
! 384: rfp->rf_desc = pdp->rp_value.str;
! 385: rfp->rf_flags |= PARSED_DESC;
! 386:
! 387: return (0);
! 388: }
! 389:
! 390: /*
! 391: * rcsparse_deltarevision()
! 392: *
! 393: * Called upon reaching a new REVISION entry in the delta section.
! 394: * A new rcs_delta structure will be prepared in pdp->rp_delta for further
! 395: * parsing.
! 396: *
! 397: * REVISION
! 398: *
! 399: * Always returns 0.
! 400: */
! 401: static int
! 402: rcsparse_deltarevision(RCSFILE *rfp, struct rcs_pdata *pdp)
! 403: {
! 404: struct rcs_delta *rdp;
! 405:
! 406: rdp = xcalloc(1, sizeof(*rdp));
! 407: TAILQ_INIT(&rdp->rd_branches);
! 408: rdp->rd_num = pdp->rp_value.rev;
! 409: pdp->rp_delta = rdp;
! 410:
! 411: return (0);
! 412: }
! 413:
! 414: /*
! 415: * rcsparse_date()
! 416: *
! 417: * Parses the specified date of current delta pdp->rp_delta.
! 418: *
! 419: * date YYYY.MM.DD.HH.MM.SS;
! 420: *
! 421: * Returns 0 on success or 1 on failure.
! 422: */
! 423: static int
! 424: rcsparse_date(RCSFILE *rfp, struct rcs_pdata *pdp)
! 425: {
! 426: if (rcsparse_token(rfp, RCS_TYPE_DATE) != RCS_TYPE_DATE)
! 427: return (1);
! 428:
! 429: pdp->rp_delta->rd_date = pdp->rp_value.date;
! 430:
! 431: return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
! 432: }
! 433:
! 434: /*
! 435: * rcsparse_author()
! 436: *
! 437: * Parses the specified author of current delta pdp->rp_delta.
! 438: *
! 439: * author LOGIN;
! 440: *
! 441: * Returns 0 on success or 1 on failure.
! 442: */
! 443: static int
! 444: rcsparse_author(RCSFILE *rfp, struct rcs_pdata *pdp)
! 445: {
! 446: if (rcsparse_token(rfp, RCS_TYPE_LOGIN) != RCS_TYPE_LOGIN)
! 447: return (1);
! 448:
! 449: pdp->rp_delta->rd_author = pdp->rp_value.str;
! 450:
! 451: return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
! 452: }
! 453:
! 454: /*
! 455: * rcsparse_state()
! 456: *
! 457: * Parses the specified state of current delta pdp->rp_delta.
! 458: *
! 459: * state STATE;
! 460: *
! 461: * Returns 0 on success or 1 on failure.
! 462: */
! 463: static int
! 464: rcsparse_state(RCSFILE *rfp, struct rcs_pdata *pdp)
! 465: {
! 466: if (rcsparse_token(rfp, RCS_TYPE_STATE) != RCS_TYPE_STATE)
! 467: return (1);
! 468:
! 469: pdp->rp_delta->rd_state = pdp->rp_value.str;
! 470:
! 471: return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
! 472: }
! 473:
! 474: /*
! 475: * rcsparse_branches()
! 476: *
! 477: * Parses the specified branches of current delta pdp->rp_delta.
! 478: *
! 479: * branches [REVISION ...];
! 480: *
! 481: * Returns 0 on success or 1 on failure.
! 482: */
! 483: static int
! 484: rcsparse_branches(RCSFILE *rfp, struct rcs_pdata *pdp)
! 485: {
! 486: struct rcs_branch *rb;
! 487: int type;
! 488:
! 489: while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_REVISION))
! 490: == RCS_TYPE_REVISION) {
! 491: rb = xmalloc(sizeof(*rb));
! 492: rb->rb_num = pdp->rp_value.rev;
! 493: TAILQ_INSERT_TAIL(&(pdp->rp_delta->rd_branches), rb, rb_list);
! 494: }
! 495:
! 496: return (type != RCS_TOK_SCOLON);
! 497: }
! 498:
! 499: /*
! 500: * rcsparse_next()
! 501: *
! 502: * Parses the specified next revision of current delta pdp->rp_delta.
! 503: *
! 504: * next [REVISION];
! 505: *
! 506: * Returns 0 on success or 1 on failure.
! 507: */
! 508: static int
! 509: rcsparse_next(RCSFILE *rfp, struct rcs_pdata *pdp)
! 510: {
! 511: int type;
! 512:
! 513: type = rcsparse_token(rfp, RCS_TYPE_REVISION|RCS_TOK_SCOLON);
! 514: if (type == RCS_TYPE_REVISION) {
! 515: pdp->rp_delta->rd_next = pdp->rp_value.rev;
! 516: type = rcsparse_token(rfp, RCS_TOK_SCOLON);
! 517: } else
! 518: pdp->rp_delta->rd_next = rcsnum_alloc();
! 519:
! 520: return (type != RCS_TOK_SCOLON);
! 521: }
! 522:
! 523: /*
! 524: * rcsparse_commitid()
! 525: *
! 526: * Parses the specified commit id of current delta pdp->rp_delta. The
! 527: * commitid keyword is optional and can be omitted.
! 528: *
! 529: * [commitid ID;]
! 530: *
! 531: * Returns 0 on success or 1 on failure.
! 532: */
! 533: static int
! 534: rcsparse_commitid(RCSFILE *rfp, struct rcs_pdata *pdp)
! 535: {
! 536: if (rcsparse_token(rfp, RCS_TYPE_COMMITID) != RCS_TYPE_COMMITID)
! 537: return (1);
! 538:
! 539: /* XXX - do something with commitid */
! 540:
! 541: return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
! 542: }
! 543:
! 544: /*
! 545: * rcsparse_textrevision()
! 546: *
! 547: * Called upon reaching a new REVISION entry in the delta text section.
! 548: * pdp->rp_delta will be set to REVISION's delta (created in delta section)
! 549: * for further parsing.
! 550: *
! 551: * REVISION
! 552: *
! 553: * Returns 0 on success or 1 on failure.
! 554: */
! 555: static int
! 556: rcsparse_textrevision(RCSFILE *rfp, struct rcs_pdata *pdp)
! 557: {
! 558: struct rcs_delta *rdp;
! 559:
! 560: TAILQ_FOREACH(rdp, &rfp->rf_delta, rd_list) {
! 561: if (rcsnum_cmp(rdp->rd_num, pdp->rp_value.rev, 0) == 0)
! 562: break;
! 563: }
! 564: if (rdp == NULL) {
! 565: rcsparse_warnx(rfp, "delta for revision \"%s\" not found",
! 566: pdp->rp_buf);
! 567: rcsnum_free(pdp->rp_value.rev);
! 568: return (1);
! 569: }
! 570: pdp->rp_delta = rdp;
! 571:
! 572: rcsnum_free(pdp->rp_value.rev);
! 573: return (0);
! 574: }
! 575:
! 576: /*
! 577: * rcsparse_log()
! 578: *
! 579: * Parses the specified log of current deltatext pdp->rp_delta.
! 580: *
! 581: * log @[...]@
! 582: *
! 583: * Returns 0 on success or 1 on failure.
! 584: */
! 585: static int
! 586: rcsparse_log(RCSFILE *rfp, struct rcs_pdata *pdp)
! 587: {
! 588: if (rcsparse_token(rfp, RCS_TYPE_STRING) != RCS_TYPE_STRING)
! 589: return (1);
! 590:
! 591: pdp->rp_delta->rd_log = pdp->rp_value.str;
! 592:
! 593: return (0);
! 594: }
! 595:
! 596: /*
! 597: * rcsparse_text()
! 598: *
! 599: * Parses the specified text of current deltatext pdp->rp_delta.
! 600: *
! 601: * text @[...]@
! 602: *
! 603: * Returns 0 on success or 1 on failure.
! 604: */
! 605: static int
! 606: rcsparse_text(RCSFILE *rfp, struct rcs_pdata *pdp)
! 607: {
! 608: if (rcsparse_token(rfp, RCS_TYPE_STRING) != RCS_TYPE_STRING)
! 609: return (1);
! 610:
! 611: pdp->rp_delta->rd_tlen = pdp->rp_tlen - 1;
! 612: if (pdp->rp_delta->rd_tlen == 0) {
! 613: pdp->rp_delta->rd_text = xstrdup("");
! 614: } else {
! 615: pdp->rp_delta->rd_text = xmalloc(pdp->rp_delta->rd_tlen);
! 616: memcpy(pdp->rp_delta->rd_text, pdp->rp_buf,
! 617: pdp->rp_delta->rd_tlen);
! 618: }
! 619: xfree(pdp->rp_value.str);
! 620:
! 621: return (0);
! 622: }
! 623:
! 624: /*
! 625: * rcsparse_head()
! 626: *
! 627: * Parses the head revision of RCS file <rfp>.
! 628: *
! 629: * head [REVISION];
! 630: *
! 631: * Returns 0 on success or 1 on failure.
! 632: */
! 633: static int
! 634: rcsparse_head(RCSFILE *rfp, struct rcs_pdata *pdp)
! 635: {
! 636: int type;
! 637:
! 638: type = rcsparse_token(rfp, RCS_TYPE_REVISION|RCS_TOK_SCOLON);
! 639: if (type == RCS_TYPE_REVISION) {
! 640: rfp->rf_head = pdp->rp_value.rev;
! 641: type = rcsparse_token(rfp, RCS_TOK_SCOLON);
! 642: }
! 643:
! 644: return (type != RCS_TOK_SCOLON);
! 645: }
! 646:
! 647: /*
! 648: * rcsparse_branch()
! 649: *
! 650: * Parses the default branch of RCS file <rfp>. The branch keyword is
! 651: * optional and can be omitted.
! 652: *
! 653: * [branch BRANCH;]
! 654: *
! 655: * Returns 0 on success or 1 on failure.
! 656: */
! 657: static int
! 658: rcsparse_branch(RCSFILE *rfp, struct rcs_pdata *pdp)
! 659: {
! 660: int type;
! 661:
! 662: type = rcsparse_token(rfp, RCS_TYPE_BRANCH|RCS_TOK_SCOLON);
! 663: if (type == RCS_TYPE_BRANCH) {
! 664: rfp->rf_branch = pdp->rp_value.rev;
! 665: type = rcsparse_token(rfp, RCS_TOK_SCOLON);
! 666: }
! 667:
! 668: return (type != RCS_TOK_SCOLON);
! 669: }
! 670:
! 671: /*
! 672: * rcsparse_access()
! 673: *
! 674: * Parses the access list of RCS file <rfp>.
! 675: *
! 676: * access [LOGIN ...];
! 677: *
! 678: * Returns 0 on success or 1 on failure.
! 679: */
! 680: static int
! 681: rcsparse_access(RCSFILE *rfp, struct rcs_pdata *pdp)
! 682: {
! 683: struct rcs_access *ap;
! 684: int type;
! 685:
! 686: while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_LOGIN))
! 687: == RCS_TYPE_LOGIN) {
! 688: ap = xmalloc(sizeof(*ap));
! 689: ap->ra_name = pdp->rp_value.str;
! 690: TAILQ_INSERT_TAIL(&(rfp->rf_access), ap, ra_list);
! 691: }
! 692:
! 693: return (type != RCS_TOK_SCOLON);
! 694: }
! 695:
! 696: /*
! 697: * rcsparse_symbols()
! 698: *
! 699: * Parses the symbol list of RCS file <rfp>.
! 700: *
! 701: * symbols [SYMBOL:REVISION ...];
! 702: *
! 703: * Returns 0 on success or 1 on failure.
! 704: */
! 705: static int
! 706: rcsparse_symbols(RCSFILE *rfp, struct rcs_pdata *pdp)
! 707: {
! 708: struct rcs_sym *symp;
! 709: char *name;
! 710: int type;
! 711:
! 712: while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_SYMBOL)) ==
! 713: RCS_TYPE_SYMBOL) {
! 714: name = pdp->rp_value.str;
! 715: if (rcsparse_token(rfp, RCS_TOK_COLON) != RCS_TOK_COLON ||
! 716: rcsparse_token(rfp, RCS_TYPE_NUMBER) != RCS_TYPE_NUMBER) {
! 717: xfree(name);
! 718: return (1);
! 719: }
! 720: symp = xmalloc(sizeof(*symp));
! 721: symp->rs_name = name;
! 722: symp->rs_num = pdp->rp_value.rev;
! 723: TAILQ_INSERT_TAIL(&(rfp->rf_symbols), symp, rs_list);
! 724: }
! 725:
! 726: return (type != RCS_TOK_SCOLON);
! 727: }
! 728:
! 729: /*
! 730: * rcsparse_locks()
! 731: *
! 732: * Parses the lock list of RCS file <rfp>.
! 733: *
! 734: * locks [SYMBOL:REVISION ...];
! 735: *
! 736: * Returns 0 on success or 1 on failure.
! 737: */
! 738: static int
! 739: rcsparse_locks(RCSFILE *rfp, struct rcs_pdata *pdp)
! 740: {
! 741: struct rcs_lock *lkp;
! 742: char *name;
! 743: int type;
! 744:
! 745: while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_LOGIN)) ==
! 746: RCS_TYPE_LOGIN) {
! 747: name = pdp->rp_value.str;
! 748: if (rcsparse_token(rfp, RCS_TOK_COLON) != RCS_TOK_COLON ||
! 749: rcsparse_token(rfp, RCS_TYPE_REVISION) !=
! 750: RCS_TYPE_REVISION) {
! 751: xfree(name);
! 752: return (1);
! 753: }
! 754: lkp = xmalloc(sizeof(*lkp));
! 755: lkp->rl_name = name;
! 756: lkp->rl_num = pdp->rp_value.rev;
! 757: TAILQ_INSERT_TAIL(&(rfp->rf_locks), lkp, rl_list);
! 758: }
! 759:
! 760: return (type != RCS_TOK_SCOLON);
! 761: }
! 762:
! 763: /*
! 764: * rcsparse_locks()
! 765: *
! 766: * Parses the strict keyword of RCS file <rfp>. The strict keyword is
! 767: * optional and can be omitted.
! 768: *
! 769: * [strict;]
! 770: *
! 771: * Returns 0 on success or 1 on failure.
! 772: */
! 773: static int
! 774: rcsparse_strict(RCSFILE *rfp, struct rcs_pdata *pdp)
! 775: {
! 776: rfp->rf_flags |= RCS_SLOCK;
! 777:
! 778: return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
! 779: }
! 780:
! 781: /*
! 782: * rcsparse_comment()
! 783: *
! 784: * Parses the comment of RCS file <rfp>. The comment keyword is optional
! 785: * and can be omitted.
! 786: *
! 787: * [comment [@[...]@];]
! 788: *
! 789: * Returns 0 on success or 1 on failure.
! 790: */
! 791: static int
! 792: rcsparse_comment(RCSFILE *rfp, struct rcs_pdata *pdp)
! 793: {
! 794: int type;
! 795:
! 796: type = rcsparse_token(rfp, RCS_TYPE_STRING|RCS_TOK_SCOLON);
! 797: if (type == RCS_TYPE_STRING) {
! 798: rfp->rf_comment = pdp->rp_value.str;
! 799: type = rcsparse_token(rfp, RCS_TOK_SCOLON);
! 800: }
! 801:
! 802: return (type != RCS_TOK_SCOLON);
! 803: }
! 804:
! 805: /*
! 806: * rcsparse_expand()
! 807: *
! 808: * Parses expand of RCS file <rfp>. The expand keyword is optional and
! 809: * can be omitted.
! 810: *
! 811: * [expand [@[...]@];]
! 812: *
! 813: * Returns 0 on success or 1 on failure.
! 814: */
! 815: static int
! 816: rcsparse_expand(RCSFILE *rfp, struct rcs_pdata *pdp)
! 817: {
! 818: int type;
! 819:
! 820: type = rcsparse_token(rfp, RCS_TYPE_STRING|RCS_TOK_SCOLON);
! 821: if (type == RCS_TYPE_STRING) {
! 822: rfp->rf_expand = pdp->rp_value.str;
! 823: type = rcsparse_token(rfp, RCS_TOK_SCOLON);
! 824: }
! 825:
! 826: return (type != RCS_TOK_SCOLON);
! 827: }
! 828:
! 829: #define RBUF_PUTC(ch) \
! 830: do { \
! 831: if (bp == pdp->rp_bufend - 1) { \
! 832: len = bp - pdp->rp_buf; \
! 833: rcsparse_growbuf(rfp); \
! 834: bp = pdp->rp_buf + len; \
! 835: } \
! 836: *(bp++) = (ch); \
! 837: pdp->rp_tlen++; \
! 838: } while (0);
! 839:
! 840: static int
! 841: rcsparse_string(RCSFILE *rfp, int allowed)
! 842: {
! 843: struct rcs_pdata *pdp;
! 844: int c;
! 845: size_t len;
! 846: char *bp;
! 847:
! 848: pdp = (struct rcs_pdata *)rfp->rf_pdata;
! 849:
! 850: bp = pdp->rp_buf;
! 851: pdp->rp_tlen = 0;
! 852: *bp = '\0';
! 853:
! 854: for (;;) {
! 855: c = getc(pdp->rp_file);
! 856: if (c == '@') {
! 857: c = getc(pdp->rp_file);
! 858: if (c == EOF) {
! 859: return (EOF);
! 860: } else if (c != '@') {
! 861: ungetc(c, pdp->rp_file);
! 862: break;
! 863: }
! 864: }
! 865:
! 866: if (c == EOF) {
! 867: return (EOF);
! 868: } else if (c == '\n')
! 869: pdp->rp_lineno++;
! 870:
! 871: RBUF_PUTC(c);
! 872: }
! 873:
! 874: bp = pdp->rp_buf + pdp->rp_tlen;
! 875: RBUF_PUTC('\0');
! 876:
! 877: if (!(allowed & RCS_TYPE_STRING)) {
! 878: rcsparse_warnx(rfp, "unexpected RCS string");
! 879: return (0);
! 880: }
! 881:
! 882: pdp->rp_value.str = xstrdup(pdp->rp_buf);
! 883:
! 884: return (RCS_TYPE_STRING);
! 885: }
! 886:
! 887: static int
! 888: rcsparse_token(RCSFILE *rfp, int allowed)
! 889: {
! 890: const struct rcs_keyword *p;
! 891: struct rcs_pdata *pdp;
! 892: int c, pre, ret, type;
! 893: char *bp;
! 894: size_t len;
! 895: RCSNUM *datenum;
! 896:
! 897: pdp = (struct rcs_pdata *)rfp->rf_pdata;
! 898:
! 899: if (pdp->rp_token != -1) {
! 900: /* no need to check for allowed here */
! 901: type = pdp->rp_token;
! 902: pdp->rp_token = -1;
! 903: return (type);
! 904: }
! 905:
! 906: /* skip whitespaces */
! 907: c = EOF;
! 908: do {
! 909: pre = c;
! 910: c = getc(pdp->rp_file);
! 911: if (c == EOF) {
! 912: if (ferror(pdp->rp_file)) {
! 913: rcsparse_warnx(rfp, "error during parsing");
! 914: return (0);
! 915: }
! 916: if (pre != '\n')
! 917: rcsparse_warnx(rfp,
! 918: "no newline at end of file");
! 919: return (EOF);
! 920: } else if (c == '\n')
! 921: pdp->rp_lineno++;
! 922: } while (isspace(c));
! 923:
! 924: pdp->rp_msglineno = pdp->rp_lineno;
! 925: type = 0;
! 926: switch (c) {
! 927: case '@':
! 928: ret = rcsparse_string(rfp, allowed);
! 929: if (ret == EOF && ferror(pdp->rp_file)) {
! 930: rcsparse_warnx(rfp, "error during parsing");
! 931: return (0);
! 932: }
! 933: return (ret);
! 934: /* NOTREACHED */
! 935: case ':':
! 936: type = RCS_TOK_COLON;
! 937: if (type & allowed)
! 938: return (type);
! 939: rcsparse_warnx(rfp, "unexpected token \"%c\"", c);
! 940: return (0);
! 941: /* NOTREACHED */
! 942: case ';':
! 943: type = RCS_TOK_SCOLON;
! 944: if (type & allowed)
! 945: return (type);
! 946: rcsparse_warnx(rfp, "unexpected token \"%c\"", c);
! 947: return (0);
! 948: /* NOTREACHED */
! 949: case ',':
! 950: type = RCS_TOK_COMMA;
! 951: if (type & allowed)
! 952: return (type);
! 953: rcsparse_warnx(rfp, "unexpected token \"%c\"", c);
! 954: return (0);
! 955: /* NOTREACHED */
! 956: default:
! 957: if (!isgraph(c)) {
! 958: rcsparse_warnx(rfp, "unexpected character 0x%.2X", c);
! 959: return (0);
! 960: }
! 961: break;
! 962: }
! 963: allowed &= ~(RCS_TOK_COLON|RCS_TOK_SCOLON|RCS_TOK_COMMA);
! 964:
! 965: bp = pdp->rp_buf;
! 966: pdp->rp_tlen = 0;
! 967: *bp = '\0';
! 968:
! 969: for (;;) {
! 970: if (c == EOF) {
! 971: if (ferror(pdp->rp_file))
! 972: rcsparse_warnx(rfp, "error during parsing");
! 973: else
! 974: rcsparse_warnx(rfp, "unexpected end of file");
! 975: return (0);
! 976: } else if (c == '\n')
! 977: pdp->rp_lineno++;
! 978:
! 979: RBUF_PUTC(c);
! 980:
! 981: c = getc(pdp->rp_file);
! 982:
! 983: if (isspace(c)) {
! 984: if (c == '\n')
! 985: pdp->rp_lineno++;
! 986: RBUF_PUTC('\0');
! 987: break;
! 988: } else if (c == ';' || c == ':' || c == ',') {
! 989: ungetc(c, pdp->rp_file);
! 990: RBUF_PUTC('\0');
! 991: break;
! 992: } else if (!isgraph(c)) {
! 993: rcsparse_warnx(rfp, "unexpected character 0x%.2X", c);
! 994: return (0);
! 995: }
! 996: }
! 997:
! 998: switch (allowed) {
! 999: case RCS_TYPE_COMMITID:
! 1000: /* XXX validate commitd */
! 1001: break;
! 1002: case RCS_TYPE_LOGIN:
! 1003: if (!valid_login(pdp->rp_buf)) {
! 1004: rcsparse_warnx(rfp, "invalid login \"%s\"",
! 1005: pdp->rp_buf);
! 1006: return (0);
! 1007: }
! 1008: pdp->rp_value.str = xstrdup(pdp->rp_buf);
! 1009: break;
! 1010: case RCS_TYPE_SYMBOL:
! 1011: if (!rcs_sym_check(pdp->rp_buf)) {
! 1012: rcsparse_warnx(rfp, "invalid symbol \"%s\"",
! 1013: pdp->rp_buf);
! 1014: return (0);
! 1015: }
! 1016: pdp->rp_value.str = xstrdup(pdp->rp_buf);
! 1017: break;
! 1018: /* FALLTHROUGH */
! 1019: case RCS_TYPE_STATE:
! 1020: if (rcs_state_check(pdp->rp_buf)) {
! 1021: rcsparse_warnx(rfp, "invalid state \"%s\"",
! 1022: pdp->rp_buf);
! 1023: return (0);
! 1024: }
! 1025: pdp->rp_value.str = xstrdup(pdp->rp_buf);
! 1026: break;
! 1027: case RCS_TYPE_DATE:
! 1028: if ((datenum = rcsnum_parse(pdp->rp_buf)) == NULL) {
! 1029: rcsparse_warnx(rfp, "invalid date \"%s\"", pdp->rp_buf);
! 1030: return (0);
! 1031: }
! 1032: if (datenum->rn_len != 6) {
! 1033: rcsnum_free(datenum);
! 1034: rcsparse_warnx(rfp, "invalid date \"%s\"", pdp->rp_buf);
! 1035: return (0);
! 1036: }
! 1037: pdp->rp_value.date.tm_year = datenum->rn_id[0];
! 1038: if (pdp->rp_value.date.tm_year >= 1900)
! 1039: pdp->rp_value.date.tm_year -= 1900;
! 1040: pdp->rp_value.date.tm_mon = datenum->rn_id[1] - 1;
! 1041: pdp->rp_value.date.tm_mday = datenum->rn_id[2];
! 1042: pdp->rp_value.date.tm_hour = datenum->rn_id[3];
! 1043: pdp->rp_value.date.tm_min = datenum->rn_id[4];
! 1044: pdp->rp_value.date.tm_sec = datenum->rn_id[5];
! 1045: rcsnum_free(datenum);
! 1046: break;
! 1047: case RCS_TYPE_NUMBER:
! 1048: pdp->rp_value.rev = rcsnum_parse(pdp->rp_buf);
! 1049: if (pdp->rp_value.rev == NULL) {
! 1050: rcsparse_warnx(rfp, "invalid number \"%s\"",
! 1051: pdp->rp_buf);
! 1052: return (0);
! 1053: }
! 1054: break;
! 1055: case RCS_TYPE_BRANCH:
! 1056: pdp->rp_value.rev = rcsnum_parse(pdp->rp_buf);
! 1057: if (pdp->rp_value.rev == NULL) {
! 1058: rcsparse_warnx(rfp, "invalid branch \"%s\"",
! 1059: pdp->rp_buf);
! 1060: return (0);
! 1061: }
! 1062: if (!RCSNUM_ISBRANCH(pdp->rp_value.rev)) {
! 1063: rcsnum_free(pdp->rp_value.rev);
! 1064: rcsparse_warnx(rfp, "expected branch, got \"%s\"",
! 1065: pdp->rp_buf);
! 1066: return (0);
! 1067: }
! 1068: break;
! 1069: case RCS_TYPE_KEYWORD:
! 1070: if (islower(*pdp->rp_buf)) {
! 1071: p = bsearch(pdp->rp_buf, keywords,
! 1072: sizeof(keywords) / sizeof(keywords[0]),
! 1073: sizeof(keywords[0]), kw_cmp);
! 1074: if (p != NULL)
! 1075: return (p->k_val);
! 1076: }
! 1077: allowed = RCS_TYPE_REVISION;
! 1078: /* FALLTHROUGH */
! 1079: case RCS_TYPE_REVISION:
! 1080: pdp->rp_value.rev = rcsnum_parse(pdp->rp_buf);
! 1081: if (pdp->rp_value.rev != NULL) {
! 1082: if (RCSNUM_ISBRANCH(pdp->rp_value.rev)) {
! 1083: rcsnum_free(pdp->rp_value.rev);
! 1084: rcsparse_warnx(rfp,
! 1085: "expected revision, got \"%s\"",
! 1086: pdp->rp_buf);
! 1087: return (0);
! 1088: }
! 1089: break;
! 1090: }
! 1091: /* FALLTHROUGH */
! 1092: default:
! 1093: RBUF_PUTC('\0');
! 1094: rcsparse_warnx(rfp, "unexpected token \"%s\"", pdp->rp_buf);
! 1095: return (0);
! 1096: /* NOTREACHED */
! 1097: }
! 1098:
! 1099: return (allowed);
! 1100: }
! 1101:
! 1102: static int
! 1103: rcsparse(RCSFILE *rfp, struct rcs_section *sec)
! 1104: {
! 1105: struct rcs_pdata *pdp;
! 1106: int i, token;
! 1107:
! 1108: pdp = (struct rcs_pdata *)rfp->rf_pdata;
! 1109: i = 0;
! 1110:
! 1111: token = 0;
! 1112: for (i = 0; sec[i].token != 0; i++) {
! 1113: token = rcsparse_token(rfp, RCS_TYPE_KEYWORD);
! 1114: if (token == 0)
! 1115: return (1);
! 1116:
! 1117: while (token != sec[i].token) {
! 1118: if (sec[i].parse == NULL)
! 1119: goto end;
! 1120: if (sec[i].opt) {
! 1121: i++;
! 1122: continue;
! 1123: }
! 1124: if (token == EOF || (!(rfp->rf_flags & PARSED_DELTAS) &&
! 1125: token == RCS_TOK_DESC))
! 1126: goto end;
! 1127: rcsparse_warnx(rfp, "unexpected token \"%s\"",
! 1128: pdp->rp_buf);
! 1129: return (1);
! 1130: }
! 1131:
! 1132: if (sec[i].parse(rfp, pdp))
! 1133: return (1);
! 1134: }
! 1135: end:
! 1136: if (token == RCS_TYPE_REVISION)
! 1137: pdp->rp_token = token;
! 1138: else if (token == RCS_TOK_DESC)
! 1139: pdp->rp_token = RCS_TOK_DESC;
! 1140: else if (token == EOF)
! 1141: rfp->rf_flags |= RCS_PARSED;
! 1142:
! 1143: return (0);
! 1144: }
! 1145:
! 1146: static int
! 1147: rcsparse_deltatext(RCSFILE *rfp)
! 1148: {
! 1149: struct rcs_pdata *pdp;
! 1150: int ret;
! 1151:
! 1152: if (rfp->rf_flags & PARSED_DELTATEXTS)
! 1153: return (0);
! 1154:
! 1155: if (!(rfp->rf_flags & PARSED_DESC))
! 1156: if ((ret = rcsparse_desc(rfp)))
! 1157: return (ret);
! 1158:
! 1159: pdp = (struct rcs_pdata *)rfp->rf_pdata;
! 1160:
! 1161: if (rcsparse(rfp, sec_deltatext))
! 1162: return (-1);
! 1163:
! 1164: if (rfp->rf_flags & RCS_PARSED)
! 1165: rfp->rf_flags |= PARSED_DELTATEXTS;
! 1166:
! 1167: return (1);
! 1168: }
! 1169:
! 1170: static int
! 1171: rcsparse_delta(RCSFILE *rfp)
! 1172: {
! 1173: struct rcs_pdata *pdp;
! 1174:
! 1175: if (rfp->rf_flags & PARSED_DELTAS)
! 1176: return (0);
! 1177:
! 1178: pdp = (struct rcs_pdata *)rfp->rf_pdata;
! 1179: if (pdp->rp_token == RCS_TOK_DESC) {
! 1180: rfp->rf_flags |= PARSED_DELTAS;
! 1181: return (0);
! 1182: }
! 1183:
! 1184: if (rcsparse(rfp, sec_delta))
! 1185: return (-1);
! 1186:
! 1187: if (pdp->rp_delta != NULL) {
! 1188: TAILQ_INSERT_TAIL(&rfp->rf_delta, pdp->rp_delta, rd_list);
! 1189: pdp->rp_delta = NULL;
! 1190: rfp->rf_ndelta++;
! 1191: return (1);
! 1192: }
! 1193:
! 1194: return (0);
! 1195: }
! 1196:
! 1197: /*
! 1198: * rcsparse_growbuf()
! 1199: *
! 1200: * Attempt to grow the internal parse buffer for the RCS file <rf> by
! 1201: * RCS_BUFEXTSIZE.
! 1202: * In case of failure, the original buffer is left unmodified.
! 1203: */
! 1204: static void
! 1205: rcsparse_growbuf(RCSFILE *rfp)
! 1206: {
! 1207: struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
! 1208:
! 1209: pdp->rp_buf = xrealloc(pdp->rp_buf, 1,
! 1210: pdp->rp_blen + RCS_BUFEXTSIZE);
! 1211: pdp->rp_blen += RCS_BUFEXTSIZE;
! 1212: pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
! 1213: }
! 1214:
! 1215: /*
! 1216: * Borrowed from src/usr.sbin/user/user.c:
! 1217: * return 1 if `login' is a valid login name
! 1218: */
! 1219: static int
! 1220: valid_login(char *login_name)
! 1221: {
! 1222: unsigned char *cp;
! 1223:
! 1224: /* The first character cannot be a hyphen */
! 1225: if (*login_name == '-')
! 1226: return 0;
! 1227:
! 1228: for (cp = login_name ; *cp ; cp++) {
! 1229: /* We allow '$' as the last character for samba */
! 1230: if (!isalnum(*cp) && *cp != '.' && *cp != '_' && *cp != '-' &&
! 1231: !(*cp == '$' && *(cp + 1) == '\0')) {
! 1232: return 0;
! 1233: }
! 1234: }
! 1235: if ((char *)cp - login_name > _PW_NAME_LEN)
! 1236: return 0;
! 1237: return 1;
! 1238: }
! 1239:
! 1240: static int
! 1241: kw_cmp(const void *k, const void *e)
! 1242: {
! 1243: return (strcmp(k, ((const struct rcs_keyword *)e)->k_name));
! 1244: }
! 1245:
! 1246: static void
! 1247: rcsparse_warnx(RCSFILE *rfp, char *fmt, ...)
! 1248: {
! 1249: struct rcs_pdata *pdp;
! 1250: va_list ap;
! 1251: char *nfmt;
! 1252:
! 1253: pdp = (struct rcs_pdata *)rfp->rf_pdata;
! 1254: va_start(ap, fmt);
! 1255: if (asprintf(&nfmt, "%s:%d: %s", rfp->rf_path, pdp->rp_msglineno, fmt)
! 1256: == -1)
! 1257: nfmt = fmt;
! 1258: cvs_vlog(LP_ERR, nfmt, ap);
! 1259: va_end(ap);
! 1260: if (nfmt != fmt)
! 1261: free(nfmt);
! 1262: }