Annotation of src/usr.bin/cvs/hist.c, Revision 1.2
1.2 ! deraadt 1: /* $OpenBSD: hist.c,v 1.1.1.1 2004/07/13 22:02:40 jfb Exp $ */
1.1 jfb 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);
1.2 ! deraadt 190: } else {
1.1 jfb 191: histp->chf_bused = (size_t)ret;
192: }
193:
194: return (ret);
195: }
196:
197:
198: /*
199: * cvs_hist_parse()
200: *
201: * Parse the current contents of the internal buffer of <histp> and regenerate
202: * the buffered history entries.
203: * Returns the number of entries parsed on success, or -1 on failure.
204: */
205:
206: int
207: cvs_hist_parse(CVSHIST *histp)
208: {
209: u_int i, fld;
210: char *fields[CVS_HIST_NBFLD], *sp, *bep, *ep, *errp;
211:
212: sp = histp->chf_buf;
213: bep = histp->chf_buf + histp->chf_bused - 1;
214:
215: for (i = 0; i < CVS_HIST_CACHE; i++) {
216: ep = memchr(sp, '\n', bep - sp);
217: if (ep == NULL) {
218: /*
219: * No record or incomplete record left to parse,
220: * so adjust the next read offset in consequence.
221: */
222: histp->chf_off += (off_t)(sp - histp->chf_buf);
223: break;
1.2 ! deraadt 224: } else if (ep == bep) {
1.1 jfb 225: histp->chf_off += (off_t)histp->chf_bused;
226: }
227: *(ep++) = '\0';
228:
229: printf("hist(%s)\n", sp);
230:
231: histp->chf_hent[i].ch_event = *sp++;
232:
233: /* split the record in fields */
234: fields[0] = sp;
235:
236: fld = 1;
237: while (sp < ep) {
238: if (*sp == '|') {
239: *sp = '\0';
240: fields[fld++] = sp + 1;
241: }
242: if (fld == CVS_HIST_NBFLD)
243: break;
244: sp++;
245: }
246: #if 0
247: for (fld = 0; fld < CVS_HIST_NBFLD; fld++)
248: printf("fields[%u] = `%s'\n", fld, fields[fld]);
249: #endif
250:
251: histp->chf_hent[i].ch_date = (time_t)strtol(fields[0],
252: &errp, 16);
253: if (*errp != '\0') {
254: cvs_log(LP_ERR,
255: "parse error in date field of CVS history entry");
256: continue;
257: }
258:
259: histp->chf_hent[i].ch_user = fields[1];
260: histp->chf_hent[i].ch_curdir = fields[2];
261: histp->chf_hent[i].ch_repo = fields[3];
262: histp->chf_hent[i].ch_rev = rcsnum_alloc();
263: rcsnum_aton(fields[4], NULL, histp->chf_hent[i].ch_rev);
264: histp->chf_hent[i].ch_arg = fields[5];
265: sp = ep;
266: }
267:
268: /* update indexes */
269: histp->chf_sindex += histp->chf_nbhent;
270: histp->chf_nbhent = i;
271: histp->chf_cindex = 0;
272:
273:
274: return (i);
275: }
276:
277:
278: /*
279: * cvs_hist_fmt()
280: *
281: * Format the contents of the CVS history entry <ent> into the format used in
282: * the CVS `history' file, and store the resulting string in <buf>, which is
283: * of size <blen>.
284: */
285:
286: static int
287: cvs_hist_fmt(const struct cvs_hent *ent, char *buf, size_t blen)
288: {
289: char numbuf[64];
290:
291: if (rcsnum_tostr(ent->ch_rev, numbuf, sizeof(numbuf)) == NULL) {
292: return (-1);
293: }
294:
295: return snprintf(buf, blen, "%c%8x|%s|%s|%s|%s|%s",
296: ent->ch_event, ent->ch_date, ent->ch_user, ent->ch_curdir,
297: ent->ch_repo, numbuf, ent->ch_arg);
298: }