1 /* Implementation for file attribute munging features.
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details. */
17 static void fileattr_read (void);
18 static int writeattr_proc (Node
*, void *);
20 /* Where to look for CVSREP_FILEATTR. */
21 static char *fileattr_stored_repos
;
23 /* The in-memory attributes. */
24 static List
*attrlist
;
25 static char *fileattr_default_attrs
;
26 /* We have already tried to read attributes and failed in this directory
27 (for example, there is no CVSREP_FILEATTR file). */
28 static int attr_read_attempted
;
30 /* Have the in-memory attributes been modified since we read them? */
31 static int attrs_modified
;
33 /* More in-memory attributes: linked list of unrecognized
34 fileattr lines. We pass these on unchanged. */
39 static struct unrecog
*unrecog_head
;
43 /* Note that if noone calls fileattr_get, this is very cheap. No stat(),
44 no open(), no nothing. */
46 fileattr_startdir (const char *repos
)
48 assert (fileattr_stored_repos
== NULL
);
49 fileattr_stored_repos
= xstrdup (repos
);
50 assert (attrlist
== NULL
);
51 attr_read_attempted
= 0;
52 assert (unrecog_head
== NULL
);
58 fileattr_delproc (Node
*node
)
60 assert (node
->data
!= NULL
);
65 /* Read all the attributes for the current directory into memory. */
74 /* If there are no attributes, don't waste time repeatedly looking
75 for the CVSREP_FILEATTR file. */
76 if (attr_read_attempted
)
79 /* If NULL was passed to fileattr_startdir, then it isn't kosher to look
81 assert (fileattr_stored_repos
!= NULL
);
83 fname
= Xasprintf ("%s/%s", fileattr_stored_repos
, CVSREP_FILEATTR
);
85 attr_read_attempted
= 1;
86 fp
= CVS_FOPEN (fname
, FOPEN_BINARY_READ
);
89 if (!existence_error (errno
))
90 error (0, errno
, "cannot read %s", fname
);
94 attrlist
= getlist ();
97 nread
= getline (&line
, &line_len
, fp
);
100 /* Remove trailing newline.
101 * It is okay to reference line[nread - 1] here, since getline must
102 * always return 1 character or EOF, but we need to verify that the
103 * character we eat is the newline, since getline can return a line
104 * w/o a newline just before returning EOF.
106 if (line
[nread
- 1] == '\n') line
[nread
- 1] = '\0';
112 p
= strchr (line
, '\t');
115 "file attribute database corruption: tab missing in %s",
116 primary_root_inverse_translate (fname
));
118 newnode
= getnode ();
119 newnode
->type
= FILEATTR
;
120 newnode
->delproc
= fileattr_delproc
;
121 newnode
->key
= xstrdup (line
+ 1);
122 newnode
->data
= xstrdup (p
);
123 if (addnode (attrlist
, newnode
) != 0)
124 /* If the same filename appears twice in the file, discard
125 any line other than the first for that filename. This
126 is the way that CVS has behaved since file attributes
127 were first introduced. */
130 else if (line
[0] == 'D')
133 /* Currently nothing to skip here, but for future expansion,
134 ignore anything located here. */
135 p
= strchr (line
, '\t');
138 "file attribute database corruption: tab missing in %s",
141 if (fileattr_default_attrs
) free (fileattr_default_attrs
);
142 fileattr_default_attrs
= xstrdup (p
);
146 /* Unrecognized type, we want to just preserve the line without
147 changing it, for future expansion. */
150 new = xmalloc (sizeof (struct unrecog
));
151 new->line
= xstrdup (line
);
152 new->next
= unrecog_head
;
157 error (0, errno
, "cannot read %s", fname
);
161 error (0, errno
, "cannot close %s", fname
);
169 fileattr_get (const char *filename
, const char *attrname
)
172 size_t attrname_len
= strlen (attrname
);
175 if (attrlist
== NULL
)
177 if (attrlist
== NULL
)
178 /* Either nothing has any attributes, or fileattr_read already printed
182 if (filename
== NULL
)
183 p
= fileattr_default_attrs
;
186 node
= findnode (attrlist
, filename
);
188 /* A file not mentioned has no attributes. */
194 if (strncmp (attrname
, p
, attrname_len
) == 0
195 && p
[attrname_len
] == '=')
198 return p
+ attrname_len
+ 1;
205 /* The file doesn't have this attribute. */
212 fileattr_get0 (const char *filename
, const char *attrname
)
218 cp
= fileattr_get (filename
, attrname
);
221 cpend
= strchr (cp
, ';');
223 cpend
= cp
+ strlen (cp
);
224 retval
= xmalloc (cpend
- cp
+ 1);
225 strncpy (retval
, cp
, cpend
- cp
);
226 retval
[cpend
- cp
] = '\0';
233 fileattr_modify (char *list
, const char *attrname
, const char *attrval
, int namevalsep
, int entsep
)
237 size_t attrname_len
= strlen (attrname
);
239 /* Portion of list before the attribute to be replaced. */
242 /* Portion of list after the attribute to be replaced. */
251 /* post is NULL unless set otherwise. */
257 p2
= strchr (p
, entsep
);
266 if (strncmp (attrname
, p
, attrname_len
) == 0
267 && p
[attrname_len
] == namevalsep
)
272 /* Don't include the preceding entsep. */
285 if (preend
== pre
&& attrval
== NULL
&& post
== p2
)
288 retval
= xmalloc ((preend
- pre
)
290 + (attrval
== NULL
? 0 : (attrname_len
+ 1
297 strncpy (retval
, pre
, preend
- pre
);
298 rp
= retval
+ (preend
- pre
);
307 strcat (retval
, attrname
);
308 rp
= retval
+ strlen (retval
);
310 strcpy (rp
, attrval
);
314 rp
= retval
+ strlen (retval
);
315 if (preend
!= pre
|| attrval
!= NULL
)
317 strncpy (rp
, post
, p2
- post
);
325 fileattr_set (const char *filename
, const char *attrname
, const char *attrval
)
330 if (filename
== NULL
)
332 p
= fileattr_modify (fileattr_default_attrs
, attrname
, attrval
,
334 if (fileattr_default_attrs
!= NULL
)
335 free (fileattr_default_attrs
);
336 fileattr_default_attrs
= p
;
340 if (attrlist
== NULL
)
342 if (attrlist
== NULL
)
344 /* Not sure this is a graceful way to handle things
345 in the case where fileattr_read was unable to read the file. */
346 /* No attributes existed previously. */
347 attrlist
= getlist ();
350 node
= findnode (attrlist
, filename
);
354 /* Attempt to remove an attribute which wasn't there. */
357 /* First attribute for this file. */
359 node
->type
= FILEATTR
;
360 node
->delproc
= fileattr_delproc
;
361 node
->key
= xstrdup (filename
);
362 node
->data
= Xasprintf ("%s=%s", attrname
, attrval
);
363 addnode (attrlist
, node
);
366 p
= fileattr_modify (node
->data
, attrname
, attrval
, '=', ';');
381 fileattr_getall (const char *filename
)
386 if (attrlist
== NULL
)
388 if (attrlist
== NULL
)
389 /* Either nothing has any attributes, or fileattr_read already printed
393 if (filename
== NULL
)
394 p
= fileattr_default_attrs
;
397 node
= findnode (attrlist
, filename
);
399 /* A file not mentioned has no attributes. */
409 fileattr_setall (const char *filename
, const char *attrs
)
413 if (filename
== NULL
)
415 if (fileattr_default_attrs
!= NULL
)
416 free (fileattr_default_attrs
);
417 fileattr_default_attrs
= xstrdup (attrs
);
421 if (attrlist
== NULL
)
423 if (attrlist
== NULL
)
425 /* Not sure this is a graceful way to handle things
426 in the case where fileattr_read was unable to read the file. */
427 /* No attributes existed previously. */
428 attrlist
= getlist ();
431 node
= findnode (attrlist
, filename
);
434 /* The file had no attributes. Add them if we have any to add. */
438 node
->type
= FILEATTR
;
439 node
->delproc
= fileattr_delproc
;
440 node
->key
= xstrdup (filename
);
441 node
->data
= xstrdup (attrs
);
442 addnode (attrlist
, node
);
452 node
->data
= xstrdup (attrs
);
462 fileattr_newfile (const char *filename
)
466 if (attrlist
== NULL
)
469 if (fileattr_default_attrs
== NULL
)
472 if (attrlist
== NULL
)
474 /* Not sure this is a graceful way to handle things
475 in the case where fileattr_read was unable to read the file. */
476 /* No attributes existed previously. */
477 attrlist
= getlist ();
481 node
->type
= FILEATTR
;
482 node
->delproc
= fileattr_delproc
;
483 node
->key
= xstrdup (filename
);
484 node
->data
= xstrdup (fileattr_default_attrs
);
485 addnode (attrlist
, node
);
492 writeattr_proc (Node
*node
, void *data
)
494 FILE *fp
= (FILE *)data
;
496 fputs (node
->key
, fp
);
498 fputs (node
->data
, fp
);
506 * callback proc to run a script when fileattrs are updated.
509 postwatch_proc (const char *repository
, const char *filter
, void *closure
)
512 const char *srepos
= Short_Repository (repository
);
514 TRACE (TRACE_FUNCTION
, "postwatch_proc (%s, %s)", repository
, filter
);
521 * Cast any NULL arguments as appropriate pointers as this is an
522 * stdarg function and we need to be certain the caller gets what
525 cmdline
= format_cmdline (
526 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
528 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
530 "c", "s", cvs_cmd_name
,
531 #ifdef SERVER_SUPPORT
532 "R", "s", referrer
? referrer
->original
: "NONE",
533 #endif /* SERVER_SUPPORT */
535 "r", "s", current_parsed_root
->directory
,
538 if (!cmdline
|| !strlen (cmdline
))
540 if (cmdline
) free (cmdline
);
541 error (0, 0, "postwatch proc resolved to the empty string!");
549 /* FIXME - read the comment in verifymsg_proc() about why we use abs()
550 * below() and shouldn't.
552 return abs (run_exec (RUN_TTY
, RUN_TTY
, RUN_TTY
,
553 RUN_NORMAL
| RUN_SIGIGNORE
));
559 fileattr_write (void)
572 /* If NULL was passed to fileattr_startdir, then it isn't kosher to set
574 assert (fileattr_stored_repos
!= NULL
);
576 fname
= Xasprintf ("%s/%s", fileattr_stored_repos
, CVSREP_FILEATTR
);
578 if (list_isempty (attrlist
)
579 && fileattr_default_attrs
== NULL
580 && unrecog_head
== NULL
)
582 /* There are no attributes. */
583 if (unlink_file (fname
) < 0)
585 if (!existence_error (errno
))
587 error (0, errno
, "cannot remove %s", fname
);
591 /* Now remove CVSREP directory, if empty. The main reason we bother
592 is that CVS 1.6 and earlier will choke if a CVSREP directory
593 exists, so provide the user a graceful way to remove it. */
594 strcpy (fname
, fileattr_stored_repos
);
596 strcat (fname
, CVSREP
);
597 if (CVS_RMDIR (fname
) < 0)
599 if (errno
!= ENOTEMPTY
601 /* Don't know why we would be here if there is no CVSREP
602 directory, but it seemed to be happening anyway, so
604 && !existence_error (errno
))
605 error (0, errno
, "cannot remove %s", fname
);
612 omask
= umask (cvsumask
);
613 fp
= CVS_FOPEN (fname
, FOPEN_BINARY_WRITE
);
616 if (existence_error (errno
))
618 /* Maybe the CVSREP directory doesn't exist. Try creating it. */
621 repname
= Xasprintf ("%s/%s", fileattr_stored_repos
, CVSREP
);
623 if (CVS_MKDIR (repname
, 0777) < 0 && errno
!= EEXIST
)
625 error (0, errno
, "cannot make directory %s", repname
);
626 (void) umask (omask
);
633 fp
= CVS_FOPEN (fname
, FOPEN_BINARY_WRITE
);
637 error (0, errno
, "cannot write %s", fname
);
638 (void) umask (omask
);
643 (void) umask (omask
);
645 /* First write the "F" attributes. */
646 walklist (attrlist
, writeattr_proc
, fp
);
648 /* Then the "D" attribute. */
649 if (fileattr_default_attrs
!= NULL
)
652 fputs (fileattr_default_attrs
, fp
);
656 /* Then any other attributes. */
657 for (p
= unrecog_head
; p
!= NULL
; p
= p
->next
)
664 error (0, errno
, "cannot close %s", fname
);
668 Parse_Info (CVSROOTADM_POSTWATCH
, fileattr_stored_repos
, postwatch_proc
,
677 /* Note that attrs_modified will ordinarily be zero, but there are
678 a few cases in which fileattr_write will fail to zero it (if
679 noexec is set, or error conditions). This probably is the way
682 if (fileattr_stored_repos
!= NULL
)
683 free (fileattr_stored_repos
);
684 fileattr_stored_repos
= NULL
;
685 if (fileattr_default_attrs
!= NULL
)
686 free (fileattr_default_attrs
);
687 fileattr_default_attrs
= NULL
;
690 struct unrecog
*p
= unrecog_head
;
691 unrecog_head
= p
->next
;