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