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