Annotation of src/usr.bin/cvs/entries.c, Revision 1.7
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:
40: #define CVS_ENTRIES_NFIELDS 5
41: #define CVS_ENTRIES_DELIM '/'
42:
43:
1.3 jfb 44:
45: static struct cvs_ent* cvs_ent_clone (const struct cvs_ent *);
46:
47:
48:
1.1 jfb 49: /*
50: * cvs_ent_open()
51: *
52: * Open the CVS Entries file for the directory <dir>.
53: * Returns a pointer to the CVSENTRIES file structure on success, or NULL
54: * on failure.
55: */
56:
57: CVSENTRIES*
1.2 jfb 58: cvs_ent_open(const char *dir, int flags)
1.1 jfb 59: {
60: size_t len;
1.3 jfb 61: char entpath[MAXPATHLEN], ebuf[128], mode[4];
1.1 jfb 62: FILE *fp;
63: struct cvs_ent *ent;
64: CVSENTRIES *ep;
65:
1.3 jfb 66: memset(mode, 0, sizeof(mode));
1.4 jfb 67: switch (flags & O_ACCMODE) {
68: case O_RDWR:
69: mode[1] = '+';
70: /* fallthrough */
71: case O_RDONLY:
1.3 jfb 72: mode[0] = 'r';
1.4 jfb 73: break;
74: case O_WRONLY:
1.7 ! jfb 75: mode[0] = 'a';
1.4 jfb 76: break;
1.3 jfb 77: }
78:
1.1 jfb 79: snprintf(entpath, sizeof(entpath), "%s/" CVS_PATH_ENTRIES, dir);
1.3 jfb 80: fp = fopen(entpath, mode);
1.1 jfb 81: if (fp == NULL) {
82: cvs_log(LP_ERRNO, "cannot open CVS/Entries for reading",
83: entpath);
84: return (NULL);
85: }
86:
87: ep = (CVSENTRIES *)malloc(sizeof(CVSENTRIES));
88: if (ep == NULL) {
89: cvs_log(LP_ERRNO, "failed to allocate Entries data");
90: (void)fclose(fp);
91: return (NULL);
92: }
1.5 jfb 93: memset(ep, 0, sizeof(*ep));
94:
1.1 jfb 95: ep->cef_path = strdup(dir);
96: if (ep->cef_path == NULL) {
97: cvs_log(LP_ERRNO, "failed to copy Entries path");
98: free(ep);
99: (void)fclose(fp);
100: return (NULL);
101: }
102:
1.4 jfb 103: ep->cef_cur = NULL;
104: TAILQ_INIT(&(ep->cef_ent));
1.3 jfb 105:
1.1 jfb 106: while (fgets(ebuf, sizeof(ebuf), fp) != NULL) {
107: len = strlen(ebuf);
108: if ((len > 0) && (ebuf[len - 1] == '\n'))
109: ebuf[--len] = '\0';
1.7 ! jfb 110: if (strcmp(ebuf, "D") == 0)
! 111: break;
1.1 jfb 112: ent = cvs_ent_parse(ebuf);
113: if (ent == NULL)
114: continue;
115:
1.4 jfb 116: TAILQ_INSERT_TAIL(&(ep->cef_ent), ent, ce_list);
1.1 jfb 117: }
118:
1.4 jfb 119: /* only keep a pointer to the open file if we're in writing mode */
120: if ((flags & O_WRONLY) || (flags & O_RDWR))
121: ep->cef_file = fp;
122: else
123: (void)fclose(fp);
124:
1.1 jfb 125: return (ep);
126: }
127:
128:
129: /*
130: * cvs_ent_close()
131: *
1.5 jfb 132: * Close the Entries file <ep> and free all data. Any reference to entries
133: * structure within that file become invalid.
1.1 jfb 134: */
135:
136: void
137: cvs_ent_close(CVSENTRIES *ep)
138: {
1.5 jfb 139: struct cvs_ent *ent;
140:
141: if (ep->cef_file != NULL)
1.6 jfb 142: (void)fclose(ep->cef_file);
1.5 jfb 143: if (ep->cef_path != NULL)
144: free(ep->cef_path);
145:
146: while (!TAILQ_EMPTY(&(ep->cef_ent))) {
147: ent = TAILQ_FIRST(&(ep->cef_ent));
148: TAILQ_REMOVE(&(ep->cef_ent), ent, ce_list);
149: cvs_ent_free(ent);
150: }
151:
1.1 jfb 152: free(ep);
153: }
154:
155:
156: /*
157: * cvs_ent_add()
158: *
1.3 jfb 159: * Add the entry <ent> to the Entries file <ef>.
1.7 ! jfb 160: * Returns 0 on success, or -1 on failure.
1.1 jfb 161: */
162:
163: int
164: cvs_ent_add(CVSENTRIES *ef, struct cvs_ent *ent)
165: {
166: void *tmp;
1.7 ! jfb 167: char nbuf[64];
1.3 jfb 168:
169: if (ef->cef_file == NULL) {
170: cvs_log(LP_ERR, "Entries file is opened in read-only mode");
171: return (-1);
172: }
1.1 jfb 173:
174: if (cvs_ent_get(ef, ent->ce_name) != NULL)
175: return (-1);
176:
1.3 jfb 177: if (fseek(ef->cef_file, (long)0, SEEK_END) == -1) {
178: cvs_log(LP_ERRNO, "failed to seek to end of CVS/Entries file");
1.1 jfb 179: return (-1);
180: }
1.7 ! jfb 181: rcsnum_tostr(ent->ce_rev, nbuf, sizeof(nbuf));
! 182: fprintf(ef->cef_file, "/%s/%s/%s/%s/\n", ent->ce_name, nbuf,
! 183: ent->ce_timestamp, ent->ce_opts);
1.3 jfb 184:
1.4 jfb 185: TAILQ_INSERT_TAIL(&(ef->cef_ent), ent, ce_list);
1.3 jfb 186: return (0);
187: }
188:
189:
190: /*
191: * cvs_ent_addln()
192: *
193: * Add a line to the Entries file.
194: */
195:
196: int
197: cvs_ent_addln(CVSENTRIES *ef, const char *line)
198: {
199: void *tmp;
200: struct cvs_ent *ent;
201:
202: if (ef->cef_file == NULL) {
203: cvs_log(LP_ERR, "Entries file is opened in read-only mode");
204: return (-1);
205: }
206:
207: ent = cvs_ent_parse(line);
208: if (ent == NULL)
209: return (-1);
210:
211: if (cvs_ent_get(ef, ent->ce_name) != NULL)
212: return (-1);
1.1 jfb 213:
1.4 jfb 214: TAILQ_INSERT_TAIL(&(ef->cef_ent), ent, ce_list);
1.1 jfb 215: return (0);
216: }
217:
218:
219: /*
220: * cvs_ent_get()
221: *
222: * Get the CVS entry from the Entries file <ef> whose 'name' portion matches
223: * <file>.
224: * Returns a pointer to the cvs entry structure on success, or NULL on failure.
225: */
226:
227: struct cvs_ent*
228: cvs_ent_get(CVSENTRIES *ef, const char *file)
229: {
230: u_int i;
1.4 jfb 231: struct cvs_ent *ep;
1.1 jfb 232:
1.4 jfb 233: TAILQ_FOREACH(ep, &(ef->cef_ent), ce_list)
234: if (strcmp(ep->ce_name, file) == 0)
235: return (ep);
1.1 jfb 236:
237: return (NULL);
238: }
239:
240:
241: /*
242: * cvs_ent_next()
243: *
1.4 jfb 244: * This function is used to iterate over the entries in an Entries file. The
245: * first call will return the first entry of the file and each subsequent call
246: * will return the entry following the last one returned.
1.1 jfb 247: * Returns a pointer to the cvs entry structure on success, or NULL on failure.
248: */
249:
250: struct cvs_ent*
251: cvs_ent_next(CVSENTRIES *ef)
252: {
1.5 jfb 253: if (ef->cef_cur == NULL)
1.4 jfb 254: ef->cef_cur = TAILQ_FIRST(&(ef->cef_ent));
1.5 jfb 255: else
256: ef->cef_cur = TAILQ_NEXT(ef->cef_cur, ce_list);
257: return (ef->cef_cur);
1.1 jfb 258: }
259:
260:
261: /*
262: * cvs_ent_parse()
263: *
264: * Parse a single line from a CVS/Entries file and return a cvs_entry structure
265: * containing all the parsed information.
266: */
267:
268: struct cvs_ent*
269: cvs_ent_parse(const char *entry)
270: {
271: int i;
272: char *fields[CVS_ENTRIES_NFIELDS], *sp, *dp;
273: struct cvs_ent *entp;
274:
275: entp = (struct cvs_ent *)malloc(sizeof(*entp));
276: if (entp == NULL) {
277: cvs_log(LP_ERRNO, "failed to allocate CVS entry");
278: return (NULL);
279: }
1.5 jfb 280: memset(entp, 0, sizeof(*entp));
1.1 jfb 281:
282: entp->ce_rev = rcsnum_alloc();
283: if (entp->ce_rev == NULL) {
284: free(entp);
285: return (NULL);
286: }
287:
288: entp->ce_line = strdup(entry);
289: if (entp->ce_line == NULL) {
1.5 jfb 290: cvs_ent_free(entp);
1.1 jfb 291: return (NULL);
292: }
293:
294: entp->ce_buf = strdup(entry);
295: if (entp->ce_buf == NULL) {
1.5 jfb 296: cvs_ent_free(entp);
1.1 jfb 297: return (NULL);
298: }
299: sp = entp->ce_buf;
300:
301: if (*sp == CVS_ENTRIES_DELIM)
302: entp->ce_type = CVS_ENT_FILE;
303: else if (*sp == 'D') {
304: entp->ce_type = CVS_ENT_DIR;
305: sp++;
306: }
307: else {
308: /* unknown entry, ignore for future expansion */
309: entp->ce_type = CVS_ENT_NONE;
310: sp++;
311: }
312:
313: sp++;
314: i = 0;
315: do {
316: dp = strchr(sp, CVS_ENTRIES_DELIM);
317: if (dp != NULL)
318: *(dp++) = '\0';
319: fields[i++] = sp;
320: sp = dp;
321: } while ((dp != NULL) && (i < CVS_ENTRIES_NFIELDS));
322:
323: entp->ce_name = fields[0];
324:
325: if (entp->ce_type == CVS_ENT_FILE) {
326: rcsnum_aton(fields[1], NULL, entp->ce_rev);
327: entp->ce_timestamp = fields[2];
328: entp->ce_opts = fields[3];
329: entp->ce_tag = fields[4];
330: }
331:
332: return (entp);
1.5 jfb 333: }
334:
335:
336: /*
337: * cvs_ent_free()
338: *
339: * Free a single CVS entries structure.
340: */
341:
342: void
343: cvs_ent_free(struct cvs_ent *ent)
344: {
345: if (ent->ce_rev != NULL)
346: rcsnum_free(ent->ce_rev);
347: if (ent->ce_line != NULL)
348: free(ent->ce_line);
349: if (ent->ce_buf != NULL)
350: free(ent->ce_buf);
351: free(ent);
352: }
353:
354:
355: /*
356: * cvs_ent_getent()
357: *
358: * Get a single entry from the CVS/Entries file of the basename portion of
359: * path <path> and return that entry. That entry must later be freed using
360: * cvs_ent_free().
361: */
362:
363: struct cvs_ent*
364: cvs_ent_getent(const char *path)
365: {
366: char base[MAXPATHLEN], file[MAXPATHLEN];
367: CVSENTRIES *entf;
368: struct cvs_ent *ep;
369:
370: cvs_splitpath(path, base, sizeof(base), file, sizeof(file));
371:
372: entf = cvs_ent_open(base, O_RDONLY);
373: if (entf == NULL)
374: return (NULL);
375:
376: ep = cvs_ent_get(entf, file);
377: if (ep != NULL) {
378: /* take it out of the queue so it doesn't get freed */
379: TAILQ_REMOVE(&(entf->cef_ent), ep, ce_list);
380: }
381:
382: cvs_ent_close(entf);
383: return (ep);
1.1 jfb 384: }