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