Bug Summary

File:src/usr.bin/cvs/entries.c
Warning:line 272, column 8
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name entries.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/cvs/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.bin/cvs -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/cvs/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/cvs/entries.c
1/* $OpenBSD: entries.c,v 1.107 2016/10/13 20:51:25 fcambus Exp $ */
2/*
3 * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
4 *
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.
16 */
17
18#include <errno(*__errno()).h>
19#include <stdlib.h>
20#include <string.h>
21#include <time.h>
22#include <unistd.h>
23
24#include "cvs.h"
25#include "remote.h"
26
27#define CVS_ENTRIES_NFIELDS6 6
28#define CVS_ENTRIES_DELIM'/' '/'
29
30static struct cvs_ent_line *ent_get_line(CVSENTRIES *, const char *);
31
32CVSENTRIES *current_list = NULL((void *)0);
33
34CVSENTRIES *
35cvs_ent_open(const char *dir)
36{
37 FILE *fp;
38 CVSENTRIES *ep;
39 char *p, buf[PATH_MAX1024];
40 struct cvs_ent *ent;
41 struct cvs_ent_line *line;
42
43 cvs_log(LP_TRACE4, "cvs_ent_open(%s)", dir);
44
45 (void)xsnprintf(buf, sizeof(buf), "%s/%s", dir, CVS_PATH_ENTRIES"CVS" "/Entries");
46
47 if (current_list != NULL((void *)0) && !strcmp(current_list->cef_path, buf))
1
Assuming 'current_list' is not equal to NULL
2
Assuming the condition is false
3
Taking false branch
48 return (current_list);
49
50 if (current_list
3.1
'current_list' is not equal to NULL
!= NULL((void *)0)) {
4
Taking true branch
51 cvs_ent_close(current_list, ENT_SYNC1);
5
Calling 'cvs_ent_close'
52 current_list = NULL((void *)0);
53 }
54
55 ep = xcalloc(1, sizeof(*ep));
56 ep->cef_path = xstrdup(buf);
57
58 (void)xsnprintf(buf, sizeof(buf), "%s/%s",
59 dir, CVS_PATH_BACKUPENTRIES"CVS" "/Entries.Backup");
60
61 ep->cef_bpath = xstrdup(buf);
62
63 (void)xsnprintf(buf, sizeof(buf), "%s/%s", dir, CVS_PATH_LOGENTRIES"CVS" "/Entries.Log");
64
65 ep->cef_lpath = xstrdup(buf);
66
67 TAILQ_INIT(&(ep->cef_ent))do { (&(ep->cef_ent))->tqh_first = ((void *)0); (&
(ep->cef_ent))->tqh_last = &(&(ep->cef_ent))
->tqh_first; } while (0)
;
68
69 if ((fp = fopen(ep->cef_path, "r")) != NULL((void *)0)) {
70 while (fgets(buf, sizeof(buf), fp)) {
71 buf[strcspn(buf, "\n")] = '\0';
72
73 if (buf[0] == 'D' && buf[1] == '\0')
74 break;
75
76 line = xmalloc(sizeof(*line));
77 line->buf = xstrdup(buf);
78 TAILQ_INSERT_TAIL(&(ep->cef_ent), line, entries_list)do { (line)->entries_list.tqe_next = ((void *)0); (line)->
entries_list.tqe_prev = (&(ep->cef_ent))->tqh_last;
*(&(ep->cef_ent))->tqh_last = (line); (&(ep->
cef_ent))->tqh_last = &(line)->entries_list.tqe_next
; } while (0)
;
79 }
80
81 (void)fclose(fp);
82 }
83
84 if ((fp = fopen(ep->cef_lpath, "r")) != NULL((void *)0)) {
85 while (fgets(buf, sizeof(buf), fp)) {
86 buf[strcspn(buf, "\n")] = '\0';
87
88 if (strlen(buf) < 2)
89 fatal("cvs_ent_open: %s: malformed line %s",
90 ep->cef_lpath, buf);
91
92 p = &buf[2];
93
94 if (buf[0] == 'A') {
95 line = xmalloc(sizeof(*line));
96 line->buf = xstrdup(p);
97 TAILQ_INSERT_TAIL(&(ep->cef_ent), line,do { (line)->entries_list.tqe_next = ((void *)0); (line)->
entries_list.tqe_prev = (&(ep->cef_ent))->tqh_last;
*(&(ep->cef_ent))->tqh_last = (line); (&(ep->
cef_ent))->tqh_last = &(line)->entries_list.tqe_next
; } while (0)
98 entries_list)do { (line)->entries_list.tqe_next = ((void *)0); (line)->
entries_list.tqe_prev = (&(ep->cef_ent))->tqh_last;
*(&(ep->cef_ent))->tqh_last = (line); (&(ep->
cef_ent))->tqh_last = &(line)->entries_list.tqe_next
; } while (0)
;
99 } else if (buf[0] == 'R') {
100 ent = cvs_ent_parse(p);
101 line = ent_get_line(ep, ent->ce_name);
102 if (line != NULL((void *)0)) {
103 TAILQ_REMOVE(&(ep->cef_ent), line,do { if (((line)->entries_list.tqe_next) != ((void *)0)) (
line)->entries_list.tqe_next->entries_list.tqe_prev = (
line)->entries_list.tqe_prev; else (&(ep->cef_ent))
->tqh_last = (line)->entries_list.tqe_prev; *(line)->
entries_list.tqe_prev = (line)->entries_list.tqe_next; ; ;
} while (0)
104 entries_list)do { if (((line)->entries_list.tqe_next) != ((void *)0)) (
line)->entries_list.tqe_next->entries_list.tqe_prev = (
line)->entries_list.tqe_prev; else (&(ep->cef_ent))
->tqh_last = (line)->entries_list.tqe_prev; *(line)->
entries_list.tqe_prev = (line)->entries_list.tqe_next; ; ;
} while (0)
;
105 free(line->buf);
106 free(line);
107 }
108 cvs_ent_free(ent);
109 }
110 }
111
112 (void)fclose(fp);
113 }
114
115 current_list = ep;
116 return (ep);
117}
118
119struct cvs_ent *
120cvs_ent_parse(const char *entry)
121{
122 int i;
123 struct tm t, dt;
124 struct cvs_ent *ent;
125 char *fields[CVS_ENTRIES_NFIELDS6], *buf, *sp, *dp, *p;
126
127 buf = sp = xstrdup(entry);
128 i = 0;
129 do {
130 dp = strchr(sp, CVS_ENTRIES_DELIM'/');
131 if (dp != NULL((void *)0))
132 *(dp++) = '\0';
133 fields[i++] = sp;
134 sp = dp;
135 } while (dp != NULL((void *)0) && i < CVS_ENTRIES_NFIELDS6);
136
137 if (i < CVS_ENTRIES_NFIELDS6)
138 fatal("missing fields in entry line '%s'", entry);
139
140 ent = xmalloc(sizeof(*ent));
141 ent->ce_buf = buf;
142
143 if (*fields[0] == '\0')
144 ent->ce_type = CVS_ENT_FILE1;
145 else if (*fields[0] == 'D')
146 ent->ce_type = CVS_ENT_DIR2;
147 else
148 ent->ce_type = CVS_ENT_NONE0;
149
150 ent->ce_status = CVS_ENT_REG0;
151 ent->ce_name = fields[1];
152 ent->ce_rev = NULL((void *)0);
153 ent->ce_date = -1;
154 ent->ce_tag = NULL((void *)0);
155 ent->ce_time = NULL((void *)0);
156
157 if (ent->ce_type == CVS_ENT_FILE1) {
158 if (*fields[2] == '-') {
159 ent->ce_status = CVS_ENT_REMOVED2;
160 sp = fields[2] + 1;
161 } else if (*fields[2] == CVS_SERVER_QUESTIONABLE'?') {
162 sp = NULL((void *)0);
163 ent->ce_status = CVS_ENT_UNKNOWN4;
164 } else {
165 sp = fields[2];
166 if (fields[2][0] == '0' && fields[2][1] == '\0')
167 ent->ce_status = CVS_ENT_ADDED1;
168 }
169
170 if (sp != NULL((void *)0)) {
171 if ((ent->ce_rev = rcsnum_parse(sp)) == NULL((void *)0)) {
172 fatal("failed to parse entry revision '%s'",
173 entry);
174 }
175 }
176
177 if (fields[3][0] == '\0' ||
178 strncmp(fields[3], CVS_DATE_DUMMY"dummy timestamp",
179 sizeof(CVS_DATE_DUMMY"dummy timestamp") - 1) == 0 ||
180 strncmp(fields[3], "Initial ", 8) == 0 ||
181 strcmp(fields[3], "Result of merge") == 0) {
182 ent->ce_mtime = CVS_DATE_DMSEC(time_t)-1;
183 } else if (cvs_server_active == 1 &&
184 strncmp(fields[3], CVS_SERVER_UNCHANGED"d[o.o]b",
185 strlen(CVS_SERVER_UNCHANGED"d[o.o]b")) == 0) {
186 ent->ce_mtime = CVS_SERVER_UPTODATE(time_t)-2;
187 } else {
188 p = fields[3];
189 if (strncmp(fields[3], "Result of merge+", 16) == 0)
190 p += 16;
191
192 ent->ce_time = xstrdup(p);
193
194 /* Date field can be a '+=' with remote to indicate
195 * conflict. In this case do nothing. */
196 if (strptime(p, "%a %b %d %T %Y", &t) != NULL((void *)0)) {
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 }
202 }
203 }
204
205 ent->ce_conflict = fields[3];
206 if ((dp = strchr(ent->ce_conflict, '+')) != NULL((void *)0))
207 *dp = '\0';
208 else
209 ent->ce_conflict = NULL((void *)0);
210
211 if (strcmp(fields[4], ""))
212 ent->ce_opts = fields[4];
213 else
214 ent->ce_opts = NULL((void *)0);
215
216 if (strcmp(fields[5], "")) {
217 switch (*fields[5]) {
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((void *)0);
227 break;
228 case 'T':
229 ent->ce_tag = fields[5] + 1;
230 break;
231 default:
232 fatal("invalid sticky entry");
233 }
234 }
235
236 return (ent);
237}
238
239struct cvs_ent *
240cvs_ent_get(CVSENTRIES *ep, const char *name)
241{
242 struct cvs_ent *ent;
243 struct cvs_ent_line *l;
244
245 l = ent_get_line(ep, name);
246 if (l == NULL((void *)0))
247 return (NULL((void *)0));
248
249 ent = cvs_ent_parse(l->buf);
250 return (ent);
251}
252
253void
254cvs_ent_close(CVSENTRIES *ep, int writefile)
255{
256 FILE *fp;
257 struct cvs_ent_line *l;
258 int dflag;
259
260 dflag = 1;
261 cvs_log(LP_TRACE4, "cvs_ent_close(%s, %d)", ep->cef_bpath, writefile);
262
263 if (cvs_cmdop == CVS_OP_EXPORT9)
6
Assuming 'cvs_cmdop' is not equal to CVS_OP_EXPORT
7
Taking false branch
264 writefile = 0;
265
266 fp = NULL((void *)0);
267 if (writefile
7.1
'writefile' is 1
)
8
Taking true branch
268 fp = fopen(ep->cef_bpath, "w");
269
270 while ((l = TAILQ_FIRST(&(ep->cef_ent))((&(ep->cef_ent))->tqh_first)) != NULL((void *)0)) {
9
Assuming the condition is true
10
Loop condition is true. Entering loop body
19
Loop condition is true. Entering loop body
271 if (fp
19.1
'fp' is not equal to NULL
!= NULL((void *)0)
) {
11
Assuming 'fp' is not equal to NULL
12
Taking true branch
20
Taking true branch
272 if (l->buf[0] == 'D')
13
Assuming the condition is false
14
Taking false branch
21
Use of memory after it is freed
273 dflag = 0;
274
275 fputs(l->buf, fp);
276 fputc('\n', fp);
277 }
278
279 TAILQ_REMOVE(&(ep->cef_ent), l, entries_list)do { if (((l)->entries_list.tqe_next) != ((void *)0)) (l)->
entries_list.tqe_next->entries_list.tqe_prev = (l)->entries_list
.tqe_prev; else (&(ep->cef_ent))->tqh_last = (l)->
entries_list.tqe_prev; *(l)->entries_list.tqe_prev = (l)->
entries_list.tqe_next; ; ; } while (0)
;
15
Assuming field 'tqe_next' is equal to null
16
Taking false branch
17
Loop condition is false. Exiting loop
280 free(l->buf);
281 free(l);
18
Memory is released
282 }
283
284 if (fp != NULL((void *)0)) {
285 if (dflag) {
286 fputc('D', fp);
287 fputc('\n', fp);
288 }
289 (void)fclose(fp);
290
291 if (rename(ep->cef_bpath, ep->cef_path) == -1)
292 fatal("cvs_ent_close: rename: `%s'->`%s': %s",
293 ep->cef_bpath, ep->cef_path, strerror(errno(*__errno())));
294
295 (void)unlink(ep->cef_lpath);
296 }
297
298 free(ep->cef_path);
299 free(ep->cef_bpath);
300 free(ep->cef_lpath);
301 free(ep);
302}
303
304void
305cvs_ent_add(CVSENTRIES *ep, const char *line)
306{
307 FILE *fp;
308 struct cvs_ent_line *l;
309 struct cvs_ent *ent;
310
311 if ((ent = cvs_ent_parse(line)) == NULL((void *)0))
312 fatal("cvs_ent_add: parsing failed '%s'", line);
313
314 l = ent_get_line(ep, ent->ce_name);
315 if (l != NULL((void *)0))
316 cvs_ent_remove(ep, ent->ce_name);
317
318 cvs_ent_free(ent);
319
320 if (cvs_server_active == 0)
321 cvs_log(LP_TRACE4, "cvs_ent_add(%s, %s)", ep->cef_path, line);
322
323 if ((fp = fopen(ep->cef_lpath, "a")) == NULL((void *)0))
324 fatal("cvs_ent_add: fopen: `%s': %s",
325 ep->cef_lpath, strerror(errno(*__errno())));
326
327 fputs("A ", fp);
328 fputs(line, fp);
329 fputc('\n', fp);
330
331 (void)fclose(fp);
332
333 l = xmalloc(sizeof(*l));
334 l->buf = xstrdup(line);
335 TAILQ_INSERT_TAIL(&(ep->cef_ent), l, entries_list)do { (l)->entries_list.tqe_next = ((void *)0); (l)->entries_list
.tqe_prev = (&(ep->cef_ent))->tqh_last; *(&(ep->
cef_ent))->tqh_last = (l); (&(ep->cef_ent))->tqh_last
= &(l)->entries_list.tqe_next; } while (0)
;
336}
337
338void
339cvs_ent_remove(CVSENTRIES *ep, const char *name)
340{
341 FILE *fp;
342 struct cvs_ent_line *l;
343
344 if (cvs_server_active == 0)
345 cvs_log(LP_TRACE4, "cvs_ent_remove(%s, %s)", ep->cef_path, name);
346
347 l = ent_get_line(ep, name);
348 if (l == NULL((void *)0))
349 return;
350
351 if ((fp = fopen(ep->cef_lpath, "a")) == NULL((void *)0))
352 fatal("cvs_ent_remove: fopen: `%s': %s", ep->cef_lpath,
353 strerror(errno(*__errno())));
354
355 fputs("R ", fp);
356 fputs(l->buf, fp);
357 fputc('\n', fp);
358
359 (void)fclose(fp);
360
361 TAILQ_REMOVE(&(ep->cef_ent), l, entries_list)do { if (((l)->entries_list.tqe_next) != ((void *)0)) (l)->
entries_list.tqe_next->entries_list.tqe_prev = (l)->entries_list
.tqe_prev; else (&(ep->cef_ent))->tqh_last = (l)->
entries_list.tqe_prev; *(l)->entries_list.tqe_prev = (l)->
entries_list.tqe_next; ; ; } while (0)
;
362 free(l->buf);
363 free(l);
364}
365
366/*
367 * cvs_ent_line_str()
368 *
369 * Build CVS/Entries line.
370 *
371 */
372void
373cvs_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);
383}
384
385void
386cvs_ent_free(struct cvs_ent *ent)
387{
388 free(ent->ce_rev);
389 free(ent->ce_time);
390 free(ent->ce_buf);
391 free(ent);
392}
393
394static struct cvs_ent_line *
395ent_get_line(CVSENTRIES *ep, const char *name)
396{
397 char *p, *s;
398 struct cvs_ent_line *l;
399
400 TAILQ_FOREACH(l, &(ep->cef_ent), entries_list)for((l) = ((&(ep->cef_ent))->tqh_first); (l) != ((void
*)0); (l) = ((l)->entries_list.tqe_next))
{
401 if (l->buf[0] == 'D')
402 p = &(l->buf[2]);
403 else
404 p = &(l->buf[1]);
405
406 if ((s = strchr(p, '/')) == NULL((void *)0))
407 fatal("ent_get_line: bad entry line '%s'", l->buf);
408
409 *s = '\0';
410
411 if (!strcmp(p, name)) {
412 *s = '/';
413 return (l);
414 }
415
416 *s = '/';
417 }
418
419 return (NULL((void *)0));
420}
421
422void
423cvs_parse_tagfile(char *dir, char **tagp, char **datep, int *nbp)
424{
425 FILE *fp;
426 int i, linenum;
427 size_t len;
428 struct tm datetm;
429 char linebuf[128], tagpath[PATH_MAX1024];
430
431 cvs_directory_date = -1;
432
433 if (tagp != NULL((void *)0))
434 *tagp = NULL((void *)0);
435
436 if (datep != NULL((void *)0))
437 *datep = NULL((void *)0);
438
439 if (nbp != NULL((void *)0))
440 *nbp = 0;
441
442 i = snprintf(tagpath, PATH_MAX1024, "%s/%s", dir, CVS_PATH_TAG"CVS" "/Tag");
443 if (i < 0 || i >= PATH_MAX1024)
444 return;
445
446 if ((fp = fopen(tagpath, "r")) == NULL((void *)0)) {
447 if (errno(*__errno()) != ENOENT2)
448 cvs_log(LP_NOTICE0, "failed to open `%s' : %s", tagpath,
449 strerror(errno(*__errno())));
450 return;
451 }
452
453 linenum = 0;
454
455 while (fgets(linebuf, (int)sizeof(linebuf), fp) != NULL((void *)0)) {
456 linenum++;
457 if ((len = strlen(linebuf)) == 0)
458 continue;
459 if (linebuf[len - 1] != '\n') {
460 cvs_log(LP_NOTICE0, "line too long in `%s:%d'",
461 tagpath, linenum);
462 break;
463 }
464 linebuf[--len] = '\0';
465
466 switch (*linebuf) {
467 case 'T':
468 if (tagp != NULL((void *)0))
469 *tagp = xstrdup(linebuf + 1);
470 break;
471 case 'D':
472 if (sscanf(linebuf + 1, "%d.%d.%d.%d.%d.%d",
473 &datetm.tm_year, &datetm.tm_mon, &datetm.tm_mday,
474 &datetm.tm_hour, &datetm.tm_min, &datetm.tm_sec) !=
475 6)
476 fatal("wrong date specification");
477 datetm.tm_year -= 1900;
478 datetm.tm_mon -= 1;
479
480 cvs_directory_date = timegm(&datetm);
481
482 if (datep != NULL((void *)0))
483 *datep = xstrdup(linebuf + 1);
484 break;
485 case 'N':
486 if (tagp != NULL((void *)0))
487 *tagp = xstrdup(linebuf + 1);
488 if (nbp != NULL((void *)0))
489 *nbp = 1;
490 break;
491 default:
492 break;
493 }
494 }
495 if (ferror(fp)(!__isthreaded ? (((fp)->_flags & 0x0040) != 0) : (ferror
)(fp))
)
496 cvs_log(LP_NOTICE0, "failed to read line from `%s'", tagpath);
497
498 (void)fclose(fp);
499}
500
501void
502cvs_write_tagfile(const char *dir, char *tag, char *date)
503{
504 FILE *fp;
505 RCSNUM *rev;
506 char tagpath[PATH_MAX1024];
507 char sticky[CVS_REV_BUFSZ32];
508 struct tm datetm;
509 int i;
510
511 cvs_log(LP_TRACE4, "cvs_write_tagfile(%s, %s, %s)", dir,
512 tag != NULL((void *)0) ? tag : "", date != NULL((void *)0) ? date : "");
513
514 if (cvs_noexec == 1)
515 return;
516
517 i = snprintf(tagpath, PATH_MAX1024, "%s/%s", dir, CVS_PATH_TAG"CVS" "/Tag");
518 if (i < 0 || i >= PATH_MAX1024)
519 return;
520
521 if (tag != NULL((void *)0) || cvs_specified_date != -1 ||
522 cvs_directory_date != -1) {
523 if ((fp = fopen(tagpath, "w+")) == NULL((void *)0)) {
524 if (errno(*__errno()) != ENOENT2) {
525 cvs_log(LP_NOTICE0, "failed to open `%s' : %s",
526 tagpath, strerror(errno(*__errno())));
527 }
528 return;
529 }
530
531 if (tag != NULL((void *)0)) {
532 if ((rev = rcsnum_parse(tag)) != NULL((void *)0)) {
533 (void)xsnprintf(sticky, sizeof(sticky),
534 "N%s", tag);
535 free(rev);
536 } else {
537 (void)xsnprintf(sticky, sizeof(sticky),
538 "T%s", tag);
539 }
540 } else {
541 if (cvs_specified_date != -1)
542 gmtime_r(&cvs_specified_date, &datetm);
543 else
544 gmtime_r(&cvs_directory_date, &datetm);
545 (void)strftime(sticky, sizeof(sticky),
546 "D"CVS_DATE_FMT"%Y.%m.%d.%H.%M.%S", &datetm);
547 }
548
549 (void)fprintf(fp, "%s\n", sticky);
550 (void)fclose(fp);
551 }
552}