Annotation of src/usr.bin/cvs/entries.c, Revision 1.97
1.97 ! tobias 1: /* $OpenBSD: entries.c,v 1.96 2008/06/09 22:31:24 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:
1.87 tobias 76: if (strlen(buf) < 2)
77: fatal("cvs_ent_open: %s: malformed line %s",
78: ep->cef_lpath, buf);
79:
80: p = &buf[2];
1.57 joris 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.91 joris 110: struct tm t, dt;
1.5 jfb 111: struct cvs_ent *ent;
1.96 tobias 112: char *fields[CVS_ENTRIES_NFIELDS], *buf, *sp, *dp, *p;
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.92 joris 140: ent->ce_date = -1;
141: ent->ce_tag = 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.96 tobias 159: strcmp(fields[3], "Result of merge") == 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.96 tobias 166: p = fields[3];
167: if (strncmp(fields[3], "Result of merge+", 16) == 0)
168: p += 16;
169:
1.78 niallo 170: /* Date field can be a '+=' with remote to indicate
171: * conflict. In this case do nothing. */
1.96 tobias 172: if (strptime(p, "%a %b %d %T %Y", &t) != NULL) {
1.78 niallo 173:
174: t.tm_isdst = -1; /* Figure out DST. */
175: t.tm_gmtoff = 0;
176: ent->ce_mtime = mktime(&t);
177: ent->ce_mtime += t.tm_gmtoff;
178: }
1.70 joris 179: }
1.3 jfb 180: }
1.1 jfb 181:
1.57 joris 182: ent->ce_conflict = fields[3];
183: if ((dp = strchr(ent->ce_conflict, '+')) != NULL)
184: *dp = '\0';
185: else
186: ent->ce_conflict = NULL;
1.1 jfb 187:
1.57 joris 188: if (strcmp(fields[4], ""))
189: ent->ce_opts = fields[4];
190: else
191: ent->ce_opts = NULL;
1.8 jfb 192:
1.91 joris 193: if (strcmp(fields[5], "")) {
1.95 deraadt 194: switch (*fields[5]) {
1.91 joris 195: case 'D':
196: if (sscanf(fields[5] + 1, "%d.%d.%d.%d.%d.%d",
197: &dt.tm_year, &dt.tm_mon, &dt.tm_mday,
198: &dt.tm_hour, &dt.tm_min, &dt.tm_sec) != 6)
199: fatal("wrong date specification");
200: dt.tm_year -= 1900;
201: dt.tm_mon -= 1;
202: ent->ce_date = timegm(&dt);
203: ent->ce_tag = NULL;
204: break;
205: case 'T':
206: ent->ce_tag = fields[5] + 1;
207: break;
208: default:
209: fatal("invalid sticky entry");
210: }
1.92 joris 211: }
1.3 jfb 212:
1.57 joris 213: return (ent);
1.3 jfb 214: }
215:
1.57 joris 216: struct cvs_ent *
217: cvs_ent_get(CVSENTRIES *ep, const char *name)
1.3 jfb 218: {
219: struct cvs_ent *ent;
1.57 joris 220: struct cvs_ent_line *l;
1.3 jfb 221:
1.57 joris 222: l = ent_get_line(ep, name);
223: if (l == NULL)
224: return (NULL);
1.3 jfb 225:
1.57 joris 226: ent = cvs_ent_parse(l->buf);
227: return (ent);
1.1 jfb 228: }
229:
1.57 joris 230: void
231: cvs_ent_close(CVSENTRIES *ep, int writefile)
1.9 jfb 232: {
1.57 joris 233: FILE *fp;
234: struct cvs_ent_line *l;
1.85 tobias 235: int dflag;
236:
237: dflag = 1;
1.44 xsa 238:
1.57 joris 239: if (writefile) {
240: if ((fp = fopen(ep->cef_bpath, "w")) == NULL)
1.63 xsa 241: fatal("cvs_ent_close: fopen: `%s': %s",
242: ep->cef_path, strerror(errno));
1.57 joris 243: }
1.16 jfb 244:
1.57 joris 245: while ((l = TAILQ_FIRST(&(ep->cef_ent))) != NULL) {
246: if (writefile) {
1.85 tobias 247: if (l->buf[0] == 'D')
248: dflag = 0;
249:
1.57 joris 250: fputs(l->buf, fp);
251: fputc('\n', fp);
1.51 joris 252: }
1.57 joris 253:
254: TAILQ_REMOVE(&(ep->cef_ent), l, entries_list);
255: xfree(l->buf);
256: xfree(l);
1.22 jfb 257: }
1.9 jfb 258:
1.57 joris 259: if (writefile) {
1.85 tobias 260: if (dflag) {
261: fputc('D', fp);
262: fputc('\n', fp);
263: }
1.57 joris 264: (void)fclose(fp);
265:
266: if (rename(ep->cef_bpath, ep->cef_path) == -1)
1.63 xsa 267: fatal("cvs_ent_close: rename: `%s'->`%s': %s",
268: ep->cef_bpath, ep->cef_path, strerror(errno));
1.57 joris 269:
270: (void)unlink(ep->cef_lpath);
271: }
1.9 jfb 272:
1.57 joris 273: xfree(ep->cef_path);
274: xfree(ep->cef_bpath);
275: xfree(ep->cef_lpath);
276: xfree(ep);
1.9 jfb 277: }
278:
1.57 joris 279: void
280: cvs_ent_add(CVSENTRIES *ep, const char *line)
1.1 jfb 281: {
1.57 joris 282: FILE *fp;
283: struct cvs_ent_line *l;
1.39 xsa 284: struct cvs_ent *ent;
1.1 jfb 285:
1.57 joris 286: if ((ent = cvs_ent_parse(line)) == NULL)
287: fatal("cvs_ent_add: parsing failed '%s'", line);
1.1 jfb 288:
1.57 joris 289: l = ent_get_line(ep, ent->ce_name);
290: if (l != NULL)
291: cvs_ent_remove(ep, ent->ce_name);
1.1 jfb 292:
1.57 joris 293: cvs_ent_free(ent);
1.1 jfb 294:
1.61 joris 295: if (cvs_server_active == 0)
296: cvs_log(LP_TRACE, "cvs_ent_add(%s, %s)", ep->cef_path, line);
1.1 jfb 297:
1.57 joris 298: if ((fp = fopen(ep->cef_lpath, "a")) == NULL)
1.63 xsa 299: fatal("cvs_ent_add: fopen: `%s': %s",
300: ep->cef_lpath, strerror(errno));
1.1 jfb 301:
1.87 tobias 302: fputs("A ", fp);
1.57 joris 303: fputs(line, fp);
304: fputc('\n', fp);
1.1 jfb 305:
1.57 joris 306: (void)fclose(fp);
1.10 jfb 307:
1.57 joris 308: l = (struct cvs_ent_line *)xmalloc(sizeof(*l));
309: l->buf = xstrdup(line);
310: TAILQ_INSERT_TAIL(&(ep->cef_ent), l, entries_list);
311: }
1.10 jfb 312:
1.57 joris 313: void
314: cvs_ent_remove(CVSENTRIES *ep, const char *name)
315: {
316: FILE *fp;
317: struct cvs_ent_line *l;
1.1 jfb 318:
1.61 joris 319: if (cvs_server_active == 0)
320: cvs_log(LP_TRACE, "cvs_ent_remove(%s, %s)", ep->cef_path, name);
1.1 jfb 321:
1.57 joris 322: l = ent_get_line(ep, name);
323: if (l == NULL)
324: return;
1.1 jfb 325:
1.57 joris 326: if ((fp = fopen(ep->cef_lpath, "a")) == NULL)
1.63 xsa 327: fatal("cvs_ent_remove: fopen: `%s': %s", ep->cef_lpath,
328: strerror(errno));
1.38 joris 329:
1.87 tobias 330: fputs("R ", fp);
1.57 joris 331: fputs(l->buf, fp);
332: fputc('\n', fp);
1.24 jfb 333:
1.57 joris 334: (void)fclose(fp);
1.24 jfb 335:
1.57 joris 336: TAILQ_REMOVE(&(ep->cef_ent), l, entries_list);
337: xfree(l->buf);
338: xfree(l);
1.90 xsa 339: }
340:
341: /*
342: * cvs_ent_line_str()
343: *
344: * Build CVS/Entries line.
345: *
346: */
347: void
348: cvs_ent_line_str(const char *name, char *rev, char *tstamp, char *opts,
349: char *sticky, int isdir, int isremoved, char *buf, size_t len)
350: {
351: if (isdir == 1) {
352: (void)xsnprintf(buf, len, "D/%s////", name);
353: return;
354: }
355:
356: (void)xsnprintf(buf, len, "/%s/%s%s/%s/%s/%s",
357: name, isremoved == 1 ? "-" : "", rev, tstamp, opts, sticky);
1.5 jfb 358: }
359:
360: void
361: cvs_ent_free(struct cvs_ent *ent)
362: {
363: if (ent->ce_rev != NULL)
364: rcsnum_free(ent->ce_rev);
1.57 joris 365: xfree(ent->ce_buf);
1.53 joris 366: xfree(ent);
1.5 jfb 367: }
1.8 jfb 368:
1.57 joris 369: static struct cvs_ent_line *
370: ent_get_line(CVSENTRIES *ep, const char *name)
1.8 jfb 371: {
1.57 joris 372: char *p, *s;
373: struct cvs_ent_line *l;
1.8 jfb 374:
1.57 joris 375: TAILQ_FOREACH(l, &(ep->cef_ent), entries_list) {
376: if (l->buf[0] == 'D')
377: p = &(l->buf[2]);
378: else
379: p = &(l->buf[1]);
380:
381: if ((s = strchr(p, '/')) == NULL)
382: fatal("ent_get_line: bad entry line '%s'", l->buf);
383:
384: *s = '\0';
385:
386: if (!strcmp(p, name)) {
387: *s = '/';
388: return (l);
1.15 jfb 389: }
1.8 jfb 390:
1.57 joris 391: *s = '/';
1.8 jfb 392: }
393:
1.57 joris 394: return (NULL);
1.59 xsa 395: }
396:
397: void
398: cvs_parse_tagfile(char *dir, char **tagp, char **datep, int *nbp)
399: {
400: FILE *fp;
1.73 xsa 401: int i, linenum;
1.59 xsa 402: size_t len;
1.91 joris 403: struct tm datetm;
1.69 otto 404: char linebuf[128], tagpath[MAXPATHLEN];
1.59 xsa 405:
406: if (tagp != NULL)
1.60 joris 407: *tagp = NULL;
1.59 xsa 408:
409: if (datep != NULL)
1.60 joris 410: *datep = NULL;
1.59 xsa 411:
412: if (nbp != NULL)
413: *nbp = 0;
414:
1.73 xsa 415: i = snprintf(tagpath, MAXPATHLEN, "%s/%s", dir, CVS_PATH_TAG);
416: if (i < 0 || i >= MAXPATHLEN)
1.69 otto 417: return;
1.59 xsa 418:
419: if ((fp = fopen(tagpath, "r")) == NULL) {
420: if (errno != ENOENT)
421: cvs_log(LP_NOTICE, "failed to open `%s' : %s", tagpath,
422: strerror(errno));
1.69 otto 423: return;
1.59 xsa 424: }
425:
426: linenum = 0;
427:
428: while (fgets(linebuf, (int)sizeof(linebuf), fp) != NULL) {
429: linenum++;
430: if ((len = strlen(linebuf)) == 0)
431: continue;
432: if (linebuf[len - 1] != '\n') {
433: cvs_log(LP_NOTICE, "line too long in `%s:%d'",
434: tagpath, linenum);
435: break;
436: }
437: linebuf[--len] = '\0';
438:
439: switch (*linebuf) {
440: case 'T':
441: if (tagp != NULL)
442: *tagp = xstrdup(linebuf + 1);
443: break;
444: case 'D':
1.91 joris 445: if (sscanf(linebuf + 1, "%d.%d.%d.%d.%d.%d",
446: &datetm.tm_year, &datetm.tm_mon, &datetm.tm_mday,
447: &datetm.tm_hour, &datetm.tm_min, &datetm.tm_sec) !=
448: 6)
449: fatal("wrong date specification");
450: datetm.tm_year -= 1900;
451: datetm.tm_mon -= 1;
452:
1.92 joris 453: if (cvs_specified_date == -1)
1.91 joris 454: cvs_specified_date = timegm(&datetm);
455:
1.59 xsa 456: if (datep != NULL)
457: *datep = xstrdup(linebuf + 1);
458: break;
459: case 'N':
460: if (tagp != NULL)
461: *tagp = xstrdup(linebuf + 1);
462: if (nbp != NULL)
463: *nbp = 1;
464: break;
465: default:
466: break;
467: }
468: }
469: if (ferror(fp))
470: cvs_log(LP_NOTICE, "failed to read line from `%s'", tagpath);
471:
472: (void)fclose(fp);
473: }
474:
475: void
1.88 tobias 476: cvs_write_tagfile(const char *dir, char *tag, char *date)
1.59 xsa 477: {
478: FILE *fp;
1.88 tobias 479: RCSNUM *rev;
1.69 otto 480: char tagpath[MAXPATHLEN];
1.82 joris 481: char sticky[CVS_REV_BUFSZ];
1.97 ! tobias 482: struct tm datetm;
1.73 xsa 483: int i;
1.59 xsa 484:
1.88 tobias 485: cvs_log(LP_TRACE, "cvs_write_tagfile(%s, %s, %s)", dir,
486: tag != NULL ? tag : "", date != NULL ? date : "");
487:
1.59 xsa 488: if (cvs_noexec == 1)
489: return;
490:
1.73 xsa 491: i = snprintf(tagpath, MAXPATHLEN, "%s/%s", dir, CVS_PATH_TAG);
492: if (i < 0 || i >= MAXPATHLEN)
1.69 otto 493: return;
1.59 xsa 494:
1.92 joris 495: if (tag != NULL || cvs_specified_date != -1) {
1.59 xsa 496: if ((fp = fopen(tagpath, "w+")) == NULL) {
497: if (errno != ENOENT) {
498: cvs_log(LP_NOTICE, "failed to open `%s' : %s",
499: tagpath, strerror(errno));
500: }
1.69 otto 501: return;
1.59 xsa 502: }
1.79 joris 503:
1.59 xsa 504: if (tag != NULL) {
1.88 tobias 505: if ((rev = rcsnum_parse(tag)) != NULL) {
1.82 joris 506: (void)xsnprintf(sticky, sizeof(sticky),
507: "N%s", tag);
1.88 tobias 508: rcsnum_free(rev);
1.82 joris 509: } else {
510: (void)xsnprintf(sticky, sizeof(sticky),
511: "T%s", tag);
512: }
513: } else {
1.97 ! tobias 514: gmtime_r(&cvs_specified_date, &datetm);
1.94 xsa 515: (void)strftime(sticky, sizeof(sticky),
1.97 ! tobias 516: "D"CVS_DATE_FMT, &datetm);
1.82 joris 517: }
518:
519: if (cvs_server_active == 1)
520: cvs_server_set_sticky(dir, sticky);
1.59 xsa 521:
1.82 joris 522: (void)fprintf(fp, "%s\n", sticky);
1.59 xsa 523: (void)fclose(fp);
524: }
1.1 jfb 525: }