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. */
18 static void fileattr_read PROTO ((void));
19 static int writeattr_proc PROTO ((Node *, void *));
21 /* Where to look for CVSREP_FILEATTR. */
22 static char *fileattr_stored_repos;
24 /* The in-memory attributes. */
25 static List *attrlist;
26 static char *fileattr_default_attrs;
27 /* We have already tried to read attributes and failed in this directory
28 (for example, there is no CVSREP_FILEATTR file). */
29 static int attr_read_attempted;
31 /* Have the in-memory attributes been modified since we read them? */
32 static int attrs_modified;
34 /* More in-memory attributes: linked list of unrecognized
35 fileattr lines. We pass these on unchanged. */
40 static struct unrecog *unrecog_head;
42 /* Note that if noone calls fileattr_get, this is very cheap. No stat(),
43 no open(), no nothing. */
45 fileattr_startdir (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);
56 fileattr_delproc (node)
59 assert (node->data != NULL);
64 /* Read all the attributes for the current directory into memory. */
73 /* If there are no attributes, don't waste time repeatedly looking
74 for the CVSREP_FILEATTR file. */
75 if (attr_read_attempted)
78 /* If NULL was passed to fileattr_startdir, then it isn't kosher to look
80 assert (fileattr_stored_repos != NULL);
82 fname = xmalloc (strlen (fileattr_stored_repos)
84 + sizeof (CVSREP_FILEATTR)
87 strcpy (fname, fileattr_stored_repos);
89 strcat (fname, CVSREP_FILEATTR);
91 attr_read_attempted = 1;
92 fp = CVS_FOPEN (fname, FOPEN_BINARY_READ);
95 if (!existence_error (errno))
96 error (0, errno, "cannot read %s", fname);
100 attrlist = getlist ();
103 nread = getline (&line, &line_len, fp);
106 /* Remove trailing newline. */
107 line[nread - 1] = '\0';
113 p = strchr (line, '\t');
116 "file attribute database corruption: tab missing in %s",
119 newnode = getnode ();
120 newnode->type = FILEATTR;
121 newnode->delproc = fileattr_delproc;
122 newnode->key = xstrdup (line + 1);
123 newnode->data = xstrdup (p);
124 if (addnode (attrlist, newnode) != 0)
125 /* If the same filename appears twice in the file, discard
126 any line other than the first for that filename. This
127 is the way that CVS has behaved since file attributes
128 were first introduced. */
131 else if (line[0] == 'D')
134 /* Currently nothing to skip here, but for future expansion,
135 ignore anything located here. */
136 p = strchr (line, '\t');
139 "file attribute database corruption: tab missing in %s",
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 = (struct unrecog *) 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);
167 fileattr_get (filename, attrname)
168 const char *filename;
169 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. */
210 fileattr_get0 (filename, attrname)
211 const char *filename;
212 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';
231 fileattr_modify (list, attrname, attrval, namevalsep, entsep)
233 const char *attrname;
240 size_t attrname_len = strlen (attrname);
242 /* Portion of list before the attribute to be replaced. */
245 /* Portion of list after the attribute to be replaced. */
254 /* post is NULL unless set otherwise. */
260 p2 = strchr (p, entsep);
269 if (strncmp (attrname, p, attrname_len) == 0
270 && p[attrname_len] == namevalsep)
275 /* Don't include the preceding entsep. */
288 if (preend == pre && attrval == NULL && post == p2)
291 retval = xmalloc ((preend - pre)
293 + (attrval == NULL ? 0 : (attrname_len + 1
300 strncpy (retval, pre, preend - pre);
301 rp = retval + (preend - pre);
310 strcat (retval, attrname);
311 rp = retval + strlen (retval);
313 strcpy (rp, attrval);
317 rp = retval + strlen (retval);
318 if (preend != pre || attrval != NULL)
320 strncpy (rp, post, p2 - post);
328 fileattr_set (filename, attrname, attrval)
329 const char *filename;
330 const char *attrname;
336 if (filename == NULL)
338 p = fileattr_modify (fileattr_default_attrs, attrname, attrval,
340 if (fileattr_default_attrs != NULL)
341 free (fileattr_default_attrs);
342 fileattr_default_attrs = p;
346 if (attrlist == NULL)
348 if (attrlist == NULL)
350 /* Not sure this is a graceful way to handle things
351 in the case where fileattr_read was unable to read the file. */
352 /* No attributes existed previously. */
353 attrlist = getlist ();
356 node = findnode (attrlist, filename);
360 /* Attempt to remove an attribute which wasn't there. */
363 /* First attribute for this file. */
365 node->type = FILEATTR;
366 node->delproc = fileattr_delproc;
367 node->key = xstrdup (filename);
368 node->data = xmalloc (strlen (attrname) + 1 + strlen (attrval) + 1);
369 strcpy (node->data, attrname);
370 strcat (node->data, "=");
371 strcat (node->data, attrval);
372 addnode (attrlist, node);
375 p = fileattr_modify (node->data, attrname, attrval, '=', ';');
388 fileattr_getall (filename)
389 const char *filename;
394 if (attrlist == NULL)
396 if (attrlist == NULL)
397 /* Either nothing has any attributes, or fileattr_read already printed
401 if (filename == NULL)
402 p = fileattr_default_attrs;
405 node = findnode (attrlist, filename);
407 /* A file not mentioned has no attributes. */
415 fileattr_setall (filename, attrs)
416 const char *filename;
421 if (filename == NULL)
423 if (fileattr_default_attrs != NULL)
424 free (fileattr_default_attrs);
425 fileattr_default_attrs = xstrdup (attrs);
429 if (attrlist == NULL)
431 if (attrlist == NULL)
433 /* Not sure this is a graceful way to handle things
434 in the case where fileattr_read was unable to read the file. */
435 /* No attributes existed previously. */
436 attrlist = getlist ();
439 node = findnode (attrlist, filename);
442 /* The file had no attributes. Add them if we have any to add. */
446 node->type = FILEATTR;
447 node->delproc = fileattr_delproc;
448 node->key = xstrdup (filename);
449 node->data = xstrdup (attrs);
450 addnode (attrlist, node);
460 node->data = xstrdup (attrs);
468 fileattr_newfile (filename)
469 const char *filename;
473 if (attrlist == NULL)
476 if (fileattr_default_attrs == NULL)
479 if (attrlist == NULL)
481 /* Not sure this is a graceful way to handle things
482 in the case where fileattr_read was unable to read the file. */
483 /* No attributes existed previously. */
484 attrlist = getlist ();
488 node->type = FILEATTR;
489 node->delproc = fileattr_delproc;
490 node->key = xstrdup (filename);
491 node->data = xstrdup (fileattr_default_attrs);
492 addnode (attrlist, node);
497 writeattr_proc (node, data)
501 FILE *fp = (FILE *)data;
503 fputs (node->key, fp);
505 fputs (node->data, fp);
524 /* If NULL was passed to fileattr_startdir, then it isn't kosher to set
526 assert (fileattr_stored_repos != NULL);
528 fname = xmalloc (strlen (fileattr_stored_repos)
530 + sizeof (CVSREP_FILEATTR)
533 strcpy (fname, fileattr_stored_repos);
535 strcat (fname, CVSREP_FILEATTR);
537 if (list_isempty (attrlist)
538 && fileattr_default_attrs == NULL
539 && unrecog_head == NULL)
541 /* There are no attributes. */
542 if (unlink_file (fname) < 0)
544 if (!existence_error (errno))
546 error (0, errno, "cannot remove %s", fname);
550 /* Now remove CVSREP directory, if empty. The main reason we bother
551 is that CVS 1.6 and earlier will choke if a CVSREP directory
552 exists, so provide the user a graceful way to remove it. */
553 strcpy (fname, fileattr_stored_repos);
555 strcat (fname, CVSREP);
556 if (CVS_RMDIR (fname) < 0)
558 if (errno != ENOTEMPTY
560 /* Don't know why we would be here if there is no CVSREP
561 directory, but it seemed to be happening anyway, so
563 && !existence_error (errno))
564 error (0, errno, "cannot remove %s", fname);
571 omask = umask (cvsumask);
572 fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE);
575 if (existence_error (errno))
577 /* Maybe the CVSREP directory doesn't exist. Try creating it. */
580 repname = xmalloc (strlen (fileattr_stored_repos)
584 strcpy (repname, fileattr_stored_repos);
585 strcat (repname, "/");
586 strcat (repname, CVSREP);
588 if (CVS_MKDIR (repname, 0777) < 0 && errno != EEXIST)
590 error (0, errno, "cannot make directory %s", repname);
591 (void) umask (omask);
597 fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE);
601 error (0, errno, "cannot write %s", fname);
602 (void) umask (omask);
606 (void) umask (omask);
608 /* First write the "F" attributes. */
609 walklist (attrlist, writeattr_proc, fp);
611 /* Then the "D" attribute. */
612 if (fileattr_default_attrs != NULL)
615 fputs (fileattr_default_attrs, fp);
619 /* Then any other attributes. */
620 for (p = unrecog_head; p != NULL; p = p->next)
627 error (0, errno, "cannot close %s", fname);
635 /* Note that attrs_modified will ordinarily be zero, but there are
636 a few cases in which fileattr_write will fail to zero it (if
637 noexec is set, or error conditions). This probably is the way
640 if (fileattr_stored_repos != NULL)
641 free (fileattr_stored_repos);
642 fileattr_stored_repos = NULL;
643 if (fileattr_default_attrs != NULL)
644 free (fileattr_default_attrs);
645 fileattr_default_attrs = NULL;
648 struct unrecog *p = unrecog_head;
649 unrecog_head = p->next;