[BACK]Return to entries.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / cvs

Annotation of src/usr.bin/cvs/entries.c, Revision 1.98

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