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 if (fileattr_default_attrs) free (fileattr_default_attrs);
143 fileattr_default_attrs = xstrdup (p);
147 /* Unrecognized type, we want to just preserve the line without
148 changing it, for future expansion. */
151 new = (struct unrecog *) xmalloc (sizeof (struct unrecog));
152 new->line = xstrdup (line);
153 new->next = unrecog_head;
158 error (0, errno, "cannot read %s", fname);
162 error (0, errno, "cannot close %s", fname);
168 fileattr_get (filename, attrname)
169 const char *filename;
170 const char *attrname;
173 size_t attrname_len = strlen (attrname);
176 if (attrlist == NULL)
178 if (attrlist == NULL)
179 /* Either nothing has any attributes, or fileattr_read already printed
183 if (filename == NULL)
184 p = fileattr_default_attrs;
187 node = findnode (attrlist, filename);
189 /* A file not mentioned has no attributes. */
195 if (strncmp (attrname, p, attrname_len) == 0
196 && p[attrname_len] == '=')
199 return p + attrname_len + 1;
206 /* The file doesn't have this attribute. */
211 fileattr_get0 (filename, attrname)
212 const char *filename;
213 const char *attrname;
219 cp = fileattr_get (filename, attrname);
222 cpend = strchr (cp, ';');
224 cpend = cp + strlen (cp);
225 retval = xmalloc (cpend - cp + 1);
226 strncpy (retval, cp, cpend - cp);
227 retval[cpend - cp] = '\0';
232 fileattr_modify (list, attrname, attrval, namevalsep, entsep)
234 const char *attrname;
241 size_t attrname_len = strlen (attrname);
243 /* Portion of list before the attribute to be replaced. */
246 /* Portion of list after the attribute to be replaced. */
255 /* post is NULL unless set otherwise. */
261 p2 = strchr (p, entsep);
270 if (strncmp (attrname, p, attrname_len) == 0
271 && p[attrname_len] == namevalsep)
276 /* Don't include the preceding entsep. */
289 if (preend == pre && attrval == NULL && post == p2)
292 retval = xmalloc ((preend - pre)
294 + (attrval == NULL ? 0 : (attrname_len + 1
301 strncpy (retval, pre, preend - pre);
302 rp = retval + (preend - pre);
311 strcat (retval, attrname);
312 rp = retval + strlen (retval);
314 strcpy (rp, attrval);
318 rp = retval + strlen (retval);
319 if (preend != pre || attrval != NULL)
321 strncpy (rp, post, p2 - post);
329 fileattr_set (filename, attrname, attrval)
330 const char *filename;
331 const char *attrname;
337 if (filename == NULL)
339 p = fileattr_modify (fileattr_default_attrs, attrname, attrval,
341 if (fileattr_default_attrs != NULL)
342 free (fileattr_default_attrs);
343 fileattr_default_attrs = p;
347 if (attrlist == NULL)
349 if (attrlist == NULL)
351 /* Not sure this is a graceful way to handle things
352 in the case where fileattr_read was unable to read the file. */
353 /* No attributes existed previously. */
354 attrlist = getlist ();
357 node = findnode (attrlist, filename);
361 /* Attempt to remove an attribute which wasn't there. */
364 /* First attribute for this file. */
366 node->type = FILEATTR;
367 node->delproc = fileattr_delproc;
368 node->key = xstrdup (filename);
369 node->data = xmalloc (strlen (attrname) + 1 + strlen (attrval) + 1);
370 strcpy (node->data, attrname);
371 strcat (node->data, "=");
372 strcat (node->data, attrval);
373 addnode (attrlist, node);
376 p = fileattr_modify (node->data, attrname, attrval, '=', ';');
389 fileattr_getall (filename)
390 const char *filename;
395 if (attrlist == NULL)
397 if (attrlist == NULL)
398 /* Either nothing has any attributes, or fileattr_read already printed
402 if (filename == NULL)
403 p = fileattr_default_attrs;
406 node = findnode (attrlist, filename);
408 /* A file not mentioned has no attributes. */
416 fileattr_setall (filename, attrs)
417 const char *filename;
422 if (filename == NULL)
424 if (fileattr_default_attrs != NULL)
425 free (fileattr_default_attrs);
426 fileattr_default_attrs = xstrdup (attrs);
430 if (attrlist == NULL)
432 if (attrlist == NULL)
434 /* Not sure this is a graceful way to handle things
435 in the case where fileattr_read was unable to read the file. */
436 /* No attributes existed previously. */
437 attrlist = getlist ();
440 node = findnode (attrlist, filename);
443 /* The file had no attributes. Add them if we have any to add. */
447 node->type = FILEATTR;
448 node->delproc = fileattr_delproc;
449 node->key = xstrdup (filename);
450 node->data = xstrdup (attrs);
451 addnode (attrlist, node);
461 node->data = xstrdup (attrs);
469 fileattr_newfile (filename)
470 const char *filename;
474 if (attrlist == NULL)
477 if (fileattr_default_attrs == NULL)
480 if (attrlist == NULL)
482 /* Not sure this is a graceful way to handle things
483 in the case where fileattr_read was unable to read the file. */
484 /* No attributes existed previously. */
485 attrlist = getlist ();
489 node->type = FILEATTR;
490 node->delproc = fileattr_delproc;
491 node->key = xstrdup (filename);
492 node->data = xstrdup (fileattr_default_attrs);
493 addnode (attrlist, node);
498 writeattr_proc (node, data)
502 FILE *fp = (FILE *)data;
504 fputs (node->key, fp);
506 fputs (node->data, fp);
525 /* If NULL was passed to fileattr_startdir, then it isn't kosher to set
527 assert (fileattr_stored_repos != NULL);
529 fname = xmalloc (strlen (fileattr_stored_repos)
531 + sizeof (CVSREP_FILEATTR)
534 strcpy (fname, fileattr_stored_repos);
536 strcat (fname, CVSREP_FILEATTR);
538 if (list_isempty (attrlist)
539 && fileattr_default_attrs == NULL
540 && unrecog_head == NULL)
542 /* There are no attributes. */
543 if (unlink_file (fname) < 0)
545 if (!existence_error (errno))
547 error (0, errno, "cannot remove %s", fname);
551 /* Now remove CVSREP directory, if empty. The main reason we bother
552 is that CVS 1.6 and earlier will choke if a CVSREP directory
553 exists, so provide the user a graceful way to remove it. */
554 strcpy (fname, fileattr_stored_repos);
556 strcat (fname, CVSREP);
557 if (CVS_RMDIR (fname) < 0)
559 if (errno != ENOTEMPTY
561 /* Don't know why we would be here if there is no CVSREP
562 directory, but it seemed to be happening anyway, so
564 && !existence_error (errno))
565 error (0, errno, "cannot remove %s", fname);
572 omask = umask (cvsumask);
573 fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE);
576 if (existence_error (errno))
578 /* Maybe the CVSREP directory doesn't exist. Try creating it. */
581 repname = xmalloc (strlen (fileattr_stored_repos)
585 strcpy (repname, fileattr_stored_repos);
586 strcat (repname, "/");
587 strcat (repname, CVSREP);
589 if (CVS_MKDIR (repname, 0777) < 0 && errno != EEXIST)
591 error (0, errno, "cannot make directory %s", repname);
592 (void) umask (omask);
599 fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE);
603 error (0, errno, "cannot write %s", fname);
604 (void) umask (omask);
609 (void) umask (omask);
611 /* First write the "F" attributes. */
612 walklist (attrlist, writeattr_proc, fp);
614 /* Then the "D" attribute. */
615 if (fileattr_default_attrs != NULL)
618 fputs (fileattr_default_attrs, fp);
622 /* Then any other attributes. */
623 for (p = unrecog_head; p != NULL; p = p->next)
630 error (0, errno, "cannot close %s", fname);
638 /* Note that attrs_modified will ordinarily be zero, but there are
639 a few cases in which fileattr_write will fail to zero it (if
640 noexec is set, or error conditions). This probably is the way
643 if (fileattr_stored_repos != NULL)
644 free (fileattr_stored_repos);
645 fileattr_stored_repos = NULL;
646 if (fileattr_default_attrs != NULL)
647 free (fileattr_default_attrs);
648 fileattr_default_attrs = NULL;
651 struct unrecog *p = unrecog_head;
652 unrecog_head = p->next;