117721Speter/* Implementation for file attribute munging features.
217721Speter
317721Speter   This program is free software; you can redistribute it and/or modify
417721Speter   it under the terms of the GNU General Public License as published by
517721Speter   the Free Software Foundation; either version 2, or (at your option)
617721Speter   any later version.
717721Speter
817721Speter   This program is distributed in the hope that it will be useful,
917721Speter   but WITHOUT ANY WARRANTY; without even the implied warranty of
1017721Speter   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1125839Speter   GNU General Public License for more details.  */
1217721Speter
1317721Speter#include "cvs.h"
1417721Speter#include "getline.h"
1517721Speter#include "fileattr.h"
1617721Speter#include <assert.h>
1717721Speter
1817721Speterstatic void fileattr_read PROTO ((void));
1917721Speterstatic int writeattr_proc PROTO ((Node *, void *));
2017721Speter
2117721Speter/* Where to look for CVSREP_FILEATTR.  */
2217721Speterstatic char *fileattr_stored_repos;
2317721Speter
2417721Speter/* The in-memory attributes.  */
2517721Speterstatic List *attrlist;
2617721Speterstatic char *fileattr_default_attrs;
2717721Speter/* We have already tried to read attributes and failed in this directory
2817721Speter   (for example, there is no CVSREP_FILEATTR file).  */
2917721Speterstatic int attr_read_attempted;
3017721Speter
3117721Speter/* Have the in-memory attributes been modified since we read them?  */
3217721Speterstatic int attrs_modified;
3317721Speter
3425839Speter/* More in-memory attributes: linked list of unrecognized
3525839Speter   fileattr lines.  We pass these on unchanged.  */
3625839Speterstruct unrecog {
3725839Speter    char *line;
3825839Speter    struct unrecog *next;
3925839Speter};
4025839Speterstatic struct unrecog *unrecog_head;
4125839Speter
4217721Speter/* Note that if noone calls fileattr_get, this is very cheap.  No stat(),
4317721Speter   no open(), no nothing.  */
4417721Spetervoid
4517721Speterfileattr_startdir (repos)
46128266Speter    const char *repos;
4717721Speter{
4817721Speter    assert (fileattr_stored_repos == NULL);
4917721Speter    fileattr_stored_repos = xstrdup (repos);
5017721Speter    assert (attrlist == NULL);
5117721Speter    attr_read_attempted = 0;
5225839Speter    assert (unrecog_head == NULL);
5317721Speter}
5417721Speter
5517721Speterstatic void
5617721Speterfileattr_delproc (node)
5717721Speter    Node *node;
5817721Speter{
5917721Speter    assert (node->data != NULL);
6017721Speter    free (node->data);
6117721Speter    node->data = NULL;
6217721Speter}
6317721Speter
6417721Speter/* Read all the attributes for the current directory into memory.  */
6517721Speterstatic void
6617721Speterfileattr_read ()
6717721Speter{
6817721Speter    char *fname;
6917721Speter    FILE *fp;
7017721Speter    char *line = NULL;
7117721Speter    size_t line_len = 0;
7217721Speter
7317721Speter    /* If there are no attributes, don't waste time repeatedly looking
7417721Speter       for the CVSREP_FILEATTR file.  */
7517721Speter    if (attr_read_attempted)
7617721Speter	return;
7717721Speter
7817721Speter    /* If NULL was passed to fileattr_startdir, then it isn't kosher to look
7917721Speter       at attributes.  */
8017721Speter    assert (fileattr_stored_repos != NULL);
8117721Speter
8217721Speter    fname = xmalloc (strlen (fileattr_stored_repos)
8317721Speter		     + 1
8417721Speter		     + sizeof (CVSREP_FILEATTR)
8517721Speter		     + 1);
8617721Speter
8717721Speter    strcpy (fname, fileattr_stored_repos);
8817721Speter    strcat (fname, "/");
8917721Speter    strcat (fname, CVSREP_FILEATTR);
9017721Speter
9117721Speter    attr_read_attempted = 1;
9225839Speter    fp = CVS_FOPEN (fname, FOPEN_BINARY_READ);
9317721Speter    if (fp == NULL)
9417721Speter    {
9517721Speter	if (!existence_error (errno))
9617721Speter	    error (0, errno, "cannot read %s", fname);
9717721Speter	free (fname);
9817721Speter	return;
9917721Speter    }
10017721Speter    attrlist = getlist ();
10117721Speter    while (1) {
10217721Speter	int nread;
10317721Speter	nread = getline (&line, &line_len, fp);
10417721Speter	if (nread < 0)
10517721Speter	    break;
10617721Speter	/* Remove trailing newline.  */
10717721Speter	line[nread - 1] = '\0';
10817721Speter	if (line[0] == 'F')
10917721Speter	{
11017721Speter	    char *p;
11117721Speter	    Node *newnode;
11217721Speter
11317721Speter	    p = strchr (line, '\t');
11444852Speter	    if (p == NULL)
11544852Speter		error (1, 0,
11644852Speter		       "file attribute database corruption: tab missing in %s",
11744852Speter		       fname);
11817721Speter	    *p++ = '\0';
11917721Speter	    newnode = getnode ();
12017721Speter	    newnode->type = FILEATTR;
12117721Speter	    newnode->delproc = fileattr_delproc;
12217721Speter	    newnode->key = xstrdup (line + 1);
12317721Speter	    newnode->data = xstrdup (p);
12425839Speter	    if (addnode (attrlist, newnode) != 0)
12525839Speter		/* If the same filename appears twice in the file, discard
12625839Speter		   any line other than the first for that filename.  This
12725839Speter		   is the way that CVS has behaved since file attributes
12825839Speter		   were first introduced.  */
12966525Speter		freenode (newnode);
13017721Speter	}
13117721Speter	else if (line[0] == 'D')
13217721Speter	{
13317721Speter	    char *p;
13417721Speter	    /* Currently nothing to skip here, but for future expansion,
13517721Speter	       ignore anything located here.  */
13617721Speter	    p = strchr (line, '\t');
13744852Speter	    if (p == NULL)
13844852Speter		error (1, 0,
13944852Speter		       "file attribute database corruption: tab missing in %s",
14044852Speter		       fname);
14117721Speter	    ++p;
142175261Sobrien	    if (fileattr_default_attrs) free (fileattr_default_attrs);
14317721Speter	    fileattr_default_attrs = xstrdup (p);
14417721Speter	}
14525839Speter	else
14625839Speter	{
14725839Speter	    /* Unrecognized type, we want to just preserve the line without
14825839Speter	       changing it, for future expansion.  */
14925839Speter	    struct unrecog *new;
15025839Speter
15125839Speter	    new = (struct unrecog *) xmalloc (sizeof (struct unrecog));
15225839Speter	    new->line = xstrdup (line);
15325839Speter	    new->next = unrecog_head;
15425839Speter	    unrecog_head = new;
15525839Speter	}
15617721Speter    }
15717721Speter    if (ferror (fp))
15817721Speter	error (0, errno, "cannot read %s", fname);
15917721Speter    if (line != NULL)
16017721Speter	free (line);
16117721Speter    if (fclose (fp) < 0)
16217721Speter	error (0, errno, "cannot close %s", fname);
16317721Speter    attrs_modified = 0;
16417721Speter    free (fname);
16517721Speter}
16617721Speter
16717721Speterchar *
16817721Speterfileattr_get (filename, attrname)
16925839Speter    const char *filename;
17025839Speter    const char *attrname;
17117721Speter{
17217721Speter    Node *node;
17317721Speter    size_t attrname_len = strlen (attrname);
17417721Speter    char *p;
17517721Speter
17617721Speter    if (attrlist == NULL)
17717721Speter	fileattr_read ();
17817721Speter    if (attrlist == NULL)
17917721Speter	/* Either nothing has any attributes, or fileattr_read already printed
18017721Speter	   an error message.  */
18117721Speter	return NULL;
18217721Speter
18317721Speter    if (filename == NULL)
18417721Speter	p = fileattr_default_attrs;
18517721Speter    else
18617721Speter    {
18717721Speter	node = findnode (attrlist, filename);
18817721Speter	if (node == NULL)
18917721Speter	    /* A file not mentioned has no attributes.  */
19017721Speter	    return NULL;
19117721Speter	p = node->data;
19217721Speter    }
19317721Speter    while (p)
19417721Speter    {
19517721Speter	if (strncmp (attrname, p, attrname_len) == 0
19617721Speter	    && p[attrname_len] == '=')
19717721Speter	{
19817721Speter	    /* Found it.  */
19917721Speter	    return p + attrname_len + 1;
20017721Speter	}
20117721Speter	p = strchr (p, ';');
20217721Speter	if (p == NULL)
20317721Speter	    break;
20417721Speter	++p;
20517721Speter    }
20617721Speter    /* The file doesn't have this attribute.  */
20717721Speter    return NULL;
20817721Speter}
20917721Speter
21017721Speterchar *
21117721Speterfileattr_get0 (filename, attrname)
21225839Speter    const char *filename;
21325839Speter    const char *attrname;
21417721Speter{
21517721Speter    char *cp;
21617721Speter    char *cpend;
21717721Speter    char *retval;
21817721Speter
21917721Speter    cp = fileattr_get (filename, attrname);
22017721Speter    if (cp == NULL)
22117721Speter	return NULL;
22217721Speter    cpend = strchr (cp, ';');
22317721Speter    if (cpend == NULL)
22417721Speter	cpend = cp + strlen (cp);
22517721Speter    retval = xmalloc (cpend - cp + 1);
22617721Speter    strncpy (retval, cp, cpend - cp);
22717721Speter    retval[cpend - cp] = '\0';
22817721Speter    return retval;
22917721Speter}
23017721Speter
23117721Speterchar *
23217721Speterfileattr_modify (list, attrname, attrval, namevalsep, entsep)
23317721Speter    char *list;
23425839Speter    const char *attrname;
23525839Speter    const char *attrval;
23617721Speter    int namevalsep;
23717721Speter    int entsep;
23817721Speter{
23917721Speter    char *retval;
24017721Speter    char *rp;
24117721Speter    size_t attrname_len = strlen (attrname);
24217721Speter
24317721Speter    /* Portion of list before the attribute to be replaced.  */
24417721Speter    char *pre;
24517721Speter    char *preend;
24617721Speter    /* Portion of list after the attribute to be replaced.  */
24717721Speter    char *post;
24817721Speter
24917721Speter    char *p;
25017721Speter    char *p2;
25117721Speter
25217721Speter    p = list;
25317721Speter    pre = list;
25417721Speter    preend = NULL;
25517721Speter    /* post is NULL unless set otherwise.  */
25617721Speter    post = NULL;
25717721Speter    p2 = NULL;
25817721Speter    if (list != NULL)
25917721Speter    {
26017721Speter	while (1) {
26117721Speter	    p2 = strchr (p, entsep);
26217721Speter	    if (p2 == NULL)
26317721Speter	    {
26417721Speter		p2 = p + strlen (p);
26517721Speter		if (preend == NULL)
26617721Speter		    preend = p2;
26717721Speter	    }
26817721Speter	    else
26917721Speter		++p2;
27017721Speter	    if (strncmp (attrname, p, attrname_len) == 0
27117721Speter		&& p[attrname_len] == namevalsep)
27217721Speter	    {
27317721Speter		/* Found it.  */
27417721Speter		preend = p;
27517721Speter		if (preend > list)
27617721Speter		    /* Don't include the preceding entsep.  */
27717721Speter		    --preend;
27817721Speter
27917721Speter		post = p2;
28017721Speter	    }
28117721Speter	    if (p2[0] == '\0')
28217721Speter		break;
28317721Speter	    p = p2;
28417721Speter	}
28517721Speter    }
28617721Speter    if (post == NULL)
28717721Speter	post = p2;
28817721Speter
28917721Speter    if (preend == pre && attrval == NULL && post == p2)
29017721Speter	return NULL;
29117721Speter
29217721Speter    retval = xmalloc ((preend - pre)
29317721Speter		      + 1
29417721Speter		      + (attrval == NULL ? 0 : (attrname_len + 1
29517721Speter						+ strlen (attrval)))
29617721Speter		      + 1
29717721Speter		      + (p2 - post)
29817721Speter		      + 1);
29917721Speter    if (preend != pre)
30017721Speter    {
30117721Speter	strncpy (retval, pre, preend - pre);
30217721Speter	rp = retval + (preend - pre);
30317721Speter	if (attrval != NULL)
30417721Speter	    *rp++ = entsep;
30517721Speter	*rp = '\0';
30617721Speter    }
30717721Speter    else
30817721Speter	retval[0] = '\0';
30917721Speter    if (attrval != NULL)
31017721Speter    {
31117721Speter	strcat (retval, attrname);
31217721Speter	rp = retval + strlen (retval);
31317721Speter	*rp++ = namevalsep;
31417721Speter	strcpy (rp, attrval);
31517721Speter    }
31617721Speter    if (post != p2)
31717721Speter    {
31817721Speter	rp = retval + strlen (retval);
31917721Speter	if (preend != pre || attrval != NULL)
32017721Speter	    *rp++ = entsep;
32117721Speter	strncpy (rp, post, p2 - post);
32217721Speter	rp += p2 - post;
32317721Speter	*rp = '\0';
32417721Speter    }
32517721Speter    return retval;
32617721Speter}
32717721Speter
32817721Spetervoid
32917721Speterfileattr_set (filename, attrname, attrval)
33025839Speter    const char *filename;
33125839Speter    const char *attrname;
33225839Speter    const char *attrval;
33317721Speter{
33417721Speter    Node *node;
33517721Speter    char *p;
33617721Speter
33717721Speter    if (filename == NULL)
33817721Speter    {
33917721Speter	p = fileattr_modify (fileattr_default_attrs, attrname, attrval,
34017721Speter			     '=', ';');
34117721Speter	if (fileattr_default_attrs != NULL)
34217721Speter	    free (fileattr_default_attrs);
34317721Speter	fileattr_default_attrs = p;
34425839Speter	attrs_modified = 1;
34517721Speter	return;
34617721Speter    }
34717721Speter    if (attrlist == NULL)
34817721Speter	fileattr_read ();
34917721Speter    if (attrlist == NULL)
35017721Speter    {
35117721Speter	/* Not sure this is a graceful way to handle things
35217721Speter	   in the case where fileattr_read was unable to read the file.  */
35317721Speter        /* No attributes existed previously.  */
35417721Speter	attrlist = getlist ();
35517721Speter    }
35617721Speter
35717721Speter    node = findnode (attrlist, filename);
35817721Speter    if (node == NULL)
35917721Speter    {
36017721Speter	if (attrval == NULL)
36117721Speter	    /* Attempt to remove an attribute which wasn't there.  */
36217721Speter	    return;
36317721Speter
36417721Speter	/* First attribute for this file.  */
36517721Speter	node = getnode ();
36617721Speter	node->type = FILEATTR;
36717721Speter	node->delproc = fileattr_delproc;
36817721Speter	node->key = xstrdup (filename);
36917721Speter	node->data = xmalloc (strlen (attrname) + 1 + strlen (attrval) + 1);
37017721Speter	strcpy (node->data, attrname);
37117721Speter	strcat (node->data, "=");
37217721Speter	strcat (node->data, attrval);
37317721Speter	addnode (attrlist, node);
37417721Speter    }
37517721Speter
37617721Speter    p = fileattr_modify (node->data, attrname, attrval, '=', ';');
37717721Speter    if (p == NULL)
37817721Speter	delnode (node);
37917721Speter    else
38025839Speter    {
38125839Speter	free (node->data);
38217721Speter	node->data = p;
38325839Speter    }
38425839Speter
38525839Speter    attrs_modified = 1;
38617721Speter}
38717721Speter
38832785Speterchar *
38932785Speterfileattr_getall (filename)
39032785Speter    const char *filename;
39132785Speter{
39232785Speter    Node *node;
39332785Speter    char *p;
39432785Speter
39532785Speter    if (attrlist == NULL)
39632785Speter	fileattr_read ();
39732785Speter    if (attrlist == NULL)
39832785Speter	/* Either nothing has any attributes, or fileattr_read already printed
39932785Speter	   an error message.  */
40032785Speter	return NULL;
40132785Speter
40232785Speter    if (filename == NULL)
40332785Speter	p = fileattr_default_attrs;
40432785Speter    else
40532785Speter    {
40632785Speter	node = findnode (attrlist, filename);
40732785Speter	if (node == NULL)
40832785Speter	    /* A file not mentioned has no attributes.  */
40932785Speter	    return NULL;
41032785Speter	p = node->data;
41132785Speter    }
41232785Speter    return xstrdup (p);
41332785Speter}
41432785Speter
41517721Spetervoid
41632785Speterfileattr_setall (filename, attrs)
41732785Speter    const char *filename;
41832785Speter    const char *attrs;
41932785Speter{
42032785Speter    Node *node;
42132785Speter
42232785Speter    if (filename == NULL)
42332785Speter    {
42432785Speter	if (fileattr_default_attrs != NULL)
42532785Speter	    free (fileattr_default_attrs);
42632785Speter	fileattr_default_attrs = xstrdup (attrs);
42732785Speter	attrs_modified = 1;
42832785Speter	return;
42932785Speter    }
43032785Speter    if (attrlist == NULL)
43132785Speter	fileattr_read ();
43232785Speter    if (attrlist == NULL)
43332785Speter    {
43432785Speter	/* Not sure this is a graceful way to handle things
43532785Speter	   in the case where fileattr_read was unable to read the file.  */
43632785Speter        /* No attributes existed previously.  */
43732785Speter	attrlist = getlist ();
43832785Speter    }
43932785Speter
44032785Speter    node = findnode (attrlist, filename);
44132785Speter    if (node == NULL)
44232785Speter    {
44332785Speter	/* The file had no attributes.  Add them if we have any to add.  */
44432785Speter	if (attrs != NULL)
44532785Speter	{
44632785Speter	    node = getnode ();
44732785Speter	    node->type = FILEATTR;
44832785Speter	    node->delproc = fileattr_delproc;
44932785Speter	    node->key = xstrdup (filename);
45032785Speter	    node->data = xstrdup (attrs);
45132785Speter	    addnode (attrlist, node);
45232785Speter	}
45332785Speter    }
45432785Speter    else
45532785Speter    {
45632785Speter	if (attrs == NULL)
45732785Speter	    delnode (node);
45832785Speter	else
45932785Speter	{
46032785Speter	    free (node->data);
46132785Speter	    node->data = xstrdup (attrs);
46232785Speter	}
46332785Speter    }
46432785Speter
46532785Speter    attrs_modified = 1;
46632785Speter}
46732785Speter
46832785Spetervoid
46917721Speterfileattr_newfile (filename)
47025839Speter    const char *filename;
47117721Speter{
47217721Speter    Node *node;
47317721Speter
47417721Speter    if (attrlist == NULL)
47517721Speter	fileattr_read ();
47617721Speter
47717721Speter    if (fileattr_default_attrs == NULL)
47817721Speter	return;
47917721Speter
48017721Speter    if (attrlist == NULL)
48117721Speter    {
48217721Speter	/* Not sure this is a graceful way to handle things
48317721Speter	   in the case where fileattr_read was unable to read the file.  */
48417721Speter        /* No attributes existed previously.  */
48517721Speter	attrlist = getlist ();
48617721Speter    }
48717721Speter
48817721Speter    node = getnode ();
48917721Speter    node->type = FILEATTR;
49017721Speter    node->delproc = fileattr_delproc;
49117721Speter    node->key = xstrdup (filename);
49217721Speter    node->data = xstrdup (fileattr_default_attrs);
49317721Speter    addnode (attrlist, node);
49417721Speter    attrs_modified = 1;
49517721Speter}
49617721Speter
49717721Speterstatic int
49817721Speterwriteattr_proc (node, data)
49917721Speter    Node *node;
50017721Speter    void *data;
50117721Speter{
50217721Speter    FILE *fp = (FILE *)data;
50317721Speter    fputs ("F", fp);
50417721Speter    fputs (node->key, fp);
50517721Speter    fputs ("\t", fp);
50617721Speter    fputs (node->data, fp);
50717721Speter    fputs ("\012", fp);
50817721Speter    return 0;
50917721Speter}
51017721Speter
51117721Spetervoid
51217721Speterfileattr_write ()
51317721Speter{
51417721Speter    FILE *fp;
51517721Speter    char *fname;
51617721Speter    mode_t omask;
51766525Speter    struct unrecog *p;
51817721Speter
51917721Speter    if (!attrs_modified)
52017721Speter	return;
52117721Speter
52217721Speter    if (noexec)
52317721Speter	return;
52417721Speter
52517721Speter    /* If NULL was passed to fileattr_startdir, then it isn't kosher to set
52617721Speter       attributes.  */
52717721Speter    assert (fileattr_stored_repos != NULL);
52817721Speter
52917721Speter    fname = xmalloc (strlen (fileattr_stored_repos)
53017721Speter		     + 1
53117721Speter		     + sizeof (CVSREP_FILEATTR)
53217721Speter		     + 1);
53317721Speter
53417721Speter    strcpy (fname, fileattr_stored_repos);
53517721Speter    strcat (fname, "/");
53617721Speter    strcat (fname, CVSREP_FILEATTR);
53717721Speter
53825839Speter    if (list_isempty (attrlist)
53925839Speter	&& fileattr_default_attrs == NULL
54025839Speter	&& unrecog_head == NULL)
54117721Speter    {
54217721Speter	/* There are no attributes.  */
54317721Speter	if (unlink_file (fname) < 0)
54417721Speter	{
54517721Speter	    if (!existence_error (errno))
54617721Speter	    {
54717721Speter		error (0, errno, "cannot remove %s", fname);
54817721Speter	    }
54917721Speter	}
55017721Speter
55117721Speter	/* Now remove CVSREP directory, if empty.  The main reason we bother
55217721Speter	   is that CVS 1.6 and earlier will choke if a CVSREP directory
55317721Speter	   exists, so provide the user a graceful way to remove it.  */
55417721Speter	strcpy (fname, fileattr_stored_repos);
55517721Speter	strcat (fname, "/");
55617721Speter	strcat (fname, CVSREP);
55725839Speter	if (CVS_RMDIR (fname) < 0)
55817721Speter	{
55917721Speter	    if (errno != ENOTEMPTY
56017721Speter
56117721Speter		/* Don't know why we would be here if there is no CVSREP
56217721Speter		   directory, but it seemed to be happening anyway, so
56317721Speter		   check for it.  */
56417721Speter		&& !existence_error (errno))
56517721Speter		error (0, errno, "cannot remove %s", fname);
56617721Speter	}
56717721Speter
56817721Speter	free (fname);
56917721Speter	return;
57017721Speter    }
57117721Speter
57217721Speter    omask = umask (cvsumask);
57325839Speter    fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE);
57417721Speter    if (fp == NULL)
57517721Speter    {
57617721Speter	if (existence_error (errno))
57717721Speter	{
57817721Speter	    /* Maybe the CVSREP directory doesn't exist.  Try creating it.  */
57917721Speter	    char *repname;
58017721Speter
58117721Speter	    repname = xmalloc (strlen (fileattr_stored_repos)
58217721Speter			       + 1
58317721Speter			       + sizeof (CVSREP)
58417721Speter			       + 1);
58517721Speter	    strcpy (repname, fileattr_stored_repos);
58617721Speter	    strcat (repname, "/");
58717721Speter	    strcat (repname, CVSREP);
58817721Speter
58917721Speter	    if (CVS_MKDIR (repname, 0777) < 0 && errno != EEXIST)
59017721Speter	    {
59117721Speter		error (0, errno, "cannot make directory %s", repname);
59217721Speter		(void) umask (omask);
593175261Sobrien		free (fname);
59417721Speter		free (repname);
59517721Speter		return;
59617721Speter	    }
59717721Speter	    free (repname);
59817721Speter
59925839Speter	    fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE);
60017721Speter	}
60117721Speter	if (fp == NULL)
60217721Speter	{
60317721Speter	    error (0, errno, "cannot write %s", fname);
60417721Speter	    (void) umask (omask);
605175261Sobrien	    free (fname);
60617721Speter	    return;
60717721Speter	}
60817721Speter    }
60917721Speter    (void) umask (omask);
61025839Speter
61125839Speter    /* First write the "F" attributes.  */
61217721Speter    walklist (attrlist, writeattr_proc, fp);
61325839Speter
61425839Speter    /* Then the "D" attribute.  */
61517721Speter    if (fileattr_default_attrs != NULL)
61617721Speter    {
61717721Speter	fputs ("D\t", fp);
61817721Speter	fputs (fileattr_default_attrs, fp);
61917721Speter	fputs ("\012", fp);
62017721Speter    }
62125839Speter
62225839Speter    /* Then any other attributes.  */
62366525Speter    for (p = unrecog_head; p != NULL; p = p->next)
62425839Speter    {
62525839Speter	fputs (p->line, fp);
62625839Speter	fputs ("\012", fp);
62725839Speter    }
62825839Speter
62917721Speter    if (fclose (fp) < 0)
63017721Speter	error (0, errno, "cannot close %s", fname);
63117721Speter    attrs_modified = 0;
63217721Speter    free (fname);
63317721Speter}
63417721Speter
63517721Spetervoid
63617721Speterfileattr_free ()
63717721Speter{
63832785Speter    /* Note that attrs_modified will ordinarily be zero, but there are
63932785Speter       a few cases in which fileattr_write will fail to zero it (if
64032785Speter       noexec is set, or error conditions).  This probably is the way
64132785Speter       it should be.  */
64217721Speter    dellist (&attrlist);
64317721Speter    if (fileattr_stored_repos != NULL)
64417721Speter	free (fileattr_stored_repos);
64517721Speter    fileattr_stored_repos = NULL;
64617721Speter    if (fileattr_default_attrs != NULL)
64717721Speter	free (fileattr_default_attrs);
64817721Speter    fileattr_default_attrs = NULL;
64966525Speter    while (unrecog_head)
65066525Speter    {
65166525Speter	struct unrecog *p = unrecog_head;
65266525Speter	unrecog_head = p->next;
65366525Speter	free (p->line);
65466525Speter	free (p);
65566525Speter    }
65617721Speter}
657