org_heading.c 58 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017
  1. /**
  2. * @file org_heading.c
  3. */
  4. /*
  5. * This program is free software: you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation, either vers* ion 3 of
  8. * the License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see
  17. * <http://www.gnu.org/licenses/>.
  18. */
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <stdbool.h>
  22. #include "debug.h"
  23. #include "config.h"
  24. #include "gl_array_list.h"
  25. #include "gl_list.h"
  26. #include "print.h"
  27. #include "doc_elt.h"
  28. #include "doc_elt_ops.h"
  29. #include "doc_elt_util.h"
  30. #include "doc_ref.h"
  31. #include "org_heading.h"
  32. #include "org_text.h"
  33. /* Forward Declarations */
  34. /* org_heading_data */
  35. typedef struct org_heading_data org_heading_data;
  36. static void org_heading_data_free (org_heading_data *self);
  37. static org_heading_data *org_heading_data_create_empty ();
  38. static bool org_heading_content_isupdated (org_heading *heading, size_t data_index);
  39. static bool org_heading_subelts_isupdated (org_heading *heading);
  40. static bool compare_body_text (org_heading_data *a_data,
  41. org_heading_data *b_data, merge_ctxt *ctxt);
  42. static size_t merge_tags (substr anc_str, substr loc_str, substr rem_sub,
  43. size_t curr_col, bool gen_ws, print_ctxt *ctxt,
  44. doc_stream *out);
  45. static void print_LU_RU (doc_ref *ref, print_ctxt *ctxt, doc_stream *out);
  46. static void print_LU_RD (doc_ref *ref, print_ctxt *ctxt, doc_stream *out);
  47. static void print_LD_RU (doc_ref *ref, print_ctxt *ctxt, doc_stream *out);
  48. static void print_LD_RD (doc_ref *ref, print_ctxt *ctxt, doc_stream *out);
  49. static void print_LI_RI (doc_ref *ref, print_ctxt *ctxt, doc_stream *out);
  50. static void print_LI_RX (doc_ref *ref, print_ctxt *ctxt, doc_stream *out);
  51. static void print_LX_RI (doc_ref *ref, print_ctxt *ctxt, doc_stream *out);
  52. static void print_LX_RX (doc_ref *ref, print_ctxt *ctxt, doc_stream *out);
  53. static void print_LR (doc_ref *ref, print_ctxt *ctxt, doc_stream *out);
  54. static void print_L (doc_ref *ref, print_ctxt *ctxt, doc_stream *out);
  55. static void print_R (doc_ref *ref, print_ctxt *ctxt, doc_stream *out);
  56. static void print (org_heading *h, size_t index, print_ctxt *ctxt, doc_stream *out);
  57. static int print_stars (org_heading *h, size_t index, print_ctxt *ctxt, doc_stream *out);
  58. static void print_subelts (doc_ref *ref, print_ctxt *ctxt, doc_stream *out);
  59. /* merging helpers */
  60. static void org_movement_merge_print (doc_ref *ref, print_ctxt *ctxt, doc_stream *out);
  61. /* property */
  62. typedef struct property property;
  63. /* Declaration of org_element operations table */
  64. static doc_elt_ops_print org_heading_print_op;
  65. static doc_elt_ops_isrelated org_heading_isrelated_op;
  66. static doc_elt_ops_compare org_heading_compare_op;
  67. static doc_elt_ops_merge org_heading_merge_op;
  68. static doc_elt_ops_isupdated org_heading_isupdated_op;
  69. static doc_elt_ops_note_delete org_heading_note_delete;
  70. static doc_elt_ops_note_insert org_heading_note_insert;
  71. static doc_elt_ops_get_key org_heading_get_key;
  72. doc_elt_ops org_heading_ops = {
  73. /* printing */
  74. .print = &org_heading_print_op,
  75. /* comparing */
  76. .isrelated = &org_heading_isrelated_op,
  77. .compare = &org_heading_compare_op,
  78. /* merging */
  79. .merge = &org_heading_merge_op,
  80. .isupdated = &org_heading_isupdated_op,
  81. .note_delete = &org_heading_note_delete,
  82. .note_insert = &org_heading_note_insert,
  83. /* Global mapping */
  84. .get_key = &org_heading_get_key
  85. };
  86. /* Core org_heading struct */
  87. typedef struct org_heading_data
  88. {
  89. int level; /*< The heading level, number of stars. */
  90. int rel_level; /*< The relative heading level */
  91. substr entire_text; /*< The complete heading text. No stars. */
  92. substr lead_ws; /*< Whitespace between stars and content. */
  93. substr todo; /*< The todo state substring */
  94. substr body_text; /*< The basic heading text */
  95. substr body_ws; /*< Whitespace between the body and any tags. */
  96. substr tags_str; /*< The tags, as a substring rather than a list. */
  97. substr post_text; /*< Any text after tags. */
  98. substr linebreak; /*< Any linebreaks. */
  99. bool global_search_merge;
  100. bool local_list_merge;
  101. property *uid;
  102. } org_heading_data;
  103. /* merged org_heading struct */
  104. typedef struct org_heading
  105. {
  106. doc_elt elt; /*< The element interface. */
  107. org_heading_data* data[3]; /*< The data for each elt version. */
  108. bool isupdated[3]; /*< Indicates if a corresponding data entry is updated. */
  109. gl_list_t subtext; /*< A list of children text elements. */
  110. gl_list_t subheadings; /*< A list of children heading elements. */
  111. doc_key key;
  112. doc_ref *ref;
  113. } org_heading;
  114. /**
  115. * Property
  116. */
  117. typedef struct property
  118. {
  119. substr string; /*< A string representation of the entire line. */
  120. substr key; /*< The property key, a substring. */
  121. substr value; /*< The property value, a substring. */
  122. } property;
  123. /* Constructor, Destructor */
  124. org_heading *
  125. org_heading_create_empty (doc_elt_ops *elt_ops)
  126. {
  127. org_heading *h = calloc (1, sizeof (org_heading));
  128. doc_elt_set_type ((doc_elt *)h, ORG_HEADING);
  129. doc_elt_set_ops ((doc_elt *)h, elt_ops);
  130. h->subheadings = gl_list_nx_create_empty (GL_ARRAY_LIST, NULL, NULL, NULL, true);
  131. h->subtext = gl_list_nx_create_empty (GL_ARRAY_LIST, NULL, NULL, NULL, true);
  132. return h;
  133. }
  134. void
  135. org_heading_free (org_heading *self)
  136. {
  137. /**
  138. * @todo Implement org_heading_free.
  139. */
  140. free (self);
  141. }
  142. void
  143. org_heading_initversion (org_heading *heading, doc_src src)
  144. {
  145. heading->data[srctoindex(src)] = org_heading_data_create_empty();
  146. return;
  147. }
  148. bool
  149. org_heading_containsversion (org_heading *heading, doc_src src)
  150. {
  151. return (heading->data[srctoindex(src)] != NULL);
  152. }
  153. static org_heading_data *
  154. org_heading_data_create_empty ()
  155. {
  156. org_heading_data *data = calloc (1, sizeof (org_heading_data));
  157. return data;
  158. }
  159. static void
  160. org_heading_data_free (org_heading_data *self)
  161. {
  162. free (self);
  163. }
  164. /* Adding sub elements */
  165. void
  166. org_heading_add_subtext_last (org_heading *heading, doc_src src, doc_elt *text)
  167. {
  168. /* wrap the element in a doc_ref, and add it to the list */
  169. doc_ref *ref = doc_ref_create_empty ();
  170. doc_ref_set_src (ref, src);
  171. doc_ref_set_elt (ref, (doc_elt *) text);
  172. assert (heading->ref != NULL);
  173. doc_ref_set_parent (ref, heading->ref);
  174. /**
  175. * @TODO make text elements honour movement
  176. */
  177. gl_list_nx_add_last (heading->subtext, ref);
  178. return;
  179. }
  180. void
  181. org_heading_add_subheading_last (org_heading *heading, doc_src src, doc_elt *subheading)
  182. {
  183. /* wrap the element in a doc_ref, and add it to the list */
  184. doc_ref *ref = doc_ref_create_empty ();
  185. doc_ref_add_src (ref, src);
  186. doc_ref_set_elt (ref, subheading);
  187. assert (heading->ref != NULL);
  188. doc_ref_set_parent (ref, heading->ref);
  189. org_heading_set_doc_ref ((org_heading *)subheading, ref);
  190. gl_list_nx_add_last (heading->subheadings, ref);
  191. return;
  192. }
  193. doc_ref*
  194. org_heading_get_doc_ref (org_heading *heading)
  195. {
  196. return heading->ref;
  197. }
  198. void org_heading_set_doc_ref (org_heading *heading, doc_ref *ref)
  199. {
  200. heading->ref = ref;
  201. return;
  202. }
  203. /**
  204. * This function is called when an org_heading is parseded, and placed
  205. * into a document tree. This is called after org_heading_
  206. */
  207. void
  208. org_heading_set_parse_ctxt (org_heading *heading, doc_src src, parse_ctxt *ctxt)
  209. {
  210. /* Set the relative level of the heading. Use the final context of
  211. * this heading, and it's parsed absolute heading level, to
  212. * determine
  213. */
  214. size_t index = srctoindex (src);
  215. heading->data[index]->rel_level = (heading->data[index]->level
  216. - ctxt->current_level);
  217. return;
  218. }
  219. /**
  220. * This function is called by the parser. Set entire text is a subparser
  221. */
  222. void
  223. org_heading_set_entire_text (org_heading *heading, char *string, int length,
  224. doc_src src, parse_ctxt *ctxt)
  225. {
  226. /* Set the entire line of a heading.
  227. * The heading will parse the line, setting up substrings and extracting tags.
  228. *
  229. * Heading String Format
  230. * *** TODO [#A] Heading Body Text [%] [/] <> :TAG1:TAG2: etc etc etc\n
  231. * | | | | | | | | |
  232. * 0 1 2 3 4 5 6 7 8
  233. *
  234. * 0. Stars
  235. * 1. pre_ws
  236. * 2. todo
  237. * 3,4. body_text
  238. * 5. body_ws
  239. * 6. tags
  240. * 7. post_text
  241. * 7. linebreak
  242. */
  243. size_t index = srctoindex(src);
  244. heading->data[index]->entire_text.string = string;
  245. heading->data[index]->entire_text.length = length;
  246. size_t i = 0;
  247. size_t count = 0; /* Used to count the length of a token. */
  248. size_t lbound = 0; /* Lower bound, inclusive */
  249. size_t ubound = length; /* Upper bound, exclusive */
  250. /* Parse stars:
  251. * Count the lead stars.
  252. * Store the stars as an integer.
  253. */
  254. for (count = 0; (lbound + count) < ubound; count++)
  255. {
  256. if (string[lbound + count] != '*')
  257. {
  258. break;
  259. }
  260. }
  261. heading->data[index]->level = (count);
  262. heading->data[index]->rel_level = (count - ctxt->current_level);
  263. lbound = lbound + count;
  264. debug_msg (ORG_HEADING, 3, "Parse ABS LVL=%d, REL LVL=%d\n",
  265. heading->data[index]->level,
  266. heading->data[index]->rel_level);
  267. /* Parse lead_whitespace:
  268. * This is the whitespace between the stars and heading.
  269. * Store it as a substring.
  270. */
  271. for (count = 0; count < ubound - lbound; count++)
  272. {
  273. if (!iswhitespace (string[lbound + count]))
  274. {
  275. break;
  276. }
  277. }
  278. heading->data[index]->lead_ws.string = string + lbound;
  279. heading->data[index]->lead_ws.length = count;
  280. lbound = lbound + count;
  281. /* Parse TODO State:
  282. * Read in the first word of the heading.
  283. * If it is a TODO keyword:
  284. * Set the TODO field of data,
  285. * Increment lbound by the length of the todo.
  286. * Otherwise do nothing.
  287. */
  288. for (count = 0; (lbound + count) < ubound; count++)
  289. {
  290. if (iswhitespace (string[lbound + count]))
  291. {
  292. break;
  293. }
  294. }
  295. if (istodo (string + lbound, count, ctxt))
  296. {
  297. heading->data[index]->todo.string = string + lbound;
  298. heading->data[index]->todo.length = count;
  299. lbound = lbound + count;
  300. }
  301. else
  302. {
  303. heading->data[index]->todo.string = NULL;
  304. heading->data[index]->todo.length = 0;
  305. }
  306. /* Scan trailing linebreaks
  307. * Scan right to left.
  308. */
  309. for (count = 0; count < (ubound - lbound); count++)
  310. {
  311. if (!islinebreak (string[ubound - count - 1]))
  312. {
  313. break;
  314. }
  315. }
  316. heading->data[index]->linebreak.string = string + ubound - count;
  317. heading->data[index]->linebreak.length = count;
  318. ubound = ubound - count;
  319. /* Parse Tags:
  320. * Scan right to left.
  321. * Skip trailing newline characters,
  322. * These are placed in the post-text string.
  323. * Scan for the first tag.
  324. * Any text to the right of the tags is placed in the post_text.
  325. * Only the leftmost tags list is considered
  326. * Any text to the left of the tags is placed in the body_text.
  327. * The spacing between tags and body_text is not stored.
  328. * It is calculated if any
  329. */
  330. size_t tags_ubound = ubound; /* The upper bound of all tags. */
  331. size_t tags_lbound = ubound; /* The lower bound of all tags. */
  332. bool foundtags = false;
  333. bool done = false;
  334. for (i = 0; i < (ubound - lbound); i++)
  335. {
  336. if (string [ubound - i - 1] == ':')
  337. {
  338. debug_msg (DOC_ELT, 5, "Found first colon, at %d\n", ubound - i - 1);
  339. if (foundtags = false)
  340. {
  341. tags_ubound = ubound - i;
  342. debug_msg (DOC_ELT, 5, "Setting tags_ubound =%d\n", tags_ubound);
  343. }
  344. int j;
  345. for (j = 2; j < (tags_ubound - lbound); j++)
  346. {
  347. if (iswhitespace (string[tags_ubound - j]))
  348. {
  349. debug_msg (DOC_ELT, 5, "Found whitespace at %d, tagsfound=%d\n",
  350. tags_ubound - j - 1, foundtags);
  351. if (!foundtags)
  352. i += (j+1);
  353. break;
  354. }
  355. else if (string[tags_ubound - j] == ':')
  356. {
  357. debug_msg (DOC_ELT, 5, "Found tag\n");
  358. foundtags = true;
  359. tags_lbound = tags_ubound - j;
  360. debug_msg (DOC_ELT, 5,
  361. "tags_lbound = %d; tags_ubound = %d\n",
  362. tags_lbound, tags_ubound);
  363. }
  364. else
  365. {
  366. debug_msg (DOC_ELT, 5,
  367. "not all tags found\n",
  368. tags_lbound, tags_ubound);
  369. foundtags = false;
  370. }
  371. }
  372. if (foundtags)
  373. {
  374. break;
  375. }
  376. }
  377. }
  378. if (foundtags)
  379. {
  380. heading->data[index]->tags_str.string = string + tags_lbound;
  381. heading->data[index]->tags_str.length = tags_ubound - tags_lbound;
  382. }
  383. else /* reset the ubound and lbound if no tags were found */
  384. {
  385. tags_ubound = ubound;
  386. tags_lbound = ubound;
  387. }
  388. /*
  389. * The post_text is all text between the linebreak and tags.
  390. */
  391. heading->data[index]->post_text.string = string + tags_ubound;
  392. heading->data[index]->post_text.length = ubound - tags_ubound;
  393. ubound = tags_lbound;
  394. /* Store the whitespace between body_text and tags as body_ws */
  395. int l = lbound;
  396. int u = lbound;
  397. for (i = lbound; i < tags_lbound; i++)
  398. {
  399. if (iswhitespace (string[i]))
  400. {
  401. int j;
  402. bool foundwhitespace = true;
  403. for (j = i; j < tags_lbound; j++)
  404. {
  405. if (!iswhitespace (string[j]))
  406. {
  407. i = j;
  408. foundwhitespace = false;
  409. break;
  410. }
  411. }
  412. if (foundwhitespace)
  413. {
  414. break;
  415. }
  416. }
  417. }
  418. /* The trailing body_text whitespace will be through the
  419. * range [i .. tags_lbound).
  420. */
  421. heading->data[index]->body_text.string = string + lbound;
  422. heading->data[index]->body_text.length = i - lbound;
  423. heading->data[index]->body_ws.string = string + i;
  424. heading->data[index]->body_ws.length = tags_lbound - i;
  425. return;
  426. }
  427. int
  428. org_heading_get_level (org_heading *heading, doc_src src)
  429. {
  430. int index = srctoindex (src);
  431. return heading->data[index]->level;
  432. }
  433. /*
  434. * doc_elt interface
  435. */
  436. /**
  437. * Test if two headings have the same title. Compare the body_text of
  438. * both headings, ignoring cookies and whitespace.
  439. *
  440. * Essentially, if two headings have the same words, then they
  441. * related, regardless of spaces, cookies, tags, & todo state.
  442. */
  443. static bool
  444. org_heading_isrelated_op (doc_ref *a_ref, doc_ref *b_ref, merge_ctxt *ctxt)
  445. {
  446. /* isrelated is used by various matching algorithms to see if the
  447. * elements stored at two refs are related. Two elements are
  448. * related if they represent different versions of the same
  449. * elements.
  450. *
  451. * Two headings are the same if they share a key. If neither has a
  452. * key, the body text of the heading is used.
  453. */
  454. doc_elt *elt_a = doc_ref_get_elt (a_ref);
  455. doc_elt *elt_b = doc_ref_get_elt (b_ref);
  456. bool isrelated = false;
  457. /* Before we can compare the elements as headings, we must make sure
  458. * that both are, in fact, actually headings. For this function to
  459. * be called, one element is expected to be a heading, the other may
  460. * or may not be. This isrelated operation needs both elements to
  461. * be headings, and will return false if they are not.
  462. */
  463. if ((doc_elt_get_type (elt_a) == ORG_HEADING) &&
  464. (doc_elt_get_type (elt_b) == ORG_HEADING))
  465. {
  466. /* Each doc_ref may store multiple versions of the same element.
  467. * Use only a single version to check if both headings are
  468. * equal, prioritizing using the ancestor.
  469. */
  470. org_heading *a_heading = (org_heading *) doc_ref_get_elt (a_ref);
  471. doc_src a_src;
  472. if (doc_ref_contains (a_ref, ANC_SRC))
  473. a_src = ANC_SRC;
  474. else if (doc_ref_contains (a_ref, LOC_SRC))
  475. a_src = LOC_SRC;
  476. else if (doc_ref_contains (a_ref, REM_SRC))
  477. a_src = REM_SRC;
  478. size_t a_index = srctoindex (a_src);
  479. org_heading_data *a_data = a_heading->data[a_index];
  480. org_heading *b_heading = (org_heading *) doc_ref_get_elt (b_ref);
  481. doc_src b_src;
  482. if (doc_ref_contains (b_ref, ANC_SRC))
  483. b_src = ANC_SRC;
  484. else if (doc_ref_contains (b_ref, LOC_SRC))
  485. b_src = LOC_SRC;
  486. else if (doc_ref_contains (b_ref, REM_SRC))
  487. b_src = REM_SRC;
  488. size_t b_index = srctoindex (b_src);
  489. org_heading_data *b_data = b_heading->data[b_index];
  490. assert (b_data && a_data);
  491. /* check if this element is already mapped, IF it is, make sure
  492. it is only related to its other */
  493. if ((a_heading->data[b_index] != NULL)
  494. || (b_heading->data[a_index] != NULL))
  495. {
  496. isrelated = (a_heading == b_heading);
  497. }
  498. else
  499. {
  500. /* compare the key if it exists, use the heading body
  501. otherwise */
  502. if (a_heading->key.length > 0)
  503. {
  504. if (b_heading->key.length > 0)
  505. {
  506. isrelated = doc_key_eql (&(a_heading->key), &(b_heading->key));
  507. }
  508. else
  509. {
  510. isrelated = false;
  511. }
  512. }
  513. else
  514. {
  515. if (b_heading->key.length > 0)
  516. {
  517. isrelated = false;
  518. }
  519. else
  520. {
  521. isrelated = compare_body_text (a_data, b_data, ctxt);
  522. } /* end not key, so compare the lines */
  523. }/* end missing 1 key */
  524. } /* end same types of element */
  525. }
  526. return isrelated;
  527. }
  528. /*
  529. * Test if two headings have the same title. Compare the body_text of
  530. * both headings, ignoring cookies and whitespace.
  531. *
  532. * Essentially, if two headings have the same words, then they
  533. * related, regardless of spaces, cookies, tags, & todo state.
  534. */
  535. static bool
  536. compare_body_text (org_heading_data *a_heading_data,
  537. org_heading_data *b_heading_data, merge_ctxt *ctxt)
  538. {
  539. bool isrelated = true;
  540. size_t a_i = 0;
  541. size_t b_i = 0;
  542. bool a_is_cookie = false, b_is_cookie = false;
  543. bool a_whitespace = true, b_whitespace = true; /*skip all leading
  544. whitespace */
  545. size_t a_lookahead, b_lookahead;
  546. /* set an upperbound and lower bound to strip out all leading antd
  547. ending whitespace */
  548. substr a_body = a_heading_data->body_text;
  549. substr b_body = b_heading_data->body_text;
  550. int i;
  551. while ((a_i < a_body.length) ||
  552. (b_i < b_body.length))
  553. {
  554. a_is_cookie = false;
  555. b_is_cookie = false;
  556. /* compress all whitespace into a single space */
  557. /* Eat whitespace. Compress all white space into a single space */
  558. if ((a_i < a_body.length) &&
  559. (iswhitespace (a_body.string[a_i])))
  560. {
  561. while((a_i < a_body.length) &&
  562. (iswhitespace (a_body.string[a_i])))
  563. {
  564. a_i++;
  565. }
  566. a_whitespace = true;
  567. }
  568. if ((b_i < b_body.length) &&
  569. (iswhitespace (b_body.string[b_i])))
  570. {
  571. while((b_i < b_body.length) &&
  572. (iswhitespace (b_body.string[b_i])))
  573. {
  574. b_i++;
  575. }
  576. b_whitespace = true;
  577. }
  578. /* check for cookies.
  579. * priority cookie: "[#ABC]"
  580. * statistics: "[535/5353]"
  581. * or "[902%]"
  582. */
  583. if ( (a_i < a_body.length) &&
  584. (a_body.string[a_i] == '[') )
  585. {
  586. /* look a head to see if it is a cookie */
  587. bool quit = false;
  588. a_lookahead = a_i + 1;
  589. /* The state of the cookie parser:
  590. * 0: try to find what kind of cookie it is
  591. * 1: statistics cookie
  592. * 2: priority cookie
  593. * 3: wrap up cookie, ignoring numbers
  594. * 4: wrap up cookie
  595. */
  596. size_t state = 0;
  597. while ( (a_lookahead < a_body.length) &&
  598. (!quit) )
  599. {
  600. switch (state)
  601. {
  602. case 0: /* no idea what kind of cookie this is */
  603. /* check for a priority */
  604. if (a_body.string[a_lookahead] == '#')
  605. {
  606. state = 2;;
  607. }
  608. else if (isnumber(a_body.string[a_lookahead]))
  609. {
  610. state = 1; /* scan a statistics cookie */
  611. }
  612. else if (a_body.string[a_lookahead] == '/')
  613. {
  614. state = 4; /* ignore numbers and wrap up */
  615. }
  616. else if (a_body.string[a_lookahead] == '%')
  617. {
  618. state = 3; /* wrap up cookie */
  619. }
  620. else /* is not a cookie */
  621. {
  622. quit = true;
  623. }
  624. /* advance to the next character */
  625. a_lookahead ++;
  626. break;
  627. case 1: /* check for a statistics cookie */
  628. if (isnumber(a_body.string[a_lookahead]))
  629. {
  630. state = 1; /* scan a statistics cookie */
  631. }
  632. else if (a_body.string[a_lookahead] == '/')
  633. {
  634. state = 4; /* ignore numbers and wrap up */
  635. }
  636. else if (a_body.string[a_lookahead] == '%')
  637. {
  638. state = 3; /* wrap up cookie */
  639. }
  640. else /* is not a cookie */
  641. {
  642. quit = true; /* cookie finished too soon */
  643. }
  644. /* advance to the next character */
  645. a_lookahead ++;
  646. break;
  647. case 2: /* scanning for a priority cookie */
  648. if ((a_lookahead < a_body.length) &&
  649. (ispriority (a_body.string[a_lookahead], ctxt)))
  650. {
  651. state = 4;
  652. a_lookahead++;
  653. }
  654. else /* not a cookie */
  655. quit = true;
  656. break;
  657. case 3: /* skip numbers and wrap up cookie */
  658. while (isnumber(a_body.string[a_lookahead]))
  659. {
  660. a_lookahead++;
  661. }
  662. case 4: /* scan for propper wrapup */
  663. /* note the fallthrough from above */
  664. if (a_body.string[a_lookahead] = ']')
  665. {
  666. a_is_cookie = true;
  667. }
  668. a_lookahead++;
  669. /* exit no matter what the next text is */
  670. quit = true;
  671. break;
  672. }
  673. } /* end while */
  674. } /* end if */
  675. /* eat cookies in b */
  676. if ( (b_i < b_body.length) &&
  677. (b_body.string[b_i] == '[') )
  678. {
  679. bool quit = false;
  680. size_t state = 0;
  681. b_lookahead = b_i + 1;
  682. while ( (b_lookahead < b_body.length) &&
  683. (!quit) )
  684. {
  685. switch (state)
  686. {
  687. case 0: /* no idea what kind of cookie this is */
  688. /* check for a priority */
  689. if (b_body.string[b_lookahead] == '#')
  690. {
  691. state = 2;;
  692. }
  693. else if (isnumber(b_body.string[b_lookahead]))
  694. {
  695. state = 1; /* scan a statistics cookie */
  696. }
  697. else if (b_body.string[b_lookahead] == '/')
  698. {
  699. state = 4; /* ignore numbers and wrap up */
  700. }
  701. else if (b_body.string[b_lookahead] == '%')
  702. {
  703. state = 3; /* wrap up cookie */
  704. }
  705. else /* is not a cookie */
  706. {
  707. quit = true;
  708. }
  709. /* advance to the next character */
  710. b_lookahead ++;
  711. break;
  712. case 1: /* check for a statistics cookie */
  713. if (isnumber(b_body.string[b_lookahead]))
  714. {
  715. state = 1; /* scan a statistics cookie */
  716. }
  717. else if (b_body.string[b_lookahead] == '/')
  718. {
  719. state = 4; /* ignore numbers and wrap up */
  720. }
  721. else if (b_body.string[b_lookahead] == '%')
  722. {
  723. state = 3; /* wrap up cookie */
  724. }
  725. else /* is not a cookie */
  726. {
  727. quit = true; /* cookie finished too soon */
  728. }
  729. /* advance to the next character */
  730. b_lookahead ++;
  731. break;
  732. case 2: /* scanning for a priority cookie */
  733. if ((b_lookahead < b_body.length) &&
  734. (ispriority (b_body.string[b_lookahead], ctxt)))
  735. {
  736. state = 4;
  737. b_lookahead++;
  738. }
  739. else /* not a cookie */
  740. quit = true;
  741. break;
  742. case 3: /* skip numbers and wrap up cookie */
  743. while (isnumber(b_body.string[b_lookahead]))
  744. {
  745. b_lookahead++;
  746. }
  747. case 4: /* scan for propper wrapup */
  748. /* note the fallthrough from above */
  749. if (b_body.string[b_lookahead] = ']')
  750. {
  751. b_is_cookie = true;
  752. b_lookahead++;
  753. }
  754. /* exit no matter what the next text is */
  755. quit = true;
  756. break;
  757. }
  758. }
  759. } /* finish eating cookies */
  760. /* compare the next character */
  761. {
  762. if (a_is_cookie)
  763. {
  764. /* skip the cookie */
  765. a_i = a_lookahead;
  766. }
  767. if (b_is_cookie)
  768. {
  769. /* skip the cookie */
  770. b_i = b_lookahead;
  771. }
  772. if ((!b_is_cookie) && (!a_is_cookie))
  773. {
  774. if ((a_i < a_body.length) &&
  775. (b_i < b_body.length))
  776. {
  777. /* compare the next character */
  778. if ( !(a_whitespace == b_whitespace
  779. && b_body.string[b_i] == a_body.string[a_i]) )
  780. {
  781. /* break from the loop */
  782. isrelated = false;
  783. b_i = -1;
  784. a_i = -1;
  785. }
  786. else
  787. {
  788. a_i ++;
  789. b_i ++;
  790. a_whitespace = false;
  791. b_whitespace = false;
  792. }
  793. }
  794. else
  795. {
  796. /* break from the loop */
  797. isrelated = false;
  798. b_i = -1;
  799. a_i = -1;
  800. }
  801. }
  802. }
  803. } /* end while: compare the lines */
  804. return isrelated;
  805. }
  806. static int
  807. org_heading_compare_op (doc_elt *a, doc_src a_src, doc_elt *b, doc_src b_src)
  808. {
  809. /**
  810. * @todo Implement org_heading compare operator.
  811. */
  812. return 0;
  813. }
  814. static void
  815. org_heading_merge_op (doc_ref *a_ref, doc_ref *b_ref, merge_ctxt *ctxt)
  816. {
  817. /* Merge both headings, copying in all data from b_ref to a_ref.
  818. * Overwrite an org_heading data if one already exists.
  819. */
  820. debug_msg (ORG_HEADING, 3, "Merging org_heading\n");
  821. org_heading *a_heading = (org_heading *) doc_ref_get_elt (a_ref);
  822. org_heading *b_heading = (org_heading *) doc_ref_get_elt (b_ref);
  823. assert (a_heading != NULL);
  824. assert (b_heading != NULL);
  825. /* check if the elements have already been merged */
  826. /* merge the data of the elements. Ignore the doc_src sources, and
  827. * check if the data exists in the heading to copy over */
  828. if (b_heading->data[ANC_INDEX] != NULL)
  829. a_heading->data[ANC_INDEX] = b_heading->data[ANC_INDEX];
  830. if (b_heading->data[LOC_INDEX] != NULL)
  831. a_heading->data[LOC_INDEX] = b_heading->data[LOC_INDEX];
  832. if (b_heading->data[REM_INDEX] != NULL)
  833. a_heading->data[REM_INDEX] = b_heading->data[REM_INDEX];
  834. /* merge the doc_src of the elements */
  835. /* Deal with match strategy specific behaviour */
  836. if (ctxt->strategy == GLOBAL_SEARCH_MERGE)
  837. {
  838. if (b_heading->data[ANC_INDEX] != NULL)
  839. a_heading->data[ANC_INDEX]->global_search_merge = true;
  840. if (b_heading->data[LOC_INDEX] != NULL)
  841. a_heading->data[LOC_INDEX]->global_search_merge = true;
  842. if (b_heading->data[REM_INDEX] != NULL)
  843. a_heading->data[REM_INDEX]->global_search_merge = true;
  844. }
  845. else if (ctxt->strategy == LOCAL_LIST_MERGE)
  846. {
  847. if (b_heading->data[ANC_INDEX] != NULL)
  848. a_heading->data[ANC_INDEX]->local_list_merge = true;
  849. if (b_heading->data[LOC_INDEX] != NULL)
  850. a_heading->data[LOC_INDEX]->local_list_merge = true;
  851. if (b_heading->data[REM_INDEX] != NULL)
  852. a_heading->data[REM_INDEX]->local_list_merge = true;
  853. }
  854. /* Merge the children elements */
  855. debug_msg (DOC_ELT, 5, "Merging heading subtext\n");
  856. doc_reflist_merge (b_ref, a_heading->subtext, b_heading->subtext, ctxt);
  857. debug_msg (DOC_ELT, 3, "Merging heading subheadings\n");
  858. doc_reflist_merge (b_ref, a_heading->subheadings, b_heading->subheadings, ctxt);
  859. /* the doc_refs are updated externally */
  860. return;
  861. }
  862. /*
  863. * Isupdated_op will return true if any version located at the passed
  864. * ref is updated from the ancestor in a way that might conflict with
  865. * a parent update.
  866. *
  867. * This function is primarily used to test for conflicts with delete
  868. * operations. If the parent was deleted, a child update will
  869. * conflict with the delete at the parent's level. This function
  870. * counts inserted elements as updates, but does not count deletions.
  871. */
  872. static bool
  873. org_heading_isupdated_op (doc_ref *ref)
  874. {
  875. doc_elt *elt = doc_ref_get_elt (ref);
  876. org_heading *heading = (org_heading *)elt;
  877. org_heading_data *anc_data;
  878. org_heading_data *loc_data;
  879. org_heading_data *rem_data;
  880. anc_data = heading->data[ANC_INDEX];
  881. loc_data = heading->data[LOC_INDEX];
  882. rem_data = heading->data[REM_INDEX];
  883. bool isupdated = false;
  884. /* Check each version located at ref for updates. If a version's
  885. data is nonexistant, then do not count that as an update.*/
  886. if ((loc_data != NULL) && doc_ref_contains (ref, LOC_SRC))
  887. {
  888. isupdated = isupdated || org_heading_content_isupdated(heading, LOC_INDEX);
  889. }
  890. if ((rem_data != NULL) && doc_ref_contains (ref, REM_SRC))
  891. {
  892. isupdated = isupdated || org_heading_content_isupdated(heading, REM_INDEX);
  893. }
  894. isupdated = isupdated || org_heading_subelts_isupdated (heading);
  895. return isupdated;
  896. }
  897. /**
  898. * Compare the data in slot data_index with the ancestor data. Try to
  899. * detect if the content has been updated.
  900. */
  901. static bool
  902. org_heading_content_isupdated (org_heading *heading, size_t data_index)
  903. {
  904. org_heading_data *anc_data = heading->data[ANC_INDEX];
  905. org_heading_data *new_data = heading->data[data_index];
  906. /**
  907. * @todo Check for add and delete operations. These operations
  908. * count as updates according to this function.
  909. */
  910. /* return true if:
  911. * - moved
  912. * - children updated
  913. * - level changed
  914. * - body text changed
  915. * - todo text changed
  916. * - tags changed
  917. * Don't return true if:
  918. * - deleted
  919. */
  920. bool isupdated = false;
  921. if (anc_data != NULL)
  922. {
  923. if (new_data != NULL)
  924. {
  925. isupdated = (substreql (anc_data->entire_text, new_data->entire_text) == 0);
  926. }
  927. else
  928. {
  929. }
  930. }
  931. else
  932. {
  933. if (new_data != NULL)
  934. {
  935. isupdated = true;
  936. }
  937. else
  938. {
  939. }
  940. }
  941. /**
  942. * @todo Cache the calculated update. This may require a
  943. * force_isupdated function.
  944. */
  945. return isupdated;
  946. }
  947. static bool
  948. org_heading_subelts_isupdated (org_heading *heading)
  949. {
  950. /**
  951. * @todo Cache the calculated isupdated status.
  952. */
  953. return (doc_reflist_isupdated (heading->subheadings) ||
  954. doc_reflist_isupdated (heading->subtext));
  955. }
  956. /*
  957. * Printing and Merging.
  958. */
  959. void
  960. org_heading_print_op (doc_ref *ref, print_ctxt *ctxt, doc_stream *out)
  961. {
  962. /* Letter | Meaning
  963. * U | Updated: An updated element exists in this version.
  964. * D | Deleted: The element was deleted in this version.
  965. * I | Inserted: The element is new, and has no ancestor.
  966. * X | Does not exist: The element is unassociated with the version.
  967. */
  968. org_heading *heading = (org_heading *)doc_ref_get_elt (ref);
  969. if ( heading->data[ANC_INDEX] != NULL)
  970. {
  971. if (heading->data[LOC_INDEX] != NULL)
  972. {
  973. if (heading->data[REM_INDEX] != NULL)
  974. {
  975. print_LU_RU (ref, ctxt, out);
  976. }
  977. else
  978. {
  979. print_LU_RD (ref, ctxt, out);
  980. }
  981. }
  982. else
  983. {
  984. if (heading->data[REM_INDEX] != NULL)
  985. {
  986. print_LD_RU (ref, ctxt, out);
  987. }
  988. else
  989. {
  990. print_LD_RD (ref, ctxt, out);
  991. }
  992. }
  993. }
  994. else
  995. {
  996. if (heading->data[LOC_INDEX] != NULL)
  997. {
  998. if (heading->data[REM_INDEX] != NULL)
  999. {
  1000. print_LI_RI (ref, ctxt, out);
  1001. }
  1002. else
  1003. {
  1004. print_LI_RX (ref, ctxt, out);
  1005. }
  1006. }
  1007. else
  1008. {
  1009. if (heading->data[REM_INDEX] != NULL)
  1010. {
  1011. print_LX_RI (ref, ctxt, out);
  1012. }
  1013. else
  1014. {
  1015. print_LX_RX (ref, ctxt, out);
  1016. }
  1017. }
  1018. }
  1019. return;
  1020. }
  1021. static inline void
  1022. print_LU_RU (doc_ref *ref, print_ctxt *ctxt, doc_stream *out)
  1023. {
  1024. debug_msg (DOC_ELT, 5, "Begin\n");
  1025. org_heading *h = (org_heading *)doc_ref_get_elt (ref);
  1026. bool loc_moved = (h->data[LOC_INDEX]->global_search_merge
  1027. && !h->data[LOC_INDEX]->local_list_merge);
  1028. bool rem_moved =( h->data[REM_INDEX]->global_search_merge
  1029. && !h->data[REM_INDEX]->local_list_merge);
  1030. bool loc_ishere = doc_ref_contains (ref, LOC_SRC);
  1031. bool rem_ishere = doc_ref_contains (ref, REM_SRC);
  1032. debug_msg (ORG_HEADING, 4,
  1033. "LOC: local_list_merge =%d, global_search_merge =%d\n",
  1034. h->data[LOC_INDEX]->local_list_merge,
  1035. h->data[LOC_INDEX]->global_search_merge);
  1036. debug_msg (ORG_HEADING, 4,
  1037. "REM: local_list_merge =%d, global_search_merge =%d\n",
  1038. h->data[REM_INDEX]->local_list_merge,
  1039. h->data[REM_INDEX]->global_search_merge);
  1040. if (loc_moved)
  1041. {
  1042. if (rem_moved)
  1043. {
  1044. if (loc_ishere)
  1045. {
  1046. if (rem_ishere)
  1047. {
  1048. print_LR (ref, ctxt, out);
  1049. }
  1050. else
  1051. {
  1052. enter_movement_conflict (ctxt, local_side, "Moved\n", out);
  1053. print_LR (ref, ctxt, out);
  1054. print_subelts (ref, ctxt, out);
  1055. enter_movement_conflict (ctxt, remote_side, NULL, out);
  1056. enter_movement_conflict (ctxt, no_conflict, "Moved\n", out);
  1057. }
  1058. }
  1059. else
  1060. {
  1061. if (rem_ishere)
  1062. {
  1063. enter_movement_conflict (ctxt, local_side, "Moved\n", out);
  1064. enter_movement_conflict (ctxt, remote_side, NULL, out);
  1065. print_LR (ref, ctxt, out);
  1066. print_subelts (ref, ctxt, out);
  1067. enter_movement_conflict (ctxt, no_conflict, "Moved\n", out);
  1068. }
  1069. else
  1070. {
  1071. /* Print Nothing */
  1072. }
  1073. }
  1074. }
  1075. else
  1076. {
  1077. if (loc_ishere)
  1078. {
  1079. print_LR (ref, ctxt, out);
  1080. print_subelts (ref, ctxt, out);
  1081. }
  1082. else
  1083. {
  1084. }
  1085. }
  1086. }
  1087. else
  1088. {
  1089. if (rem_moved)
  1090. {
  1091. if (rem_ishere)
  1092. {
  1093. print_LR (ref, ctxt, out);
  1094. print_subelts (ref, ctxt, out);
  1095. }
  1096. else
  1097. {
  1098. }
  1099. }
  1100. else
  1101. {
  1102. if (loc_ishere && rem_ishere)
  1103. {
  1104. print_LR (ref, ctxt, out);
  1105. print_subelts (ref, ctxt, out);
  1106. }
  1107. else
  1108. {
  1109. }
  1110. }
  1111. }
  1112. return;
  1113. }
  1114. static inline void
  1115. print_LU_RD (doc_ref *ref, print_ctxt *ctxt, doc_stream *out)
  1116. {
  1117. debug_msg (DOC_ELT, 5, "Begin\n");
  1118. doc_elt *elt = doc_ref_get_elt (ref);
  1119. org_heading *heading = (org_heading *)elt;
  1120. bool local_ishere = doc_ref_contains (ref, LOC_SRC);
  1121. if (local_ishere)
  1122. {
  1123. if (org_heading_content_isupdated (heading, LOC_INDEX) ||
  1124. org_heading_subelts_isupdated (heading))
  1125. {
  1126. enter_structural_conflict (ctxt, local_side, "Updated\n", out);
  1127. print_L (ref, ctxt, out);
  1128. print_subelts (ref, ctxt, out);
  1129. //enter_structural_conflict (ctxt, remote_side, NULL, out);
  1130. enter_structural_conflict (ctxt, no_conflict, "Deleted\n", out);
  1131. }
  1132. }
  1133. return;
  1134. }
  1135. static inline void
  1136. print_LD_RU (doc_ref *ref, print_ctxt *ctxt, doc_stream *out)
  1137. {
  1138. debug_msg (DOC_ELT, 5, "Begin\n");
  1139. doc_elt *elt = doc_ref_get_elt (ref);
  1140. org_heading *heading = (org_heading *)elt;
  1141. bool remote_ishere = doc_ref_contains (ref, REM_SRC);
  1142. debug_msg (DOC_ELT, 5, "remote_ishere = %d\n", remote_ishere);
  1143. debug_msg (DOC_ELT, 5, "doc_ref = %d\n", doc_ref_get_src (ref));
  1144. if (remote_ishere)
  1145. {
  1146. if (org_heading_content_isupdated (heading, REM_INDEX) ||
  1147. org_heading_subelts_isupdated (heading))
  1148. {
  1149. //enter_structural_conflict (ctxt, local_side, "Deleted\n", out);
  1150. enter_structural_conflict (ctxt, remote_side, NULL, out);
  1151. print_R (ref, ctxt, out);
  1152. print_subelts (ref, ctxt, out);
  1153. enter_structural_conflict (ctxt, no_conflict, "Updated\n", out);
  1154. }
  1155. }
  1156. return;
  1157. }
  1158. static inline void
  1159. print_LD_RD (doc_ref *ref, print_ctxt *ctxt, doc_stream *out)
  1160. {
  1161. debug_msg (DOC_ELT, 5, "Begin\n");
  1162. return;
  1163. }
  1164. static inline void
  1165. print_LI_RI (doc_ref *ref, print_ctxt *ctxt, doc_stream *out)
  1166. {
  1167. debug_msg (DOC_ELT, 5, "Begin\n");
  1168. bool loc_ishere = doc_ref_contains (ref, LOC_SRC);
  1169. bool rem_ishere = doc_ref_contains (ref, REM_SRC);
  1170. if (loc_ishere && rem_ishere)
  1171. {
  1172. print_LR (ref, ctxt, out);
  1173. print_subelts (ref, ctxt, out);
  1174. }
  1175. return;
  1176. }
  1177. static inline void
  1178. print_LI_RX (doc_ref *ref, print_ctxt *ctxt, doc_stream *out)
  1179. {
  1180. debug_msg (DOC_ELT, 5, "Begin\n");
  1181. bool loc_ishere = doc_ref_contains (ref, LOC_SRC);
  1182. if (loc_ishere)
  1183. {
  1184. print_L (ref, ctxt, out);
  1185. print_subelts (ref, ctxt, out);
  1186. }
  1187. return;
  1188. }
  1189. /**
  1190. * The remote version of the heading is inserted. There is no
  1191. * corresponding local version. Print the remote version if it is
  1192. * located at the current doc ref. It should always be located at the
  1193. * doc_ref, because there is no corresponding ancestor.
  1194. */
  1195. static inline void
  1196. print_LX_RI (doc_ref *ref, print_ctxt *ctxt, doc_stream *out)
  1197. {
  1198. debug_msg (DOC_ELT, 5, "Begin\n");
  1199. bool rem_ishere = doc_ref_contains (ref, REM_SRC);
  1200. if (rem_ishere)
  1201. {
  1202. print_R (ref, ctxt, out);
  1203. print_subelts (ref, ctxt, out);
  1204. }
  1205. return;
  1206. }
  1207. /**
  1208. * Print the heading where local never existed, and remote never
  1209. * existed. Should never be called.
  1210. */
  1211. static inline void
  1212. print_LX_RX (doc_ref *ref, print_ctxt *ctxt, doc_stream *out)
  1213. {
  1214. debug_msg (DOC_ELT, 5, "Begin\n");
  1215. return;
  1216. }
  1217. /**
  1218. * Print the local and remote versions of the heading merged. Will
  1219. * cause a content conflict if the updates cannot be consolidated.
  1220. */
  1221. static void
  1222. print_LR (doc_ref *ref, print_ctxt *ctxt, doc_stream *out)
  1223. {
  1224. debug_msg (DOC_ELT, 5, "Begin\n");
  1225. org_heading *h = (org_heading *)doc_ref_get_elt (ref);
  1226. org_heading_data *anc_data = h->data[ANC_INDEX];
  1227. org_heading_data *loc_data = h->data[LOC_INDEX];
  1228. org_heading_data *rem_data = h->data[REM_INDEX];
  1229. /* Scan each field for an update.
  1230. If it has updated, make a note.
  1231. If both versions have updated, note a conflict.
  1232. */
  1233. if (anc_data == NULL)
  1234. {
  1235. if (substreql (loc_data->entire_text, rem_data->entire_text))
  1236. {
  1237. substrprint (loc_data->entire_text, out);
  1238. }
  1239. else
  1240. {
  1241. enter_content_conflict (ctxt, local_side, "Updated\n", out);
  1242. print (h, LOC_INDEX, ctxt, out);
  1243. enter_content_conflict (ctxt, remote_side, NULL, out);
  1244. print (h, REM_INDEX, ctxt, out);
  1245. enter_content_conflict (ctxt, no_conflict, "Updated\n", out);
  1246. }
  1247. }
  1248. else
  1249. {
  1250. bool conflict = false;
  1251. /* Level */
  1252. bool loc_level_update = (loc_data->rel_level != anc_data->rel_level);
  1253. bool rem_level_update = (rem_data->rel_level != anc_data->rel_level);
  1254. if (loc_data->level != rem_data->level)
  1255. {
  1256. conflict = conflict || (loc_level_update && rem_level_update);
  1257. }
  1258. /* lead_ws */
  1259. bool loc_lead_ws_update = !substreql (loc_data->lead_ws, anc_data->lead_ws);
  1260. bool rem_lead_ws_update = !substreql (rem_data->lead_ws, anc_data->lead_ws);
  1261. //if (!substreql (loc_data->lead_ws, rem_data->lead_ws))
  1262. {
  1263. //conflict = conflict || (loc_lead_ws_update && rem_lead_ws_update);
  1264. }
  1265. /* todo */
  1266. bool loc_todo_update = !substreql (loc_data->todo, anc_data->todo);
  1267. bool rem_todo_update = !substreql (rem_data->todo, anc_data->todo);
  1268. if (!substreql (loc_data->todo, rem_data->todo))
  1269. {
  1270. conflict = conflict || ( loc_todo_update && rem_todo_update);
  1271. }
  1272. /* body text
  1273. * strip the whitespace off the ends of the body text, so that
  1274. * it is ignored when trying to see if there is a conflict or
  1275. * not */
  1276. bool loc_body_text_update = !substreql (loc_data->body_text, anc_data->body_text);
  1277. bool rem_body_text_update = !substreql (rem_data->body_text, anc_data->body_text);
  1278. /* set an upperbound and lower bound to strip out all leading antd
  1279. ending whitespace */
  1280. substr loc_body = loc_data->body_text;
  1281. substr rem_body = rem_data->body_text;
  1282. int i;
  1283. for(i = 0; i < loc_body.length; i++)
  1284. {
  1285. if (!iswhitespace (loc_body.string[i]))
  1286. break;
  1287. }
  1288. loc_body.length -= i;
  1289. loc_body.string += i;
  1290. for(i = 0; i < rem_body.length; i++)
  1291. {
  1292. if (!iswhitespace (rem_body.string[i]))
  1293. break;
  1294. }
  1295. rem_body.length -= i;
  1296. rem_body.string += i;
  1297. for(i = 0; i < loc_body.length; i++)
  1298. {
  1299. if (!iswhitespace (loc_body.string[loc_body.length-i]))
  1300. break;
  1301. }
  1302. loc_body.length -= (i -1);
  1303. for(i = 0; i < rem_body.length; i++)
  1304. {
  1305. if (!iswhitespace (rem_body.string[rem_body.length-i]))
  1306. break;
  1307. }
  1308. rem_body.length -= (i -1);
  1309. if (!substreql (loc_body, rem_body))
  1310. {
  1311. conflict = conflict || (loc_body_text_update && rem_body_text_update);
  1312. }
  1313. /* tags_str */
  1314. bool loc_tags_str_update = !substreql (loc_data->tags_str, anc_data->tags_str);
  1315. bool rem_tags_str_update = !substreql (rem_data->tags_str, anc_data->tags_str);
  1316. /* Tags cannot cause a conflict */
  1317. /* Post text */
  1318. bool loc_post_text_update = !substreql (loc_data->post_text, anc_data->post_text);
  1319. bool rem_post_text_update = !substreql (rem_data->post_text, anc_data->post_text);
  1320. if (!substreql (loc_data->post_text, rem_data->post_text))
  1321. {
  1322. conflict = conflict || (loc_post_text_update && rem_post_text_update);
  1323. }
  1324. /* linebreak */
  1325. bool loc_linebreak_update = !substreql (loc_data->linebreak, anc_data->linebreak);
  1326. bool rem_linebreak_update = !substreql (rem_data->linebreak, anc_data->linebreak);
  1327. //conflict = conflict || (loc_post_text_update && rem_post_text_update);
  1328. if (conflict)
  1329. {
  1330. /* Print both version unmerged */
  1331. enter_content_conflict (ctxt, local_side, "Updated\n", out);
  1332. print (h, LOC_INDEX, ctxt, out);
  1333. enter_content_conflict (ctxt, remote_side, NULL, out);
  1334. print (h, REM_INDEX, ctxt, out);
  1335. enter_content_conflict (ctxt, no_conflict, "Updated\n", out);
  1336. ctxt->current_level += (h->data[REM_INDEX]->rel_level > h->data[LOC_INDEX]->rel_level) ?
  1337. h->data[REM_INDEX]->rel_level : h->data[LOC_INDEX]->rel_level;
  1338. }
  1339. else
  1340. {
  1341. int col = 0;
  1342. /* Print Stars */
  1343. int i = 0;
  1344. int level = 0;
  1345. size_t index;
  1346. if (loc_level_update)
  1347. index = LOC_INDEX;
  1348. else
  1349. index = REM_INDEX;
  1350. debug_msg (ORG_HEADING, 5, "cur=%d, rel=%d \n", ctxt->current_level, level);
  1351. col += print_stars (h, index, ctxt, out);
  1352. /* Update the context */
  1353. ctxt->current_level += h->data[index]->rel_level;
  1354. /* Lead Witespace */
  1355. if (loc_lead_ws_update)
  1356. col += substrprint (loc_data->lead_ws, out);
  1357. else if (rem_lead_ws_update)
  1358. col += substrprint (rem_data->lead_ws, out);
  1359. else
  1360. col += substrprint (anc_data->lead_ws, out);
  1361. /* Todo State */
  1362. if (loc_todo_update)
  1363. col += substrprint (loc_data->todo, out);
  1364. else if (rem_todo_update)
  1365. col += substrprint (rem_data->todo, out);
  1366. else
  1367. col += substrprint (anc_data->todo, out);
  1368. /* body_text */
  1369. bool generate_body_ws = true;
  1370. if (loc_body_text_update)
  1371. {
  1372. col += substrprint (loc_data->body_text, out);
  1373. if (!generate_body_ws)
  1374. {
  1375. col += substrprint (loc_data->body_ws, out);
  1376. }
  1377. }
  1378. else if (rem_body_text_update)
  1379. {
  1380. col += substrprint (rem_data->body_text, out);
  1381. if (!generate_body_ws)
  1382. {
  1383. col += substrprint (rem_data->body_ws, out);
  1384. }
  1385. }
  1386. else
  1387. {
  1388. col += substrprint (anc_data->body_text, out);
  1389. if (!generate_body_ws)
  1390. {
  1391. col += substrprint (anc_data->body_ws, out);
  1392. }
  1393. }
  1394. /* print the tags, possibly generate whitespace */
  1395. col += merge_tags(anc_data->tags_str,
  1396. loc_data->tags_str,
  1397. rem_data->tags_str,
  1398. col, generate_body_ws, ctxt, out);
  1399. /* Post text */
  1400. if (loc_post_text_update)
  1401. col += substrprint (loc_data->post_text, out);
  1402. else if (rem_post_text_update)
  1403. col += substrprint (rem_data->post_text, out);
  1404. else
  1405. col += substrprint (anc_data->post_text, out);
  1406. /* Line break */
  1407. if (loc_linebreak_update)
  1408. col += substrprint (loc_data->linebreak, out);
  1409. else if (rem_linebreak_update)
  1410. col += substrprint (rem_data->linebreak, out);
  1411. else
  1412. col += substrprint (anc_data->linebreak, out);
  1413. }
  1414. }
  1415. return;
  1416. }
  1417. /**
  1418. * Print the local version of the heading. Update the context
  1419. * accordingly. Subelts will use the updated context when printing.
  1420. */
  1421. static inline void
  1422. print_L (doc_ref *ref, print_ctxt *ctxt, doc_stream *out)
  1423. {
  1424. debug_msg (DOC_ELT, 5, "Begin\n");
  1425. org_heading *h = (org_heading *)doc_ref_get_elt (ref);
  1426. print (h, LOC_INDEX, ctxt, out);
  1427. ctxt->current_level += h->data[LOC_INDEX]->rel_level;
  1428. return;
  1429. }
  1430. /**
  1431. * Print the remote version of the heading.
  1432. */
  1433. static inline void
  1434. print_R (doc_ref *ref, print_ctxt *ctxt, doc_stream *out)
  1435. {
  1436. debug_msg (DOC_ELT, 5, "Begin\n");
  1437. org_heading *h = (org_heading *)doc_ref_get_elt (ref);
  1438. print (h, REM_INDEX, ctxt, out);
  1439. ctxt->current_level += h->data[REM_INDEX]->rel_level;
  1440. return;
  1441. }
  1442. /**
  1443. * Print a single, unmerged version of the heading H. Print the
  1444. * version of the heading corresponding to the index.
  1445. */
  1446. static void
  1447. print (org_heading *h, size_t index, print_ctxt *ctxt, doc_stream *out)
  1448. {
  1449. print_stars (h, index, ctxt, out);
  1450. substrprint (h->data[index]->lead_ws, out);
  1451. substrprint (h->data[index]->todo, out);
  1452. substrprint (h->data[index]->body_text, out);
  1453. substrprint (h->data[index]->body_ws, out);
  1454. substrprint (h->data[index]->tags_str, out);
  1455. substrprint (h->data[index]->post_text, out);
  1456. substrprint (h->data[index]->linebreak, out);
  1457. return;
  1458. }
  1459. /**
  1460. * Print the lead stars of a heading. Print the stars of the version
  1461. * corresponding to index. Use the print ctxt's current_level and the
  1462. * heading's relative level to print the correct number of stars.
  1463. * Returns the number of characters written to OUT.
  1464. */
  1465. static int
  1466. print_stars (org_heading *h, size_t index, print_ctxt *ctxt, doc_stream *out)
  1467. {
  1468. debug_msg (ORG_HEADING, 5, "level: %d", h->data[index]->rel_level, out);
  1469. int num = h->data[index]->rel_level + ctxt->current_level;
  1470. int i = 0;
  1471. for (i = 0; i < num; i++)
  1472. {
  1473. doc_stream_putc ('*', out);
  1474. }
  1475. return num;
  1476. }
  1477. /**
  1478. * Print all sub-elements of the heading stored at ref, giving them
  1479. * the passed print_ctxt. If the print ctxt should be changed, it
  1480. * must be done before this function is called.
  1481. */
  1482. static void
  1483. print_subelts (doc_ref *ref, print_ctxt *ctxt, doc_stream *out)
  1484. {
  1485. org_heading *h = (org_heading *)doc_ref_get_elt (ref);
  1486. doc_reflist_print (h->subtext, ctxt, out);
  1487. doc_reflist_print (h->subheadings, ctxt, out);
  1488. return;
  1489. }
  1490. static void
  1491. org_heading_note_delete (doc_ref *ref, merge_ctxt *ctxt)
  1492. {
  1493. org_heading * heading = (org_heading *)doc_ref_get_elt(ref);
  1494. debug_msg (ORG_HEADING, 5, "key ='");
  1495. if (ORG_HEADING_PRINTLEVEL >= 5)
  1496. {
  1497. fwrite (heading->key.string, 1, heading->key.length, stderr);
  1498. fprintf (stderr, "'\n");
  1499. }
  1500. if ((heading->key.length > 0) &&
  1501. (doc_ref_contains (ref, ANC_SRC)))
  1502. {
  1503. debug_msg (ORG_HEADING, 5, "registering\n");
  1504. /* org_text does not have global matching, do nothing */
  1505. smerger_register_delete (ctxt->global_smerger, ref, ctxt);
  1506. }
  1507. else
  1508. {
  1509. /* do nothing */
  1510. }
  1511. doc_reflist_note_delete (heading->subheadings, ctxt);
  1512. return;
  1513. }
  1514. static void
  1515. org_heading_note_insert (doc_ref *ref, merge_ctxt *ctxt)
  1516. {
  1517. /*
  1518. * Note a movement for all vesions that doc_ref represents.
  1519. */
  1520. org_heading *heading = (org_heading *)doc_ref_get_elt (ref);
  1521. debug_msg (ORG_HEADING, 5, "key = '");
  1522. if (ORG_HEADING_PRINTLEVEL >= 5)
  1523. {
  1524. fwrite (heading->key.string, 1, heading->key.length, stderr);
  1525. fprintf (stderr, "'\n");
  1526. }
  1527. if (heading->key.length > 0)
  1528. {
  1529. /* org_text does not have global matching, do nothing */
  1530. smerger_register_insert (ctxt->global_smerger, ref, ctxt);
  1531. }
  1532. else
  1533. {
  1534. /* Do nothing */
  1535. }
  1536. doc_reflist_note_insert (heading->subheadings, ctxt);
  1537. return;
  1538. }
  1539. void
  1540. org_heading_set_key (org_heading *h, char *string, size_t length)
  1541. {
  1542. h->key.string = string;
  1543. h->key.length = length;
  1544. return;
  1545. }
  1546. static doc_key *
  1547. org_heading_get_key (doc_elt * elt)
  1548. {
  1549. return &((org_heading *) elt )->key;// &org_heading_key;
  1550. }
  1551. static size_t
  1552. parse_tags (gl_list_t tags, substr s)
  1553. {
  1554. int ubound = 1;
  1555. int lbound = 1;
  1556. substr *tag;
  1557. for (ubound=1; ubound< s.length; ubound++)
  1558. {
  1559. if (s.string[ubound] == ':')
  1560. {
  1561. /* create a new tag element */
  1562. tag = malloc (sizeof (substr));
  1563. tag->length = (ubound -lbound);
  1564. tag->string = (s.string + lbound);
  1565. /* add it to the list */
  1566. gl_list_nx_add_last (tags, tag);
  1567. /* try to find the next element */
  1568. lbound = ubound+1;
  1569. }
  1570. }
  1571. return;
  1572. }
  1573. static size_t
  1574. merge_tags (substr anc_str, substr loc_str, substr rem_str, size_t curr_col,
  1575. bool gen_ws, print_ctxt *ctxt, doc_stream *out)
  1576. {
  1577. /**
  1578. * @TODO try to maintain some semblance of the ordering
  1579. */
  1580. size_t char_count = 0;
  1581. /* assume that the two strings are both correctly setup as the
  1582. length of tags. list diff the two tags, and print them assuming
  1583. they will start at the correct location */
  1584. /* scan the substrings and add them to a list */
  1585. gl_list_t anc_list = gl_list_nx_create_empty (GL_ARRAY_LIST, NULL, NULL,
  1586. NULL, true);
  1587. gl_list_t loc_list = gl_list_nx_create_empty (GL_ARRAY_LIST, NULL, NULL,
  1588. NULL, true);
  1589. gl_list_t rem_list = gl_list_nx_create_empty (GL_ARRAY_LIST, NULL, NULL,
  1590. NULL, true);
  1591. parse_tags (anc_list, anc_str);
  1592. parse_tags (loc_list, loc_str);
  1593. parse_tags (rem_list, rem_str);
  1594. char_count = anc_str.length + loc_str.length + rem_str.length + 1;
  1595. if (gl_list_size (anc_list))
  1596. char_count--;
  1597. if (gl_list_size (loc_list))
  1598. char_count--;
  1599. if (gl_list_size (rem_list))
  1600. char_count--;
  1601. /* combine the tags into one list */
  1602. /* if a tag is not in one of local or remote, mark the substr as 0
  1603. length in the ancestor. If the element is in local or remote,
  1604. and anestor, mark local and remote substr as length 0
  1605. this strategy will not maintain the order when merging
  1606. */
  1607. gl_list_iterator_t anc_i, loc_i, rem_i;
  1608. anc_i = gl_list_iterator (anc_list);
  1609. substr *anc_tag, *loc_tag, *rem_tag;
  1610. bool loc_found = false;
  1611. bool rem_found = false;
  1612. /* filter out the nodes that match the ancestor */
  1613. while (gl_list_iterator_next (&anc_i, (const void **) &anc_tag, NULL))
  1614. {
  1615. loc_found = false;
  1616. rem_found = false;
  1617. loc_i = gl_list_iterator (loc_list);
  1618. rem_i = gl_list_iterator (rem_list);
  1619. while (gl_list_iterator_next (&loc_i, (const void **) &loc_tag, NULL))
  1620. {
  1621. if (substreql (*anc_tag, *loc_tag))
  1622. {
  1623. debug_msg (DOC, 5, "loc anc match\n");
  1624. /* found a match, remove from loc */
  1625. char_count -= (loc_tag->length + 1);
  1626. loc_tag->length = 0;
  1627. loc_found = true;
  1628. break;
  1629. }
  1630. }
  1631. while (gl_list_iterator_next (&rem_i, (const void **) &rem_tag, NULL))
  1632. {
  1633. if (substreql (*anc_tag, *rem_tag))
  1634. {
  1635. debug_msg (DOC, 5, "rem anc match\n");
  1636. /* found a match, remove from rem */
  1637. char_count -= (rem_tag->length + 1);
  1638. rem_tag->length = 0;
  1639. rem_found = true;
  1640. break;
  1641. }
  1642. }
  1643. if (!rem_found || !loc_found)
  1644. {
  1645. /* no match found, remove anc */
  1646. debug_msg (DOC, 5, "no anc match\n");
  1647. char_count -= (anc_tag->length + 1);
  1648. anc_tag->length = 0;
  1649. }
  1650. }
  1651. rem_i = gl_list_iterator (rem_list);
  1652. while (gl_list_iterator_next (&rem_i, (const void **) &rem_tag, NULL))
  1653. {
  1654. loc_found = false;
  1655. loc_i = gl_list_iterator (loc_list);
  1656. while (gl_list_iterator_next (&loc_i, (const void **) &loc_tag, NULL))
  1657. {
  1658. if (loc_tag->length > 0 && substreql (*rem_tag, *loc_tag))
  1659. {
  1660. /* found a match, remove from loc and print it */
  1661. debug_msg (DOC, 5, "rem loc match\n");
  1662. char_count -= (loc_tag->length + 1);
  1663. loc_tag->length = 0;
  1664. loc_found = true;
  1665. break;
  1666. }
  1667. }
  1668. }
  1669. /* print every element with a length of more than 0 */
  1670. bool close_mark = (char_count > 3);
  1671. if (close_mark && gen_ws)
  1672. {
  1673. debug_msg (DOC_ELT, 5, "Generating %d long body_ws\n",
  1674. (ctxt->rmargin - 1 - char_count - curr_col));
  1675. int i;
  1676. doc_stream_putc(' ', out);
  1677. //curr_col += 1;
  1678. for (i=0; i < (ctxt->rmargin - 1 - char_count - curr_col); i++)
  1679. {
  1680. doc_stream_putc(' ', out);
  1681. }
  1682. }
  1683. anc_i = gl_list_iterator (anc_list);
  1684. while (gl_list_iterator_next (&anc_i, (const void **) &anc_tag, NULL))
  1685. {
  1686. if (anc_tag->length > 0)
  1687. {
  1688. close_mark = true;
  1689. doc_stream_putc (':', out);
  1690. substrprint (*anc_tag, out);
  1691. }
  1692. }
  1693. rem_i = gl_list_iterator (rem_list);
  1694. while (gl_list_iterator_next (&rem_i, (const void **) &rem_tag, NULL))
  1695. {
  1696. if (rem_tag->length > 0)
  1697. {
  1698. /* found a match, remove from loc and print it */
  1699. doc_stream_putc (':', out);
  1700. substrprint (*rem_tag, out);
  1701. close_mark = true;
  1702. }
  1703. }
  1704. loc_i = gl_list_iterator (loc_list);
  1705. while (gl_list_iterator_next (&loc_i, (const void **) &loc_tag, NULL))
  1706. {
  1707. if (loc_tag->length > 0)
  1708. {
  1709. /* found a match, remove from loc and print it */
  1710. doc_stream_putc (':', out);
  1711. substrprint (*loc_tag, out);
  1712. close_mark = true;
  1713. }
  1714. }
  1715. if (close_mark)
  1716. doc_stream_putc (':', out);
  1717. return char_count;
  1718. }
  1719. /* For every element, check to see if some element is below it. Ignor
  1720. * NULLs. Return a value indicating if the element was found below */
  1721. bool
  1722. org_heading_check_for_target (org_heading *this, org_heading* target)
  1723. {
  1724. /* first check to see if the target exist anywhere below */
  1725. gl_list_iterator_t i;
  1726. i = gl_list_iterator (this->subheadings);
  1727. doc_ref *ref = NULL;
  1728. bool found = false;
  1729. org_heading *heading;
  1730. debug_msg (ORG_HEADING, 3, "checking subheadings for target...\n");
  1731. while (!found
  1732. && gl_list_iterator_next (&i, (const void **) &ref, NULL))
  1733. {
  1734. heading = (org_heading *)doc_ref_get_elt (ref);
  1735. if (heading == target)
  1736. {
  1737. debug_msg (ORG_HEADING, 1, "found loop!!\n");
  1738. found = true;
  1739. }
  1740. else
  1741. {
  1742. found = org_heading_check_for_target(heading, target);
  1743. }
  1744. }
  1745. debug_msg (ORG_HEADING, 3, "found = %d\n", found);
  1746. gl_list_iterator_free (&i);
  1747. return found;
  1748. }
  1749. /* will call thes function on its children after it searches for
  1750. itself */
  1751. bool
  1752. org_heading_check_for_loop (org_heading *this)
  1753. {
  1754. /* first check to see if the target exist anywhere below */
  1755. gl_list_iterator_t i;
  1756. i = gl_list_iterator (this->subheadings);
  1757. doc_ref *ref = NULL;
  1758. bool found = false;
  1759. org_heading *heading;
  1760. debug_msg (ORG_HEADING, 3, "checking for loops\n");
  1761. if (org_heading_check_for_target (this, this))
  1762. {
  1763. debug_msg (ORG_HEADING, 1, "found loop!!\n");
  1764. found = true;
  1765. }
  1766. debug_msg (ORG_HEADING, 3, "checking subheadings for loops\n");
  1767. while (gl_list_iterator_next (&i, (const void **) &ref, NULL))
  1768. {
  1769. heading = (org_heading *)doc_ref_get_elt (ref);
  1770. org_heading_check_for_loop (heading);
  1771. }
  1772. gl_list_iterator_free (&i);
  1773. return found;
  1774. }