xref: /dflybsd-src/contrib/cvs-1.12/src/fileattr.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
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