xref: /openbsd-src/gnu/usr.bin/cvs/src/fileattr.c (revision 13571821e83933f3c1d7fd1ab5ff9cd54f0eea7f)
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    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
16 
17 #include "cvs.h"
18 #include "getline.h"
19 #include "fileattr.h"
20 #include <assert.h>
21 
22 static void fileattr_read PROTO ((void));
23 static int writeattr_proc PROTO ((Node *, void *));
24 
25 /* Where to look for CVSREP_FILEATTR.  */
26 static char *fileattr_stored_repos;
27 
28 /* The in-memory attributes.  */
29 static List *attrlist;
30 static char *fileattr_default_attrs;
31 /* We have already tried to read attributes and failed in this directory
32    (for example, there is no CVSREP_FILEATTR file).  */
33 static int attr_read_attempted;
34 
35 /* Have the in-memory attributes been modified since we read them?  */
36 static int attrs_modified;
37 
38 /* Note that if noone calls fileattr_get, this is very cheap.  No stat(),
39    no open(), no nothing.  */
40 void
41 fileattr_startdir (repos)
42     char *repos;
43 {
44     assert (fileattr_stored_repos == NULL);
45     fileattr_stored_repos = xstrdup (repos);
46     assert (attrlist == NULL);
47     attr_read_attempted = 0;
48 }
49 
50 static void
51 fileattr_delproc (node)
52     Node *node;
53 {
54     free (node->data);
55 }
56 
57 /* Read all the attributes for the current directory into memory.  */
58 static void
59 fileattr_read ()
60 {
61     char *fname;
62     FILE *fp;
63     char *line = NULL;
64     size_t line_len = 0;
65 
66     /* If there are no attributes, don't waste time repeatedly looking
67        for the CVSREP_FILEATTR file.  */
68     if (attr_read_attempted)
69 	return;
70 
71     /* If NULL was passed to fileattr_startdir, then it isn't kosher to look
72        at attributes.  */
73     assert (fileattr_stored_repos != NULL);
74 
75     fname = xmalloc (strlen (fileattr_stored_repos)
76 		     + 1
77 		     + sizeof (CVSREP_FILEATTR)
78 		     + 1);
79 
80     strcpy (fname, fileattr_stored_repos);
81     strcat (fname, "/");
82     strcat (fname, CVSREP_FILEATTR);
83 
84     attr_read_attempted = 1;
85     fp = fopen (fname, "r");
86     if (fp == NULL)
87     {
88 	if (!existence_error (errno))
89 	    error (0, errno, "cannot read %s", fname);
90 	free (fname);
91 	return;
92     }
93     attrlist = getlist ();
94     while (1) {
95 	int nread;
96 	nread = getline (&line, &line_len, fp);
97 	if (nread < 0)
98 	    break;
99 	/* Remove trailing newline.  */
100 	line[nread - 1] = '\0';
101 	if (line[0] == 'F')
102 	{
103 	    char *p;
104 	    Node *newnode;
105 
106 	    p = strchr (line, '\t');
107 	    *p++ = '\0';
108 	    newnode = getnode ();
109 	    newnode->type = FILEATTR;
110 	    newnode->delproc = fileattr_delproc;
111 	    newnode->key = xstrdup (line + 1);
112 	    newnode->data = xstrdup (p);
113 	    addnode (attrlist, newnode);
114 	}
115 	else if (line[0] == 'D')
116 	{
117 	    char *p;
118 	    /* Currently nothing to skip here, but for future expansion,
119 	       ignore anything located here.  */
120 	    p = strchr (line, '\t');
121 	    ++p;
122 	    fileattr_default_attrs = xstrdup (p);
123 	}
124 	/* else just ignore the line, for future expansion.  */
125     }
126     if (ferror (fp))
127 	error (0, errno, "cannot read %s", fname);
128     if (line != NULL)
129 	free (line);
130     if (fclose (fp) < 0)
131 	error (0, errno, "cannot close %s", fname);
132     attrs_modified = 0;
133     free (fname);
134 }
135 
136 char *
137 fileattr_get (filename, attrname)
138     char *filename;
139     char *attrname;
140 {
141     Node *node;
142     size_t attrname_len = strlen (attrname);
143     char *p;
144 
145     if (attrlist == NULL)
146 	fileattr_read ();
147     if (attrlist == NULL)
148 	/* Either nothing has any attributes, or fileattr_read already printed
149 	   an error message.  */
150 	return NULL;
151 
152     node = findnode (attrlist, filename);
153     if (node == NULL)
154 	/* A file not mentioned has no attributes.  */
155 	return NULL;
156     p = node->data;
157     while (1) {
158 	if (strncmp (attrname, p, attrname_len) == 0
159 	    && p[attrname_len] == '=')
160 	{
161 	    /* Found it.  */
162 	    return p + attrname_len + 1;
163 	}
164 	p = strchr (p, ';');
165 	if (p == NULL)
166 	    break;
167 	++p;
168     }
169     /* The file doesn't have this attribute.  */
170     return NULL;
171 }
172 
173 char *
174 fileattr_get0 (filename, attrname)
175     char *filename;
176     char *attrname;
177 {
178     char *cp;
179     char *cpend;
180     char *retval;
181 
182     cp = fileattr_get (filename, attrname);
183     if (cp == NULL)
184 	return NULL;
185     cpend = strchr (cp, ';');
186     if (cpend == NULL)
187 	cpend = cp + strlen (cp);
188     retval = xmalloc (cpend - cp + 1);
189     strncpy (retval, cp, cpend - cp);
190     retval[cpend - cp] = '\0';
191     return retval;
192 }
193 
194 char *
195 fileattr_modify (list, attrname, attrval, namevalsep, entsep)
196     char *list;
197     char *attrname;
198     char *attrval;
199     int namevalsep;
200     int entsep;
201 {
202     char *retval;
203     char *rp;
204     size_t attrname_len = strlen (attrname);
205 
206     /* Portion of list before the attribute to be replaced.  */
207     char *pre;
208     char *preend;
209     /* Portion of list after the attribute to be replaced.  */
210     char *post;
211 
212     char *p;
213     char *p2;
214 
215     p = list;
216     pre = list;
217     preend = NULL;
218     /* post is NULL unless set otherwise.  */
219     post = NULL;
220     p2 = NULL;
221     if (list != NULL)
222     {
223 	while (1) {
224 	    p2 = strchr (p, entsep);
225 	    if (p2 == NULL)
226 	    {
227 		p2 = p + strlen (p);
228 		if (preend == NULL)
229 		    preend = p2;
230 	    }
231 	    else
232 		++p2;
233 	    if (strncmp (attrname, p, attrname_len) == 0
234 		&& p[attrname_len] == namevalsep)
235 	    {
236 		/* Found it.  */
237 		preend = p;
238 		if (preend > list)
239 		    /* Don't include the preceding entsep.  */
240 		    --preend;
241 
242 		post = p2;
243 	    }
244 	    if (p2[0] == '\0')
245 		break;
246 	    p = p2;
247 	}
248     }
249     if (post == NULL)
250 	post = p2;
251 
252     if (preend == pre && attrval == NULL && post == p2)
253 	return NULL;
254 
255     retval = xmalloc ((preend - pre)
256 		      + 1
257 		      + (attrval == NULL ? 0 : (attrname_len + 1
258 						+ strlen (attrval)))
259 		      + 1
260 		      + (p2 - post)
261 		      + 1);
262     if (preend != pre)
263     {
264 	strncpy (retval, pre, preend - pre);
265 	rp = retval + (preend - pre);
266 	if (attrval != NULL)
267 	    *rp++ = entsep;
268 	*rp = '\0';
269     }
270     else
271 	retval[0] = '\0';
272     if (attrval != NULL)
273     {
274 	strcat (retval, attrname);
275 	rp = retval + strlen (retval);
276 	*rp++ = namevalsep;
277 	strcpy (rp, attrval);
278     }
279     if (post != p2)
280     {
281 	rp = retval + strlen (retval);
282 	if (preend != pre || attrval != NULL)
283 	    *rp++ = entsep;
284 	strncpy (rp, post, p2 - post);
285 	rp += p2 - post;
286 	*rp = '\0';
287     }
288     return retval;
289 }
290 
291 void
292 fileattr_set (filename, attrname, attrval)
293     char *filename;
294     char *attrname;
295     char *attrval;
296 {
297     Node *node;
298     char *p;
299 
300     attrs_modified = 1;
301 
302     if (filename == NULL)
303     {
304 	p = fileattr_modify (fileattr_default_attrs, attrname, attrval,
305 			     '=', ';');
306 	if (fileattr_default_attrs != NULL)
307 	    free (fileattr_default_attrs);
308 	fileattr_default_attrs = p;
309 	return;
310     }
311     if (attrlist == NULL)
312 	fileattr_read ();
313     if (attrlist == NULL)
314     {
315 	/* Not sure this is a graceful way to handle things
316 	   in the case where fileattr_read was unable to read the file.  */
317         /* No attributes existed previously.  */
318 	attrlist = getlist ();
319     }
320 
321     node = findnode (attrlist, filename);
322     if (node == NULL)
323     {
324 	if (attrval == NULL)
325 	    /* Attempt to remove an attribute which wasn't there.  */
326 	    return;
327 
328 	/* First attribute for this file.  */
329 	node = getnode ();
330 	node->type = FILEATTR;
331 	node->delproc = fileattr_delproc;
332 	node->key = xstrdup (filename);
333 	node->data = xmalloc (strlen (attrname) + 1 + strlen (attrval) + 1);
334 	strcpy (node->data, attrname);
335 	strcat (node->data, "=");
336 	strcat (node->data, attrval);
337 	addnode (attrlist, node);
338     }
339 
340     p = fileattr_modify (node->data, attrname, attrval, '=', ';');
341     free (node->data);
342     if (p == NULL)
343 	delnode (node);
344     else
345 	node->data = p;
346 }
347 
348 void
349 fileattr_newfile (filename)
350     char *filename;
351 {
352     Node *node;
353 
354     if (attrlist == NULL)
355 	fileattr_read ();
356 
357     if (fileattr_default_attrs == NULL)
358 	return;
359 
360     if (attrlist == NULL)
361     {
362 	/* Not sure this is a graceful way to handle things
363 	   in the case where fileattr_read was unable to read the file.  */
364         /* No attributes existed previously.  */
365 	attrlist = getlist ();
366     }
367 
368     node = getnode ();
369     node->type = FILEATTR;
370     node->delproc = fileattr_delproc;
371     node->key = xstrdup (filename);
372     node->data = xstrdup (fileattr_default_attrs);
373     addnode (attrlist, node);
374     attrs_modified = 1;
375 }
376 
377 static int
378 writeattr_proc (node, data)
379     Node *node;
380     void *data;
381 {
382     FILE *fp = (FILE *)data;
383     fputs ("F", fp);
384     fputs (node->key, fp);
385     fputs ("\t", fp);
386     fputs (node->data, fp);
387     fputs ("\n", fp);
388     return 0;
389 }
390 
391 void
392 fileattr_write ()
393 {
394     FILE *fp;
395     char *fname;
396     mode_t omask;
397 
398     if (!attrs_modified)
399 	return;
400 
401     if (noexec)
402 	return;
403 
404     /* If NULL was passed to fileattr_startdir, then it isn't kosher to set
405        attributes.  */
406     assert (fileattr_stored_repos != NULL);
407 
408     fname = xmalloc (strlen (fileattr_stored_repos)
409 		     + 1
410 		     + sizeof (CVSREP_FILEATTR)
411 		     + 1);
412 
413     strcpy (fname, fileattr_stored_repos);
414     strcat (fname, "/");
415     strcat (fname, CVSREP_FILEATTR);
416 
417     if (list_isempty (attrlist) && fileattr_default_attrs == NULL)
418     {
419 	/* There are no attributes.  */
420 	if (unlink_file (fname) < 0)
421 	{
422 	    if (!existence_error (errno))
423 	    {
424 		error (0, errno, "cannot remove %s", fname);
425 	    }
426 	}
427 
428 	/* Now remove CVSREP directory, if empty.  The main reason we bother
429 	   is that CVS 1.6 and earlier will choke if a CVSREP directory
430 	   exists, so provide the user a graceful way to remove it.  */
431 	strcpy (fname, fileattr_stored_repos);
432 	strcat (fname, "/");
433 	strcat (fname, CVSREP);
434 	if (rmdir (fname) < 0)
435 	{
436 	    if (errno != ENOTEMPTY
437 
438 		/* Don't know why we would be here if there is no CVSREP
439 		   directory, but it seemed to be happening anyway, so
440 		   check for it.  */
441 		&& !existence_error (errno))
442 		error (0, errno, "cannot remove %s", fname);
443 	}
444 
445 	free (fname);
446 	return;
447     }
448 
449     omask = umask (cvsumask);
450     fp = fopen (fname, "w");
451     if (fp == NULL)
452     {
453 	if (existence_error (errno))
454 	{
455 	    /* Maybe the CVSREP directory doesn't exist.  Try creating it.  */
456 	    char *repname;
457 
458 	    repname = xmalloc (strlen (fileattr_stored_repos)
459 			       + 1
460 			       + sizeof (CVSREP)
461 			       + 1);
462 	    strcpy (repname, fileattr_stored_repos);
463 	    strcat (repname, "/");
464 	    strcat (repname, CVSREP);
465 
466 	    if (CVS_MKDIR (repname, 0777) < 0 && errno != EEXIST)
467 	    {
468 		error (0, errno, "cannot make directory %s", repname);
469 		(void) umask (omask);
470 		free (repname);
471 		return;
472 	    }
473 	    free (repname);
474 
475 	    fp = fopen (fname, "w");
476 	}
477 	if (fp == NULL)
478 	{
479 	    error (0, errno, "cannot write %s", fname);
480 	    (void) umask (omask);
481 	    return;
482 	}
483     }
484     (void) umask (omask);
485     walklist (attrlist, writeattr_proc, fp);
486     if (fileattr_default_attrs != NULL)
487     {
488 	fputs ("D\t", fp);
489 	fputs (fileattr_default_attrs, fp);
490 	fputs ("\n", fp);
491     }
492     if (fclose (fp) < 0)
493 	error (0, errno, "cannot close %s", fname);
494     attrs_modified = 0;
495     free (fname);
496 }
497 
498 void
499 fileattr_free ()
500 {
501     dellist (&attrlist);
502     if (fileattr_stored_repos != NULL)
503 	free (fileattr_stored_repos);
504     fileattr_stored_repos = NULL;
505     if (fileattr_default_attrs != NULL)
506 	free (fileattr_default_attrs);
507     fileattr_default_attrs = NULL;
508 }
509