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