/contrib/cvs/src/classify.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 474 lines · 304 code · 35 blank · 135 comment · 135 complexity · 9d42fd1d0edb83629ce8710a82fca9c5 MD5 · raw file

  1. /*
  2. * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
  3. *
  4. * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
  5. * and others.
  6. *
  7. * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
  8. * Portions Copyright (C) 1989-1992, Brian Berliner
  9. *
  10. * You may distribute under the terms of the GNU General Public License as
  11. * specified in the README file that comes with the CVS source distribution.
  12. *
  13. */
  14. #include <sys/cdefs.h>
  15. __FBSDID("$FreeBSD$");
  16. #include "cvs.h"
  17. static void sticky_ck PROTO ((struct file_info *finfo, int aflag,
  18. Vers_TS * vers));
  19. static inline int keywords_may_change PROTO ((int aflag, Vers_TS * vers));
  20. static inline int
  21. keywords_may_change (aflag, vers)
  22. int aflag;
  23. Vers_TS * vers;
  24. {
  25. int retval;
  26. if (/* Options are different... */
  27. strcmp (vers->entdata->options, vers->options)
  28. /* ...or... */
  29. || (/* ...clearing stickies... */
  30. aflag
  31. /* ...and... */
  32. && (/* ...there used to be a tag which subs in Name keys... */
  33. (vers->entdata->tag && !isdigit (vers->entdata->tag[0])
  34. && vers->tag && !isdigit (vers->tag[0])
  35. && strcmp (vers->entdata->tag, vers->tag))
  36. /* ...or there used to be a keyword mode which may be
  37. * changed by -A...
  38. */
  39. || (strlen (vers->entdata->options)
  40. && strcmp (vers->entdata->options, vers->options)
  41. && strcmp (vers->entdata->options, "-kkv")
  42. && strcmp (vers->entdata->options, "-kb"))))
  43. /* ...or... */
  44. || (/* ...this is not commit... */
  45. strcmp (cvs_cmd_name, "commit")
  46. /* ...and... */
  47. && (/* ...the tag is changing in a way that affects Name keys... */
  48. (vers->entdata->tag && vers->tag
  49. && strcmp (vers->entdata->tag, vers->tag)
  50. && !(isdigit (vers->entdata->tag[0])
  51. && isdigit (vers->entdata->tag[0])))
  52. || (!vers->entdata->tag && vers->tag
  53. && !isdigit (vers->tag[0])))))
  54. retval = 1;
  55. else
  56. retval = 0;
  57. return retval;
  58. }
  59. /*
  60. * Classify the state of a file
  61. */
  62. Ctype
  63. Classify_File (finfo, tag, date, options, force_tag_match, aflag, versp,
  64. pipeout)
  65. struct file_info *finfo;
  66. char *tag;
  67. char *date;
  68. /* Keyword expansion options. Can be either NULL or "" to
  69. indicate none are specified here. */
  70. char *options;
  71. int force_tag_match;
  72. int aflag;
  73. Vers_TS **versp;
  74. int pipeout;
  75. {
  76. Vers_TS *vers;
  77. Ctype ret;
  78. /* get all kinds of good data about the file */
  79. vers = Version_TS (finfo, options, tag, date,
  80. force_tag_match, 0);
  81. if (vers->vn_user == NULL)
  82. {
  83. /* No entry available, ts_rcs is invalid */
  84. if (vers->vn_rcs == NULL)
  85. {
  86. /* there is no RCS file either */
  87. if (vers->ts_user == NULL)
  88. {
  89. /* there is no user file */
  90. /* FIXME: Why do we skip this message if vers->tag or
  91. vers->date is set? It causes "cvs update -r tag98 foo"
  92. to silently do nothing, which is seriously confusing
  93. behavior. "cvs update foo" gives this message, which
  94. is what I would expect. */
  95. if (!force_tag_match || !(vers->tag || vers->date))
  96. if (!really_quiet)
  97. error (0, 0, "nothing known about %s", finfo->fullname);
  98. ret = T_UNKNOWN;
  99. }
  100. else
  101. {
  102. /* there is a user file */
  103. /* FIXME: Why do we skip this message if vers->tag or
  104. vers->date is set? It causes "cvs update -r tag98 foo"
  105. to silently do nothing, which is seriously confusing
  106. behavior. "cvs update foo" gives this message, which
  107. is what I would expect. */
  108. if (!force_tag_match || !(vers->tag || vers->date))
  109. if (!really_quiet)
  110. error (0, 0, "use `%s add' to create an entry for %s",
  111. program_name, finfo->fullname);
  112. ret = T_UNKNOWN;
  113. }
  114. }
  115. else if (RCS_isdead (vers->srcfile, vers->vn_rcs))
  116. {
  117. /* there is an RCS file, but it's dead */
  118. if (vers->ts_user == NULL)
  119. ret = T_UPTODATE;
  120. else
  121. {
  122. error (0, 0, "use `%s add' to create an entry for %s",
  123. program_name, finfo->fullname);
  124. ret = T_UNKNOWN;
  125. }
  126. }
  127. else if (!pipeout && vers->ts_user && No_Difference (finfo, vers))
  128. {
  129. /* the files were different so it is a conflict */
  130. if (!really_quiet)
  131. error (0, 0, "move away %s; it is in the way",
  132. finfo->fullname);
  133. ret = T_CONFLICT;
  134. }
  135. else
  136. /* no user file or no difference, just checkout */
  137. ret = T_CHECKOUT;
  138. }
  139. else if (strcmp (vers->vn_user, "0") == 0)
  140. {
  141. /* An entry for a new-born file; ts_rcs is dummy */
  142. if (vers->ts_user == NULL)
  143. {
  144. if (pipeout)
  145. {
  146. ret = T_CHECKOUT;
  147. }
  148. else
  149. {
  150. /*
  151. * There is no user file, but there should be one; remove the
  152. * entry
  153. */
  154. if (!really_quiet)
  155. error (0, 0, "warning: new-born %s has disappeared",
  156. finfo->fullname);
  157. ret = T_REMOVE_ENTRY;
  158. }
  159. }
  160. else if (vers->vn_rcs == NULL ||
  161. RCS_isdead (vers->srcfile, vers->vn_rcs))
  162. /* No RCS file or RCS file revision is dead */
  163. ret = T_ADDED;
  164. else
  165. {
  166. if (pipeout)
  167. {
  168. ret = T_CHECKOUT;
  169. }
  170. else
  171. {
  172. if (vers->srcfile->flags & INATTIC
  173. && vers->srcfile->flags & VALID)
  174. {
  175. /* This file has been added on some branch other than
  176. the one we are looking at. In the branch we are
  177. looking at, the file was already valid. */
  178. if (!really_quiet)
  179. error (0, 0,
  180. "conflict: %s has been added, but already exists",
  181. finfo->fullname);
  182. }
  183. else
  184. {
  185. /*
  186. * There is an RCS file, so someone else must have checked
  187. * one in behind our back; conflict
  188. */
  189. if (!really_quiet)
  190. error (0, 0,
  191. "conflict: %s created independently by second party",
  192. finfo->fullname);
  193. }
  194. ret = T_CONFLICT;
  195. }
  196. }
  197. }
  198. else if (vers->vn_user[0] == '-')
  199. {
  200. /* An entry for a removed file, ts_rcs is invalid */
  201. if (vers->ts_user == NULL)
  202. {
  203. /* There is no user file (as it should be) */
  204. if (vers->vn_rcs == NULL
  205. || RCS_isdead (vers->srcfile, vers->vn_rcs))
  206. {
  207. /*
  208. * There is no RCS file; this is all-right, but it has been
  209. * removed independently by a second party; remove the entry
  210. */
  211. ret = T_REMOVE_ENTRY;
  212. }
  213. else if (strcmp (vers->vn_rcs, vers->vn_user + 1) == 0)
  214. /*
  215. * The RCS file is the same version as the user file was, and
  216. * that's OK; remove it
  217. */
  218. ret = T_REMOVED;
  219. else if (pipeout)
  220. /*
  221. * The RCS file doesn't match the user's file, but it doesn't
  222. * matter in this case
  223. */
  224. ret = T_NEEDS_MERGE;
  225. else
  226. {
  227. /*
  228. * The RCS file is a newer version than the removed user file
  229. * and this is definitely not OK; make it a conflict.
  230. */
  231. if (!really_quiet)
  232. error (0, 0,
  233. "conflict: removed %s was modified by second party",
  234. finfo->fullname);
  235. ret = T_CONFLICT;
  236. }
  237. }
  238. else
  239. {
  240. /* The user file shouldn't be there */
  241. if (!really_quiet)
  242. error (0, 0, "%s should be removed and is still there",
  243. finfo->fullname);
  244. ret = T_REMOVED;
  245. }
  246. }
  247. else
  248. {
  249. /* A normal entry, TS_Rcs is valid */
  250. if (vers->vn_rcs == NULL || RCS_isdead (vers->srcfile, vers->vn_rcs))
  251. {
  252. /* There is no RCS file */
  253. if (vers->ts_user == NULL)
  254. {
  255. /* There is no user file, so just remove the entry */
  256. if (!really_quiet)
  257. error (0, 0, "warning: %s is not (any longer) pertinent",
  258. finfo->fullname);
  259. ret = T_REMOVE_ENTRY;
  260. }
  261. else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
  262. {
  263. /*
  264. * The user file is still unmodified, so just remove it from
  265. * the entry list
  266. */
  267. if (!really_quiet)
  268. error (0, 0, "%s is no longer in the repository",
  269. finfo->fullname);
  270. ret = T_REMOVE_ENTRY;
  271. }
  272. else if (No_Difference (finfo, vers))
  273. {
  274. /* they are different -> conflict */
  275. if (!really_quiet)
  276. error (0, 0,
  277. "conflict: %s is modified but no longer in the repository",
  278. finfo->fullname);
  279. ret = T_CONFLICT;
  280. }
  281. else
  282. {
  283. /* they weren't really different */
  284. if (!really_quiet)
  285. error (0, 0,
  286. "warning: %s is not (any longer) pertinent",
  287. finfo->fullname);
  288. ret = T_REMOVE_ENTRY;
  289. }
  290. }
  291. else if (strcmp (vers->vn_rcs, vers->vn_user) == 0)
  292. {
  293. /* The RCS file is the same version as the user file */
  294. if (vers->ts_user == NULL)
  295. {
  296. /*
  297. * There is no user file, so note that it was lost and
  298. * extract a new version
  299. */
  300. /* Comparing the cvs_cmd_name against "update", in
  301. addition to being an ugly way to operate, means
  302. that this message does not get printed by the
  303. server. That might be considered just a straight
  304. bug, although there is one subtlety: that case also
  305. gets hit when a patch fails and the client fetches
  306. a file. I'm not sure there is currently any way
  307. for the server to distinguish those two cases. */
  308. if (strcmp (cvs_cmd_name, "update") == 0)
  309. if (!really_quiet)
  310. error (0, 0, "warning: %s was lost", finfo->fullname);
  311. ret = T_CHECKOUT;
  312. }
  313. else if (!strcmp (vers->ts_user,
  314. vers->ts_conflict
  315. ? vers->ts_conflict : vers->ts_rcs))
  316. {
  317. /*
  318. * The user file is still unmodified, so nothing special at
  319. * all to do -- no lists updated, unless the sticky -k option
  320. * has changed. If the sticky tag has changed, we just need
  321. * to re-register the entry
  322. */
  323. /* TODO: decide whether we need to check file permissions
  324. for a mismatch, and return T_CONFLICT if so. */
  325. if (keywords_may_change (aflag, vers))
  326. ret = T_PATCH;
  327. else if (vers->ts_conflict)
  328. ret = T_CONFLICT;
  329. else
  330. {
  331. ret = T_UPTODATE;
  332. sticky_ck (finfo, aflag, vers);
  333. }
  334. }
  335. else if (No_Difference (finfo, vers))
  336. {
  337. /*
  338. * they really are different; modified if we aren't
  339. * changing any sticky -k options, else needs merge
  340. */
  341. #ifdef XXX_FIXME_WHEN_RCSMERGE_IS_FIXED
  342. if (strcmp (vers->entdata->options ?
  343. vers->entdata->options : "", vers->options) == 0)
  344. ret = T_MODIFIED;
  345. else
  346. ret = T_NEEDS_MERGE;
  347. #else
  348. /* Files with conflict markers and new timestamps fall through
  349. * here, but they need to. T_CONFLICT is an error in
  350. * commit_fileproc, whereas T_CONFLICT with conflict markers
  351. * is caught but only warned about. Similarly, update_fileproc
  352. * currently reregisters a file that was conflicted but lost
  353. * its markers.
  354. */
  355. ret = T_MODIFIED;
  356. sticky_ck (finfo, aflag, vers);
  357. #endif
  358. }
  359. else if (strcmp (vers->entdata->options ?
  360. vers->entdata->options : "", vers->options) != 0)
  361. {
  362. /* file has not changed; check out if -k changed */
  363. ret = T_CHECKOUT;
  364. }
  365. else
  366. {
  367. /*
  368. * else -> note that No_Difference will Register the
  369. * file already for us, using the new tag/date. This
  370. * is the desired behaviour
  371. */
  372. ret = T_UPTODATE;
  373. }
  374. }
  375. else
  376. {
  377. /* The RCS file is a newer version than the user file */
  378. if (vers->ts_user == NULL)
  379. {
  380. /* There is no user file, so just get it */
  381. /* See comment at other "update" compare, for more
  382. thoughts on this comparison. */
  383. if (strcmp (cvs_cmd_name, "update") == 0)
  384. if (!really_quiet)
  385. error (0, 0, "warning: %s was lost", finfo->fullname);
  386. ret = T_CHECKOUT;
  387. }
  388. else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
  389. /*
  390. * The user file is still unmodified, so just get it as well
  391. */
  392. ret = T_PATCH;
  393. else if (No_Difference (finfo, vers))
  394. /* really modified, needs to merge */
  395. ret = T_NEEDS_MERGE;
  396. else
  397. ret = T_PATCH;
  398. }
  399. }
  400. /* free up the vers struct, or just return it */
  401. if (versp != (Vers_TS **) NULL)
  402. *versp = vers;
  403. else
  404. freevers_ts (&vers);
  405. /* return the status of the file */
  406. return (ret);
  407. }
  408. static void
  409. sticky_ck (finfo, aflag, vers)
  410. struct file_info *finfo;
  411. int aflag;
  412. Vers_TS *vers;
  413. {
  414. if (aflag || vers->tag || vers->date)
  415. {
  416. char *enttag = vers->entdata->tag;
  417. char *entdate = vers->entdata->date;
  418. if ((enttag && vers->tag && strcmp (enttag, vers->tag)) ||
  419. ((enttag && !vers->tag) || (!enttag && vers->tag)) ||
  420. (entdate && vers->date && strcmp (entdate, vers->date)) ||
  421. ((entdate && !vers->date) || (!entdate && vers->date)))
  422. {
  423. Register (finfo->entries, finfo->file, vers->vn_user, vers->ts_rcs,
  424. vers->options, vers->tag, vers->date, vers->ts_conflict);
  425. #ifdef SERVER_SUPPORT
  426. if (server_active)
  427. {
  428. /* We need to update the entries line on the client side.
  429. It is possible we will later update it again via
  430. server_updated or some such, but that is OK. */
  431. server_update_entries
  432. (finfo->file, finfo->update_dir, finfo->repository,
  433. strcmp (vers->ts_rcs, vers->ts_user) == 0 ?
  434. SERVER_UPDATED : SERVER_MERGED);
  435. }
  436. #endif
  437. }
  438. }
  439. }