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