1*86d7f5d3SJohn Marino /* Implementation for file attribute munging features.
2*86d7f5d3SJohn Marino
3*86d7f5d3SJohn Marino This program is free software; you can redistribute it and/or modify
4*86d7f5d3SJohn Marino it under the terms of the GNU General Public License as published by
5*86d7f5d3SJohn Marino the Free Software Foundation; either version 2, or (at your option)
6*86d7f5d3SJohn Marino any later version.
7*86d7f5d3SJohn Marino
8*86d7f5d3SJohn Marino This program is distributed in the hope that it will be useful,
9*86d7f5d3SJohn Marino but WITHOUT ANY WARRANTY; without even the implied warranty of
10*86d7f5d3SJohn Marino MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11*86d7f5d3SJohn Marino GNU General Public License for more details. */
12*86d7f5d3SJohn Marino
13*86d7f5d3SJohn Marino #include "cvs.h"
14*86d7f5d3SJohn Marino #include "getline.h"
15*86d7f5d3SJohn Marino #include "fileattr.h"
16*86d7f5d3SJohn Marino
17*86d7f5d3SJohn Marino static void fileattr_read (void);
18*86d7f5d3SJohn Marino static int writeattr_proc (Node *, void *);
19*86d7f5d3SJohn Marino
20*86d7f5d3SJohn Marino /* Where to look for CVSREP_FILEATTR. */
21*86d7f5d3SJohn Marino static char *fileattr_stored_repos;
22*86d7f5d3SJohn Marino
23*86d7f5d3SJohn Marino /* The in-memory attributes. */
24*86d7f5d3SJohn Marino static List *attrlist;
25*86d7f5d3SJohn Marino static char *fileattr_default_attrs;
26*86d7f5d3SJohn Marino /* We have already tried to read attributes and failed in this directory
27*86d7f5d3SJohn Marino (for example, there is no CVSREP_FILEATTR file). */
28*86d7f5d3SJohn Marino static int attr_read_attempted;
29*86d7f5d3SJohn Marino
30*86d7f5d3SJohn Marino /* Have the in-memory attributes been modified since we read them? */
31*86d7f5d3SJohn Marino static int attrs_modified;
32*86d7f5d3SJohn Marino
33*86d7f5d3SJohn Marino /* More in-memory attributes: linked list of unrecognized
34*86d7f5d3SJohn Marino fileattr lines. We pass these on unchanged. */
35*86d7f5d3SJohn Marino struct unrecog {
36*86d7f5d3SJohn Marino char *line;
37*86d7f5d3SJohn Marino struct unrecog *next;
38*86d7f5d3SJohn Marino };
39*86d7f5d3SJohn Marino static struct unrecog *unrecog_head;
40*86d7f5d3SJohn Marino
41*86d7f5d3SJohn Marino
42*86d7f5d3SJohn Marino
43*86d7f5d3SJohn Marino /* Note that if noone calls fileattr_get, this is very cheap. No stat(),
44*86d7f5d3SJohn Marino no open(), no nothing. */
45*86d7f5d3SJohn Marino void
fileattr_startdir(const char * repos)46*86d7f5d3SJohn Marino fileattr_startdir (const char *repos)
47*86d7f5d3SJohn Marino {
48*86d7f5d3SJohn Marino assert (fileattr_stored_repos == NULL);
49*86d7f5d3SJohn Marino fileattr_stored_repos = xstrdup (repos);
50*86d7f5d3SJohn Marino assert (attrlist == NULL);
51*86d7f5d3SJohn Marino attr_read_attempted = 0;
52*86d7f5d3SJohn Marino assert (unrecog_head == NULL);
53*86d7f5d3SJohn Marino }
54*86d7f5d3SJohn Marino
55*86d7f5d3SJohn Marino
56*86d7f5d3SJohn Marino
57*86d7f5d3SJohn Marino static void
fileattr_delproc(Node * node)58*86d7f5d3SJohn Marino fileattr_delproc (Node *node)
59*86d7f5d3SJohn Marino {
60*86d7f5d3SJohn Marino assert (node->data != NULL);
61*86d7f5d3SJohn Marino free (node->data);
62*86d7f5d3SJohn Marino node->data = NULL;
63*86d7f5d3SJohn Marino }
64*86d7f5d3SJohn Marino
65*86d7f5d3SJohn Marino /* Read all the attributes for the current directory into memory. */
66*86d7f5d3SJohn Marino static void
fileattr_read(void)67*86d7f5d3SJohn Marino fileattr_read (void)
68*86d7f5d3SJohn Marino {
69*86d7f5d3SJohn Marino char *fname;
70*86d7f5d3SJohn Marino FILE *fp;
71*86d7f5d3SJohn Marino char *line = NULL;
72*86d7f5d3SJohn Marino size_t line_len = 0;
73*86d7f5d3SJohn Marino
74*86d7f5d3SJohn Marino /* If there are no attributes, don't waste time repeatedly looking
75*86d7f5d3SJohn Marino for the CVSREP_FILEATTR file. */
76*86d7f5d3SJohn Marino if (attr_read_attempted)
77*86d7f5d3SJohn Marino return;
78*86d7f5d3SJohn Marino
79*86d7f5d3SJohn Marino /* If NULL was passed to fileattr_startdir, then it isn't kosher to look
80*86d7f5d3SJohn Marino at attributes. */
81*86d7f5d3SJohn Marino assert (fileattr_stored_repos != NULL);
82*86d7f5d3SJohn Marino
83*86d7f5d3SJohn Marino fname = Xasprintf ("%s/%s", fileattr_stored_repos, CVSREP_FILEATTR);
84*86d7f5d3SJohn Marino
85*86d7f5d3SJohn Marino attr_read_attempted = 1;
86*86d7f5d3SJohn Marino fp = CVS_FOPEN (fname, FOPEN_BINARY_READ);
87*86d7f5d3SJohn Marino if (fp == NULL)
88*86d7f5d3SJohn Marino {
89*86d7f5d3SJohn Marino if (!existence_error (errno))
90*86d7f5d3SJohn Marino error (0, errno, "cannot read %s", fname);
91*86d7f5d3SJohn Marino free (fname);
92*86d7f5d3SJohn Marino return;
93*86d7f5d3SJohn Marino }
94*86d7f5d3SJohn Marino attrlist = getlist ();
95*86d7f5d3SJohn Marino while (1) {
96*86d7f5d3SJohn Marino int nread;
97*86d7f5d3SJohn Marino nread = getline (&line, &line_len, fp);
98*86d7f5d3SJohn Marino if (nread < 0)
99*86d7f5d3SJohn Marino break;
100*86d7f5d3SJohn Marino /* Remove trailing newline.
101*86d7f5d3SJohn Marino * It is okay to reference line[nread - 1] here, since getline must
102*86d7f5d3SJohn Marino * always return 1 character or EOF, but we need to verify that the
103*86d7f5d3SJohn Marino * character we eat is the newline, since getline can return a line
104*86d7f5d3SJohn Marino * w/o a newline just before returning EOF.
105*86d7f5d3SJohn Marino */
106*86d7f5d3SJohn Marino if (line[nread - 1] == '\n') line[nread - 1] = '\0';
107*86d7f5d3SJohn Marino if (line[0] == 'F')
108*86d7f5d3SJohn Marino {
109*86d7f5d3SJohn Marino char *p;
110*86d7f5d3SJohn Marino Node *newnode;
111*86d7f5d3SJohn Marino
112*86d7f5d3SJohn Marino p = strchr (line, '\t');
113*86d7f5d3SJohn Marino if (p == NULL)
114*86d7f5d3SJohn Marino error (1, 0,
115*86d7f5d3SJohn Marino "file attribute database corruption: tab missing in %s",
116*86d7f5d3SJohn Marino primary_root_inverse_translate (fname));
117*86d7f5d3SJohn Marino *p++ = '\0';
118*86d7f5d3SJohn Marino newnode = getnode ();
119*86d7f5d3SJohn Marino newnode->type = FILEATTR;
120*86d7f5d3SJohn Marino newnode->delproc = fileattr_delproc;
121*86d7f5d3SJohn Marino newnode->key = xstrdup (line + 1);
122*86d7f5d3SJohn Marino newnode->data = xstrdup (p);
123*86d7f5d3SJohn Marino if (addnode (attrlist, newnode) != 0)
124*86d7f5d3SJohn Marino /* If the same filename appears twice in the file, discard
125*86d7f5d3SJohn Marino any line other than the first for that filename. This
126*86d7f5d3SJohn Marino is the way that CVS has behaved since file attributes
127*86d7f5d3SJohn Marino were first introduced. */
128*86d7f5d3SJohn Marino freenode (newnode);
129*86d7f5d3SJohn Marino }
130*86d7f5d3SJohn Marino else if (line[0] == 'D')
131*86d7f5d3SJohn Marino {
132*86d7f5d3SJohn Marino char *p;
133*86d7f5d3SJohn Marino /* Currently nothing to skip here, but for future expansion,
134*86d7f5d3SJohn Marino ignore anything located here. */
135*86d7f5d3SJohn Marino p = strchr (line, '\t');
136*86d7f5d3SJohn Marino if (p == NULL)
137*86d7f5d3SJohn Marino error (1, 0,
138*86d7f5d3SJohn Marino "file attribute database corruption: tab missing in %s",
139*86d7f5d3SJohn Marino fname);
140*86d7f5d3SJohn Marino ++p;
141*86d7f5d3SJohn Marino if (fileattr_default_attrs) free (fileattr_default_attrs);
142*86d7f5d3SJohn Marino fileattr_default_attrs = xstrdup (p);
143*86d7f5d3SJohn Marino }
144*86d7f5d3SJohn Marino else
145*86d7f5d3SJohn Marino {
146*86d7f5d3SJohn Marino /* Unrecognized type, we want to just preserve the line without
147*86d7f5d3SJohn Marino changing it, for future expansion. */
148*86d7f5d3SJohn Marino struct unrecog *new;
149*86d7f5d3SJohn Marino
150*86d7f5d3SJohn Marino new = xmalloc (sizeof (struct unrecog));
151*86d7f5d3SJohn Marino new->line = xstrdup (line);
152*86d7f5d3SJohn Marino new->next = unrecog_head;
153*86d7f5d3SJohn Marino unrecog_head = new;
154*86d7f5d3SJohn Marino }
155*86d7f5d3SJohn Marino }
156*86d7f5d3SJohn Marino if (ferror (fp))
157*86d7f5d3SJohn Marino error (0, errno, "cannot read %s", fname);
158*86d7f5d3SJohn Marino if (line != NULL)
159*86d7f5d3SJohn Marino free (line);
160*86d7f5d3SJohn Marino if (fclose (fp) < 0)
161*86d7f5d3SJohn Marino error (0, errno, "cannot close %s", fname);
162*86d7f5d3SJohn Marino attrs_modified = 0;
163*86d7f5d3SJohn Marino free (fname);
164*86d7f5d3SJohn Marino }
165*86d7f5d3SJohn Marino
166*86d7f5d3SJohn Marino
167*86d7f5d3SJohn Marino
168*86d7f5d3SJohn Marino char *
fileattr_get(const char * filename,const char * attrname)169*86d7f5d3SJohn Marino fileattr_get (const char *filename, const char *attrname)
170*86d7f5d3SJohn Marino {
171*86d7f5d3SJohn Marino Node *node;
172*86d7f5d3SJohn Marino size_t attrname_len = strlen (attrname);
173*86d7f5d3SJohn Marino char *p;
174*86d7f5d3SJohn Marino
175*86d7f5d3SJohn Marino if (attrlist == NULL)
176*86d7f5d3SJohn Marino fileattr_read ();
177*86d7f5d3SJohn Marino if (attrlist == NULL)
178*86d7f5d3SJohn Marino /* Either nothing has any attributes, or fileattr_read already printed
179*86d7f5d3SJohn Marino an error message. */
180*86d7f5d3SJohn Marino return NULL;
181*86d7f5d3SJohn Marino
182*86d7f5d3SJohn Marino if (filename == NULL)
183*86d7f5d3SJohn Marino p = fileattr_default_attrs;
184*86d7f5d3SJohn Marino else
185*86d7f5d3SJohn Marino {
186*86d7f5d3SJohn Marino node = findnode (attrlist, filename);
187*86d7f5d3SJohn Marino if (node == NULL)
188*86d7f5d3SJohn Marino /* A file not mentioned has no attributes. */
189*86d7f5d3SJohn Marino return NULL;
190*86d7f5d3SJohn Marino p = node->data;
191*86d7f5d3SJohn Marino }
192*86d7f5d3SJohn Marino while (p)
193*86d7f5d3SJohn Marino {
194*86d7f5d3SJohn Marino if (strncmp (attrname, p, attrname_len) == 0
195*86d7f5d3SJohn Marino && p[attrname_len] == '=')
196*86d7f5d3SJohn Marino {
197*86d7f5d3SJohn Marino /* Found it. */
198*86d7f5d3SJohn Marino return p + attrname_len + 1;
199*86d7f5d3SJohn Marino }
200*86d7f5d3SJohn Marino p = strchr (p, ';');
201*86d7f5d3SJohn Marino if (p == NULL)
202*86d7f5d3SJohn Marino break;
203*86d7f5d3SJohn Marino ++p;
204*86d7f5d3SJohn Marino }
205*86d7f5d3SJohn Marino /* The file doesn't have this attribute. */
206*86d7f5d3SJohn Marino return NULL;
207*86d7f5d3SJohn Marino }
208*86d7f5d3SJohn Marino
209*86d7f5d3SJohn Marino
210*86d7f5d3SJohn Marino
211*86d7f5d3SJohn Marino char *
fileattr_get0(const char * filename,const char * attrname)212*86d7f5d3SJohn Marino fileattr_get0 (const char *filename, const char *attrname)
213*86d7f5d3SJohn Marino {
214*86d7f5d3SJohn Marino char *cp;
215*86d7f5d3SJohn Marino char *cpend;
216*86d7f5d3SJohn Marino char *retval;
217*86d7f5d3SJohn Marino
218*86d7f5d3SJohn Marino cp = fileattr_get (filename, attrname);
219*86d7f5d3SJohn Marino if (cp == NULL)
220*86d7f5d3SJohn Marino return NULL;
221*86d7f5d3SJohn Marino cpend = strchr (cp, ';');
222*86d7f5d3SJohn Marino if (cpend == NULL)
223*86d7f5d3SJohn Marino cpend = cp + strlen (cp);
224*86d7f5d3SJohn Marino retval = xmalloc (cpend - cp + 1);
225*86d7f5d3SJohn Marino strncpy (retval, cp, cpend - cp);
226*86d7f5d3SJohn Marino retval[cpend - cp] = '\0';
227*86d7f5d3SJohn Marino return retval;
228*86d7f5d3SJohn Marino }
229*86d7f5d3SJohn Marino
230*86d7f5d3SJohn Marino
231*86d7f5d3SJohn Marino
232*86d7f5d3SJohn Marino char *
fileattr_modify(char * list,const char * attrname,const char * attrval,int namevalsep,int entsep)233*86d7f5d3SJohn Marino fileattr_modify (char *list, const char *attrname, const char *attrval, int namevalsep, int entsep)
234*86d7f5d3SJohn Marino {
235*86d7f5d3SJohn Marino char *retval;
236*86d7f5d3SJohn Marino char *rp;
237*86d7f5d3SJohn Marino size_t attrname_len = strlen (attrname);
238*86d7f5d3SJohn Marino
239*86d7f5d3SJohn Marino /* Portion of list before the attribute to be replaced. */
240*86d7f5d3SJohn Marino char *pre;
241*86d7f5d3SJohn Marino char *preend;
242*86d7f5d3SJohn Marino /* Portion of list after the attribute to be replaced. */
243*86d7f5d3SJohn Marino char *post;
244*86d7f5d3SJohn Marino
245*86d7f5d3SJohn Marino char *p;
246*86d7f5d3SJohn Marino char *p2;
247*86d7f5d3SJohn Marino
248*86d7f5d3SJohn Marino p = list;
249*86d7f5d3SJohn Marino pre = list;
250*86d7f5d3SJohn Marino preend = NULL;
251*86d7f5d3SJohn Marino /* post is NULL unless set otherwise. */
252*86d7f5d3SJohn Marino post = NULL;
253*86d7f5d3SJohn Marino p2 = NULL;
254*86d7f5d3SJohn Marino if (list != NULL)
255*86d7f5d3SJohn Marino {
256*86d7f5d3SJohn Marino while (1) {
257*86d7f5d3SJohn Marino p2 = strchr (p, entsep);
258*86d7f5d3SJohn Marino if (p2 == NULL)
259*86d7f5d3SJohn Marino {
260*86d7f5d3SJohn Marino p2 = p + strlen (p);
261*86d7f5d3SJohn Marino if (preend == NULL)
262*86d7f5d3SJohn Marino preend = p2;
263*86d7f5d3SJohn Marino }
264*86d7f5d3SJohn Marino else
265*86d7f5d3SJohn Marino ++p2;
266*86d7f5d3SJohn Marino if (strncmp (attrname, p, attrname_len) == 0
267*86d7f5d3SJohn Marino && p[attrname_len] == namevalsep)
268*86d7f5d3SJohn Marino {
269*86d7f5d3SJohn Marino /* Found it. */
270*86d7f5d3SJohn Marino preend = p;
271*86d7f5d3SJohn Marino if (preend > list)
272*86d7f5d3SJohn Marino /* Don't include the preceding entsep. */
273*86d7f5d3SJohn Marino --preend;
274*86d7f5d3SJohn Marino
275*86d7f5d3SJohn Marino post = p2;
276*86d7f5d3SJohn Marino }
277*86d7f5d3SJohn Marino if (p2[0] == '\0')
278*86d7f5d3SJohn Marino break;
279*86d7f5d3SJohn Marino p = p2;
280*86d7f5d3SJohn Marino }
281*86d7f5d3SJohn Marino }
282*86d7f5d3SJohn Marino if (post == NULL)
283*86d7f5d3SJohn Marino post = p2;
284*86d7f5d3SJohn Marino
285*86d7f5d3SJohn Marino if (preend == pre && attrval == NULL && post == p2)
286*86d7f5d3SJohn Marino return NULL;
287*86d7f5d3SJohn Marino
288*86d7f5d3SJohn Marino retval = xmalloc ((preend - pre)
289*86d7f5d3SJohn Marino + 1
290*86d7f5d3SJohn Marino + (attrval == NULL ? 0 : (attrname_len + 1
291*86d7f5d3SJohn Marino + strlen (attrval)))
292*86d7f5d3SJohn Marino + 1
293*86d7f5d3SJohn Marino + (p2 - post)
294*86d7f5d3SJohn Marino + 1);
295*86d7f5d3SJohn Marino if (preend != pre)
296*86d7f5d3SJohn Marino {
297*86d7f5d3SJohn Marino strncpy (retval, pre, preend - pre);
298*86d7f5d3SJohn Marino rp = retval + (preend - pre);
299*86d7f5d3SJohn Marino if (attrval != NULL)
300*86d7f5d3SJohn Marino *rp++ = entsep;
301*86d7f5d3SJohn Marino *rp = '\0';
302*86d7f5d3SJohn Marino }
303*86d7f5d3SJohn Marino else
304*86d7f5d3SJohn Marino retval[0] = '\0';
305*86d7f5d3SJohn Marino if (attrval != NULL)
306*86d7f5d3SJohn Marino {
307*86d7f5d3SJohn Marino strcat (retval, attrname);
308*86d7f5d3SJohn Marino rp = retval + strlen (retval);
309*86d7f5d3SJohn Marino *rp++ = namevalsep;
310*86d7f5d3SJohn Marino strcpy (rp, attrval);
311*86d7f5d3SJohn Marino }
312*86d7f5d3SJohn Marino if (post != p2)
313*86d7f5d3SJohn Marino {
314*86d7f5d3SJohn Marino rp = retval + strlen (retval);
315*86d7f5d3SJohn Marino if (preend != pre || attrval != NULL)
316*86d7f5d3SJohn Marino *rp++ = entsep;
317*86d7f5d3SJohn Marino strncpy (rp, post, p2 - post);
318*86d7f5d3SJohn Marino rp += p2 - post;
319*86d7f5d3SJohn Marino *rp = '\0';
320*86d7f5d3SJohn Marino }
321*86d7f5d3SJohn Marino return retval;
322*86d7f5d3SJohn Marino }
323*86d7f5d3SJohn Marino
324*86d7f5d3SJohn Marino void
fileattr_set(const char * filename,const char * attrname,const char * attrval)325*86d7f5d3SJohn Marino fileattr_set (const char *filename, const char *attrname, const char *attrval)
326*86d7f5d3SJohn Marino {
327*86d7f5d3SJohn Marino Node *node;
328*86d7f5d3SJohn Marino char *p;
329*86d7f5d3SJohn Marino
330*86d7f5d3SJohn Marino if (filename == NULL)
331*86d7f5d3SJohn Marino {
332*86d7f5d3SJohn Marino p = fileattr_modify (fileattr_default_attrs, attrname, attrval,
333*86d7f5d3SJohn Marino '=', ';');
334*86d7f5d3SJohn Marino if (fileattr_default_attrs != NULL)
335*86d7f5d3SJohn Marino free (fileattr_default_attrs);
336*86d7f5d3SJohn Marino fileattr_default_attrs = p;
337*86d7f5d3SJohn Marino attrs_modified = 1;
338*86d7f5d3SJohn Marino return;
339*86d7f5d3SJohn Marino }
340*86d7f5d3SJohn Marino if (attrlist == NULL)
341*86d7f5d3SJohn Marino fileattr_read ();
342*86d7f5d3SJohn Marino if (attrlist == NULL)
343*86d7f5d3SJohn Marino {
344*86d7f5d3SJohn Marino /* Not sure this is a graceful way to handle things
345*86d7f5d3SJohn Marino in the case where fileattr_read was unable to read the file. */
346*86d7f5d3SJohn Marino /* No attributes existed previously. */
347*86d7f5d3SJohn Marino attrlist = getlist ();
348*86d7f5d3SJohn Marino }
349*86d7f5d3SJohn Marino
350*86d7f5d3SJohn Marino node = findnode (attrlist, filename);
351*86d7f5d3SJohn Marino if (node == NULL)
352*86d7f5d3SJohn Marino {
353*86d7f5d3SJohn Marino if (attrval == NULL)
354*86d7f5d3SJohn Marino /* Attempt to remove an attribute which wasn't there. */
355*86d7f5d3SJohn Marino return;
356*86d7f5d3SJohn Marino
357*86d7f5d3SJohn Marino /* First attribute for this file. */
358*86d7f5d3SJohn Marino node = getnode ();
359*86d7f5d3SJohn Marino node->type = FILEATTR;
360*86d7f5d3SJohn Marino node->delproc = fileattr_delproc;
361*86d7f5d3SJohn Marino node->key = xstrdup (filename);
362*86d7f5d3SJohn Marino node->data = Xasprintf ("%s=%s", attrname, attrval);
363*86d7f5d3SJohn Marino addnode (attrlist, node);
364*86d7f5d3SJohn Marino }
365*86d7f5d3SJohn Marino
366*86d7f5d3SJohn Marino p = fileattr_modify (node->data, attrname, attrval, '=', ';');
367*86d7f5d3SJohn Marino if (p == NULL)
368*86d7f5d3SJohn Marino delnode (node);
369*86d7f5d3SJohn Marino else
370*86d7f5d3SJohn Marino {
371*86d7f5d3SJohn Marino free (node->data);
372*86d7f5d3SJohn Marino node->data = p;
373*86d7f5d3SJohn Marino }
374*86d7f5d3SJohn Marino
375*86d7f5d3SJohn Marino attrs_modified = 1;
376*86d7f5d3SJohn Marino }
377*86d7f5d3SJohn Marino
378*86d7f5d3SJohn Marino
379*86d7f5d3SJohn Marino
380*86d7f5d3SJohn Marino char *
fileattr_getall(const char * filename)381*86d7f5d3SJohn Marino fileattr_getall (const char *filename)
382*86d7f5d3SJohn Marino {
383*86d7f5d3SJohn Marino Node *node;
384*86d7f5d3SJohn Marino char *p;
385*86d7f5d3SJohn Marino
386*86d7f5d3SJohn Marino if (attrlist == NULL)
387*86d7f5d3SJohn Marino fileattr_read ();
388*86d7f5d3SJohn Marino if (attrlist == NULL)
389*86d7f5d3SJohn Marino /* Either nothing has any attributes, or fileattr_read already printed
390*86d7f5d3SJohn Marino an error message. */
391*86d7f5d3SJohn Marino return NULL;
392*86d7f5d3SJohn Marino
393*86d7f5d3SJohn Marino if (filename == NULL)
394*86d7f5d3SJohn Marino p = fileattr_default_attrs;
395*86d7f5d3SJohn Marino else
396*86d7f5d3SJohn Marino {
397*86d7f5d3SJohn Marino node = findnode (attrlist, filename);
398*86d7f5d3SJohn Marino if (node == NULL)
399*86d7f5d3SJohn Marino /* A file not mentioned has no attributes. */
400*86d7f5d3SJohn Marino return NULL;
401*86d7f5d3SJohn Marino p = node->data;
402*86d7f5d3SJohn Marino }
403*86d7f5d3SJohn Marino return xstrdup (p);
404*86d7f5d3SJohn Marino }
405*86d7f5d3SJohn Marino
406*86d7f5d3SJohn Marino
407*86d7f5d3SJohn Marino
408*86d7f5d3SJohn Marino void
fileattr_setall(const char * filename,const char * attrs)409*86d7f5d3SJohn Marino fileattr_setall (const char *filename, const char *attrs)
410*86d7f5d3SJohn Marino {
411*86d7f5d3SJohn Marino Node *node;
412*86d7f5d3SJohn Marino
413*86d7f5d3SJohn Marino if (filename == NULL)
414*86d7f5d3SJohn Marino {
415*86d7f5d3SJohn Marino if (fileattr_default_attrs != NULL)
416*86d7f5d3SJohn Marino free (fileattr_default_attrs);
417*86d7f5d3SJohn Marino fileattr_default_attrs = xstrdup (attrs);
418*86d7f5d3SJohn Marino attrs_modified = 1;
419*86d7f5d3SJohn Marino return;
420*86d7f5d3SJohn Marino }
421*86d7f5d3SJohn Marino if (attrlist == NULL)
422*86d7f5d3SJohn Marino fileattr_read ();
423*86d7f5d3SJohn Marino if (attrlist == NULL)
424*86d7f5d3SJohn Marino {
425*86d7f5d3SJohn Marino /* Not sure this is a graceful way to handle things
426*86d7f5d3SJohn Marino in the case where fileattr_read was unable to read the file. */
427*86d7f5d3SJohn Marino /* No attributes existed previously. */
428*86d7f5d3SJohn Marino attrlist = getlist ();
429*86d7f5d3SJohn Marino }
430*86d7f5d3SJohn Marino
431*86d7f5d3SJohn Marino node = findnode (attrlist, filename);
432*86d7f5d3SJohn Marino if (node == NULL)
433*86d7f5d3SJohn Marino {
434*86d7f5d3SJohn Marino /* The file had no attributes. Add them if we have any to add. */
435*86d7f5d3SJohn Marino if (attrs != NULL)
436*86d7f5d3SJohn Marino {
437*86d7f5d3SJohn Marino node = getnode ();
438*86d7f5d3SJohn Marino node->type = FILEATTR;
439*86d7f5d3SJohn Marino node->delproc = fileattr_delproc;
440*86d7f5d3SJohn Marino node->key = xstrdup (filename);
441*86d7f5d3SJohn Marino node->data = xstrdup (attrs);
442*86d7f5d3SJohn Marino addnode (attrlist, node);
443*86d7f5d3SJohn Marino }
444*86d7f5d3SJohn Marino }
445*86d7f5d3SJohn Marino else
446*86d7f5d3SJohn Marino {
447*86d7f5d3SJohn Marino if (attrs == NULL)
448*86d7f5d3SJohn Marino delnode (node);
449*86d7f5d3SJohn Marino else
450*86d7f5d3SJohn Marino {
451*86d7f5d3SJohn Marino free (node->data);
452*86d7f5d3SJohn Marino node->data = xstrdup (attrs);
453*86d7f5d3SJohn Marino }
454*86d7f5d3SJohn Marino }
455*86d7f5d3SJohn Marino
456*86d7f5d3SJohn Marino attrs_modified = 1;
457*86d7f5d3SJohn Marino }
458*86d7f5d3SJohn Marino
459*86d7f5d3SJohn Marino
460*86d7f5d3SJohn Marino
461*86d7f5d3SJohn Marino void
fileattr_newfile(const char * filename)462*86d7f5d3SJohn Marino fileattr_newfile (const char *filename)
463*86d7f5d3SJohn Marino {
464*86d7f5d3SJohn Marino Node *node;
465*86d7f5d3SJohn Marino
466*86d7f5d3SJohn Marino if (attrlist == NULL)
467*86d7f5d3SJohn Marino fileattr_read ();
468*86d7f5d3SJohn Marino
469*86d7f5d3SJohn Marino if (fileattr_default_attrs == NULL)
470*86d7f5d3SJohn Marino return;
471*86d7f5d3SJohn Marino
472*86d7f5d3SJohn Marino if (attrlist == NULL)
473*86d7f5d3SJohn Marino {
474*86d7f5d3SJohn Marino /* Not sure this is a graceful way to handle things
475*86d7f5d3SJohn Marino in the case where fileattr_read was unable to read the file. */
476*86d7f5d3SJohn Marino /* No attributes existed previously. */
477*86d7f5d3SJohn Marino attrlist = getlist ();
478*86d7f5d3SJohn Marino }
479*86d7f5d3SJohn Marino
480*86d7f5d3SJohn Marino node = getnode ();
481*86d7f5d3SJohn Marino node->type = FILEATTR;
482*86d7f5d3SJohn Marino node->delproc = fileattr_delproc;
483*86d7f5d3SJohn Marino node->key = xstrdup (filename);
484*86d7f5d3SJohn Marino node->data = xstrdup (fileattr_default_attrs);
485*86d7f5d3SJohn Marino addnode (attrlist, node);
486*86d7f5d3SJohn Marino attrs_modified = 1;
487*86d7f5d3SJohn Marino }
488*86d7f5d3SJohn Marino
489*86d7f5d3SJohn Marino
490*86d7f5d3SJohn Marino
491*86d7f5d3SJohn Marino static int
writeattr_proc(Node * node,void * data)492*86d7f5d3SJohn Marino writeattr_proc (Node *node, void *data)
493*86d7f5d3SJohn Marino {
494*86d7f5d3SJohn Marino FILE *fp = (FILE *)data;
495*86d7f5d3SJohn Marino fputs ("F", fp);
496*86d7f5d3SJohn Marino fputs (node->key, fp);
497*86d7f5d3SJohn Marino fputs ("\t", fp);
498*86d7f5d3SJohn Marino fputs (node->data, fp);
499*86d7f5d3SJohn Marino fputs ("\012", fp);
500*86d7f5d3SJohn Marino return 0;
501*86d7f5d3SJohn Marino }
502*86d7f5d3SJohn Marino
503*86d7f5d3SJohn Marino
504*86d7f5d3SJohn Marino
505*86d7f5d3SJohn Marino /*
506*86d7f5d3SJohn Marino * callback proc to run a script when fileattrs are updated.
507*86d7f5d3SJohn Marino */
508*86d7f5d3SJohn Marino static int
postwatch_proc(const char * repository,const char * filter,void * closure)509*86d7f5d3SJohn Marino postwatch_proc (const char *repository, const char *filter, void *closure)
510*86d7f5d3SJohn Marino {
511*86d7f5d3SJohn Marino char *cmdline;
512*86d7f5d3SJohn Marino const char *srepos = Short_Repository (repository);
513*86d7f5d3SJohn Marino
514*86d7f5d3SJohn Marino TRACE (TRACE_FUNCTION, "postwatch_proc (%s, %s)", repository, filter);
515*86d7f5d3SJohn Marino
516*86d7f5d3SJohn Marino /* %c = command name
517*86d7f5d3SJohn Marino * %p = shortrepos
518*86d7f5d3SJohn Marino * %r = repository
519*86d7f5d3SJohn Marino */
520*86d7f5d3SJohn Marino /*
521*86d7f5d3SJohn Marino * Cast any NULL arguments as appropriate pointers as this is an
522*86d7f5d3SJohn Marino * stdarg function and we need to be certain the caller gets what
523*86d7f5d3SJohn Marino * is expected.
524*86d7f5d3SJohn Marino */
525*86d7f5d3SJohn Marino cmdline = format_cmdline (
526*86d7f5d3SJohn Marino #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
527*86d7f5d3SJohn Marino false, srepos,
528*86d7f5d3SJohn Marino #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
529*86d7f5d3SJohn Marino filter,
530*86d7f5d3SJohn Marino "c", "s", cvs_cmd_name,
531*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
532*86d7f5d3SJohn Marino "R", "s", referrer ? referrer->original : "NONE",
533*86d7f5d3SJohn Marino #endif /* SERVER_SUPPORT */
534*86d7f5d3SJohn Marino "p", "s", srepos,
535*86d7f5d3SJohn Marino "r", "s", current_parsed_root->directory,
536*86d7f5d3SJohn Marino (char *) NULL);
537*86d7f5d3SJohn Marino
538*86d7f5d3SJohn Marino if (!cmdline || !strlen (cmdline))
539*86d7f5d3SJohn Marino {
540*86d7f5d3SJohn Marino if (cmdline) free (cmdline);
541*86d7f5d3SJohn Marino error (0, 0, "postwatch proc resolved to the empty string!");
542*86d7f5d3SJohn Marino return 1;
543*86d7f5d3SJohn Marino }
544*86d7f5d3SJohn Marino
545*86d7f5d3SJohn Marino run_setup (cmdline);
546*86d7f5d3SJohn Marino
547*86d7f5d3SJohn Marino free (cmdline);
548*86d7f5d3SJohn Marino
549*86d7f5d3SJohn Marino /* FIXME - read the comment in verifymsg_proc() about why we use abs()
550*86d7f5d3SJohn Marino * below() and shouldn't.
551*86d7f5d3SJohn Marino */
552*86d7f5d3SJohn Marino return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
553*86d7f5d3SJohn Marino RUN_NORMAL | RUN_SIGIGNORE));
554*86d7f5d3SJohn Marino }
555*86d7f5d3SJohn Marino
556*86d7f5d3SJohn Marino
557*86d7f5d3SJohn Marino
558*86d7f5d3SJohn Marino void
fileattr_write(void)559*86d7f5d3SJohn Marino fileattr_write (void)
560*86d7f5d3SJohn Marino {
561*86d7f5d3SJohn Marino FILE *fp;
562*86d7f5d3SJohn Marino char *fname;
563*86d7f5d3SJohn Marino mode_t omask;
564*86d7f5d3SJohn Marino struct unrecog *p;
565*86d7f5d3SJohn Marino
566*86d7f5d3SJohn Marino if (!attrs_modified)
567*86d7f5d3SJohn Marino return;
568*86d7f5d3SJohn Marino
569*86d7f5d3SJohn Marino if (noexec)
570*86d7f5d3SJohn Marino return;
571*86d7f5d3SJohn Marino
572*86d7f5d3SJohn Marino /* If NULL was passed to fileattr_startdir, then it isn't kosher to set
573*86d7f5d3SJohn Marino attributes. */
574*86d7f5d3SJohn Marino assert (fileattr_stored_repos != NULL);
575*86d7f5d3SJohn Marino
576*86d7f5d3SJohn Marino fname = Xasprintf ("%s/%s", fileattr_stored_repos, CVSREP_FILEATTR);
577*86d7f5d3SJohn Marino
578*86d7f5d3SJohn Marino if (list_isempty (attrlist)
579*86d7f5d3SJohn Marino && fileattr_default_attrs == NULL
580*86d7f5d3SJohn Marino && unrecog_head == NULL)
581*86d7f5d3SJohn Marino {
582*86d7f5d3SJohn Marino /* There are no attributes. */
583*86d7f5d3SJohn Marino if (unlink_file (fname) < 0)
584*86d7f5d3SJohn Marino {
585*86d7f5d3SJohn Marino if (!existence_error (errno))
586*86d7f5d3SJohn Marino {
587*86d7f5d3SJohn Marino error (0, errno, "cannot remove %s", fname);
588*86d7f5d3SJohn Marino }
589*86d7f5d3SJohn Marino }
590*86d7f5d3SJohn Marino
591*86d7f5d3SJohn Marino /* Now remove CVSREP directory, if empty. The main reason we bother
592*86d7f5d3SJohn Marino is that CVS 1.6 and earlier will choke if a CVSREP directory
593*86d7f5d3SJohn Marino exists, so provide the user a graceful way to remove it. */
594*86d7f5d3SJohn Marino strcpy (fname, fileattr_stored_repos);
595*86d7f5d3SJohn Marino strcat (fname, "/");
596*86d7f5d3SJohn Marino strcat (fname, CVSREP);
597*86d7f5d3SJohn Marino if (CVS_RMDIR (fname) < 0)
598*86d7f5d3SJohn Marino {
599*86d7f5d3SJohn Marino if (errno != ENOTEMPTY
600*86d7f5d3SJohn Marino
601*86d7f5d3SJohn Marino /* Don't know why we would be here if there is no CVSREP
602*86d7f5d3SJohn Marino directory, but it seemed to be happening anyway, so
603*86d7f5d3SJohn Marino check for it. */
604*86d7f5d3SJohn Marino && !existence_error (errno))
605*86d7f5d3SJohn Marino error (0, errno, "cannot remove %s", fname);
606*86d7f5d3SJohn Marino }
607*86d7f5d3SJohn Marino
608*86d7f5d3SJohn Marino free (fname);
609*86d7f5d3SJohn Marino return;
610*86d7f5d3SJohn Marino }
611*86d7f5d3SJohn Marino
612*86d7f5d3SJohn Marino omask = umask (cvsumask);
613*86d7f5d3SJohn Marino fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE);
614*86d7f5d3SJohn Marino if (fp == NULL)
615*86d7f5d3SJohn Marino {
616*86d7f5d3SJohn Marino if (existence_error (errno))
617*86d7f5d3SJohn Marino {
618*86d7f5d3SJohn Marino /* Maybe the CVSREP directory doesn't exist. Try creating it. */
619*86d7f5d3SJohn Marino char *repname;
620*86d7f5d3SJohn Marino
621*86d7f5d3SJohn Marino repname = Xasprintf ("%s/%s", fileattr_stored_repos, CVSREP);
622*86d7f5d3SJohn Marino
623*86d7f5d3SJohn Marino if (CVS_MKDIR (repname, 0777) < 0 && errno != EEXIST)
624*86d7f5d3SJohn Marino {
625*86d7f5d3SJohn Marino error (0, errno, "cannot make directory %s", repname);
626*86d7f5d3SJohn Marino (void) umask (omask);
627*86d7f5d3SJohn Marino free (fname);
628*86d7f5d3SJohn Marino free (repname);
629*86d7f5d3SJohn Marino return;
630*86d7f5d3SJohn Marino }
631*86d7f5d3SJohn Marino free (repname);
632*86d7f5d3SJohn Marino
633*86d7f5d3SJohn Marino fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE);
634*86d7f5d3SJohn Marino }
635*86d7f5d3SJohn Marino if (fp == NULL)
636*86d7f5d3SJohn Marino {
637*86d7f5d3SJohn Marino error (0, errno, "cannot write %s", fname);
638*86d7f5d3SJohn Marino (void) umask (omask);
639*86d7f5d3SJohn Marino free (fname);
640*86d7f5d3SJohn Marino return;
641*86d7f5d3SJohn Marino }
642*86d7f5d3SJohn Marino }
643*86d7f5d3SJohn Marino (void) umask (omask);
644*86d7f5d3SJohn Marino
645*86d7f5d3SJohn Marino /* First write the "F" attributes. */
646*86d7f5d3SJohn Marino walklist (attrlist, writeattr_proc, fp);
647*86d7f5d3SJohn Marino
648*86d7f5d3SJohn Marino /* Then the "D" attribute. */
649*86d7f5d3SJohn Marino if (fileattr_default_attrs != NULL)
650*86d7f5d3SJohn Marino {
651*86d7f5d3SJohn Marino fputs ("D\t", fp);
652*86d7f5d3SJohn Marino fputs (fileattr_default_attrs, fp);
653*86d7f5d3SJohn Marino fputs ("\012", fp);
654*86d7f5d3SJohn Marino }
655*86d7f5d3SJohn Marino
656*86d7f5d3SJohn Marino /* Then any other attributes. */
657*86d7f5d3SJohn Marino for (p = unrecog_head; p != NULL; p = p->next)
658*86d7f5d3SJohn Marino {
659*86d7f5d3SJohn Marino fputs (p->line, fp);
660*86d7f5d3SJohn Marino fputs ("\012", fp);
661*86d7f5d3SJohn Marino }
662*86d7f5d3SJohn Marino
663*86d7f5d3SJohn Marino if (fclose (fp) < 0)
664*86d7f5d3SJohn Marino error (0, errno, "cannot close %s", fname);
665*86d7f5d3SJohn Marino attrs_modified = 0;
666*86d7f5d3SJohn Marino free (fname);
667*86d7f5d3SJohn Marino
668*86d7f5d3SJohn Marino Parse_Info (CVSROOTADM_POSTWATCH, fileattr_stored_repos, postwatch_proc,
669*86d7f5d3SJohn Marino PIOPT_ALL, NULL);
670*86d7f5d3SJohn Marino }
671*86d7f5d3SJohn Marino
672*86d7f5d3SJohn Marino
673*86d7f5d3SJohn Marino
674*86d7f5d3SJohn Marino void
fileattr_free(void)675*86d7f5d3SJohn Marino fileattr_free (void)
676*86d7f5d3SJohn Marino {
677*86d7f5d3SJohn Marino /* Note that attrs_modified will ordinarily be zero, but there are
678*86d7f5d3SJohn Marino a few cases in which fileattr_write will fail to zero it (if
679*86d7f5d3SJohn Marino noexec is set, or error conditions). This probably is the way
680*86d7f5d3SJohn Marino it should be. */
681*86d7f5d3SJohn Marino dellist (&attrlist);
682*86d7f5d3SJohn Marino if (fileattr_stored_repos != NULL)
683*86d7f5d3SJohn Marino free (fileattr_stored_repos);
684*86d7f5d3SJohn Marino fileattr_stored_repos = NULL;
685*86d7f5d3SJohn Marino if (fileattr_default_attrs != NULL)
686*86d7f5d3SJohn Marino free (fileattr_default_attrs);
687*86d7f5d3SJohn Marino fileattr_default_attrs = NULL;
688*86d7f5d3SJohn Marino while (unrecog_head)
689*86d7f5d3SJohn Marino {
690*86d7f5d3SJohn Marino struct unrecog *p = unrecog_head;
691*86d7f5d3SJohn Marino unrecog_head = p->next;
692*86d7f5d3SJohn Marino free (p->line);
693*86d7f5d3SJohn Marino free (p);
694*86d7f5d3SJohn Marino }
695*86d7f5d3SJohn Marino }
696