Annotation of src/usr.bin/cvs/entries.c, Revision 1.105
1.105 ! deraadt 1: /* $OpenBSD: entries.c,v 1.104 2015/04/04 14:19:10 stsp 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.98 joris 31: CVSENTRIES *current_list = NULL;
32:
1.42 xsa 33: CVSENTRIES *
1.57 joris 34: cvs_ent_open(const char *dir)
1.1 jfb 35: {
1.57 joris 36: FILE *fp;
37: CVSENTRIES *ep;
1.103 deraadt 38: char *p, buf[PATH_MAX];
1.1 jfb 39: struct cvs_ent *ent;
1.57 joris 40: struct cvs_ent_line *line;
1.36 xsa 41:
1.98 joris 42: cvs_log(LP_TRACE, "cvs_ent_open(%s)", dir);
1.52 joris 43:
1.73 xsa 44: (void)xsnprintf(buf, sizeof(buf), "%s/%s", dir, CVS_PATH_ENTRIES);
1.64 xsa 45:
1.98 joris 46: if (current_list != NULL && !strcmp(current_list->cef_path, buf))
47: return (current_list);
48:
49: if (current_list != NULL) {
50: cvs_ent_close(current_list, ENT_SYNC);
51: current_list = NULL;
52: }
53:
1.105 ! deraadt 54: ep = xcalloc(1, sizeof(*ep));
1.57 joris 55: ep->cef_path = xstrdup(buf);
1.8 jfb 56:
1.73 xsa 57: (void)xsnprintf(buf, sizeof(buf), "%s/%s",
58: dir, CVS_PATH_BACKUPENTRIES);
1.64 xsa 59:
1.57 joris 60: ep->cef_bpath = xstrdup(buf);
1.3 jfb 61:
1.73 xsa 62: (void)xsnprintf(buf, sizeof(buf), "%s/%s", dir, CVS_PATH_LOGENTRIES);
1.64 xsa 63:
1.57 joris 64: ep->cef_lpath = xstrdup(buf);
1.1 jfb 65:
1.4 jfb 66: TAILQ_INIT(&(ep->cef_ent));
1.3 jfb 67:
1.57 joris 68: if ((fp = fopen(ep->cef_path, "r")) != NULL) {
69: while (fgets(buf, sizeof(buf), fp)) {
1.84 gilles 70: buf[strcspn(buf, "\n")] = '\0';
1.57 joris 71:
72: if (buf[0] == 'D' && buf[1] == '\0')
73: break;
74:
1.105 ! deraadt 75: line = xmalloc(sizeof(*line));
1.57 joris 76: line->buf = xstrdup(buf);
77: TAILQ_INSERT_TAIL(&(ep->cef_ent), line, entries_list);
78: }
1.52 joris 79:
1.25 jfb 80: (void)fclose(fp);
1.12 jfb 81: }
1.1 jfb 82:
1.57 joris 83: if ((fp = fopen(ep->cef_lpath, "r")) != NULL) {
84: while (fgets(buf, sizeof(buf), fp)) {
1.84 gilles 85: buf[strcspn(buf, "\n")] = '\0';
1.57 joris 86:
1.87 tobias 87: if (strlen(buf) < 2)
88: fatal("cvs_ent_open: %s: malformed line %s",
89: ep->cef_lpath, buf);
90:
91: p = &buf[2];
1.57 joris 92:
93: if (buf[0] == 'A') {
94: line = xmalloc(sizeof(*line));
95: line->buf = xstrdup(p);
96: TAILQ_INSERT_TAIL(&(ep->cef_ent), line,
97: entries_list);
98: } else if (buf[0] == 'R') {
99: ent = cvs_ent_parse(p);
100: line = ent_get_line(ep, ent->ce_name);
1.65 joris 101: if (line != NULL) {
1.57 joris 102: TAILQ_REMOVE(&(ep->cef_ent), line,
103: entries_list);
1.65 joris 104: xfree(line->buf);
105: xfree(line);
106: }
1.57 joris 107: cvs_ent_free(ent);
108: }
109: }
1.4 jfb 110:
1.52 joris 111: (void)fclose(fp);
112: }
1.12 jfb 113:
1.98 joris 114: current_list = ep;
1.1 jfb 115: return (ep);
116: }
117:
1.57 joris 118: struct cvs_ent *
119: cvs_ent_parse(const char *entry)
1.1 jfb 120: {
1.57 joris 121: int i;
1.91 joris 122: struct tm t, dt;
1.5 jfb 123: struct cvs_ent *ent;
1.96 tobias 124: char *fields[CVS_ENTRIES_NFIELDS], *buf, *sp, *dp, *p;
1.5 jfb 125:
1.72 joris 126: buf = sp = xstrdup(entry);
1.57 joris 127: i = 0;
128: do {
129: dp = strchr(sp, CVS_ENTRIES_DELIM);
130: if (dp != NULL)
131: *(dp++) = '\0';
132: fields[i++] = sp;
133: sp = dp;
134: } while (dp != NULL && i < CVS_ENTRIES_NFIELDS);
1.8 jfb 135:
1.57 joris 136: if (i < CVS_ENTRIES_NFIELDS)
137: fatal("missing fields in entry line '%s'", entry);
1.5 jfb 138:
1.72 joris 139: ent = xmalloc(sizeof(*ent));
1.57 joris 140: ent->ce_buf = buf;
1.52 joris 141:
1.57 joris 142: if (*fields[0] == '\0')
143: ent->ce_type = CVS_ENT_FILE;
144: else if (*fields[0] == 'D')
145: ent->ce_type = CVS_ENT_DIR;
146: else
147: ent->ce_type = CVS_ENT_NONE;
148:
149: ent->ce_status = CVS_ENT_REG;
150: ent->ce_name = fields[1];
151: ent->ce_rev = NULL;
1.92 joris 152: ent->ce_date = -1;
153: ent->ce_tag = NULL;
1.104 stsp 154: ent->ce_time = NULL;
1.5 jfb 155:
1.57 joris 156: if (ent->ce_type == CVS_ENT_FILE) {
157: if (*fields[2] == '-') {
158: ent->ce_status = CVS_ENT_REMOVED;
159: sp = fields[2] + 1;
1.102 joris 160: } else if (*fields[2] == CVS_SERVER_QUESTIONABLE) {
161: sp = NULL;
162: ent->ce_status = CVS_ENT_UNKNOWN;
1.57 joris 163: } else {
164: sp = fields[2];
165: if (fields[2][0] == '0' && fields[2][1] == '\0')
166: ent->ce_status = CVS_ENT_ADDED;
167: }
1.1 jfb 168:
1.102 joris 169: if (sp != NULL) {
170: if ((ent->ce_rev = rcsnum_parse(sp)) == NULL) {
171: fatal("failed to parse entry revision '%s'",
172: entry);
173: }
174: }
1.1 jfb 175:
1.72 joris 176: if (fields[3][0] == '\0' ||
1.102 joris 177: strncmp(fields[3], CVS_DATE_DUMMY,
178: sizeof(CVS_DATE_DUMMY) - 1) == 0 ||
1.57 joris 179: strncmp(fields[3], "Initial ", 8) == 0 ||
1.96 tobias 180: strcmp(fields[3], "Result of merge") == 0) {
1.57 joris 181: ent->ce_mtime = CVS_DATE_DMSEC;
1.81 joris 182: } else if (cvs_server_active == 1 &&
183: strncmp(fields[3], CVS_SERVER_UNCHANGED,
184: strlen(CVS_SERVER_UNCHANGED)) == 0) {
185: ent->ce_mtime = CVS_SERVER_UPTODATE;
186: } else {
1.96 tobias 187: p = fields[3];
188: if (strncmp(fields[3], "Result of merge+", 16) == 0)
189: p += 16;
190:
1.104 stsp 191: ent->ce_time = xstrdup(p);
192:
1.78 niallo 193: /* Date field can be a '+=' with remote to indicate
194: * conflict. In this case do nothing. */
1.96 tobias 195: if (strptime(p, "%a %b %d %T %Y", &t) != NULL) {
1.78 niallo 196: t.tm_isdst = -1; /* Figure out DST. */
197: t.tm_gmtoff = 0;
198: ent->ce_mtime = mktime(&t);
199: ent->ce_mtime += t.tm_gmtoff;
200: }
1.70 joris 201: }
1.3 jfb 202: }
1.1 jfb 203:
1.57 joris 204: ent->ce_conflict = fields[3];
205: if ((dp = strchr(ent->ce_conflict, '+')) != NULL)
206: *dp = '\0';
207: else
208: ent->ce_conflict = NULL;
1.1 jfb 209:
1.57 joris 210: if (strcmp(fields[4], ""))
211: ent->ce_opts = fields[4];
212: else
213: ent->ce_opts = NULL;
1.8 jfb 214:
1.91 joris 215: if (strcmp(fields[5], "")) {
1.95 deraadt 216: switch (*fields[5]) {
1.91 joris 217: case 'D':
218: if (sscanf(fields[5] + 1, "%d.%d.%d.%d.%d.%d",
219: &dt.tm_year, &dt.tm_mon, &dt.tm_mday,
220: &dt.tm_hour, &dt.tm_min, &dt.tm_sec) != 6)
221: fatal("wrong date specification");
222: dt.tm_year -= 1900;
223: dt.tm_mon -= 1;
224: ent->ce_date = timegm(&dt);
225: ent->ce_tag = NULL;
226: break;
227: case 'T':
228: ent->ce_tag = fields[5] + 1;
229: break;
230: default:
231: fatal("invalid sticky entry");
232: }
1.92 joris 233: }
1.3 jfb 234:
1.57 joris 235: return (ent);
1.3 jfb 236: }
237:
1.57 joris 238: struct cvs_ent *
239: cvs_ent_get(CVSENTRIES *ep, const char *name)
1.3 jfb 240: {
241: struct cvs_ent *ent;
1.57 joris 242: struct cvs_ent_line *l;
1.3 jfb 243:
1.57 joris 244: l = ent_get_line(ep, name);
245: if (l == NULL)
246: return (NULL);
1.3 jfb 247:
1.57 joris 248: ent = cvs_ent_parse(l->buf);
249: return (ent);
1.1 jfb 250: }
251:
1.57 joris 252: void
253: cvs_ent_close(CVSENTRIES *ep, int writefile)
1.9 jfb 254: {
1.57 joris 255: FILE *fp;
256: struct cvs_ent_line *l;
1.85 tobias 257: int dflag;
258:
259: dflag = 1;
1.98 joris 260: cvs_log(LP_TRACE, "cvs_ent_close(%s, %d)", ep->cef_bpath, writefile);
1.44 xsa 261:
1.98 joris 262: if (cvs_cmdop == CVS_OP_EXPORT)
263: writefile = 0;
264:
265: fp = NULL;
266: if (writefile)
267: fp = fopen(ep->cef_bpath, "w");
1.16 jfb 268:
1.57 joris 269: while ((l = TAILQ_FIRST(&(ep->cef_ent))) != NULL) {
1.98 joris 270: if (fp != NULL) {
1.85 tobias 271: if (l->buf[0] == 'D')
272: dflag = 0;
273:
1.57 joris 274: fputs(l->buf, fp);
275: fputc('\n', fp);
1.51 joris 276: }
1.57 joris 277:
278: TAILQ_REMOVE(&(ep->cef_ent), l, entries_list);
279: xfree(l->buf);
280: xfree(l);
1.22 jfb 281: }
1.9 jfb 282:
1.98 joris 283: if (fp != NULL) {
1.85 tobias 284: if (dflag) {
285: fputc('D', fp);
286: fputc('\n', fp);
287: }
1.57 joris 288: (void)fclose(fp);
289:
290: if (rename(ep->cef_bpath, ep->cef_path) == -1)
1.63 xsa 291: fatal("cvs_ent_close: rename: `%s'->`%s': %s",
292: ep->cef_bpath, ep->cef_path, strerror(errno));
1.57 joris 293:
294: (void)unlink(ep->cef_lpath);
295: }
1.9 jfb 296:
1.57 joris 297: xfree(ep->cef_path);
298: xfree(ep->cef_bpath);
299: xfree(ep->cef_lpath);
300: xfree(ep);
1.9 jfb 301: }
302:
1.57 joris 303: void
304: cvs_ent_add(CVSENTRIES *ep, const char *line)
1.1 jfb 305: {
1.57 joris 306: FILE *fp;
307: struct cvs_ent_line *l;
1.39 xsa 308: struct cvs_ent *ent;
1.1 jfb 309:
1.57 joris 310: if ((ent = cvs_ent_parse(line)) == NULL)
311: fatal("cvs_ent_add: parsing failed '%s'", line);
1.1 jfb 312:
1.57 joris 313: l = ent_get_line(ep, ent->ce_name);
314: if (l != NULL)
315: cvs_ent_remove(ep, ent->ce_name);
1.1 jfb 316:
1.57 joris 317: cvs_ent_free(ent);
1.1 jfb 318:
1.61 joris 319: if (cvs_server_active == 0)
320: cvs_log(LP_TRACE, "cvs_ent_add(%s, %s)", ep->cef_path, line);
1.1 jfb 321:
1.57 joris 322: if ((fp = fopen(ep->cef_lpath, "a")) == NULL)
1.63 xsa 323: fatal("cvs_ent_add: fopen: `%s': %s",
324: ep->cef_lpath, strerror(errno));
1.1 jfb 325:
1.87 tobias 326: fputs("A ", fp);
1.57 joris 327: fputs(line, fp);
328: fputc('\n', fp);
1.1 jfb 329:
1.57 joris 330: (void)fclose(fp);
1.10 jfb 331:
1.105 ! deraadt 332: l = xmalloc(sizeof(*l));
1.57 joris 333: l->buf = xstrdup(line);
334: TAILQ_INSERT_TAIL(&(ep->cef_ent), l, entries_list);
335: }
1.10 jfb 336:
1.57 joris 337: void
338: cvs_ent_remove(CVSENTRIES *ep, const char *name)
339: {
340: FILE *fp;
341: struct cvs_ent_line *l;
1.1 jfb 342:
1.61 joris 343: if (cvs_server_active == 0)
344: cvs_log(LP_TRACE, "cvs_ent_remove(%s, %s)", ep->cef_path, name);
1.1 jfb 345:
1.57 joris 346: l = ent_get_line(ep, name);
347: if (l == NULL)
348: return;
1.1 jfb 349:
1.57 joris 350: if ((fp = fopen(ep->cef_lpath, "a")) == NULL)
1.63 xsa 351: fatal("cvs_ent_remove: fopen: `%s': %s", ep->cef_lpath,
352: strerror(errno));
1.38 joris 353:
1.87 tobias 354: fputs("R ", fp);
1.57 joris 355: fputs(l->buf, fp);
356: fputc('\n', fp);
1.24 jfb 357:
1.57 joris 358: (void)fclose(fp);
1.24 jfb 359:
1.57 joris 360: TAILQ_REMOVE(&(ep->cef_ent), l, entries_list);
361: xfree(l->buf);
362: xfree(l);
1.90 xsa 363: }
364:
365: /*
366: * cvs_ent_line_str()
367: *
368: * Build CVS/Entries line.
369: *
370: */
371: void
372: cvs_ent_line_str(const char *name, char *rev, char *tstamp, char *opts,
373: char *sticky, int isdir, int isremoved, char *buf, size_t len)
374: {
375: if (isdir == 1) {
376: (void)xsnprintf(buf, len, "D/%s////", name);
377: return;
378: }
379:
380: (void)xsnprintf(buf, len, "/%s/%s%s/%s/%s/%s",
381: name, isremoved == 1 ? "-" : "", rev, tstamp, opts, sticky);
1.5 jfb 382: }
383:
384: void
385: cvs_ent_free(struct cvs_ent *ent)
386: {
387: if (ent->ce_rev != NULL)
388: rcsnum_free(ent->ce_rev);
1.104 stsp 389: if (ent->ce_time != NULL)
390: xfree(ent->ce_time);
391:
1.57 joris 392: xfree(ent->ce_buf);
1.53 joris 393: xfree(ent);
1.5 jfb 394: }
1.8 jfb 395:
1.57 joris 396: static struct cvs_ent_line *
397: ent_get_line(CVSENTRIES *ep, const char *name)
1.8 jfb 398: {
1.57 joris 399: char *p, *s;
400: struct cvs_ent_line *l;
1.8 jfb 401:
1.57 joris 402: TAILQ_FOREACH(l, &(ep->cef_ent), entries_list) {
403: if (l->buf[0] == 'D')
404: p = &(l->buf[2]);
405: else
406: p = &(l->buf[1]);
407:
408: if ((s = strchr(p, '/')) == NULL)
409: fatal("ent_get_line: bad entry line '%s'", l->buf);
410:
411: *s = '\0';
412:
413: if (!strcmp(p, name)) {
414: *s = '/';
415: return (l);
1.15 jfb 416: }
1.8 jfb 417:
1.57 joris 418: *s = '/';
1.8 jfb 419: }
420:
1.57 joris 421: return (NULL);
1.59 xsa 422: }
423:
424: void
425: cvs_parse_tagfile(char *dir, char **tagp, char **datep, int *nbp)
426: {
427: FILE *fp;
1.73 xsa 428: int i, linenum;
1.59 xsa 429: size_t len;
1.91 joris 430: struct tm datetm;
1.103 deraadt 431: char linebuf[128], tagpath[PATH_MAX];
1.59 xsa 432:
1.99 tobias 433: cvs_directory_date = -1;
434:
1.59 xsa 435: if (tagp != NULL)
1.60 joris 436: *tagp = NULL;
1.59 xsa 437:
438: if (datep != NULL)
1.60 joris 439: *datep = NULL;
1.59 xsa 440:
441: if (nbp != NULL)
442: *nbp = 0;
443:
1.103 deraadt 444: i = snprintf(tagpath, PATH_MAX, "%s/%s", dir, CVS_PATH_TAG);
445: if (i < 0 || i >= PATH_MAX)
1.69 otto 446: return;
1.59 xsa 447:
448: if ((fp = fopen(tagpath, "r")) == NULL) {
449: if (errno != ENOENT)
450: cvs_log(LP_NOTICE, "failed to open `%s' : %s", tagpath,
451: strerror(errno));
1.69 otto 452: return;
1.59 xsa 453: }
454:
455: linenum = 0;
456:
457: while (fgets(linebuf, (int)sizeof(linebuf), fp) != NULL) {
458: linenum++;
459: if ((len = strlen(linebuf)) == 0)
460: continue;
461: if (linebuf[len - 1] != '\n') {
462: cvs_log(LP_NOTICE, "line too long in `%s:%d'",
463: tagpath, linenum);
464: break;
465: }
466: linebuf[--len] = '\0';
467:
468: switch (*linebuf) {
469: case 'T':
470: if (tagp != NULL)
471: *tagp = xstrdup(linebuf + 1);
472: break;
473: case 'D':
1.91 joris 474: if (sscanf(linebuf + 1, "%d.%d.%d.%d.%d.%d",
475: &datetm.tm_year, &datetm.tm_mon, &datetm.tm_mday,
476: &datetm.tm_hour, &datetm.tm_min, &datetm.tm_sec) !=
477: 6)
478: fatal("wrong date specification");
479: datetm.tm_year -= 1900;
480: datetm.tm_mon -= 1;
481:
1.99 tobias 482: cvs_directory_date = timegm(&datetm);
1.91 joris 483:
1.59 xsa 484: if (datep != NULL)
485: *datep = xstrdup(linebuf + 1);
486: break;
487: case 'N':
488: if (tagp != NULL)
489: *tagp = xstrdup(linebuf + 1);
490: if (nbp != NULL)
491: *nbp = 1;
492: break;
493: default:
494: break;
495: }
496: }
497: if (ferror(fp))
498: cvs_log(LP_NOTICE, "failed to read line from `%s'", tagpath);
499:
500: (void)fclose(fp);
501: }
502:
503: void
1.88 tobias 504: cvs_write_tagfile(const char *dir, char *tag, char *date)
1.59 xsa 505: {
506: FILE *fp;
1.88 tobias 507: RCSNUM *rev;
1.103 deraadt 508: char tagpath[PATH_MAX];
1.82 joris 509: char sticky[CVS_REV_BUFSZ];
1.97 tobias 510: struct tm datetm;
1.73 xsa 511: int i;
1.59 xsa 512:
1.88 tobias 513: cvs_log(LP_TRACE, "cvs_write_tagfile(%s, %s, %s)", dir,
514: tag != NULL ? tag : "", date != NULL ? date : "");
515:
1.59 xsa 516: if (cvs_noexec == 1)
517: return;
518:
1.103 deraadt 519: i = snprintf(tagpath, PATH_MAX, "%s/%s", dir, CVS_PATH_TAG);
520: if (i < 0 || i >= PATH_MAX)
1.69 otto 521: return;
1.59 xsa 522:
1.99 tobias 523: if (tag != NULL || cvs_specified_date != -1 ||
1.100 joris 524: cvs_directory_date != -1) {
1.59 xsa 525: if ((fp = fopen(tagpath, "w+")) == NULL) {
526: if (errno != ENOENT) {
527: cvs_log(LP_NOTICE, "failed to open `%s' : %s",
528: tagpath, strerror(errno));
529: }
1.69 otto 530: return;
1.59 xsa 531: }
1.79 joris 532:
1.59 xsa 533: if (tag != NULL) {
1.88 tobias 534: if ((rev = rcsnum_parse(tag)) != NULL) {
1.82 joris 535: (void)xsnprintf(sticky, sizeof(sticky),
536: "N%s", tag);
1.88 tobias 537: rcsnum_free(rev);
1.82 joris 538: } else {
539: (void)xsnprintf(sticky, sizeof(sticky),
540: "T%s", tag);
541: }
542: } else {
1.99 tobias 543: if (cvs_specified_date != -1)
544: gmtime_r(&cvs_specified_date, &datetm);
545: else
546: gmtime_r(&cvs_directory_date, &datetm);
1.94 xsa 547: (void)strftime(sticky, sizeof(sticky),
1.97 tobias 548: "D"CVS_DATE_FMT, &datetm);
1.82 joris 549: }
1.59 xsa 550:
1.82 joris 551: (void)fprintf(fp, "%s\n", sticky);
1.59 xsa 552: (void)fclose(fp);
553: }
1.1 jfb 554: }