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