Annotation of src/usr.bin/cvs/entries.c, Revision 1.76
1.76 ! xsa 1: /* $OpenBSD: entries.c,v 1.75 2007/02/22 06:42:09 otto Exp $ */
1.1 jfb 2: /*
1.57 joris 3: * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
1.1 jfb 4: *
1.57 joris 5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 jfb 16: */
17:
1.75 otto 18: #include <errno.h>
19: #include <string.h>
20: #include <unistd.h>
1.1 jfb 21:
1.34 xsa 22: #include "cvs.h"
1.1 jfb 23:
1.42 xsa 24: #define CVS_ENTRIES_NFIELDS 6
25: #define CVS_ENTRIES_DELIM '/'
1.1 jfb 26:
1.57 joris 27: static struct cvs_ent_line *ent_get_line(CVSENTRIES *, const char *);
1.1 jfb 28:
1.42 xsa 29: CVSENTRIES *
1.57 joris 30: cvs_ent_open(const char *dir)
1.1 jfb 31: {
1.57 joris 32: FILE *fp;
1.1 jfb 33: size_t len;
1.57 joris 34: CVSENTRIES *ep;
35: char *p, buf[MAXPATHLEN];
1.1 jfb 36: struct cvs_ent *ent;
1.57 joris 37: struct cvs_ent_line *line;
1.36 xsa 38:
1.57 joris 39: ep = (CVSENTRIES *)xmalloc(sizeof(*ep));
40: memset(ep, 0, sizeof(*ep));
1.52 joris 41:
1.73 xsa 42: (void)xsnprintf(buf, sizeof(buf), "%s/%s", dir, CVS_PATH_ENTRIES);
1.64 xsa 43:
1.57 joris 44: ep->cef_path = xstrdup(buf);
1.8 jfb 45:
1.73 xsa 46: (void)xsnprintf(buf, sizeof(buf), "%s/%s",
47: dir, CVS_PATH_BACKUPENTRIES);
1.64 xsa 48:
1.57 joris 49: ep->cef_bpath = xstrdup(buf);
1.3 jfb 50:
1.73 xsa 51: (void)xsnprintf(buf, sizeof(buf), "%s/%s", dir, CVS_PATH_LOGENTRIES);
1.64 xsa 52:
1.57 joris 53: ep->cef_lpath = xstrdup(buf);
1.1 jfb 54:
1.4 jfb 55: TAILQ_INIT(&(ep->cef_ent));
1.3 jfb 56:
1.57 joris 57: if ((fp = fopen(ep->cef_path, "r")) != NULL) {
58: while (fgets(buf, sizeof(buf), fp)) {
59: len = strlen(buf);
60: if (len > 0 && buf[len - 1] == '\n')
61: buf[len - 1] = '\0';
62:
63: if (buf[0] == 'D' && buf[1] == '\0')
64: break;
65:
66: line = (struct cvs_ent_line *)xmalloc(sizeof(*line));
67: line->buf = xstrdup(buf);
68: TAILQ_INSERT_TAIL(&(ep->cef_ent), line, entries_list);
69: }
1.52 joris 70:
1.25 jfb 71: (void)fclose(fp);
1.12 jfb 72: }
1.1 jfb 73:
1.57 joris 74: if ((fp = fopen(ep->cef_lpath, "r")) != NULL) {
75: while (fgets(buf, sizeof(buf), fp)) {
76: len = strlen(buf);
1.62 ray 77: if (len > 0 && buf[len - 1] == '\n')
78: buf[len - 1] = '\0';
1.57 joris 79:
80: p = &buf[1];
81:
82: if (buf[0] == 'A') {
83: line = xmalloc(sizeof(*line));
84: line->buf = xstrdup(p);
85: TAILQ_INSERT_TAIL(&(ep->cef_ent), line,
86: entries_list);
87: } else if (buf[0] == 'R') {
88: ent = cvs_ent_parse(p);
89: line = ent_get_line(ep, ent->ce_name);
1.65 joris 90: if (line != NULL) {
1.57 joris 91: TAILQ_REMOVE(&(ep->cef_ent), line,
92: entries_list);
1.65 joris 93: xfree(line->buf);
94: xfree(line);
95: }
1.57 joris 96: cvs_ent_free(ent);
97: }
98: }
1.4 jfb 99:
1.52 joris 100: (void)fclose(fp);
101: }
1.12 jfb 102:
1.1 jfb 103: return (ep);
104: }
105:
1.57 joris 106: struct cvs_ent *
107: cvs_ent_parse(const char *entry)
1.1 jfb 108: {
1.57 joris 109: int i;
1.70 joris 110: struct tm t;
1.5 jfb 111: struct cvs_ent *ent;
1.57 joris 112: char *fields[CVS_ENTRIES_NFIELDS], *buf, *sp, *dp;
1.5 jfb 113:
1.72 joris 114: buf = sp = xstrdup(entry);
1.57 joris 115: i = 0;
116: do {
117: dp = strchr(sp, CVS_ENTRIES_DELIM);
118: if (dp != NULL)
119: *(dp++) = '\0';
120: fields[i++] = sp;
121: sp = dp;
122: } while (dp != NULL && i < CVS_ENTRIES_NFIELDS);
1.8 jfb 123:
1.57 joris 124: if (i < CVS_ENTRIES_NFIELDS)
125: fatal("missing fields in entry line '%s'", entry);
1.5 jfb 126:
1.72 joris 127: ent = xmalloc(sizeof(*ent));
1.57 joris 128: ent->ce_buf = buf;
1.52 joris 129:
1.57 joris 130: if (*fields[0] == '\0')
131: ent->ce_type = CVS_ENT_FILE;
132: else if (*fields[0] == 'D')
133: ent->ce_type = CVS_ENT_DIR;
134: else
135: ent->ce_type = CVS_ENT_NONE;
136:
137: ent->ce_status = CVS_ENT_REG;
138: ent->ce_name = fields[1];
139: ent->ce_rev = NULL;
1.5 jfb 140:
1.57 joris 141: if (ent->ce_type == CVS_ENT_FILE) {
142: if (*fields[2] == '-') {
143: ent->ce_status = CVS_ENT_REMOVED;
144: sp = fields[2] + 1;
145: } else {
146: sp = fields[2];
147: if (fields[2][0] == '0' && fields[2][1] == '\0')
148: ent->ce_status = CVS_ENT_ADDED;
149: }
1.1 jfb 150:
1.57 joris 151: if ((ent->ce_rev = rcsnum_parse(sp)) == NULL)
152: fatal("failed to parse entry revision '%s'", entry);
1.1 jfb 153:
1.72 joris 154: if (fields[3][0] == '\0' ||
155: strcmp(fields[3], CVS_DATE_DUMMY) == 0 ||
1.57 joris 156: strncmp(fields[3], "Initial ", 8) == 0 ||
157: strncmp(fields[3], "Result of merge", 15) == 0)
158: ent->ce_mtime = CVS_DATE_DMSEC;
1.70 joris 159: else {
1.72 joris 160: if (strptime(fields[3], "%a %b %d %T %Y", &t) == NULL)
161: fatal("'%s' is not a valid date", fields[3]);
1.76 ! xsa 162:
! 163: t.tm_isdst = -1; /* Figure out DST. */
1.70 joris 164: t.tm_gmtoff = 0;
165: ent->ce_mtime = mktime(&t);
1.71 otto 166: ent->ce_mtime += t.tm_gmtoff;
1.70 joris 167: }
1.3 jfb 168: }
1.1 jfb 169:
1.57 joris 170: ent->ce_conflict = fields[3];
171: if ((dp = strchr(ent->ce_conflict, '+')) != NULL)
172: *dp = '\0';
173: else
174: ent->ce_conflict = NULL;
1.1 jfb 175:
1.57 joris 176: if (strcmp(fields[4], ""))
177: ent->ce_opts = fields[4];
178: else
179: ent->ce_opts = NULL;
1.8 jfb 180:
1.57 joris 181: if (strcmp(fields[5], ""))
1.58 joris 182: ent->ce_tag = fields[5] + 1;
1.57 joris 183: else
184: ent->ce_tag = NULL;
1.3 jfb 185:
1.57 joris 186: return (ent);
1.3 jfb 187: }
188:
1.57 joris 189: struct cvs_ent *
190: cvs_ent_get(CVSENTRIES *ep, const char *name)
1.3 jfb 191: {
192: struct cvs_ent *ent;
1.57 joris 193: struct cvs_ent_line *l;
1.3 jfb 194:
1.57 joris 195: l = ent_get_line(ep, name);
196: if (l == NULL)
197: return (NULL);
1.3 jfb 198:
1.57 joris 199: ent = cvs_ent_parse(l->buf);
200: return (ent);
201: }
1.3 jfb 202:
1.57 joris 203: int
204: cvs_ent_exists(CVSENTRIES *ep, const char *name)
205: {
206: struct cvs_ent_line *l;
1.1 jfb 207:
1.57 joris 208: l = ent_get_line(ep, name);
209: if (l == NULL)
210: return (0);
1.8 jfb 211:
1.57 joris 212: return (1);
1.1 jfb 213: }
214:
1.57 joris 215: void
216: cvs_ent_close(CVSENTRIES *ep, int writefile)
1.9 jfb 217: {
1.57 joris 218: FILE *fp;
219: struct cvs_ent_line *l;
1.44 xsa 220:
1.57 joris 221: if (writefile) {
222: if ((fp = fopen(ep->cef_bpath, "w")) == NULL)
1.63 xsa 223: fatal("cvs_ent_close: fopen: `%s': %s",
224: ep->cef_path, strerror(errno));
1.57 joris 225: }
1.16 jfb 226:
1.57 joris 227: while ((l = TAILQ_FIRST(&(ep->cef_ent))) != NULL) {
228: if (writefile) {
229: fputs(l->buf, fp);
230: fputc('\n', fp);
1.51 joris 231: }
1.57 joris 232:
233: TAILQ_REMOVE(&(ep->cef_ent), l, entries_list);
234: xfree(l->buf);
235: xfree(l);
1.22 jfb 236: }
1.9 jfb 237:
1.57 joris 238: if (writefile) {
239: fputc('D', fp);
1.68 pyr 240: fputc('\n', fp);
1.57 joris 241: (void)fclose(fp);
242:
243: if (rename(ep->cef_bpath, ep->cef_path) == -1)
1.63 xsa 244: fatal("cvs_ent_close: rename: `%s'->`%s': %s",
245: ep->cef_bpath, ep->cef_path, strerror(errno));
1.57 joris 246:
247: (void)unlink(ep->cef_lpath);
248: }
1.9 jfb 249:
1.57 joris 250: xfree(ep->cef_path);
251: xfree(ep->cef_bpath);
252: xfree(ep->cef_lpath);
253: xfree(ep);
1.9 jfb 254: }
255:
1.57 joris 256: void
257: cvs_ent_add(CVSENTRIES *ep, const char *line)
1.1 jfb 258: {
1.57 joris 259: FILE *fp;
260: struct cvs_ent_line *l;
1.39 xsa 261: struct cvs_ent *ent;
1.1 jfb 262:
1.57 joris 263: if ((ent = cvs_ent_parse(line)) == NULL)
264: fatal("cvs_ent_add: parsing failed '%s'", line);
1.1 jfb 265:
1.57 joris 266: l = ent_get_line(ep, ent->ce_name);
267: if (l != NULL)
268: cvs_ent_remove(ep, ent->ce_name);
1.1 jfb 269:
1.57 joris 270: cvs_ent_free(ent);
1.1 jfb 271:
1.61 joris 272: if (cvs_server_active == 0)
273: cvs_log(LP_TRACE, "cvs_ent_add(%s, %s)", ep->cef_path, line);
1.1 jfb 274:
1.57 joris 275: if ((fp = fopen(ep->cef_lpath, "a")) == NULL)
1.63 xsa 276: fatal("cvs_ent_add: fopen: `%s': %s",
277: ep->cef_lpath, strerror(errno));
1.1 jfb 278:
1.57 joris 279: fputc('A', fp);
280: fputs(line, fp);
281: fputc('\n', fp);
1.1 jfb 282:
1.57 joris 283: (void)fclose(fp);
1.10 jfb 284:
1.57 joris 285: l = (struct cvs_ent_line *)xmalloc(sizeof(*l));
286: l->buf = xstrdup(line);
287: TAILQ_INSERT_TAIL(&(ep->cef_ent), l, entries_list);
288: }
1.10 jfb 289:
1.57 joris 290: void
291: cvs_ent_remove(CVSENTRIES *ep, const char *name)
292: {
293: FILE *fp;
294: struct cvs_ent_line *l;
1.1 jfb 295:
1.61 joris 296: if (cvs_server_active == 0)
297: cvs_log(LP_TRACE, "cvs_ent_remove(%s, %s)", ep->cef_path, name);
1.1 jfb 298:
1.57 joris 299: l = ent_get_line(ep, name);
300: if (l == NULL)
301: return;
1.1 jfb 302:
1.57 joris 303: if ((fp = fopen(ep->cef_lpath, "a")) == NULL)
1.63 xsa 304: fatal("cvs_ent_remove: fopen: `%s': %s", ep->cef_lpath,
305: strerror(errno));
1.38 joris 306:
1.57 joris 307: fputc('R', fp);
308: fputs(l->buf, fp);
309: fputc('\n', fp);
1.24 jfb 310:
1.57 joris 311: (void)fclose(fp);
1.24 jfb 312:
1.57 joris 313: TAILQ_REMOVE(&(ep->cef_ent), l, entries_list);
314: xfree(l->buf);
315: xfree(l);
1.5 jfb 316: }
317:
318: void
319: cvs_ent_free(struct cvs_ent *ent)
320: {
321: if (ent->ce_rev != NULL)
322: rcsnum_free(ent->ce_rev);
1.57 joris 323: xfree(ent->ce_buf);
1.53 joris 324: xfree(ent);
1.5 jfb 325: }
1.8 jfb 326:
1.57 joris 327: static struct cvs_ent_line *
328: ent_get_line(CVSENTRIES *ep, const char *name)
1.8 jfb 329: {
1.57 joris 330: char *p, *s;
331: struct cvs_ent_line *l;
1.8 jfb 332:
1.57 joris 333: TAILQ_FOREACH(l, &(ep->cef_ent), entries_list) {
334: if (l->buf[0] == 'D')
335: p = &(l->buf[2]);
336: else
337: p = &(l->buf[1]);
338:
339: if ((s = strchr(p, '/')) == NULL)
340: fatal("ent_get_line: bad entry line '%s'", l->buf);
341:
342: *s = '\0';
343:
344: if (!strcmp(p, name)) {
345: *s = '/';
346: return (l);
1.15 jfb 347: }
1.8 jfb 348:
1.57 joris 349: *s = '/';
1.8 jfb 350: }
351:
1.57 joris 352: return (NULL);
1.59 xsa 353: }
354:
355: void
356: cvs_parse_tagfile(char *dir, char **tagp, char **datep, int *nbp)
357: {
358: FILE *fp;
1.73 xsa 359: int i, linenum;
1.59 xsa 360: size_t len;
1.69 otto 361: char linebuf[128], tagpath[MAXPATHLEN];
1.59 xsa 362:
363: if (tagp != NULL)
1.60 joris 364: *tagp = NULL;
1.59 xsa 365:
366: if (datep != NULL)
1.60 joris 367: *datep = NULL;
1.59 xsa 368:
369: if (nbp != NULL)
370: *nbp = 0;
371:
1.73 xsa 372: i = snprintf(tagpath, MAXPATHLEN, "%s/%s", dir, CVS_PATH_TAG);
373: if (i < 0 || i >= MAXPATHLEN)
1.69 otto 374: return;
1.59 xsa 375:
376: if ((fp = fopen(tagpath, "r")) == NULL) {
377: if (errno != ENOENT)
378: cvs_log(LP_NOTICE, "failed to open `%s' : %s", tagpath,
379: strerror(errno));
1.69 otto 380: return;
1.59 xsa 381: }
382:
383: linenum = 0;
384:
385: while (fgets(linebuf, (int)sizeof(linebuf), fp) != NULL) {
386: linenum++;
387: if ((len = strlen(linebuf)) == 0)
388: continue;
389: if (linebuf[len - 1] != '\n') {
390: cvs_log(LP_NOTICE, "line too long in `%s:%d'",
391: tagpath, linenum);
392: break;
393: }
394: linebuf[--len] = '\0';
395:
396: switch (*linebuf) {
397: case 'T':
398: if (tagp != NULL)
399: *tagp = xstrdup(linebuf + 1);
400: break;
401: case 'D':
402: if (datep != NULL)
403: *datep = xstrdup(linebuf + 1);
404: break;
405: case 'N':
406: if (tagp != NULL)
407: *tagp = xstrdup(linebuf + 1);
408: if (nbp != NULL)
409: *nbp = 1;
410: break;
411: default:
412: break;
413: }
414: }
415: if (ferror(fp))
416: cvs_log(LP_NOTICE, "failed to read line from `%s'", tagpath);
417:
418: (void)fclose(fp);
419: }
420:
421: void
1.74 otto 422: cvs_write_tagfile(const char *dir, char *tag, char *date, int nb)
1.59 xsa 423: {
424: FILE *fp;
1.69 otto 425: char tagpath[MAXPATHLEN];
1.73 xsa 426: int i;
1.59 xsa 427:
428: if (cvs_noexec == 1)
429: return;
430:
1.73 xsa 431: i = snprintf(tagpath, MAXPATHLEN, "%s/%s", dir, CVS_PATH_TAG);
432: if (i < 0 || i >= MAXPATHLEN)
1.69 otto 433: return;
1.59 xsa 434:
435: if ((tag != NULL) || (date != NULL)) {
436: if ((fp = fopen(tagpath, "w+")) == NULL) {
437: if (errno != ENOENT) {
438: cvs_log(LP_NOTICE, "failed to open `%s' : %s",
439: tagpath, strerror(errno));
440: }
1.69 otto 441: return;
1.59 xsa 442: }
443: if (tag != NULL) {
444: if (nb != 0)
445: (void)fprintf(fp, "N%s\n", tag);
446: else
447: (void)fprintf(fp, "T%s\n", tag);
448: } else
449: (void)fprintf(fp, "D%s\n", date);
450:
451: (void)fclose(fp);
452: } else {
453: (void)cvs_unlink(tagpath);
454: }
1.1 jfb 455: }