]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/cvs/src/fileattr.c
This commit was generated by cvs2svn to compensate for changes in r171169,
[FreeBSD/FreeBSD.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             fileattr_default_attrs = xstrdup (p);
143         }
144         else
145         {
146             /* Unrecognized type, we want to just preserve the line without
147                changing it, for future expansion.  */
148             struct unrecog *new;
149
150             new = (struct unrecog *) xmalloc (sizeof (struct unrecog));
151             new->line = xstrdup (line);
152             new->next = unrecog_head;
153             unrecog_head = new;
154         }
155     }
156     if (ferror (fp))
157         error (0, errno, "cannot read %s", fname);
158     if (line != NULL)
159         free (line);
160     if (fclose (fp) < 0)
161         error (0, errno, "cannot close %s", fname);
162     attrs_modified = 0;
163     free (fname);
164 }
165
166 char *
167 fileattr_get (filename, attrname)
168     const char *filename;
169     const char *attrname;
170 {
171     Node *node;
172     size_t attrname_len = strlen (attrname);
173     char *p;
174
175     if (attrlist == NULL)
176         fileattr_read ();
177     if (attrlist == NULL)
178         /* Either nothing has any attributes, or fileattr_read already printed
179            an error message.  */
180         return NULL;
181
182     if (filename == NULL)
183         p = fileattr_default_attrs;
184     else
185     {
186         node = findnode (attrlist, filename);
187         if (node == NULL)
188             /* A file not mentioned has no attributes.  */
189             return NULL;
190         p = node->data;
191     }
192     while (p)
193     {
194         if (strncmp (attrname, p, attrname_len) == 0
195             && p[attrname_len] == '=')
196         {
197             /* Found it.  */
198             return p + attrname_len + 1;
199         }
200         p = strchr (p, ';');
201         if (p == NULL)
202             break;
203         ++p;
204     }
205     /* The file doesn't have this attribute.  */
206     return NULL;
207 }
208
209 char *
210 fileattr_get0 (filename, attrname)
211     const char *filename;
212     const char *attrname;
213 {
214     char *cp;
215     char *cpend;
216     char *retval;
217
218     cp = fileattr_get (filename, attrname);
219     if (cp == NULL)
220         return NULL;
221     cpend = strchr (cp, ';');
222     if (cpend == NULL)
223         cpend = cp + strlen (cp);
224     retval = xmalloc (cpend - cp + 1);
225     strncpy (retval, cp, cpend - cp);
226     retval[cpend - cp] = '\0';
227     return retval;
228 }
229
230 char *
231 fileattr_modify (list, attrname, attrval, namevalsep, entsep)
232     char *list;
233     const char *attrname;
234     const char *attrval;
235     int namevalsep;
236     int entsep;
237 {
238     char *retval;
239     char *rp;
240     size_t attrname_len = strlen (attrname);
241
242     /* Portion of list before the attribute to be replaced.  */
243     char *pre;
244     char *preend;
245     /* Portion of list after the attribute to be replaced.  */
246     char *post;
247
248     char *p;
249     char *p2;
250
251     p = list;
252     pre = list;
253     preend = NULL;
254     /* post is NULL unless set otherwise.  */
255     post = NULL;
256     p2 = NULL;
257     if (list != NULL)
258     {
259         while (1) {
260             p2 = strchr (p, entsep);
261             if (p2 == NULL)
262             {
263                 p2 = p + strlen (p);
264                 if (preend == NULL)
265                     preend = p2;
266             }
267             else
268                 ++p2;
269             if (strncmp (attrname, p, attrname_len) == 0
270                 && p[attrname_len] == namevalsep)
271             {
272                 /* Found it.  */
273                 preend = p;
274                 if (preend > list)
275                     /* Don't include the preceding entsep.  */
276                     --preend;
277
278                 post = p2;
279             }
280             if (p2[0] == '\0')
281                 break;
282             p = p2;
283         }
284     }
285     if (post == NULL)
286         post = p2;
287
288     if (preend == pre && attrval == NULL && post == p2)
289         return NULL;
290
291     retval = xmalloc ((preend - pre)
292                       + 1
293                       + (attrval == NULL ? 0 : (attrname_len + 1
294                                                 + strlen (attrval)))
295                       + 1
296                       + (p2 - post)
297                       + 1);
298     if (preend != pre)
299     {
300         strncpy (retval, pre, preend - pre);
301         rp = retval + (preend - pre);
302         if (attrval != NULL)
303             *rp++ = entsep;
304         *rp = '\0';
305     }
306     else
307         retval[0] = '\0';
308     if (attrval != NULL)
309     {
310         strcat (retval, attrname);
311         rp = retval + strlen (retval);
312         *rp++ = namevalsep;
313         strcpy (rp, attrval);
314     }
315     if (post != p2)
316     {
317         rp = retval + strlen (retval);
318         if (preend != pre || attrval != NULL)
319             *rp++ = entsep;
320         strncpy (rp, post, p2 - post);
321         rp += p2 - post;
322         *rp = '\0';
323     }
324     return retval;
325 }
326
327 void
328 fileattr_set (filename, attrname, attrval)
329     const char *filename;
330     const char *attrname;
331     const char *attrval;
332 {
333     Node *node;
334     char *p;
335
336     if (filename == NULL)
337     {
338         p = fileattr_modify (fileattr_default_attrs, attrname, attrval,
339                              '=', ';');
340         if (fileattr_default_attrs != NULL)
341             free (fileattr_default_attrs);
342         fileattr_default_attrs = p;
343         attrs_modified = 1;
344         return;
345     }
346     if (attrlist == NULL)
347         fileattr_read ();
348     if (attrlist == NULL)
349     {
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 ();
354     }
355
356     node = findnode (attrlist, filename);
357     if (node == NULL)
358     {
359         if (attrval == NULL)
360             /* Attempt to remove an attribute which wasn't there.  */
361             return;
362
363         /* First attribute for this file.  */
364         node = getnode ();
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);
373     }
374
375     p = fileattr_modify (node->data, attrname, attrval, '=', ';');
376     if (p == NULL)
377         delnode (node);
378     else
379     {
380         free (node->data);
381         node->data = p;
382     }
383
384     attrs_modified = 1;
385 }
386
387 char *
388 fileattr_getall (filename)
389     const char *filename;
390 {
391     Node *node;
392     char *p;
393
394     if (attrlist == NULL)
395         fileattr_read ();
396     if (attrlist == NULL)
397         /* Either nothing has any attributes, or fileattr_read already printed
398            an error message.  */
399         return NULL;
400
401     if (filename == NULL)
402         p = fileattr_default_attrs;
403     else
404     {
405         node = findnode (attrlist, filename);
406         if (node == NULL)
407             /* A file not mentioned has no attributes.  */
408             return NULL;
409         p = node->data;
410     }
411     return xstrdup (p);
412 }
413
414 void
415 fileattr_setall (filename, attrs)
416     const char *filename;
417     const char *attrs;
418 {
419     Node *node;
420
421     if (filename == NULL)
422     {
423         if (fileattr_default_attrs != NULL)
424             free (fileattr_default_attrs);
425         fileattr_default_attrs = xstrdup (attrs);
426         attrs_modified = 1;
427         return;
428     }
429     if (attrlist == NULL)
430         fileattr_read ();
431     if (attrlist == NULL)
432     {
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 ();
437     }
438
439     node = findnode (attrlist, filename);
440     if (node == NULL)
441     {
442         /* The file had no attributes.  Add them if we have any to add.  */
443         if (attrs != NULL)
444         {
445             node = getnode ();
446             node->type = FILEATTR;
447             node->delproc = fileattr_delproc;
448             node->key = xstrdup (filename);
449             node->data = xstrdup (attrs);
450             addnode (attrlist, node);
451         }
452     }
453     else
454     {
455         if (attrs == NULL)
456             delnode (node);
457         else
458         {
459             free (node->data);
460             node->data = xstrdup (attrs);
461         }
462     }
463
464     attrs_modified = 1;
465 }
466
467 void
468 fileattr_newfile (filename)
469     const char *filename;
470 {
471     Node *node;
472
473     if (attrlist == NULL)
474         fileattr_read ();
475
476     if (fileattr_default_attrs == NULL)
477         return;
478
479     if (attrlist == NULL)
480     {
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 ();
485     }
486
487     node = getnode ();
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);
493     attrs_modified = 1;
494 }
495
496 static int
497 writeattr_proc (node, data)
498     Node *node;
499     void *data;
500 {
501     FILE *fp = (FILE *)data;
502     fputs ("F", fp);
503     fputs (node->key, fp);
504     fputs ("\t", fp);
505     fputs (node->data, fp);
506     fputs ("\012", fp);
507     return 0;
508 }
509
510 void
511 fileattr_write ()
512 {
513     FILE *fp;
514     char *fname;
515     mode_t omask;
516     struct unrecog *p;
517
518     if (!attrs_modified)
519         return;
520
521     if (noexec)
522         return;
523
524     /* If NULL was passed to fileattr_startdir, then it isn't kosher to set
525        attributes.  */
526     assert (fileattr_stored_repos != NULL);
527
528     fname = xmalloc (strlen (fileattr_stored_repos)
529                      + 1
530                      + sizeof (CVSREP_FILEATTR)
531                      + 1);
532
533     strcpy (fname, fileattr_stored_repos);
534     strcat (fname, "/");
535     strcat (fname, CVSREP_FILEATTR);
536
537     if (list_isempty (attrlist)
538         && fileattr_default_attrs == NULL
539         && unrecog_head == NULL)
540     {
541         /* There are no attributes.  */
542         if (unlink_file (fname) < 0)
543         {
544             if (!existence_error (errno))
545             {
546                 error (0, errno, "cannot remove %s", fname);
547             }
548         }
549
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);
554         strcat (fname, "/");
555         strcat (fname, CVSREP);
556         if (CVS_RMDIR (fname) < 0)
557         {
558             if (errno != ENOTEMPTY
559
560                 /* Don't know why we would be here if there is no CVSREP
561                    directory, but it seemed to be happening anyway, so
562                    check for it.  */
563                 && !existence_error (errno))
564                 error (0, errno, "cannot remove %s", fname);
565         }
566
567         free (fname);
568         return;
569     }
570
571     omask = umask (cvsumask);
572     fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE);
573     if (fp == NULL)
574     {
575         if (existence_error (errno))
576         {
577             /* Maybe the CVSREP directory doesn't exist.  Try creating it.  */
578             char *repname;
579
580             repname = xmalloc (strlen (fileattr_stored_repos)
581                                + 1
582                                + sizeof (CVSREP)
583                                + 1);
584             strcpy (repname, fileattr_stored_repos);
585             strcat (repname, "/");
586             strcat (repname, CVSREP);
587
588             if (CVS_MKDIR (repname, 0777) < 0 && errno != EEXIST)
589             {
590                 error (0, errno, "cannot make directory %s", repname);
591                 (void) umask (omask);
592                 free (repname);
593                 return;
594             }
595             free (repname);
596
597             fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE);
598         }
599         if (fp == NULL)
600         {
601             error (0, errno, "cannot write %s", fname);
602             (void) umask (omask);
603             return;
604         }
605     }
606     (void) umask (omask);
607
608     /* First write the "F" attributes.  */
609     walklist (attrlist, writeattr_proc, fp);
610
611     /* Then the "D" attribute.  */
612     if (fileattr_default_attrs != NULL)
613     {
614         fputs ("D\t", fp);
615         fputs (fileattr_default_attrs, fp);
616         fputs ("\012", fp);
617     }
618
619     /* Then any other attributes.  */
620     for (p = unrecog_head; p != NULL; p = p->next)
621     {
622         fputs (p->line, fp);
623         fputs ("\012", fp);
624     }
625
626     if (fclose (fp) < 0)
627         error (0, errno, "cannot close %s", fname);
628     attrs_modified = 0;
629     free (fname);
630 }
631
632 void
633 fileattr_free ()
634 {
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
638        it should be.  */
639     dellist (&attrlist);
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;
646     while (unrecog_head)
647     {
648         struct unrecog *p = unrecog_head;
649         unrecog_head = p->next;
650         free (p->line);
651         free (p);
652     }
653 }