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