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