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