/contrib/cvs/src/fileattr.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 656 lines · 514 code · 66 blank · 76 comment · 155 complexity · d79b19bed349c16d6fe3d85d6df6b32d MD5 · raw file

  1. /* Implementation for file attribute munging features.
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of the GNU General Public License as published by
  4. the Free Software Foundation; either version 2, or (at your option)
  5. any later version.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details. */
  10. #include "cvs.h"
  11. #include "getline.h"
  12. #include "fileattr.h"
  13. #include <assert.h>
  14. static void fileattr_read PROTO ((void));
  15. static int writeattr_proc PROTO ((Node *, void *));
  16. /* Where to look for CVSREP_FILEATTR. */
  17. static char *fileattr_stored_repos;
  18. /* The in-memory attributes. */
  19. static List *attrlist;
  20. static char *fileattr_default_attrs;
  21. /* We have already tried to read attributes and failed in this directory
  22. (for example, there is no CVSREP_FILEATTR file). */
  23. static int attr_read_attempted;
  24. /* Have the in-memory attributes been modified since we read them? */
  25. static int attrs_modified;
  26. /* More in-memory attributes: linked list of unrecognized
  27. fileattr lines. We pass these on unchanged. */
  28. struct unrecog {
  29. char *line;
  30. struct unrecog *next;
  31. };
  32. static struct unrecog *unrecog_head;
  33. /* Note that if noone calls fileattr_get, this is very cheap. No stat(),
  34. no open(), no nothing. */
  35. void
  36. fileattr_startdir (repos)
  37. const char *repos;
  38. {
  39. assert (fileattr_stored_repos == NULL);
  40. fileattr_stored_repos = xstrdup (repos);
  41. assert (attrlist == NULL);
  42. attr_read_attempted = 0;
  43. assert (unrecog_head == NULL);
  44. }
  45. static void
  46. fileattr_delproc (node)
  47. Node *node;
  48. {
  49. assert (node->data != NULL);
  50. free (node->data);
  51. node->data = NULL;
  52. }
  53. /* Read all the attributes for the current directory into memory. */
  54. static void
  55. fileattr_read ()
  56. {
  57. char *fname;
  58. FILE *fp;
  59. char *line = NULL;
  60. size_t line_len = 0;
  61. /* If there are no attributes, don't waste time repeatedly looking
  62. for the CVSREP_FILEATTR file. */
  63. if (attr_read_attempted)
  64. return;
  65. /* If NULL was passed to fileattr_startdir, then it isn't kosher to look
  66. at attributes. */
  67. assert (fileattr_stored_repos != NULL);
  68. fname = xmalloc (strlen (fileattr_stored_repos)
  69. + 1
  70. + sizeof (CVSREP_FILEATTR)
  71. + 1);
  72. strcpy (fname, fileattr_stored_repos);
  73. strcat (fname, "/");
  74. strcat (fname, CVSREP_FILEATTR);
  75. attr_read_attempted = 1;
  76. fp = CVS_FOPEN (fname, FOPEN_BINARY_READ);
  77. if (fp == NULL)
  78. {
  79. if (!existence_error (errno))
  80. error (0, errno, "cannot read %s", fname);
  81. free (fname);
  82. return;
  83. }
  84. attrlist = getlist ();
  85. while (1) {
  86. int nread;
  87. nread = getline (&line, &line_len, fp);
  88. if (nread < 0)
  89. break;
  90. /* Remove trailing newline. */
  91. line[nread - 1] = '\0';
  92. if (line[0] == 'F')
  93. {
  94. char *p;
  95. Node *newnode;
  96. p = strchr (line, '\t');
  97. if (p == NULL)
  98. error (1, 0,
  99. "file attribute database corruption: tab missing in %s",
  100. fname);
  101. *p++ = '\0';
  102. newnode = getnode ();
  103. newnode->type = FILEATTR;
  104. newnode->delproc = fileattr_delproc;
  105. newnode->key = xstrdup (line + 1);
  106. newnode->data = xstrdup (p);
  107. if (addnode (attrlist, newnode) != 0)
  108. /* If the same filename appears twice in the file, discard
  109. any line other than the first for that filename. This
  110. is the way that CVS has behaved since file attributes
  111. were first introduced. */
  112. freenode (newnode);
  113. }
  114. else if (line[0] == 'D')
  115. {
  116. char *p;
  117. /* Currently nothing to skip here, but for future expansion,
  118. ignore anything located here. */
  119. p = strchr (line, '\t');
  120. if (p == NULL)
  121. error (1, 0,
  122. "file attribute database corruption: tab missing in %s",
  123. fname);
  124. ++p;
  125. if (fileattr_default_attrs) free (fileattr_default_attrs);
  126. fileattr_default_attrs = xstrdup (p);
  127. }
  128. else
  129. {
  130. /* Unrecognized type, we want to just preserve the line without
  131. changing it, for future expansion. */
  132. struct unrecog *new;
  133. new = (struct unrecog *) xmalloc (sizeof (struct unrecog));
  134. new->line = xstrdup (line);
  135. new->next = unrecog_head;
  136. unrecog_head = new;
  137. }
  138. }
  139. if (ferror (fp))
  140. error (0, errno, "cannot read %s", fname);
  141. if (line != NULL)
  142. free (line);
  143. if (fclose (fp) < 0)
  144. error (0, errno, "cannot close %s", fname);
  145. attrs_modified = 0;
  146. free (fname);
  147. }
  148. char *
  149. fileattr_get (filename, attrname)
  150. const char *filename;
  151. const char *attrname;
  152. {
  153. Node *node;
  154. size_t attrname_len = strlen (attrname);
  155. char *p;
  156. if (attrlist == NULL)
  157. fileattr_read ();
  158. if (attrlist == NULL)
  159. /* Either nothing has any attributes, or fileattr_read already printed
  160. an error message. */
  161. return NULL;
  162. if (filename == NULL)
  163. p = fileattr_default_attrs;
  164. else
  165. {
  166. node = findnode (attrlist, filename);
  167. if (node == NULL)
  168. /* A file not mentioned has no attributes. */
  169. return NULL;
  170. p = node->data;
  171. }
  172. while (p)
  173. {
  174. if (strncmp (attrname, p, attrname_len) == 0
  175. && p[attrname_len] == '=')
  176. {
  177. /* Found it. */
  178. return p + attrname_len + 1;
  179. }
  180. p = strchr (p, ';');
  181. if (p == NULL)
  182. break;
  183. ++p;
  184. }
  185. /* The file doesn't have this attribute. */
  186. return NULL;
  187. }
  188. char *
  189. fileattr_get0 (filename, attrname)
  190. const char *filename;
  191. const char *attrname;
  192. {
  193. char *cp;
  194. char *cpend;
  195. char *retval;
  196. cp = fileattr_get (filename, attrname);
  197. if (cp == NULL)
  198. return NULL;
  199. cpend = strchr (cp, ';');
  200. if (cpend == NULL)
  201. cpend = cp + strlen (cp);
  202. retval = xmalloc (cpend - cp + 1);
  203. strncpy (retval, cp, cpend - cp);
  204. retval[cpend - cp] = '\0';
  205. return retval;
  206. }
  207. char *
  208. fileattr_modify (list, attrname, attrval, namevalsep, entsep)
  209. char *list;
  210. const char *attrname;
  211. const char *attrval;
  212. int namevalsep;
  213. int entsep;
  214. {
  215. char *retval;
  216. char *rp;
  217. size_t attrname_len = strlen (attrname);
  218. /* Portion of list before the attribute to be replaced. */
  219. char *pre;
  220. char *preend;
  221. /* Portion of list after the attribute to be replaced. */
  222. char *post;
  223. char *p;
  224. char *p2;
  225. p = list;
  226. pre = list;
  227. preend = NULL;
  228. /* post is NULL unless set otherwise. */
  229. post = NULL;
  230. p2 = NULL;
  231. if (list != NULL)
  232. {
  233. while (1) {
  234. p2 = strchr (p, entsep);
  235. if (p2 == NULL)
  236. {
  237. p2 = p + strlen (p);
  238. if (preend == NULL)
  239. preend = p2;
  240. }
  241. else
  242. ++p2;
  243. if (strncmp (attrname, p, attrname_len) == 0
  244. && p[attrname_len] == namevalsep)
  245. {
  246. /* Found it. */
  247. preend = p;
  248. if (preend > list)
  249. /* Don't include the preceding entsep. */
  250. --preend;
  251. post = p2;
  252. }
  253. if (p2[0] == '\0')
  254. break;
  255. p = p2;
  256. }
  257. }
  258. if (post == NULL)
  259. post = p2;
  260. if (preend == pre && attrval == NULL && post == p2)
  261. return NULL;
  262. retval = xmalloc ((preend - pre)
  263. + 1
  264. + (attrval == NULL ? 0 : (attrname_len + 1
  265. + strlen (attrval)))
  266. + 1
  267. + (p2 - post)
  268. + 1);
  269. if (preend != pre)
  270. {
  271. strncpy (retval, pre, preend - pre);
  272. rp = retval + (preend - pre);
  273. if (attrval != NULL)
  274. *rp++ = entsep;
  275. *rp = '\0';
  276. }
  277. else
  278. retval[0] = '\0';
  279. if (attrval != NULL)
  280. {
  281. strcat (retval, attrname);
  282. rp = retval + strlen (retval);
  283. *rp++ = namevalsep;
  284. strcpy (rp, attrval);
  285. }
  286. if (post != p2)
  287. {
  288. rp = retval + strlen (retval);
  289. if (preend != pre || attrval != NULL)
  290. *rp++ = entsep;
  291. strncpy (rp, post, p2 - post);
  292. rp += p2 - post;
  293. *rp = '\0';
  294. }
  295. return retval;
  296. }
  297. void
  298. fileattr_set (filename, attrname, attrval)
  299. const char *filename;
  300. const char *attrname;
  301. const char *attrval;
  302. {
  303. Node *node;
  304. char *p;
  305. if (filename == NULL)
  306. {
  307. p = fileattr_modify (fileattr_default_attrs, attrname, attrval,
  308. '=', ';');
  309. if (fileattr_default_attrs != NULL)
  310. free (fileattr_default_attrs);
  311. fileattr_default_attrs = p;
  312. attrs_modified = 1;
  313. return;
  314. }
  315. if (attrlist == NULL)
  316. fileattr_read ();
  317. if (attrlist == NULL)
  318. {
  319. /* Not sure this is a graceful way to handle things
  320. in the case where fileattr_read was unable to read the file. */
  321. /* No attributes existed previously. */
  322. attrlist = getlist ();
  323. }
  324. node = findnode (attrlist, filename);
  325. if (node == NULL)
  326. {
  327. if (attrval == NULL)
  328. /* Attempt to remove an attribute which wasn't there. */
  329. return;
  330. /* First attribute for this file. */
  331. node = getnode ();
  332. node->type = FILEATTR;
  333. node->delproc = fileattr_delproc;
  334. node->key = xstrdup (filename);
  335. node->data = xmalloc (strlen (attrname) + 1 + strlen (attrval) + 1);
  336. strcpy (node->data, attrname);
  337. strcat (node->data, "=");
  338. strcat (node->data, attrval);
  339. addnode (attrlist, node);
  340. }
  341. p = fileattr_modify (node->data, attrname, attrval, '=', ';');
  342. if (p == NULL)
  343. delnode (node);
  344. else
  345. {
  346. free (node->data);
  347. node->data = p;
  348. }
  349. attrs_modified = 1;
  350. }
  351. char *
  352. fileattr_getall (filename)
  353. const char *filename;
  354. {
  355. Node *node;
  356. char *p;
  357. if (attrlist == NULL)
  358. fileattr_read ();
  359. if (attrlist == NULL)
  360. /* Either nothing has any attributes, or fileattr_read already printed
  361. an error message. */
  362. return NULL;
  363. if (filename == NULL)
  364. p = fileattr_default_attrs;
  365. else
  366. {
  367. node = findnode (attrlist, filename);
  368. if (node == NULL)
  369. /* A file not mentioned has no attributes. */
  370. return NULL;
  371. p = node->data;
  372. }
  373. return xstrdup (p);
  374. }
  375. void
  376. fileattr_setall (filename, attrs)
  377. const char *filename;
  378. const char *attrs;
  379. {
  380. Node *node;
  381. if (filename == NULL)
  382. {
  383. if (fileattr_default_attrs != NULL)
  384. free (fileattr_default_attrs);
  385. fileattr_default_attrs = xstrdup (attrs);
  386. attrs_modified = 1;
  387. return;
  388. }
  389. if (attrlist == NULL)
  390. fileattr_read ();
  391. if (attrlist == NULL)
  392. {
  393. /* Not sure this is a graceful way to handle things
  394. in the case where fileattr_read was unable to read the file. */
  395. /* No attributes existed previously. */
  396. attrlist = getlist ();
  397. }
  398. node = findnode (attrlist, filename);
  399. if (node == NULL)
  400. {
  401. /* The file had no attributes. Add them if we have any to add. */
  402. if (attrs != NULL)
  403. {
  404. node = getnode ();
  405. node->type = FILEATTR;
  406. node->delproc = fileattr_delproc;
  407. node->key = xstrdup (filename);
  408. node->data = xstrdup (attrs);
  409. addnode (attrlist, node);
  410. }
  411. }
  412. else
  413. {
  414. if (attrs == NULL)
  415. delnode (node);
  416. else
  417. {
  418. free (node->data);
  419. node->data = xstrdup (attrs);
  420. }
  421. }
  422. attrs_modified = 1;
  423. }
  424. void
  425. fileattr_newfile (filename)
  426. const char *filename;
  427. {
  428. Node *node;
  429. if (attrlist == NULL)
  430. fileattr_read ();
  431. if (fileattr_default_attrs == NULL)
  432. return;
  433. if (attrlist == NULL)
  434. {
  435. /* Not sure this is a graceful way to handle things
  436. in the case where fileattr_read was unable to read the file. */
  437. /* No attributes existed previously. */
  438. attrlist = getlist ();
  439. }
  440. node = getnode ();
  441. node->type = FILEATTR;
  442. node->delproc = fileattr_delproc;
  443. node->key = xstrdup (filename);
  444. node->data = xstrdup (fileattr_default_attrs);
  445. addnode (attrlist, node);
  446. attrs_modified = 1;
  447. }
  448. static int
  449. writeattr_proc (node, data)
  450. Node *node;
  451. void *data;
  452. {
  453. FILE *fp = (FILE *)data;
  454. fputs ("F", fp);
  455. fputs (node->key, fp);
  456. fputs ("\t", fp);
  457. fputs (node->data, fp);
  458. fputs ("\012", fp);
  459. return 0;
  460. }
  461. void
  462. fileattr_write ()
  463. {
  464. FILE *fp;
  465. char *fname;
  466. mode_t omask;
  467. struct unrecog *p;
  468. if (!attrs_modified)
  469. return;
  470. if (noexec)
  471. return;
  472. /* If NULL was passed to fileattr_startdir, then it isn't kosher to set
  473. attributes. */
  474. assert (fileattr_stored_repos != NULL);
  475. fname = xmalloc (strlen (fileattr_stored_repos)
  476. + 1
  477. + sizeof (CVSREP_FILEATTR)
  478. + 1);
  479. strcpy (fname, fileattr_stored_repos);
  480. strcat (fname, "/");
  481. strcat (fname, CVSREP_FILEATTR);
  482. if (list_isempty (attrlist)
  483. && fileattr_default_attrs == NULL
  484. && unrecog_head == NULL)
  485. {
  486. /* There are no attributes. */
  487. if (unlink_file (fname) < 0)
  488. {
  489. if (!existence_error (errno))
  490. {
  491. error (0, errno, "cannot remove %s", fname);
  492. }
  493. }
  494. /* Now remove CVSREP directory, if empty. The main reason we bother
  495. is that CVS 1.6 and earlier will choke if a CVSREP directory
  496. exists, so provide the user a graceful way to remove it. */
  497. strcpy (fname, fileattr_stored_repos);
  498. strcat (fname, "/");
  499. strcat (fname, CVSREP);
  500. if (CVS_RMDIR (fname) < 0)
  501. {
  502. if (errno != ENOTEMPTY
  503. /* Don't know why we would be here if there is no CVSREP
  504. directory, but it seemed to be happening anyway, so
  505. check for it. */
  506. && !existence_error (errno))
  507. error (0, errno, "cannot remove %s", fname);
  508. }
  509. free (fname);
  510. return;
  511. }
  512. omask = umask (cvsumask);
  513. fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE);
  514. if (fp == NULL)
  515. {
  516. if (existence_error (errno))
  517. {
  518. /* Maybe the CVSREP directory doesn't exist. Try creating it. */
  519. char *repname;
  520. repname = xmalloc (strlen (fileattr_stored_repos)
  521. + 1
  522. + sizeof (CVSREP)
  523. + 1);
  524. strcpy (repname, fileattr_stored_repos);
  525. strcat (repname, "/");
  526. strcat (repname, CVSREP);
  527. if (CVS_MKDIR (repname, 0777) < 0 && errno != EEXIST)
  528. {
  529. error (0, errno, "cannot make directory %s", repname);
  530. (void) umask (omask);
  531. free (fname);
  532. free (repname);
  533. return;
  534. }
  535. free (repname);
  536. fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE);
  537. }
  538. if (fp == NULL)
  539. {
  540. error (0, errno, "cannot write %s", fname);
  541. (void) umask (omask);
  542. free (fname);
  543. return;
  544. }
  545. }
  546. (void) umask (omask);
  547. /* First write the "F" attributes. */
  548. walklist (attrlist, writeattr_proc, fp);
  549. /* Then the "D" attribute. */
  550. if (fileattr_default_attrs != NULL)
  551. {
  552. fputs ("D\t", fp);
  553. fputs (fileattr_default_attrs, fp);
  554. fputs ("\012", fp);
  555. }
  556. /* Then any other attributes. */
  557. for (p = unrecog_head; p != NULL; p = p->next)
  558. {
  559. fputs (p->line, fp);
  560. fputs ("\012", fp);
  561. }
  562. if (fclose (fp) < 0)
  563. error (0, errno, "cannot close %s", fname);
  564. attrs_modified = 0;
  565. free (fname);
  566. }
  567. void
  568. fileattr_free ()
  569. {
  570. /* Note that attrs_modified will ordinarily be zero, but there are
  571. a few cases in which fileattr_write will fail to zero it (if
  572. noexec is set, or error conditions). This probably is the way
  573. it should be. */
  574. dellist (&attrlist);
  575. if (fileattr_stored_repos != NULL)
  576. free (fileattr_stored_repos);
  577. fileattr_stored_repos = NULL;
  578. if (fileattr_default_attrs != NULL)
  579. free (fileattr_default_attrs);
  580. fileattr_default_attrs = NULL;
  581. while (unrecog_head)
  582. {
  583. struct unrecog *p = unrecog_head;
  584. unrecog_head = p->next;
  585. free (p->line);
  586. free (p);
  587. }
  588. }