Annotation of src/usr.bin/cvs/entries.c, Revision 1.11
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:
1.10 jfb 40: #define CVS_ENTRIES_NFIELDS 6
1.1 jfb 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;
1.8 jfb 63: struct stat st;
1.1 jfb 64: struct cvs_ent *ent;
65: CVSENTRIES *ep;
66:
1.3 jfb 67: memset(mode, 0, sizeof(mode));
1.8 jfb 68:
69: snprintf(entpath, sizeof(entpath), "%s/" CVS_PATH_ENTRIES, dir);
70:
1.4 jfb 71: switch (flags & O_ACCMODE) {
1.8 jfb 72: case O_WRONLY:
1.4 jfb 73: case O_RDWR:
1.8 jfb 74: /* we have to use append otherwise the file gets truncated */
75: mode[0] = 'w';
1.4 jfb 76: mode[1] = '+';
1.8 jfb 77: break;
1.4 jfb 78: case O_RDONLY:
1.3 jfb 79: mode[0] = 'r';
1.4 jfb 80: break;
1.3 jfb 81: }
82:
1.8 jfb 83: /* we can use 'r' if the file already exists */
84: if (stat(entpath, &st) == 0)
85: mode[0] = 'r';
86:
87:
1.3 jfb 88: fp = fopen(entpath, mode);
1.1 jfb 89: if (fp == NULL) {
1.8 jfb 90: cvs_log(LP_ERRNO, "cannot open %s for %s", entpath,
91: mode[1] == '+' ? "writing" : "reading");
1.1 jfb 92: return (NULL);
93: }
94:
95: ep = (CVSENTRIES *)malloc(sizeof(CVSENTRIES));
96: if (ep == NULL) {
97: cvs_log(LP_ERRNO, "failed to allocate Entries data");
98: (void)fclose(fp);
99: return (NULL);
100: }
1.5 jfb 101: memset(ep, 0, sizeof(*ep));
102:
1.1 jfb 103: ep->cef_path = strdup(dir);
104: if (ep->cef_path == NULL) {
105: cvs_log(LP_ERRNO, "failed to copy Entries path");
106: free(ep);
107: (void)fclose(fp);
108: return (NULL);
109: }
110:
1.4 jfb 111: ep->cef_cur = NULL;
112: TAILQ_INIT(&(ep->cef_ent));
1.3 jfb 113:
1.8 jfb 114: rewind(fp);
115:
1.1 jfb 116: while (fgets(ebuf, sizeof(ebuf), fp) != NULL) {
117: len = strlen(ebuf);
118: if ((len > 0) && (ebuf[len - 1] == '\n'))
119: ebuf[--len] = '\0';
1.7 jfb 120: if (strcmp(ebuf, "D") == 0)
121: break;
1.1 jfb 122: ent = cvs_ent_parse(ebuf);
123: if (ent == NULL)
124: continue;
125:
1.4 jfb 126: TAILQ_INSERT_TAIL(&(ep->cef_ent), ent, ce_list);
1.1 jfb 127: }
128:
1.4 jfb 129: /* only keep a pointer to the open file if we're in writing mode */
1.8 jfb 130: if ((flags & O_WRONLY) || (flags & O_RDWR)) {
131: ep->cef_flags |= CVS_ENTF_WR;
1.4 jfb 132: ep->cef_file = fp;
1.8 jfb 133: }
1.4 jfb 134: else
135: (void)fclose(fp);
136:
1.8 jfb 137: ep->cef_flags |= CVS_ENTF_SYNC;
1.1 jfb 138: return (ep);
139: }
140:
141:
142: /*
143: * cvs_ent_close()
144: *
1.5 jfb 145: * Close the Entries file <ep> and free all data. Any reference to entries
146: * structure within that file become invalid.
1.1 jfb 147: */
148:
149: void
150: cvs_ent_close(CVSENTRIES *ep)
151: {
1.5 jfb 152: struct cvs_ent *ent;
153:
1.8 jfb 154: if ((ep->cef_flags & CVS_ENTF_WR) &&
155: !(ep->cef_flags & CVS_ENTF_SYNC)) {
156: /* implicit sync with disk */
157: (void)cvs_ent_write(ep);
158: }
159:
1.5 jfb 160: if (ep->cef_file != NULL)
1.6 jfb 161: (void)fclose(ep->cef_file);
1.5 jfb 162: if (ep->cef_path != NULL)
163: free(ep->cef_path);
164:
165: while (!TAILQ_EMPTY(&(ep->cef_ent))) {
166: ent = TAILQ_FIRST(&(ep->cef_ent));
167: TAILQ_REMOVE(&(ep->cef_ent), ent, ce_list);
168: cvs_ent_free(ent);
169: }
170:
1.1 jfb 171: free(ep);
172: }
173:
174:
175: /*
176: * cvs_ent_add()
177: *
1.8 jfb 178: * Add the entry <ent> to the Entries file <ef>. The disk contents are not
179: * modified until a call to cvs_ent_write() is performed. This is done
180: * implicitly on a call to cvs_ent_close() on an Entries file that has been
181: * opened for writing.
1.7 jfb 182: * Returns 0 on success, or -1 on failure.
1.1 jfb 183: */
184:
185: int
186: cvs_ent_add(CVSENTRIES *ef, struct cvs_ent *ent)
187: {
1.3 jfb 188: if (ef->cef_file == NULL) {
189: cvs_log(LP_ERR, "Entries file is opened in read-only mode");
190: return (-1);
191: }
1.1 jfb 192:
193: if (cvs_ent_get(ef, ent->ce_name) != NULL)
194: return (-1);
195:
1.8 jfb 196: TAILQ_INSERT_TAIL(&(ef->cef_ent), ent, ce_list);
197:
198: ef->cef_flags &= ~CVS_ENTF_SYNC;
1.3 jfb 199:
200: return (0);
201: }
202:
203:
204: /*
205: * cvs_ent_addln()
206: *
207: * Add a line to the Entries file.
208: */
209:
210: int
211: cvs_ent_addln(CVSENTRIES *ef, const char *line)
212: {
213: struct cvs_ent *ent;
214:
215: if (ef->cef_file == NULL) {
216: cvs_log(LP_ERR, "Entries file is opened in read-only mode");
217: return (-1);
218: }
219:
220: ent = cvs_ent_parse(line);
221: if (ent == NULL)
222: return (-1);
223:
224: if (cvs_ent_get(ef, ent->ce_name) != NULL)
225: return (-1);
1.1 jfb 226:
1.4 jfb 227: TAILQ_INSERT_TAIL(&(ef->cef_ent), ent, ce_list);
1.8 jfb 228: ef->cef_flags &= ~CVS_ENTF_SYNC;
229:
1.1 jfb 230: return (0);
231: }
232:
233:
234: /*
1.9 jfb 235: * cvs_ent_remove()
236: *
237: * Remove an entry from the Entries file <ef>. The entry's name is given
238: * by <name>.
239: */
240:
241: int
242: cvs_ent_remove(CVSENTRIES *ef, const char *name)
243: {
244: struct cvs_ent *ent;
245:
246: ent = cvs_ent_get(ef, name);
247: if (ent == NULL)
248: return (-1);
249:
250: TAILQ_REMOVE(&(ef->cef_ent), ent, ce_list);
251: cvs_ent_free(ent);
252:
253: ef->cef_flags &= ~CVS_ENTF_SYNC;
254:
255: return (0);
256: }
257:
258:
259: /*
1.1 jfb 260: * cvs_ent_get()
261: *
262: * Get the CVS entry from the Entries file <ef> whose 'name' portion matches
263: * <file>.
264: * Returns a pointer to the cvs entry structure on success, or NULL on failure.
265: */
266:
267: struct cvs_ent*
268: cvs_ent_get(CVSENTRIES *ef, const char *file)
269: {
1.4 jfb 270: struct cvs_ent *ep;
1.1 jfb 271:
1.4 jfb 272: TAILQ_FOREACH(ep, &(ef->cef_ent), ce_list)
273: if (strcmp(ep->ce_name, file) == 0)
274: return (ep);
1.1 jfb 275:
276: return (NULL);
277: }
278:
279:
280: /*
281: * cvs_ent_next()
282: *
1.4 jfb 283: * This function is used to iterate over the entries in an Entries file. The
284: * first call will return the first entry of the file and each subsequent call
285: * will return the entry following the last one returned.
1.1 jfb 286: * Returns a pointer to the cvs entry structure on success, or NULL on failure.
287: */
288:
289: struct cvs_ent*
290: cvs_ent_next(CVSENTRIES *ef)
291: {
1.5 jfb 292: if (ef->cef_cur == NULL)
1.4 jfb 293: ef->cef_cur = TAILQ_FIRST(&(ef->cef_ent));
1.5 jfb 294: else
295: ef->cef_cur = TAILQ_NEXT(ef->cef_cur, ce_list);
296: return (ef->cef_cur);
1.1 jfb 297: }
298:
299:
300: /*
301: * cvs_ent_parse()
302: *
303: * Parse a single line from a CVS/Entries file and return a cvs_entry structure
304: * containing all the parsed information.
305: */
306:
307: struct cvs_ent*
308: cvs_ent_parse(const char *entry)
309: {
310: int i;
1.10 jfb 311: char *fields[CVS_ENTRIES_NFIELDS], *buf, *sp, *dp;
1.1 jfb 312: struct cvs_ent *entp;
313:
1.10 jfb 314: buf = strdup(entry);
315: if (buf == NULL) {
316: cvs_log(LP_ERRNO, "failed to allocate entry copy");
317: return (NULL);
318: }
319:
320: sp = buf;
321: i = 0;
322: do {
323: dp = strchr(sp, CVS_ENTRIES_DELIM);
324: if (dp != NULL)
325: *(dp++) = '\0';
326: fields[i++] = sp;
327: sp = dp;
328: } while ((dp != NULL) && (i < CVS_ENTRIES_NFIELDS));
329:
330: if (i < CVS_ENTRIES_NFIELDS) {
331: cvs_log(LP_ERR, "missing fields in entry line `%s'", entry);
332: return (NULL);
333: }
334:
1.1 jfb 335: entp = (struct cvs_ent *)malloc(sizeof(*entp));
336: if (entp == NULL) {
337: cvs_log(LP_ERRNO, "failed to allocate CVS entry");
338: return (NULL);
339: }
1.5 jfb 340: memset(entp, 0, sizeof(*entp));
1.10 jfb 341: entp->ce_buf = buf;
1.1 jfb 342:
343: entp->ce_rev = rcsnum_alloc();
344: if (entp->ce_rev == NULL) {
1.10 jfb 345: cvs_ent_free(entp);
1.1 jfb 346: return (NULL);
347: }
348:
349: entp->ce_line = strdup(entry);
350: if (entp->ce_line == NULL) {
1.5 jfb 351: cvs_ent_free(entp);
1.1 jfb 352: return (NULL);
353: }
354:
1.10 jfb 355: if (*fields[0] == '\0')
1.1 jfb 356: entp->ce_type = CVS_ENT_FILE;
1.10 jfb 357: else if (*fields[0] == 'D')
1.1 jfb 358: entp->ce_type = CVS_ENT_DIR;
1.10 jfb 359: else
1.1 jfb 360: entp->ce_type = CVS_ENT_NONE;
361:
1.10 jfb 362: entp->ce_name = fields[1];
1.1 jfb 363:
364: if (entp->ce_type == CVS_ENT_FILE) {
1.10 jfb 365: rcsnum_aton(fields[2], NULL, entp->ce_rev);
366: entp->ce_timestamp = fields[3];
367: entp->ce_opts = fields[4];
368: entp->ce_tag = fields[5];
1.1 jfb 369: }
370:
371: return (entp);
1.5 jfb 372: }
373:
374:
375: /*
376: * cvs_ent_free()
377: *
378: * Free a single CVS entries structure.
379: */
380:
381: void
382: cvs_ent_free(struct cvs_ent *ent)
383: {
384: if (ent->ce_rev != NULL)
385: rcsnum_free(ent->ce_rev);
386: if (ent->ce_line != NULL)
387: free(ent->ce_line);
388: if (ent->ce_buf != NULL)
389: free(ent->ce_buf);
390: free(ent);
391: }
392:
393:
394: /*
395: * cvs_ent_getent()
396: *
397: * Get a single entry from the CVS/Entries file of the basename portion of
398: * path <path> and return that entry. That entry must later be freed using
399: * cvs_ent_free().
400: */
401:
402: struct cvs_ent*
403: cvs_ent_getent(const char *path)
404: {
1.11 ! jfb 405: char base[MAXPATHLEN], *file;
1.5 jfb 406: CVSENTRIES *entf;
407: struct cvs_ent *ep;
408:
1.11 ! jfb 409: cvs_splitpath(path, base, sizeof(base), &file);
1.5 jfb 410:
411: entf = cvs_ent_open(base, O_RDONLY);
412: if (entf == NULL)
413: return (NULL);
414:
415: ep = cvs_ent_get(entf, file);
416: if (ep != NULL) {
417: /* take it out of the queue so it doesn't get freed */
418: TAILQ_REMOVE(&(entf->cef_ent), ep, ce_list);
419: }
420:
421: cvs_ent_close(entf);
422: return (ep);
1.8 jfb 423: }
424:
425:
426: /*
427: * cvs_ent_write()
428: *
429: * Explicitly write the contents of the Entries file <ef> to disk.
430: * Returns 0 on success, or -1 on failure.
431: */
432:
433: int
434: cvs_ent_write(CVSENTRIES *ef)
435: {
436: char revbuf[64];
437: struct cvs_ent *ent;
438:
439: if (ef->cef_file == NULL)
440: return (-1);
441:
442: if (ef->cef_flags & CVS_ENTF_SYNC)
443: return (0);
444:
445: /* reposition ourself at beginning of file */
446: rewind(ef->cef_file);
447: TAILQ_FOREACH(ent, &(ef->cef_ent), ce_list) {
448: if (ent->ce_type == CVS_ENT_DIR)
449: putc('D', ef->cef_file);
450:
451: rcsnum_tostr(ent->ce_rev, revbuf, sizeof(revbuf));
452: fprintf(ef->cef_file, "/%s/%s/%s/%s/%s\n", ent->ce_name,
453: revbuf, ent->ce_timestamp, "", "");
454: }
455:
456: /* terminating line */
457: fprintf(ef->cef_file, "D\n");
458:
459: ef->cef_flags |= CVS_ENTF_SYNC;
460:
461: return (0);
1.1 jfb 462: }