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