]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/cvs/src/fileattr.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / cvs / src / fileattr.c
1 /* Implementation for file attribute munging features.
2
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)
6    any later version.
7
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.  */
12
13 #include "cvs.h"
14 #include "getline.h"
15 #include "fileattr.h"
16 #include <assert.h>
17
18 static void fileattr_read PROTO ((void));
19 static int writeattr_proc PROTO ((Node *, void *));
20
21 /* Where to look for CVSREP_FILEATTR.  */
22 static char *fileattr_stored_repos;
23
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;
30
31 /* Have the in-memory attributes been modified since we read them?  */
32 static int attrs_modified;
33
34 /* More in-memory attributes: linked list of unrecognized
35    fileattr lines.  We pass these on unchanged.  */
36 struct unrecog {
37     char *line;
38     struct unrecog *next;
39 };
40 static struct unrecog *unrecog_head;
41
42 /* Note that if noone calls fileattr_get, this is very cheap.  No stat(),
43    no open(), no nothing.  */
44 void
45 fileattr_startdir (repos)
46     const char *repos;
47 {
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);
53 }
54
55 static void
56 fileattr_delproc (node)
57     Node *node;
58 {
59     assert (node->data != NULL);
60     free (node->data);
61     node->data = NULL;
62 }
63
64 /* Read all the attributes for the current directory into memory.  */
65 static void
66 fileattr_read ()
67 {
68     char *fname;
69     FILE *fp;
70     char *line = NULL;
71     size_t line_len = 0;
72
73     /* If there are no attributes, don't waste time repeatedly looking
74        for the CVSREP_FILEATTR file.  */
75     if (attr_read_attempted)
76         return;
77
78     /* If NULL was passed to fileattr_startdir, then it isn't kosher to look
79        at attributes.  */
80     assert (fileattr_stored_repos != NULL);
81
82     fname = xmalloc (strlen (fileattr_stored_repos)
83                      + 1
84                      + sizeof (CVSREP_FILEATTR)
85                      + 1);
86
87     strcpy (fname, fileattr_stored_repos);
88     strcat (fname, "/");
89     strcat (fname, CVSREP_FILEATTR);
90
91     attr_read_attempted = 1;
92     fp = CVS_FOPEN (fname, FOPEN_BINARY_READ);
93     if (fp == NULL)
94     {
95         if (!existence_error (errno))
96             error (0, errno, "cannot read %s", fname);
97         free (fname);
98         return;
99     }
100     attrlist = getlist ();
101     while (1) {
102         int nread;
103         nread = getline (&line, &line_len, fp);
104         if (nread < 0)
105             break;
106         /* Remove trailing newline.  */
107         line[nread - 1] = '\0';
108         if (line[0] == 'F')
109         {
110             char *p;
111             Node *newnode;
112
113             p = strchr (line, '\t');
114             if (p == NULL)
115                 error (1, 0,
116                        "file attribute database corruption: tab missing in %s",
117                        fname);
118             *p++ = '\0';
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.  */
129                 freenode (newnode);
130         }
131         else if (line[0] == 'D')
132         {
133             char *p;
134             /* Currently nothing to skip here, but for future expansion,
135                ignore anything located here.  */
136             p = strchr (line, '\t');
137             if (p == NULL)
138                 error (1, 0,
139                        "file attribute database corruption: tab missing in %s",
140                        fname);
141             ++p;
142             if (fileattr_default_attrs) free (fileattr_default_attrs);
143             fileattr_default_attrs = xstrdup (p);
144         }
145         else
146         {
147             /* Unrecognized type, we want to just preserve the line without
148                changing it, for future expansion.  */
149             struct unrecog *new;
150
151             new = (struct unrecog *) xmalloc (sizeof (struct unrecog));
152             new->line = xstrdup (line);
153             new->next = unrecog_head;
154             unrecog_head = new;
155         }
156     }
157     if (ferror (fp))
158         error (0, errno, "cannot read %s", fname);
159     if (line != NULL)
160         free (line);
161     if (fclose (fp) < 0)
162         error (0, errno, "cannot close %s", fname);
163     attrs_modified = 0;
164     free (fname);
165 }
166
167 char *
168 fileattr_get (filename, attrname)
169     const char *filename;
170     const char *attrname;
171 {
172     Node *node;
173     size_t attrname_len = strlen (attrname);
174     char *p;
175
176     if (attrlist == NULL)
177         fileattr_read ();
178     if (attrlist == NULL)
179         /* Either nothing has any attributes, or fileattr_read already printed
180            an error message.  */
181         return NULL;
182
183     if (filename == NULL)
184         p = fileattr_default_attrs;
185     else
186     {
187         node = findnode (attrlist, filename);
188         if (node == NULL)
189             /* A file not mentioned has no attributes.  */
190             return NULL;
191         p = node->data;
192     }
193     while (p)
194     {
195         if (strncmp (attrname, p, attrname_len) == 0
196             && p[attrname_len] == '=')
197         {
198             /* Found it.  */
199             return p + attrname_len + 1;
200         }
201         p = strchr (p, ';');
202         if (p == NULL)
203             break;
204         ++p;
205     }
206     /* The file doesn't have this attribute.  */
207     return NULL;
208 }
209
210 char *
211 fileattr_get0 (filename, attrname)
212     const char *filename;
213     const char *attrname;
214 {
215     char *cp;
216     char *cpend;
217     char *retval;
218
219     cp = fileattr_get (filename, attrname);
220     if (cp == NULL)
221         return NULL;
222     cpend = strchr (cp, ';');
223     if (cpend == NULL)
224         cpend = cp + strlen (cp);
225     retval = xmalloc (cpend - cp + 1);
226     strncpy (retval, cp, cpend - cp);
227     retval[cpend - cp] = '\0';
228     return retval;
229 }
230
231 char *
232 fileattr_modify (list, attrname, attrval, namevalsep, entsep)
233     char *list;
234     const char *attrname;
235     const char *attrval;
236     int namevalsep;
237     int entsep;
238 {
239     char *retval;
240     char *rp;
241     size_t attrname_len = strlen (attrname);
242
243     /* Portion of list before the attribute to be replaced.  */
244     char *pre;
245     char *preend;
246     /* Portion of list after the attribute to be replaced.  */
247     char *post;
248
249     char *p;
250     char *p2;
251
252     p = list;
253     pre = list;
254     preend = NULL;
255     /* post is NULL unless set otherwise.  */
256     post = NULL;
257     p2 = NULL;
258     if (list != NULL)
259     {
260         while (1) {
261             p2 = strchr (p, entsep);
262             if (p2 == NULL)
263             {
264                 p2 = p + strlen (p);
265                 if (preend == NULL)
266                     preend = p2;
267             }
268             else
269                 ++p2;
270             if (strncmp (attrname, p, attrname_len) == 0
271                 && p[attrname_len] == namevalsep)
272             {
273                 /* Found it.  */
274                 preend = p;
275                 if (preend > list)
276                     /* Don't include the preceding entsep.  */
277                     --preend;
278
279                 post = p2;
280             }
281             if (p2[0] == '\0')
282                 break;
283             p = p2;
284         }
285     }
286     if (post == NULL)
287         post = p2;
288
289     if (preend == pre && attrval == NULL && post == p2)
290         return NULL;
291
292     retval = xmalloc ((preend - pre)
293                       + 1
294                       + (attrval == NULL ? 0 : (attrname_len + 1
295                                                 + strlen (attrval)))
296                       + 1
297                       + (p2 - post)
298                       + 1);
299     if (preend != pre)
300     {
301         strncpy (retval, pre, preend - pre);
302         rp = retval + (preend - pre);
303         if (attrval != NULL)
304             *rp++ = entsep;
305         *rp = '\0';
306     }
307     else
308         retval[0] = '\0';
309     if (attrval != NULL)
310     {
311         strcat (retval, attrname);
312         rp = retval + strlen (retval);
313         *rp++ = namevalsep;
314         strcpy (rp, attrval);
315     }
316     if (post != p2)
317     {
318         rp = retval + strlen (retval);
319         if (preend != pre || attrval != NULL)
320             *rp++ = entsep;
321         strncpy (rp, post, p2 - post);
322         rp += p2 - post;
323         *rp = '\0';
324     }
325     return retval;
326 }
327
328 void
329 fileattr_set (filename, attrname, attrval)
330     const char *filename;
331     const char *attrname;
332     const char *attrval;
333 {
334     Node *node;
335     char *p;
336
337     if (filename == NULL)
338     {
339         p = fileattr_modify (fileattr_default_attrs, attrname, attrval,
340                              '=', ';');
341         if (fileattr_default_attrs != NULL)
342             free (fileattr_default_attrs);
343         fileattr_default_attrs = p;
344         attrs_modified = 1;
345         return;
346     }
347     if (attrlist == NULL)
348         fileattr_read ();
349     if (attrlist == NULL)
350     {
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 ();
355     }
356
357     node = findnode (attrlist, filename);
358     if (node == NULL)
359     {
360         if (attrval == NULL)
361             /* Attempt to remove an attribute which wasn't there.  */
362             return;
363
364         /* First attribute for this file.  */
365         node = getnode ();
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);
374     }
375
376     p = fileattr_modify (node->data, attrname, attrval, '=', ';');
377     if (p == NULL)
378         delnode (node);
379     else
380     {
381         free (node->data);
382         node->data = p;
383     }
384
385     attrs_modified = 1;
386 }
387
388 char *
389 fileattr_getall (filename)
390     const char *filename;
391 {
392     Node *node;
393     char *p;
394
395     if (attrlist == NULL)
396         fileattr_read ();
397     if (attrlist == NULL)
398         /* Either nothing has any attributes, or fileattr_read already printed
399            an error message.  */
400         return NULL;
401
402     if (filename == NULL)
403         p = fileattr_default_attrs;
404     else
405     {
406         node = findnode (attrlist, filename);
407         if (node == NULL)
408             /* A file not mentioned has no attributes.  */
409             return NULL;
410         p = node->data;
411     }
412     return xstrdup (p);
413 }
414
415 void
416 fileattr_setall (filename, attrs)
417     const char *filename;
418     const char *attrs;
419 {
420     Node *node;
421
422     if (filename == NULL)
423     {
424         if (fileattr_default_attrs != NULL)
425             free (fileattr_default_attrs);
426         fileattr_default_attrs = xstrdup (attrs);
427         attrs_modified = 1;
428         return;
429     }
430     if (attrlist == NULL)
431         fileattr_read ();
432     if (attrlist == NULL)
433     {
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 ();
438     }
439
440     node = findnode (attrlist, filename);
441     if (node == NULL)
442     {
443         /* The file had no attributes.  Add them if we have any to add.  */
444         if (attrs != NULL)
445         {
446             node = getnode ();
447             node->type = FILEATTR;
448             node->delproc = fileattr_delproc;
449             node->key = xstrdup (filename);
450             node->data = xstrdup (attrs);
451             addnode (attrlist, node);
452         }
453     }
454     else
455     {
456         if (attrs == NULL)
457             delnode (node);
458         else
459         {
460             free (node->data);
461             node->data = xstrdup (attrs);
462         }
463     }
464
465     attrs_modified = 1;
466 }
467
468 void
469 fileattr_newfile (filename)
470     const char *filename;
471 {
472     Node *node;
473
474     if (attrlist == NULL)
475         fileattr_read ();
476
477     if (fileattr_default_attrs == NULL)
478         return;
479
480     if (attrlist == NULL)
481     {
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 ();
486     }
487
488     node = getnode ();
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);
494     attrs_modified = 1;
495 }
496
497 static int
498 writeattr_proc (node, data)
499     Node *node;
500     void *data;
501 {
502     FILE *fp = (FILE *)data;
503     fputs ("F", fp);
504     fputs (node->key, fp);
505     fputs ("\t", fp);
506     fputs (node->data, fp);
507     fputs ("\012", fp);
508     return 0;
509 }
510
511 void
512 fileattr_write ()
513 {
514     FILE *fp;
515     char *fname;
516     mode_t omask;
517     struct unrecog *p;
518
519     if (!attrs_modified)
520         return;
521
522     if (noexec)
523         return;
524
525     /* If NULL was passed to fileattr_startdir, then it isn't kosher to set
526        attributes.  */
527     assert (fileattr_stored_repos != NULL);
528
529     fname = xmalloc (strlen (fileattr_stored_repos)
530                      + 1
531                      + sizeof (CVSREP_FILEATTR)
532                      + 1);
533
534     strcpy (fname, fileattr_stored_repos);
535     strcat (fname, "/");
536     strcat (fname, CVSREP_FILEATTR);
537
538     if (list_isempty (attrlist)
539         && fileattr_default_attrs == NULL
540         && unrecog_head == NULL)
541     {
542         /* There are no attributes.  */
543         if (unlink_file (fname) < 0)
544         {
545             if (!existence_error (errno))
546             {
547                 error (0, errno, "cannot remove %s", fname);
548             }
549         }
550
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);
555         strcat (fname, "/");
556         strcat (fname, CVSREP);
557         if (CVS_RMDIR (fname) < 0)
558         {
559             if (errno != ENOTEMPTY
560
561                 /* Don't know why we would be here if there is no CVSREP
562                    directory, but it seemed to be happening anyway, so
563                    check for it.  */
564                 && !existence_error (errno))
565                 error (0, errno, "cannot remove %s", fname);
566         }
567
568         free (fname);
569         return;
570     }
571
572     omask = umask (cvsumask);
573     fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE);
574     if (fp == NULL)
575     {
576         if (existence_error (errno))
577         {
578             /* Maybe the CVSREP directory doesn't exist.  Try creating it.  */
579             char *repname;
580
581             repname = xmalloc (strlen (fileattr_stored_repos)
582                                + 1
583                                + sizeof (CVSREP)
584                                + 1);
585             strcpy (repname, fileattr_stored_repos);
586             strcat (repname, "/");
587             strcat (repname, CVSREP);
588
589             if (CVS_MKDIR (repname, 0777) < 0 && errno != EEXIST)
590             {
591                 error (0, errno, "cannot make directory %s", repname);
592                 (void) umask (omask);
593                 free (fname);
594                 free (repname);
595                 return;
596             }
597             free (repname);
598
599             fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE);
600         }
601         if (fp == NULL)
602         {
603             error (0, errno, "cannot write %s", fname);
604             (void) umask (omask);
605             free (fname);
606             return;
607         }
608     }
609     (void) umask (omask);
610
611     /* First write the "F" attributes.  */
612     walklist (attrlist, writeattr_proc, fp);
613
614     /* Then the "D" attribute.  */
615     if (fileattr_default_attrs != NULL)
616     {
617         fputs ("D\t", fp);
618         fputs (fileattr_default_attrs, fp);
619         fputs ("\012", fp);
620     }
621
622     /* Then any other attributes.  */
623     for (p = unrecog_head; p != NULL; p = p->next)
624     {
625         fputs (p->line, fp);
626         fputs ("\012", fp);
627     }
628
629     if (fclose (fp) < 0)
630         error (0, errno, "cannot close %s", fname);
631     attrs_modified = 0;
632     free (fname);
633 }
634
635 void
636 fileattr_free ()
637 {
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
641        it should be.  */
642     dellist (&attrlist);
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;
649     while (unrecog_head)
650     {
651         struct unrecog *p = unrecog_head;
652         unrecog_head = p->next;
653         free (p->line);
654         free (p);
655     }
656 }