/* Implementation for file attribute munging features. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "cvs.h" #include "getline.h" #include "fileattr.h" #include static void fileattr_read PROTO ((void)); static int writeattr_proc PROTO ((Node *, void *)); /* Where to look for CVSREP_FILEATTR. */ static char *fileattr_stored_repos; /* The in-memory attributes. */ static List *attrlist; static char *fileattr_default_attrs; /* We have already tried to read attributes and failed in this directory (for example, there is no CVSREP_FILEATTR file). */ static int attr_read_attempted; /* Have the in-memory attributes been modified since we read them? */ static int attrs_modified; /* Note that if noone calls fileattr_get, this is very cheap. No stat(), no open(), no nothing. */ void fileattr_startdir (repos) char *repos; { assert (fileattr_stored_repos == NULL); fileattr_stored_repos = xstrdup (repos); assert (attrlist == NULL); attr_read_attempted = 0; } static void fileattr_delproc (node) Node *node; { assert (node->data != NULL); free (node->data); node->data = NULL; } /* Read all the attributes for the current directory into memory. */ static void fileattr_read () { char *fname; FILE *fp; char *line = NULL; size_t line_len = 0; /* If there are no attributes, don't waste time repeatedly looking for the CVSREP_FILEATTR file. */ if (attr_read_attempted) return; /* If NULL was passed to fileattr_startdir, then it isn't kosher to look at attributes. */ assert (fileattr_stored_repos != NULL); fname = xmalloc (strlen (fileattr_stored_repos) + 1 + sizeof (CVSREP_FILEATTR) + 1); strcpy (fname, fileattr_stored_repos); strcat (fname, "/"); strcat (fname, CVSREP_FILEATTR); attr_read_attempted = 1; fp = fopen (fname, FOPEN_BINARY_READ); if (fp == NULL) { if (!existence_error (errno)) error (0, errno, "cannot read %s", fname); free (fname); return; } attrlist = getlist (); while (1) { int nread; nread = getline (&line, &line_len, fp); if (nread < 0) break; /* Remove trailing newline. */ line[nread - 1] = '\0'; if (line[0] == 'F') { char *p; Node *newnode; p = strchr (line, '\t'); *p++ = '\0'; newnode = getnode (); newnode->type = FILEATTR; newnode->delproc = fileattr_delproc; newnode->key = xstrdup (line + 1); newnode->data = xstrdup (p); addnode (attrlist, newnode); } else if (line[0] == 'D') { char *p; /* Currently nothing to skip here, but for future expansion, ignore anything located here. */ p = strchr (line, '\t'); ++p; fileattr_default_attrs = xstrdup (p); } /* else just ignore the line, for future expansion. */ } if (ferror (fp)) error (0, errno, "cannot read %s", fname); if (line != NULL) free (line); if (fclose (fp) < 0) error (0, errno, "cannot close %s", fname); attrs_modified = 0; free (fname); } char * fileattr_get (filename, attrname) char *filename; char *attrname; { Node *node; size_t attrname_len = strlen (attrname); char *p; if (attrlist == NULL) fileattr_read (); if (attrlist == NULL) /* Either nothing has any attributes, or fileattr_read already printed an error message. */ return NULL; if (filename == NULL) p = fileattr_default_attrs; else { node = findnode (attrlist, filename); if (node == NULL) /* A file not mentioned has no attributes. */ return NULL; p = node->data; } while (p) { if (strncmp (attrname, p, attrname_len) == 0 && p[attrname_len] == '=') { /* Found it. */ return p + attrname_len + 1; } p = strchr (p, ';'); if (p == NULL) break; ++p; } /* The file doesn't have this attribute. */ return NULL; } char * fileattr_get0 (filename, attrname) char *filename; char *attrname; { char *cp; char *cpend; char *retval; cp = fileattr_get (filename, attrname); if (cp == NULL) return NULL; cpend = strchr (cp, ';'); if (cpend == NULL) cpend = cp + strlen (cp); retval = xmalloc (cpend - cp + 1); strncpy (retval, cp, cpend - cp); retval[cpend - cp] = '\0'; return retval; } char * fileattr_modify (list, attrname, attrval, namevalsep, entsep) char *list; char *attrname; char *attrval; int namevalsep; int entsep; { char *retval; char *rp; size_t attrname_len = strlen (attrname); /* Portion of list before the attribute to be replaced. */ char *pre; char *preend; /* Portion of list after the attribute to be replaced. */ char *post; char *p; char *p2; p = list; pre = list; preend = NULL; /* post is NULL unless set otherwise. */ post = NULL; p2 = NULL; if (list != NULL) { while (1) { p2 = strchr (p, entsep); if (p2 == NULL) { p2 = p + strlen (p); if (preend == NULL) preend = p2; } else ++p2; if (strncmp (attrname, p, attrname_len) == 0 && p[attrname_len] == namevalsep) { /* Found it. */ preend = p; if (preend > list) /* Don't include the preceding entsep. */ --preend; post = p2; } if (p2[0] == '\0') break; p = p2; } } if (post == NULL) post = p2; if (preend == pre && attrval == NULL && post == p2) return NULL; retval = xmalloc ((preend - pre) + 1 + (attrval == NULL ? 0 : (attrname_len + 1 + strlen (attrval))) + 1 + (p2 - post) + 1); if (preend != pre) { strncpy (retval, pre, preend - pre); rp = retval + (preend - pre); if (attrval != NULL) *rp++ = entsep; *rp = '\0'; } else retval[0] = '\0'; if (attrval != NULL) { strcat (retval, attrname); rp = retval + strlen (retval); *rp++ = namevalsep; strcpy (rp, attrval); } if (post != p2) { rp = retval + strlen (retval); if (preend != pre || attrval != NULL) *rp++ = entsep; strncpy (rp, post, p2 - post); rp += p2 - post; *rp = '\0'; } return retval; } void fileattr_set (filename, attrname, attrval) char *filename; char *attrname; char *attrval; { Node *node; char *p; attrs_modified = 1; if (filename == NULL) { p = fileattr_modify (fileattr_default_attrs, attrname, attrval, '=', ';'); if (fileattr_default_attrs != NULL) free (fileattr_default_attrs); fileattr_default_attrs = p; return; } if (attrlist == NULL) fileattr_read (); if (attrlist == NULL) { /* Not sure this is a graceful way to handle things in the case where fileattr_read was unable to read the file. */ /* No attributes existed previously. */ attrlist = getlist (); } node = findnode (attrlist, filename); if (node == NULL) { if (attrval == NULL) /* Attempt to remove an attribute which wasn't there. */ return; /* First attribute for this file. */ node = getnode (); node->type = FILEATTR; node->delproc = fileattr_delproc; node->key = xstrdup (filename); node->data = xmalloc (strlen (attrname) + 1 + strlen (attrval) + 1); strcpy (node->data, attrname); strcat (node->data, "="); strcat (node->data, attrval); addnode (attrlist, node); } p = fileattr_modify (node->data, attrname, attrval, '=', ';'); free (node->data); node->data = NULL; if (p == NULL) delnode (node); else node->data = p; } void fileattr_newfile (filename) char *filename; { Node *node; if (attrlist == NULL) fileattr_read (); if (fileattr_default_attrs == NULL) return; if (attrlist == NULL) { /* Not sure this is a graceful way to handle things in the case where fileattr_read was unable to read the file. */ /* No attributes existed previously. */ attrlist = getlist (); } node = getnode (); node->type = FILEATTR; node->delproc = fileattr_delproc; node->key = xstrdup (filename); node->data = xstrdup (fileattr_default_attrs); addnode (attrlist, node); attrs_modified = 1; } static int writeattr_proc (node, data) Node *node; void *data; { FILE *fp = (FILE *)data; fputs ("F", fp); fputs (node->key, fp); fputs ("\t", fp); fputs (node->data, fp); fputs ("\012", fp); return 0; } void fileattr_write () { FILE *fp; char *fname; mode_t omask; if (!attrs_modified) return; if (noexec) return; /* If NULL was passed to fileattr_startdir, then it isn't kosher to set attributes. */ assert (fileattr_stored_repos != NULL); fname = xmalloc (strlen (fileattr_stored_repos) + 1 + sizeof (CVSREP_FILEATTR) + 1); strcpy (fname, fileattr_stored_repos); strcat (fname, "/"); strcat (fname, CVSREP_FILEATTR); if (list_isempty (attrlist) && fileattr_default_attrs == NULL) { /* There are no attributes. */ if (unlink_file (fname) < 0) { if (!existence_error (errno)) { error (0, errno, "cannot remove %s", fname); } } /* Now remove CVSREP directory, if empty. The main reason we bother is that CVS 1.6 and earlier will choke if a CVSREP directory exists, so provide the user a graceful way to remove it. */ strcpy (fname, fileattr_stored_repos); strcat (fname, "/"); strcat (fname, CVSREP); if (rmdir (fname) < 0) { if (errno != ENOTEMPTY /* Don't know why we would be here if there is no CVSREP directory, but it seemed to be happening anyway, so check for it. */ && !existence_error (errno)) error (0, errno, "cannot remove %s", fname); } free (fname); return; } omask = umask (cvsumask); fp = fopen (fname, FOPEN_BINARY_WRITE); if (fp == NULL) { if (existence_error (errno)) { /* Maybe the CVSREP directory doesn't exist. Try creating it. */ char *repname; repname = xmalloc (strlen (fileattr_stored_repos) + 1 + sizeof (CVSREP) + 1); strcpy (repname, fileattr_stored_repos); strcat (repname, "/"); strcat (repname, CVSREP); if (CVS_MKDIR (repname, 0777) < 0 && errno != EEXIST) { error (0, errno, "cannot make directory %s", repname); (void) umask (omask); free (repname); return; } free (repname); fp = fopen (fname, FOPEN_BINARY_WRITE); } if (fp == NULL) { error (0, errno, "cannot write %s", fname); (void) umask (omask); return; } } (void) umask (omask); walklist (attrlist, writeattr_proc, fp); if (fileattr_default_attrs != NULL) { fputs ("D\t", fp); fputs (fileattr_default_attrs, fp); fputs ("\012", fp); } if (fclose (fp) < 0) error (0, errno, "cannot close %s", fname); attrs_modified = 0; free (fname); } void fileattr_free () { dellist (&attrlist); if (fileattr_stored_repos != NULL) free (fileattr_stored_repos); fileattr_stored_repos = NULL; if (fileattr_default_attrs != NULL) free (fileattr_default_attrs); fileattr_default_attrs = NULL; }