Annotation of src/usr.bin/cvs/hist.c, Revision 1.1
1.1 ! jfb 1: /* $OpenBSD$ */
! 2: /*
! 3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
! 4: * All rights reserved.
! 5: *
! 6: * Redistribution and use in source and binary forms, with or without
! 7: * modification, are permitted provided that the following conditions
! 8: * are met:
! 9: *
! 10: * 1. Redistributions of source code must retain the above copyright
! 11: * notice, this list of conditions and the following disclaimer.
! 12: * 2. The name of the author may not be used to endorse or promote products
! 13: * derived from this software without specific prior written permission.
! 14: *
! 15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
! 16: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
! 17: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
! 18: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
! 19: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
! 20: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
! 21: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
! 22: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
! 23: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
! 24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 25: */
! 26:
! 27: #include <sys/param.h>
! 28: #include <sys/stat.h>
! 29:
! 30: #include <stdio.h>
! 31: #include <fcntl.h>
! 32: #include <stdlib.h>
! 33: #include <unistd.h>
! 34: #include <string.h>
! 35:
! 36: #include "log.h"
! 37: #include "cvs.h"
! 38:
! 39: #define CVS_HIST_BUFSIZE 8192
! 40:
! 41:
! 42:
! 43: static int cvs_hist_fillbuf (CVSHIST *);
! 44: static int cvs_hist_fmt (const struct cvs_hent *, char *, size_t);
! 45:
! 46:
! 47:
! 48:
! 49:
! 50: /*
! 51: * cvs_hist_open()
! 52: *
! 53: * Open a CVS history file.
! 54: * Returns the number of entries in the file on success, or -1 on error.
! 55: */
! 56:
! 57: CVSHIST*
! 58: cvs_hist_open(const char *path)
! 59: {
! 60: CVSHIST *histp;
! 61:
! 62: histp = (CVSHIST *)malloc(sizeof(*histp));
! 63: if (histp == NULL) {
! 64: cvs_log(LP_ERRNO, "failed to allocate CVS history");
! 65: return (NULL);
! 66: }
! 67: memset(histp, 0, sizeof(*histp));
! 68:
! 69: histp->chf_buf = (char *)malloc(CVS_HIST_BUFSIZE);
! 70: if (histp->chf_buf == NULL) {
! 71: cvs_log(LP_ERRNO,
! 72: "failed to allocate CVS history parse buffer");
! 73: free(histp);
! 74: return (NULL);
! 75: }
! 76: histp->chf_blen = CVS_HIST_BUFSIZE;
! 77: histp->chf_off = 0;
! 78:
! 79: histp->chf_sindex = 0;
! 80: histp->chf_cindex = 0;
! 81: histp->chf_nbhent = 0;
! 82:
! 83: histp->chf_fd = open(path, O_RDONLY, 0);
! 84: if (histp->chf_fd == -1) {
! 85: cvs_log(LP_ERRNO,
! 86: "failed to open CVS history file `%s'", path);
! 87: free(histp->chf_buf);
! 88: free(histp);
! 89: return (NULL);
! 90: }
! 91:
! 92: cvs_hist_fillbuf(histp);
! 93:
! 94: return (histp);
! 95: }
! 96:
! 97:
! 98: /*
! 99: * cvs_hist_close()
! 100: *
! 101: * Close the CVS history file previously opened by a call to cvs_hist_open()
! 102: */
! 103:
! 104: void
! 105: cvs_hist_close(CVSHIST *histp)
! 106: {
! 107: if (histp->chf_fd >= 0)
! 108: (void)close(histp->chf_fd);
! 109: free(histp->chf_buf);
! 110: free(histp);
! 111: }
! 112:
! 113:
! 114: /*
! 115: * cvs_hist_getnext()
! 116: *
! 117: * Get the next entry from the history file <histp>. Whenever using this
! 118: * function, it should be assumed that the return value of the previous call
! 119: * to cvs_hist_getnext() is now invalid.
! 120: * Returns the next entry from the file on success, or NULL on failure or if
! 121: * no entries are left.
! 122: */
! 123:
! 124: struct cvs_hent*
! 125: cvs_hist_getnext(CVSHIST *histp)
! 126: {
! 127: if (histp->chf_cindex == histp->chf_nbhent) {
! 128: /* no more cached entries, refill buf and parse */
! 129: cvs_hist_fillbuf(histp);
! 130: cvs_hist_parse(histp);
! 131: }
! 132: return (&(histp->chf_hent[histp->chf_cindex++]));
! 133: }
! 134:
! 135:
! 136: /*
! 137: * cvs_hist_append()
! 138: *
! 139: * Append a history entry to the history file <histp>. The file offset is
! 140: * first set to the end of the file.
! 141: * Returns 0 on success, or -1 on failure.
! 142: */
! 143:
! 144: int
! 145: cvs_hist_append(CVSHIST *histp, struct cvs_hent *hentp)
! 146: {
! 147: char hbuf[128];
! 148:
! 149: if (cvs_hist_fmt(hentp, hbuf, sizeof(hbuf)) < 0) {
! 150: cvs_log(LP_ERR, "failed to append CVS history entry");
! 151: return (-1);
! 152: }
! 153:
! 154: /* position ourself at the end */
! 155: if (lseek(histp->chf_fd, (off_t)0, SEEK_END) == -1) {
! 156: cvs_log(LP_ERRNO, "failed to seek to end of CVS history file");
! 157: return (-1);
! 158: }
! 159:
! 160: if (write(histp->chf_fd, hbuf, strlen(hbuf)) == -1) {
! 161: cvs_log(LP_ERR, "failed to write CVS history entry to file");
! 162: return (-1);
! 163: }
! 164:
! 165: return (0);
! 166: }
! 167:
! 168:
! 169:
! 170: /*
! 171: * cvs_hist_fillbuf()
! 172: *
! 173: * Fill the history file's internal buffer for future parsing.
! 174: */
! 175:
! 176: static int
! 177: cvs_hist_fillbuf(CVSHIST *histp)
! 178: {
! 179: ssize_t ret;
! 180:
! 181: /* reposition ourself in case we're missing the start of a record */
! 182: if (lseek(histp->chf_fd, histp->chf_off, SEEK_SET) == -1) {
! 183: cvs_log(LP_ERRNO, "failed to seek in CVS history file");
! 184: return (-1);
! 185: }
! 186: ret = read(histp->chf_fd, histp->chf_buf, histp->chf_blen);
! 187: if (ret == -1) {
! 188: cvs_log(LP_ERRNO, "failed to buffer CVS history file");
! 189: return (-1);
! 190: }
! 191: else {
! 192: histp->chf_bused = (size_t)ret;
! 193: }
! 194:
! 195: return (ret);
! 196: }
! 197:
! 198:
! 199: /*
! 200: * cvs_hist_parse()
! 201: *
! 202: * Parse the current contents of the internal buffer of <histp> and regenerate
! 203: * the buffered history entries.
! 204: * Returns the number of entries parsed on success, or -1 on failure.
! 205: */
! 206:
! 207: int
! 208: cvs_hist_parse(CVSHIST *histp)
! 209: {
! 210: u_int i, fld;
! 211: char *fields[CVS_HIST_NBFLD], *sp, *bep, *ep, *errp;
! 212:
! 213: sp = histp->chf_buf;
! 214: bep = histp->chf_buf + histp->chf_bused - 1;
! 215:
! 216: for (i = 0; i < CVS_HIST_CACHE; i++) {
! 217: ep = memchr(sp, '\n', bep - sp);
! 218: if (ep == NULL) {
! 219: /*
! 220: * No record or incomplete record left to parse,
! 221: * so adjust the next read offset in consequence.
! 222: */
! 223: histp->chf_off += (off_t)(sp - histp->chf_buf);
! 224: break;
! 225: }
! 226: else if (ep == bep) {
! 227: histp->chf_off += (off_t)histp->chf_bused;
! 228: }
! 229: *(ep++) = '\0';
! 230:
! 231: printf("hist(%s)\n", sp);
! 232:
! 233: histp->chf_hent[i].ch_event = *sp++;
! 234:
! 235: /* split the record in fields */
! 236: fields[0] = sp;
! 237:
! 238: fld = 1;
! 239: while (sp < ep) {
! 240: if (*sp == '|') {
! 241: *sp = '\0';
! 242: fields[fld++] = sp + 1;
! 243: }
! 244: if (fld == CVS_HIST_NBFLD)
! 245: break;
! 246: sp++;
! 247: }
! 248: #if 0
! 249: for (fld = 0; fld < CVS_HIST_NBFLD; fld++)
! 250: printf("fields[%u] = `%s'\n", fld, fields[fld]);
! 251: #endif
! 252:
! 253: histp->chf_hent[i].ch_date = (time_t)strtol(fields[0],
! 254: &errp, 16);
! 255: if (*errp != '\0') {
! 256: cvs_log(LP_ERR,
! 257: "parse error in date field of CVS history entry");
! 258: continue;
! 259: }
! 260:
! 261: histp->chf_hent[i].ch_user = fields[1];
! 262: histp->chf_hent[i].ch_curdir = fields[2];
! 263: histp->chf_hent[i].ch_repo = fields[3];
! 264: histp->chf_hent[i].ch_rev = rcsnum_alloc();
! 265: rcsnum_aton(fields[4], NULL, histp->chf_hent[i].ch_rev);
! 266: histp->chf_hent[i].ch_arg = fields[5];
! 267: sp = ep;
! 268: }
! 269:
! 270: /* update indexes */
! 271: histp->chf_sindex += histp->chf_nbhent;
! 272: histp->chf_nbhent = i;
! 273: histp->chf_cindex = 0;
! 274:
! 275:
! 276: return (i);
! 277: }
! 278:
! 279:
! 280: /*
! 281: * cvs_hist_fmt()
! 282: *
! 283: * Format the contents of the CVS history entry <ent> into the format used in
! 284: * the CVS `history' file, and store the resulting string in <buf>, which is
! 285: * of size <blen>.
! 286: */
! 287:
! 288: static int
! 289: cvs_hist_fmt(const struct cvs_hent *ent, char *buf, size_t blen)
! 290: {
! 291: char numbuf[64];
! 292:
! 293: if (rcsnum_tostr(ent->ch_rev, numbuf, sizeof(numbuf)) == NULL) {
! 294: return (-1);
! 295: }
! 296:
! 297: return snprintf(buf, blen, "%c%8x|%s|%s|%s|%s|%s",
! 298: ent->ch_event, ent->ch_date, ent->ch_user, ent->ch_curdir,
! 299: ent->ch_repo, numbuf, ent->ch_arg);
! 300: }