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