PageRenderTime 55ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/sombok-2.2.1/lib/break.c

#
C | 1351 lines | 947 code | 151 blank | 253 comment | 296 complexity | 0c6ee660e53668a89a50fa0ede5bbd70 MD5 | raw file
Possible License(s): AGPL-1.0
  1. /*
  2. * break.c - an implementation of Unicode line breaking algorithm.
  3. *
  4. * Copyright (C) 2009-2012 by Hatuka*nezumi - IKEDA Soji.
  5. *
  6. * This file is part of the Sombok Package. This program is free
  7. * software; you can redistribute it and/or modify it under the terms of
  8. * either the GNU General Public License or the Artistic License, as
  9. * specified in the README file.
  10. *
  11. */
  12. #include "sombok_constants.h"
  13. #include "sombok.h"
  14. extern propval_t *linebreak_rules[];
  15. extern size_t linebreak_rulessiz;
  16. /**
  17. * @defgroup linebreak_break break
  18. * @brief Perform line breaking algorithm
  19. *@{*/
  20. static
  21. gcstring_t *_user(linebreak_t * lbobj, unistr_t * str)
  22. {
  23. gcstring_t *result;
  24. if (str == NULL)
  25. return NULL;
  26. else if (lbobj->user_func == NULL ||
  27. ((result = (*(lbobj->user_func)) (lbobj, str)) == NULL &&
  28. !lbobj->errnum)) {
  29. if ((result = gcstring_newcopy(str, lbobj)) == NULL)
  30. lbobj->errnum = errno ? errno : ENOMEM;
  31. }
  32. return result;
  33. }
  34. static
  35. gcstring_t *_prep_sub(linebreak_t * lbobj, unistr_t * substr,
  36. unistr_t * text, size_t findex)
  37. {
  38. unistr_t unistr = { NULL, 0 };
  39. gcstring_t *ret, *s;
  40. unichar_t *prev_str;
  41. size_t prev_len;
  42. gcstring_t *(*func) (linebreak_t *, void *, unistr_t *, unistr_t *);
  43. void *data;
  44. if ((func = lbobj->prep_func[findex]) == NULL) {
  45. if ((ret = gcstring_newcopy(substr, lbobj)) == NULL)
  46. lbobj->errnum = errno ? errno : ENOMEM;
  47. return ret;
  48. }
  49. if (lbobj->prep_data == NULL)
  50. data = NULL;
  51. else
  52. data = lbobj->prep_data[findex];
  53. if ((ret = gcstring_new(NULL, lbobj)) == NULL) {
  54. lbobj->errnum = errno ? errno : ENOMEM;
  55. return NULL;
  56. }
  57. prev_str = substr->str;
  58. prev_len = substr->len;
  59. while (1) {
  60. /* Pass I: search. */
  61. unistr.str = prev_str;
  62. unistr.len = prev_len;
  63. gcstring_destroy((*func) (lbobj, data, &unistr, text));
  64. /* - no match: stop searching. */
  65. if (unistr.str == NULL)
  66. break;
  67. /* - buffer may be modified: abort. */
  68. if (unistr.len < 0 ||
  69. unistr.str < text->str ||
  70. text->str + text->len < unistr.str + unistr.len) {
  71. gcstring_destroy(ret);
  72. lbobj->errnum = EINVAL;
  73. return NULL;
  74. }
  75. /* - out of range: stop searching. */
  76. if (unistr.str < substr->str ||
  77. substr->str + substr->len < unistr.str + unistr.len)
  78. break;
  79. /* apply next callback to unmatched part. */
  80. if (prev_str <= unistr.str) {
  81. unistr_t us;
  82. us.len = unistr.str - prev_str;
  83. us.str = prev_str;
  84. if ((s = _prep_sub(lbobj, &us, text, findex + 1)) == NULL) {
  85. gcstring_destroy(ret);
  86. return NULL;
  87. }
  88. if (gcstring_append(ret, s) == NULL) {
  89. gcstring_destroy(ret);
  90. gcstring_destroy(s);
  91. lbobj->errnum = errno ? errno : ENOMEM;
  92. return NULL;
  93. }
  94. gcstring_destroy(s);
  95. }
  96. /* Pass II: process matched string. */
  97. if ((s = (*func) (lbobj, data, &unistr, NULL)) == NULL) {
  98. if (lbobj->errnum != 0) {
  99. gcstring_destroy(ret);
  100. return NULL;
  101. }
  102. if ((s = gcstring_newcopy(&unistr, lbobj)) == NULL) {
  103. gcstring_destroy(ret);
  104. lbobj->errnum = errno ? errno : ENOMEM;
  105. return NULL;
  106. }
  107. }
  108. if (gcstring_append(ret, s) == NULL) {
  109. gcstring_destroy(ret);
  110. gcstring_destroy(s);
  111. lbobj->errnum = errno ? errno : ENOMEM;
  112. return NULL;
  113. }
  114. gcstring_destroy(s);
  115. /* skip zero length match to avoid infinite loop. */
  116. if (unistr.len == 0) {
  117. if (substr->str + substr->len <= unistr.str) {
  118. prev_str = unistr.str;
  119. prev_len = 0;
  120. break;
  121. } else {
  122. prev_str = unistr.str + 1;
  123. prev_len = substr->str + substr->len - prev_str;
  124. continue;
  125. }
  126. }
  127. prev_str = unistr.str + unistr.len;
  128. prev_len = substr->str + substr->len - prev_str;
  129. }
  130. /* apply next callback to the rest of string. */
  131. if (prev_str < substr->str + substr->len) {
  132. unistr.str = prev_str;
  133. unistr.len = prev_len;
  134. if ((s = _prep_sub(lbobj, &unistr, text, findex + 1)) == NULL) {
  135. gcstring_destroy(ret);
  136. return NULL;
  137. }
  138. if (gcstring_append(ret, s) == NULL) {
  139. gcstring_destroy(ret);
  140. gcstring_destroy(s);
  141. lbobj->errnum = errno ? errno : ENOMEM;
  142. return NULL;
  143. }
  144. gcstring_destroy(s);
  145. }
  146. return ret;
  147. }
  148. static
  149. gcstring_t *_prep(linebreak_t * lbobj, unistr_t * text)
  150. {
  151. gcstring_t *ret;
  152. if (lbobj->prep_func == NULL) {
  153. if ((ret = gcstring_newcopy(text, lbobj)) == NULL)
  154. lbobj->errnum = errno ? errno : ENOMEM;
  155. return ret;
  156. }
  157. return _prep_sub(lbobj, text, text, 0);
  158. }
  159. static
  160. gcstring_t *_format(linebreak_t * lbobj, linebreak_state_t action,
  161. gcstring_t * str)
  162. {
  163. gcstring_t *result;
  164. if (str == NULL)
  165. return NULL;
  166. else if (lbobj->format_func == NULL ||
  167. ((result =
  168. (*(lbobj->format_func)) (lbobj, action, str)) == NULL &&
  169. !lbobj->errnum)) {
  170. if ((result = gcstring_copy(str)) == NULL)
  171. lbobj->errnum = errno ? errno : ENOMEM;
  172. }
  173. return result;
  174. }
  175. static
  176. double _sizing(linebreak_t * lbobj, double len,
  177. gcstring_t * pre, gcstring_t * spc, gcstring_t * str)
  178. {
  179. double ret;
  180. if (lbobj->sizing_func == NULL ||
  181. ((ret = (*(lbobj->sizing_func)) (lbobj, len, pre, spc, str))
  182. < 0.0 && !lbobj->errnum)) {
  183. if (spc != NULL)
  184. len += (double) spc->gclen;
  185. if (str != NULL)
  186. len += (double) str->gclen;
  187. return len;
  188. }
  189. return ret;
  190. }
  191. static
  192. gcstring_t *_urgent_break(linebreak_t * lbobj, gcstring_t * str)
  193. {
  194. gcstring_t *result;
  195. if (lbobj->urgent_func == NULL ||
  196. ((result = (*(lbobj->urgent_func)) (lbobj, str)) == NULL &&
  197. !lbobj->errnum)) {
  198. if ((result = gcstring_copy(str)) == NULL)
  199. lbobj->errnum = errno ? errno : ENOMEM;
  200. }
  201. return result;
  202. }
  203. #define gcstring_DESTROY(gcstr) \
  204. gcstring_destroy(gcstr); gcstr = NULL;
  205. #define IF_NULL_THEN_ABORT(x) \
  206. if ((x) == NULL) { \
  207. size_t i; \
  208. if (lbobj->errnum == 0) \
  209. lbobj->errnum = errno? errno: EINVAL; \
  210. gcstring_destroy(str); \
  211. gcstring_destroy(bufStr); \
  212. gcstring_destroy(bufSpc); \
  213. for (i = 0; i < reslen; i++) \
  214. gcstring_destroy(results[i]); \
  215. free(results); \
  216. gcstring_destroy(s); \
  217. gcstring_destroy(t); \
  218. gcstring_destroy(beforeFrg); \
  219. gcstring_destroy(fmt); \
  220. gcstring_destroy(broken); \
  221. return NULL; \
  222. }
  223. /** @fn propval_t linebreak_lbrule(propval_t b_idx, propval_t a_idx)
  224. * @deprecated Use linebreak_get_lbrule().
  225. *
  226. * Get breaking rule between two classes
  227. *
  228. * From given two line breaking classes, get breaking rule determined by
  229. * internal data.
  230. * @param[in] a_idx line breaking class.
  231. * @param[in] b_idx line breaking class.
  232. * @return line breaking action: MANDATORY, DIRECT, INDIRECT or PROHIBITED.
  233. * If action was not determined, returns DIRECT.
  234. *
  235. * @note This method gives just approximate description of line breaking
  236. * behavior. Especially, it won't give meaningful value related to classes
  237. * AI and CJ.
  238. * See also linebreak_get_lbrule().
  239. *
  240. */
  241. static
  242. propval_t _lbruleinfo(propval_t b_idx, propval_t a_idx)
  243. {
  244. propval_t result = PROP_UNKNOWN;
  245. if (b_idx < 0 || linebreak_rulessiz <= b_idx ||
  246. a_idx < 0 || linebreak_rulessiz <= a_idx);
  247. else
  248. result = linebreak_rules[b_idx][a_idx];
  249. if (result == PROP_UNKNOWN)
  250. return LINEBREAK_ACTION_DIRECT;
  251. return result;
  252. }
  253. propval_t linebreak_lbrule(propval_t b_idx, propval_t a_idx)
  254. {
  255. /* Resolve before-side class. */
  256. switch (b_idx) {
  257. /* LB1: Resolve SA, SG, XX to AL; AI and CJ cannot be resolved. */
  258. case LB_SA:
  259. case LB_SG:
  260. case LB_XX:
  261. /* LB10: Resolve CM to AL. */
  262. case LB_CM:
  263. /* Resolve HL to AL. */
  264. case LB_HL:
  265. b_idx = LB_AL;
  266. break;
  267. }
  268. /* Resolve after-side class. */
  269. switch (a_idx) {
  270. /* LB1 */
  271. case LB_SA:
  272. case LB_SG:
  273. case LB_XX:
  274. a_idx = LB_AL;
  275. break;
  276. /* LB9, LB10 */
  277. case LB_CM:
  278. /* LB9: Treat X CM as if it were X, with some exceptions. */
  279. switch (b_idx) {
  280. case LB_BK:
  281. case LB_CR:
  282. case LB_LF:
  283. case LB_NL:
  284. case LB_SP:
  285. case LB_ZW:
  286. break;
  287. default:
  288. return LINEBREAK_ACTION_PROHIBITED;
  289. }
  290. /* XXX Legacy-CM rule cannot be applied. */
  291. /* LB10: Treat any remaining combining mark as AL. */
  292. a_idx = LB_AL;
  293. if (b_idx == LB_CM)
  294. b_idx = LB_AL;
  295. break;
  296. /* Resolve HL to AL. */
  297. case LB_HL:
  298. a_idx = LB_AL;
  299. break;
  300. }
  301. /* LB25, simplified:
  302. * (CL|CP|NU) × (PO|PR)
  303. * (PO|PR) × (OP|NU)
  304. * (HY|IS|NU|SY) × NU
  305. */
  306. if (((b_idx == LB_CL || b_idx == LB_CP || b_idx == LB_NU) &&
  307. (a_idx == LB_PO || a_idx == LB_PR)) ||
  308. ((b_idx == LB_PO || b_idx == LB_PR) &&
  309. (a_idx == LB_OP || a_idx == LB_NU)) ||
  310. ((b_idx == LB_HY || b_idx == LB_IS || b_idx == LB_NU ||
  311. b_idx == LB_SY) && a_idx == LB_NU))
  312. return LINEBREAK_ACTION_PROHIBITED;
  313. return _lbruleinfo(b_idx, a_idx);
  314. }
  315. /** @fn gcstring_t** linebreak_break_partial(linebreak_t *lbobj, unistr_t *input)
  316. *
  317. * Perform line breaking algorithm with incremental inputs.
  318. *
  319. * @param[in] lbobj linebreak object.
  320. * @param[in] input Unicode string; give NULL to specify end of input.
  321. * @return array of (partial) broken grapheme cluster strings terminated by NULL.
  322. * If internal error occurred, lbobj->errnum is set then NULL is returned.
  323. */
  324. static
  325. gcstring_t **_break_partial(linebreak_t * lbobj, unistr_t * input,
  326. size_t * lenp, int eot)
  327. {
  328. int state;
  329. gcstring_t *str = NULL, *bufStr = NULL, *bufSpc = NULL;
  330. double bufCols;
  331. size_t bBeg, bLen, bCM, bSpc, aCM, urgEnd;
  332. gcstring_t **results = NULL;
  333. size_t reslen = 0;
  334. gcstring_t *s = NULL, *t = NULL, *beforeFrg = NULL, *fmt = NULL,
  335. *broken = NULL;
  336. unistr_t unistr;
  337. size_t i;
  338. gcstring_t empty = { NULL, 0, NULL, 0, 0, lbobj };
  339. /***
  340. *** Unread and additional input.
  341. ***/
  342. unistr.str = lbobj->unread.str;
  343. unistr.len = lbobj->unread.len;
  344. lbobj->unread.str = NULL;
  345. lbobj->unread.len = 0;
  346. if (input != NULL && input->len != 0) {
  347. unichar_t *_u;
  348. if ((_u = realloc(unistr.str,
  349. sizeof(unichar_t) * (unistr.len + input->len)))
  350. == NULL) {
  351. lbobj->errnum = errno;
  352. free(unistr.str);
  353. return NULL;
  354. } else
  355. unistr.str = _u;
  356. memcpy(unistr.str + unistr.len, input->str,
  357. sizeof(unichar_t) * input->len);
  358. unistr.len += input->len;
  359. }
  360. /***
  361. *** Preprocessing.
  362. ***/
  363. /* perform user breaking */
  364. if (lbobj->user_func != NULL)
  365. str = _user(lbobj, &unistr);
  366. else
  367. str = _prep(lbobj, &unistr);
  368. free(unistr.str);
  369. if (str == NULL)
  370. return NULL;
  371. /* South East Asian complex breaking. */
  372. errno = 0;
  373. linebreak_southeastasian_flagbreak(str);
  374. if (errno) {
  375. lbobj->errnum = errno;
  376. gcstring_DESTROY(str);
  377. return NULL;
  378. }
  379. /* LB21a (as of 6.1.0): HL (HY | BA) × [^ CB] */
  380. if (str != NULL && str->gclen) {
  381. propval_t lbc;
  382. for (i = 0; i < str->gclen; i++) {
  383. /* HL */
  384. if ((lbc = gcstring_lbclass(str, i)) == LB_HL &&
  385. gcstring_lbclass_ext(str, i) == lbc)
  386. /* avoid non-CM grapheme extenders */
  387. i++;
  388. else
  389. continue;
  390. /* CM* */
  391. while (i < str->gclen && gcstring_lbclass(str, i) == LB_CM)
  392. i++;
  393. if (str->gclen <= i)
  394. break;
  395. /* (HY|BA) */
  396. if (((lbc = gcstring_lbclass(str, i)) == LB_HY ||
  397. lbc == LB_BA) && gcstring_lbclass_ext(str, i) == lbc)
  398. /* avoid non-CM grapheme extenders */
  399. i++;
  400. else
  401. continue;
  402. /* CM* */
  403. while (i < str->gclen && gcstring_lbclass(str, i) == LB_CM)
  404. i++;
  405. if (str->gclen <= i)
  406. break;
  407. /* [^CB] */
  408. switch (gcstring_lbclass(str, i)) {
  409. /* prohibit break by default */
  410. case LB_BK: /* LB6 */
  411. case LB_CR:
  412. case LB_LF:
  413. case LB_NL:
  414. case LB_SP: /* LB7 */
  415. case LB_ZW:
  416. case LB_CM: /* LB9 */
  417. case LB_WJ: /* LB11 */
  418. /* allow break by default */
  419. case LB_CB: /* LB20 */
  420. continue;
  421. }
  422. if (!str->gcstr[i].flag)
  423. str->gcstr[i].flag = LINEBREAK_FLAG_PROHIBIT_BEFORE;
  424. }
  425. }
  426. /* LB25: not break in (PR|PO)? (OP|HY)? NU (NU|SY|IS)* (CL|CP)? (PR|PO)? */
  427. /* FIXME:Avoid non-CM grapheme extenders */
  428. if (str != NULL && str->gclen) {
  429. size_t st, et;
  430. for (i = 0; i < str->gclen; i++) {
  431. st = et = (size_t) - 1;
  432. /* (PR|PO)? */
  433. switch (gcstring_lbclass(str, i)) {
  434. case LB_PR:
  435. case LB_PO:
  436. if (st == (size_t) - 1)
  437. st = i;
  438. LB25_PRPO_PREFIX:
  439. i++;
  440. /* CM* */
  441. while (i < str->gclen && gcstring_lbclass(str, i) == LB_CM)
  442. i++;
  443. if (str->gclen <= i)
  444. goto LB25_BREAK;
  445. }
  446. /* (OP|HY)? */
  447. switch (gcstring_lbclass(str, i)) {
  448. case LB_OP:
  449. case LB_HY:
  450. if (st == (size_t) - 1)
  451. st = i;
  452. LB25_OPHY_PREFIX:
  453. i++;
  454. /* CM* */
  455. while (i < str->gclen && gcstring_lbclass(str, i) == LB_CM)
  456. i++;
  457. if (str->gclen <= i) {
  458. if (eot)
  459. goto LB25_BREAK;
  460. else
  461. goto LB25_FOUND; /* save possible partial sequence. */
  462. }
  463. }
  464. /* NU (NU|SY|IS)* */
  465. switch (gcstring_lbclass(str, i)) {
  466. case LB_NU:
  467. if (st == (size_t) - 1)
  468. st = i;
  469. i++;
  470. /* (NU|SY|IS|CM)* */
  471. while (i < str->gclen)
  472. switch (gcstring_lbclass(str, i)) {
  473. case LB_NU:
  474. case LB_SY:
  475. case LB_IS:
  476. case LB_CM:
  477. i++;
  478. break;
  479. /* (CL|CP) */
  480. case LB_CL:
  481. case LB_CP:
  482. goto LB25_CLCP_SUFFIX;
  483. /* (PR|PO) */
  484. case LB_PR:
  485. case LB_PO:
  486. goto LB25_PRPO_SUFFIX;
  487. default:
  488. goto LB25_FOUND;
  489. }
  490. if (str->gclen <= i)
  491. goto LB25_FOUND;
  492. break;
  493. case LB_PR:
  494. case LB_PO:
  495. st = i;
  496. goto LB25_PRPO_PREFIX;
  497. case LB_OP:
  498. case LB_HY:
  499. st = i;
  500. goto LB25_OPHY_PREFIX;
  501. default:
  502. continue;
  503. }
  504. /* (CL|CP)? */
  505. switch (gcstring_lbclass(str, i)) {
  506. case LB_CL:
  507. case LB_CP:
  508. LB25_CLCP_SUFFIX:
  509. i++;
  510. /* CM* */
  511. while (i < str->gclen && gcstring_lbclass(str, i) == LB_CM)
  512. i++;
  513. if (str->gclen <= i)
  514. goto LB25_FOUND;
  515. }
  516. /* (PR|PO)? */
  517. switch (gcstring_lbclass(str, i)) {
  518. case LB_PR:
  519. case LB_PO:
  520. LB25_PRPO_SUFFIX:
  521. et = i;
  522. i++;
  523. /* CM* */
  524. while (i < str->gclen && gcstring_lbclass(str, i) == LB_CM)
  525. i++;
  526. if (str->gclen <= i)
  527. goto LB25_FOUND;
  528. }
  529. LB25_FOUND:
  530. for (st++; st < i; st++) {
  531. if (!str->gcstr[st].flag)
  532. str->gcstr[st].flag = LINEBREAK_FLAG_PROHIBIT_BEFORE;
  533. }
  534. /* match may be overwrapped */
  535. if (et != (size_t) - 1) {
  536. i = st = et;
  537. et = (size_t) - 1;
  538. goto LB25_PRPO_PREFIX;
  539. }
  540. }
  541. LB25_BREAK:
  542. ;
  543. }
  544. /***
  545. *** Initialize status.
  546. ***/
  547. str->pos = 0;
  548. /*
  549. * Line buffer.
  550. * bufStr: Unbreakable text fragment.
  551. * bufSpc: Trailing spaces.
  552. * bufCols: Columns of bufStr: can be differ from gcstring_columns().
  553. * state: Start of text/paragraph status.
  554. * 0: Start of text not done.
  555. * 1: Start of text done while start of paragraph not done.
  556. * 2: Start of paragraph done while end of paragraph not done.
  557. */
  558. state = lbobj->state;
  559. unistr.str = lbobj->bufstr.str;
  560. unistr.len = lbobj->bufstr.len;
  561. lbobj->bufstr.str = NULL;
  562. lbobj->bufstr.len = 0;
  563. IF_NULL_THEN_ABORT(bufStr = gcstring_new(&unistr, lbobj));
  564. unistr.str = lbobj->bufspc.str;
  565. unistr.len = lbobj->bufspc.len;
  566. lbobj->bufspc.str = NULL;
  567. lbobj->bufspc.len = 0;
  568. IF_NULL_THEN_ABORT(bufSpc = gcstring_new(&unistr, lbobj));
  569. bufCols = lbobj->bufcols;
  570. /*
  571. * Indexes and flags
  572. * bBeg: Start of unbreakable text fragment.
  573. * bLen: Length of unbreakable text fragment.
  574. * bSpc: Length of trailing spaces.
  575. * urgEnd: End of substring broken by urgent breaking.
  576. *
  577. * ...read...| before :CM | spaces | after :CM |...unread...|
  578. * ^ ->bCM<- ^ ->aCM<- ^
  579. * |<-- bLen -->|<- bSpc ->| ^ |
  580. * bBeg candidate str->pos end of
  581. * breaking input
  582. * point
  583. * `read' positions shall never be read again.
  584. */
  585. bBeg = bLen = bCM = bSpc = aCM = urgEnd = 0;
  586. /* Result. */
  587. IF_NULL_THEN_ABORT(results = malloc(sizeof(gcstring_t **)));
  588. results[0] = NULL;
  589. while (1) {
  590. /***
  591. *** Chop off a pair of unbreakable character clusters from text.
  592. ***/
  593. int action = 0;
  594. propval_t lbc;
  595. double newcols;
  596. /* Go ahead reading input. */
  597. while (!gcstring_eos(str)) {
  598. lbc = gcstring_lbclass(str, str->pos);
  599. /**
  600. ** Append SP/ZW/eop to ``before'' buffer.
  601. **/
  602. switch (lbc) {
  603. /* - Explicit breaks and non-breaks */
  604. /* LB7(1): × SP+ */
  605. case LB_SP:
  606. gcstring_next(str);
  607. bSpc++;
  608. /* End of input. */
  609. continue; /* while (!gcstring_eos(str)) */
  610. /* - Mandatory breaks */
  611. /* LB4 - LB7: × SP* (BK | CR LF | CR | LF | NL) ! */
  612. case LB_BK:
  613. case LB_CR:
  614. case LB_LF:
  615. case LB_NL:
  616. gcstring_next(str);
  617. bSpc++;
  618. goto last_CHARACTER_PAIR; /* while (!gcstring_eos(str)) */
  619. /* - Explicit breaks and non-breaks */
  620. /* LB7(2): × (SP* ZW+)+ */
  621. case LB_ZW:
  622. gcstring_next(str);
  623. bLen += bSpc + 1;
  624. bCM = 0;
  625. bSpc = 0;
  626. /* End of input */
  627. continue; /* while (!gcstring_eos(str)) */
  628. }
  629. /**
  630. ** Then fill ``after'' buffer.
  631. **/
  632. gcstring_next(str);
  633. /* skip to end of unbreakable fragment by user/complex/urgent
  634. * breaking. */
  635. while (!gcstring_eos(str) && str->gcstr[str->pos].flag &
  636. LINEBREAK_FLAG_PROHIBIT_BEFORE)
  637. gcstring_next(str);
  638. /* - Combining marks */
  639. /* LB9: Treat X CM+ as if it were X
  640. * where X is anything except BK, CR, LF, NL, SP or ZW
  641. * (NB: Some CM characters may be single grapheme cluster
  642. * since they have Grapheme_Cluster_Break property Control.) */
  643. while (!gcstring_eos(str) &&
  644. gcstring_lbclass(str, str->pos) == LB_CM) {
  645. gcstring_next(str);
  646. aCM++;
  647. }
  648. /* - Start of text */
  649. /* LB2: sot × */
  650. if (0 < bLen || 0 < bSpc)
  651. break; /* while (!gcstring_eos(str)) */
  652. /* shift buffers. */
  653. bLen = str->pos - bBeg;
  654. bSpc = 0;
  655. bCM = aCM;
  656. aCM = 0;
  657. } /* while (!gcstring_eos(str)) */
  658. last_CHARACTER_PAIR:
  659. /***
  660. *** Determin line breaking action by classes of adjacent characters.
  661. ***/
  662. /* Mandatory break. */
  663. if (0 < bSpc &&
  664. (lbc = gcstring_lbclass(str, bBeg + bLen + bSpc - 1)) != LB_SP
  665. && (lbc != LB_CR || eot || !gcstring_eos(str))) {
  666. /* CR at end of input may be part of CR LF therefore not be eop. */
  667. action = LINEBREAK_ACTION_MANDATORY;
  668. /* LB11, LB12 and tailorable rules LB13 - LB31.
  669. * Or urgent breaking. */
  670. } else if (bBeg + bLen + bSpc < str->pos) {
  671. if (str->gcstr[bBeg + bLen + bSpc].flag &
  672. LINEBREAK_FLAG_ALLOW_BEFORE)
  673. action = LINEBREAK_ACTION_DIRECT;
  674. else if (str->gcstr[bBeg + bLen + bSpc].flag &
  675. LINEBREAK_FLAG_PROHIBIT_BEFORE)
  676. action = LINEBREAK_ACTION_PROHIBITED;
  677. else if (lbobj->options & LINEBREAK_OPTION_BREAK_INDENT &&
  678. bLen == 0 && 0 < bSpc)
  679. /* Allow break at sot or after breaking,
  680. * although rules don't tell it obviously. */
  681. action = LINEBREAK_ACTION_DIRECT;
  682. else {
  683. propval_t blbc, albc;
  684. size_t btail;
  685. if (bLen == 0)
  686. btail = bBeg + bSpc - 1; /* before buffer is SP only. */
  687. else
  688. btail = bBeg + bLen - bCM - 1; /* LB9 */
  689. blbc = gcstring_lbclass_ext(str, btail);
  690. switch (blbc) {
  691. /* (SG and XX are already resolved). */
  692. /* LB1: Resolve AI and CJ. */
  693. case LB_AI:
  694. blbc = (lbobj->options &
  695. LINEBREAK_OPTION_EASTASIAN_CONTEXT) ?
  696. LB_ID : LB_AL;
  697. break;
  698. case LB_CJ:
  699. blbc = (lbobj->options &
  700. LINEBREAK_OPTION_NONSTARTER_LOOSE) ?
  701. LB_ID : LB_NS;
  702. break;
  703. /* LB1: SA is resolved to AL. */
  704. case LB_SA:
  705. blbc = LB_AL;
  706. break;
  707. /* LB10: Treat any remaining CM+ as if it were AL. */
  708. case LB_CM:
  709. blbc = LB_AL;
  710. break;
  711. /* (As of 6.1.0): Treat HL as AL. */
  712. case LB_HL:
  713. blbc = LB_AL;
  714. break;
  715. /* Optionally, treat hangul syllable as if it were AL. */
  716. case LB_H2:
  717. case LB_H3:
  718. case LB_JL:
  719. case LB_JV:
  720. case LB_JT:
  721. if (lbobj->options & LINEBREAK_OPTION_HANGUL_AS_AL)
  722. blbc = LB_AL;
  723. break;
  724. }
  725. albc = gcstring_lbclass(str, bBeg + bLen + bSpc);
  726. switch (albc) {
  727. /* (SG and XX are already resolved). */
  728. /* LB1: Resolve AI and CJ. */
  729. case LB_AI:
  730. albc = (lbobj->options &
  731. LINEBREAK_OPTION_EASTASIAN_CONTEXT) ?
  732. LB_ID : LB_AL;
  733. break;
  734. case LB_CJ:
  735. albc = (lbobj->options &
  736. LINEBREAK_OPTION_NONSTARTER_LOOSE) ?
  737. LB_ID : LB_NS;
  738. break;
  739. /* LB1: SA is resolved to AL. */
  740. case LB_SA:
  741. albc = LB_AL;
  742. break;
  743. /* LB10: Treat any remaining CM+ as if it were AL. */
  744. case LB_CM:
  745. albc = LB_AL;
  746. break;
  747. /* (As of 6.1.0): Treat HL as AL. */
  748. case LB_HL:
  749. albc = LB_AL;
  750. break;
  751. /* Optionally, treat hangul syllable as if it were AL. */
  752. case LB_H2:
  753. case LB_H3:
  754. case LB_JL:
  755. case LB_JV:
  756. case LB_JT:
  757. if (lbobj->options & LINEBREAK_OPTION_HANGUL_AS_AL)
  758. albc = LB_AL;
  759. break;
  760. }
  761. action = _lbruleinfo(blbc, albc);
  762. }
  763. /* Check prohibited break. */
  764. if (action == LINEBREAK_ACTION_PROHIBITED ||
  765. (action == LINEBREAK_ACTION_INDIRECT && bSpc == 0)) {
  766. /* When conjunction is expected to exceed charmax,
  767. * try urgent breaking. */
  768. if (urgEnd < bBeg + bLen + bSpc &&
  769. 0 < lbobj->charmax &&
  770. lbobj->charmax < str->gcstr[str->pos - 1].idx +
  771. str->gcstr[str->pos - 1].len - str->gcstr[bBeg].idx) {
  772. size_t charmax, chars;
  773. IF_NULL_THEN_ABORT(s = gcstring_substr(str, bBeg,
  774. str->pos -
  775. bBeg));
  776. IF_NULL_THEN_ABORT(broken = _urgent_break(lbobj, s));
  777. gcstring_DESTROY(s);
  778. /* If any of urgently broken fragments still
  779. * exceed CharactersMax, force chop them. */
  780. charmax = lbobj->charmax;
  781. broken->pos = 0;
  782. chars = gcstring_next(broken)->len;
  783. while (!gcstring_eos(broken)) {
  784. if (broken->gcstr[broken->pos].flag &
  785. LINEBREAK_FLAG_ALLOW_BEFORE)
  786. chars = 0;
  787. else if (charmax <
  788. chars + broken->gcstr[broken->pos].len) {
  789. broken->gcstr[broken->pos].flag |=
  790. LINEBREAK_FLAG_ALLOW_BEFORE;
  791. chars = 0;
  792. } else
  793. chars += broken->gcstr[broken->pos].len;
  794. gcstring_next(broken);
  795. } /* while (!gcstring_eos(broken)) */
  796. urgEnd = broken->gclen;
  797. gcstring_replace(str, 0, str->pos, broken);
  798. gcstring_DESTROY(broken);
  799. str->pos = 0;
  800. bBeg = bLen = bCM = bSpc = aCM = 0;
  801. continue; /* while (1) */
  802. }
  803. /* if (urgEnd < ...) */
  804. /* Otherwise, fragments may be conjuncted safely. Read more. */
  805. bLen = str->pos - bBeg;
  806. bSpc = 0;
  807. bCM = aCM;
  808. aCM = 0;
  809. continue; /* while (1) */
  810. } /* if (action == ...) */
  811. } /* if (0 < bSpc && ...) */
  812. /***
  813. *** Check end of input.
  814. ***/
  815. if (!eot && str->gclen <= bBeg + bLen + bSpc) {
  816. /* Save status then output partial result. */
  817. lbobj->bufstr.str = bufStr->str;
  818. lbobj->bufstr.len = bufStr->len;
  819. bufStr->str = NULL;
  820. bufStr->len = 0;
  821. gcstring_DESTROY(bufStr);
  822. lbobj->bufspc.str = bufSpc->str;
  823. lbobj->bufspc.len = bufSpc->len;
  824. bufSpc->str = NULL;
  825. bufSpc->len = 0;
  826. gcstring_DESTROY(bufSpc);
  827. lbobj->bufcols = bufCols;
  828. s = gcstring_substr(str, bBeg, str->gclen - bBeg);
  829. lbobj->unread.str = s->str;
  830. lbobj->unread.len = s->len;
  831. s->str = NULL;
  832. s->len = 0;
  833. gcstring_DESTROY(s);
  834. lbobj->state = state;
  835. /* clenup. */
  836. gcstring_DESTROY(str);
  837. if (lenp != NULL)
  838. *lenp = reslen;
  839. return results;
  840. }
  841. /* After all, possible actions are MANDATORY and arbitrary. */
  842. /***
  843. *** Examine line breaking action
  844. ***/
  845. IF_NULL_THEN_ABORT(beforeFrg = gcstring_substr(str, bBeg, bLen));
  846. if (state == LINEBREAK_STATE_NONE) { /* sot undone. */
  847. /* Process start of text. */
  848. IF_NULL_THEN_ABORT(fmt = _format(lbobj, LINEBREAK_STATE_SOT,
  849. beforeFrg));
  850. if (gcstring_cmp(beforeFrg, fmt) != 0) {
  851. s = gcstring_substr(str, bBeg + bLen, bSpc);
  852. gcstring_append(fmt, s);
  853. gcstring_DESTROY(s);
  854. s = gcstring_substr(str, bBeg + bLen + bSpc,
  855. str->pos - (bBeg + bLen + bSpc));
  856. gcstring_append(fmt, s);
  857. gcstring_DESTROY(s);
  858. gcstring_replace(str, 0, str->pos, fmt);
  859. str->pos = 0;
  860. bBeg = bLen = bCM = bSpc = aCM = 0;
  861. urgEnd = 0;
  862. state = LINEBREAK_STATE_SOT_FORMAT;
  863. gcstring_DESTROY(fmt);
  864. gcstring_DESTROY(beforeFrg);
  865. continue; /* while (1) */
  866. }
  867. gcstring_DESTROY(fmt);
  868. state = LINEBREAK_STATE_SOL;
  869. } else if (state == LINEBREAK_STATE_SOT_FORMAT)
  870. state = LINEBREAK_STATE_SOL;
  871. else if (state == LINEBREAK_STATE_SOT) { /* sop undone. */
  872. /* Process start of paragraph. */
  873. IF_NULL_THEN_ABORT(fmt = _format(lbobj, LINEBREAK_STATE_SOP,
  874. beforeFrg));
  875. if (gcstring_cmp(beforeFrg, fmt) != 0) {
  876. s = gcstring_substr(str, bBeg + bLen, bSpc);
  877. gcstring_append(fmt, s);
  878. gcstring_DESTROY(s);
  879. s = gcstring_substr(str, bBeg + bLen + bSpc,
  880. str->pos - (bBeg + bLen + bSpc));
  881. gcstring_append(fmt, s);
  882. gcstring_DESTROY(s);
  883. gcstring_replace(str, 0, str->pos, fmt);
  884. str->pos = 0;
  885. bBeg = bLen = bCM = bSpc = aCM = 0;
  886. urgEnd = 0;
  887. state = LINEBREAK_STATE_SOP_FORMAT;
  888. gcstring_DESTROY(fmt);
  889. gcstring_DESTROY(beforeFrg);
  890. continue; /* while (1) */
  891. }
  892. gcstring_DESTROY(fmt);
  893. state = LINEBREAK_STATE_SOP;
  894. } else if (state == LINEBREAK_STATE_SOP_FORMAT)
  895. state = LINEBREAK_STATE_SOP;
  896. /***
  897. *** Check if arbitrary break is needed.
  898. ***/
  899. newcols = _sizing(lbobj, bufCols, bufStr, bufSpc, beforeFrg);
  900. if (newcols < 0.0) {
  901. IF_NULL_THEN_ABORT(NULL);
  902. }
  903. if (0 < lbobj->colmax && lbobj->colmax < newcols) {
  904. newcols = _sizing(lbobj, 0.0, &empty, &empty, beforeFrg);
  905. if (newcols < 0.0) {
  906. IF_NULL_THEN_ABORT(NULL);
  907. }
  908. /**
  909. ** When arbitrary break is expected to generate a line shorter
  910. ** than colmin or, beforeFrg will exceed colmax, try urgent
  911. ** breaking.
  912. **/
  913. if (urgEnd < bBeg + bLen + bSpc) {
  914. broken = NULL;
  915. if (0.0 < bufCols && bufCols < lbobj->colmin) {
  916. gcstring_replace(beforeFrg, 0, 0, bufSpc);
  917. gcstring_replace(beforeFrg, 0, 0, bufStr);
  918. gcstring_shrink(bufSpc, 0);
  919. gcstring_shrink(bufStr, 0);
  920. bufCols = 0.0;
  921. IF_NULL_THEN_ABORT(broken = _urgent_break(lbobj,
  922. beforeFrg));
  923. } else if (lbobj->colmax < newcols) {
  924. IF_NULL_THEN_ABORT(broken = _urgent_break(lbobj,
  925. beforeFrg));
  926. }
  927. if (broken != NULL) {
  928. s = gcstring_substr(str, bBeg + bLen, bSpc);
  929. gcstring_append(broken, s);
  930. gcstring_DESTROY(s);
  931. gcstring_replace(str, 0, bBeg + bLen + bSpc, broken);
  932. str->pos = 0;
  933. urgEnd = broken->gclen;
  934. bBeg = bLen = bCM = bSpc = aCM = 0;
  935. gcstring_DESTROY(broken);
  936. gcstring_DESTROY(beforeFrg);
  937. continue; /* while (1) */
  938. }
  939. }
  940. /**
  941. ** Otherwise, process arbitrary break.
  942. **/
  943. if (bufStr->len || bufSpc->len) {
  944. gcstring_t **r;
  945. IF_NULL_THEN_ABORT(r = realloc(results,
  946. sizeof(gcstring_t *) *
  947. (reslen + 2)));
  948. (results = r)[reslen + 1] = NULL;
  949. IF_NULL_THEN_ABORT(s = _format(lbobj, LINEBREAK_STATE_LINE,
  950. bufStr));
  951. IF_NULL_THEN_ABORT(t = _format(lbobj, LINEBREAK_STATE_EOL,
  952. bufSpc));
  953. IF_NULL_THEN_ABORT(results[reslen] =
  954. gcstring_concat(s, t));
  955. reslen++;
  956. gcstring_DESTROY(s);
  957. gcstring_DESTROY(t);
  958. IF_NULL_THEN_ABORT(fmt =
  959. _format(lbobj, LINEBREAK_STATE_SOL,
  960. beforeFrg));
  961. if (gcstring_cmp(beforeFrg, fmt) != 0) {
  962. gcstring_DESTROY(beforeFrg);
  963. beforeFrg = fmt;
  964. newcols =
  965. _sizing(lbobj, 0.0, &empty, &empty, beforeFrg);
  966. if (newcols < 0.0) {
  967. IF_NULL_THEN_ABORT(NULL);
  968. }
  969. } else
  970. gcstring_DESTROY(fmt);
  971. }
  972. gcstring_shrink(bufStr, 0);
  973. gcstring_append(bufStr, beforeFrg);
  974. gcstring_shrink(bufSpc, 0);
  975. s = gcstring_substr(str, bBeg + bLen, bSpc);
  976. gcstring_append(bufSpc, s);
  977. gcstring_DESTROY(s);
  978. bufCols = newcols;
  979. /***
  980. *** Arbitrary break is not needed.
  981. ***/
  982. } else {
  983. gcstring_append(bufStr, bufSpc);
  984. gcstring_append(bufStr, beforeFrg);
  985. gcstring_shrink(bufSpc, 0);
  986. s = gcstring_substr(str, bBeg + bLen, bSpc);
  987. gcstring_append(bufSpc, s);
  988. gcstring_DESTROY(s);
  989. bufCols = newcols;
  990. } /* if (0 < lbobj->colmax ... ) */
  991. gcstring_DESTROY(beforeFrg);
  992. /***
  993. *** Mandatory break or end-of-text.
  994. ***/
  995. if (eot && str->gclen <= bBeg + bLen + bSpc)
  996. break; /* while (1) */
  997. if (action == LINEBREAK_ACTION_MANDATORY) {
  998. /* Process mandatory break. */
  999. gcstring_t **r;
  1000. IF_NULL_THEN_ABORT(r = realloc(results,
  1001. sizeof(gcstring_t *) *
  1002. (reslen + 2)));
  1003. (results = r)[reslen + 1] = NULL;
  1004. IF_NULL_THEN_ABORT(s = _format(lbobj, LINEBREAK_STATE_LINE,
  1005. bufStr));
  1006. IF_NULL_THEN_ABORT(t = _format(lbobj, LINEBREAK_STATE_EOP,
  1007. bufSpc));
  1008. IF_NULL_THEN_ABORT(results[reslen] = gcstring_concat(s, t));
  1009. reslen++;
  1010. gcstring_DESTROY(s);
  1011. gcstring_DESTROY(t);
  1012. /* eop done then sop must be carried out. */
  1013. state = LINEBREAK_STATE_SOT;
  1014. gcstring_shrink(bufStr, 0);
  1015. gcstring_shrink(bufSpc, 0);
  1016. bufCols = 0.0;
  1017. }
  1018. /***
  1019. *** Shift buffers.
  1020. ***/
  1021. bBeg += bLen + bSpc;
  1022. bLen = str->pos - bBeg;
  1023. bSpc = 0;
  1024. bCM = aCM;
  1025. aCM = 0;
  1026. } /* while (1) */
  1027. /***
  1028. *** Process end of text.
  1029. ***/
  1030. {
  1031. gcstring_t **r;
  1032. IF_NULL_THEN_ABORT(r = realloc(results,
  1033. sizeof(gcstring_t *) * (reslen +
  1034. 2)));
  1035. (results = r)[reslen + 1] = NULL;
  1036. IF_NULL_THEN_ABORT(s =
  1037. _format(lbobj, LINEBREAK_STATE_LINE, bufStr));
  1038. IF_NULL_THEN_ABORT(t =
  1039. _format(lbobj, LINEBREAK_STATE_EOT, bufSpc));
  1040. IF_NULL_THEN_ABORT(results[reslen] = gcstring_concat(s, t));
  1041. reslen++;
  1042. gcstring_DESTROY(s);
  1043. gcstring_DESTROY(t);
  1044. }
  1045. /* clenup. */
  1046. gcstring_DESTROY(str);
  1047. gcstring_DESTROY(bufStr);
  1048. gcstring_DESTROY(bufSpc);
  1049. /* Reset status then return the rest of result. */
  1050. linebreak_reset(lbobj);
  1051. if (lenp != NULL)
  1052. *lenp = reslen;
  1053. return results;
  1054. }
  1055. gcstring_t **linebreak_break_partial(linebreak_t * lbobj, unistr_t * input)
  1056. {
  1057. return _break_partial(lbobj, input, NULL, (input == NULL));
  1058. }
  1059. /**
  1060. * Perform line breaking algorithm on complete input.
  1061. *
  1062. * This function will consume heap size proportional to input size.
  1063. * linebreak_break() is highly recommended.
  1064. *
  1065. * @param[in] lbobj linebreak object.
  1066. * @param[in] input Unicode string.
  1067. * @return array of broken grapheme cluster strings terminated by NULL.
  1068. * If internal error occurred, lbobj->errnum is set then NULL is returned.
  1069. */
  1070. gcstring_t **linebreak_break_fast(linebreak_t * lbobj, unistr_t * input)
  1071. {
  1072. gcstring_t **ret;
  1073. if (input == NULL) {
  1074. if ((ret = malloc(sizeof(gcstring_t *))) == NULL)
  1075. lbobj->errnum = errno ? errno : ENOMEM;
  1076. else
  1077. ret[0] = NULL;
  1078. return ret;
  1079. }
  1080. return _break_partial(lbobj, input, NULL, 1);
  1081. }
  1082. #define PARTIAL_LENGTH (1000)
  1083. /** Perform line breaking algorithm on complete input.
  1084. *
  1085. * This function will consume constant size of heap.
  1086. *
  1087. * @param[in] lbobj linebreak object.
  1088. * @param[in] input Unicode string.
  1089. * @return array of broken grapheme cluster strings terminated by NULL.
  1090. * If internal error occurred, lbobj->errnum is set then NULL is returned.
  1091. */
  1092. gcstring_t **linebreak_break(linebreak_t * lbobj, unistr_t * input)
  1093. {
  1094. unistr_t unistr = { NULL, 0 };
  1095. gcstring_t **ret, **appe, **r;
  1096. size_t i, j, k, retlen, appelen;
  1097. if ((ret = malloc(sizeof(gcstring_t *))) == NULL) {
  1098. lbobj->errnum = errno ? errno : ENOMEM;
  1099. return NULL;
  1100. } else
  1101. ret[0] = NULL;
  1102. if (input == NULL)
  1103. return ret;
  1104. retlen = 0;
  1105. unistr.len = PARTIAL_LENGTH;
  1106. for (k = 0; PARTIAL_LENGTH < input->len - k; k += PARTIAL_LENGTH) {
  1107. unistr.str = input->str + k;
  1108. if ((appe = _break_partial(lbobj, &unistr, &appelen, 0)) == NULL) {
  1109. for (i = 0; i < retlen; i++)
  1110. gcstring_destroy(ret[i]);
  1111. free(ret);
  1112. return NULL;
  1113. }
  1114. if (appelen) {
  1115. if ((r = realloc(ret,
  1116. sizeof(gcstring_t *) *
  1117. (retlen + appelen + 1))) == NULL) {
  1118. lbobj->errnum = errno ? errno : ENOMEM;
  1119. for (i = 0; i < retlen; i++)
  1120. gcstring_destroy(ret[i]);
  1121. free(ret);
  1122. for (j = 0; j < appelen; j++)
  1123. gcstring_destroy(appe[j]);
  1124. free(appe);
  1125. return NULL;
  1126. } else
  1127. ret = r;
  1128. memcpy(ret + retlen, appe,
  1129. sizeof(gcstring_t *) * (appelen + 1));
  1130. retlen += appelen;
  1131. }
  1132. free(appe);
  1133. }
  1134. unistr.len = input->len - k;
  1135. unistr.str = input->str + k;
  1136. if (k < input->len) {
  1137. if ((appe = _break_partial(lbobj, &unistr, &appelen, 1)) == NULL) {
  1138. for (i = 0; i < retlen; i++)
  1139. gcstring_destroy(ret[i]);
  1140. free(ret);
  1141. return NULL;
  1142. }
  1143. if (appelen) {
  1144. if ((r = realloc(ret,
  1145. sizeof(gcstring_t *) *
  1146. (retlen + appelen + 1))) == NULL) {
  1147. lbobj->errnum = errno ? errno : ENOMEM;
  1148. for (i = 0; i < retlen; i++)
  1149. gcstring_destroy(ret[i]);
  1150. free(ret);
  1151. for (j = 0; j < appelen; j++)
  1152. gcstring_destroy(appe[j]);
  1153. free(appe);
  1154. return NULL;
  1155. } else
  1156. ret = r;
  1157. memcpy(ret + retlen, appe,
  1158. sizeof(gcstring_t *) * (appelen + 1));
  1159. retlen += appelen;
  1160. }
  1161. free(appe);
  1162. }
  1163. return ret;
  1164. }
  1165. /** Perform line breaking algorithm on UTF-8 text
  1166. *
  1167. * This function will consume constant size of heap.
  1168. *
  1169. * @param[in] lbobj linebreak object.
  1170. * @param[in] input UTF-8 string, must not be NULL.
  1171. * @param[in] len length of UTF-8 string.
  1172. * @param[in] check check input. See sombok_decode_utf8().
  1173. * @return array of broken grapheme cluster strings terminated by NULL.
  1174. * If internal error occurred, lbobj->errnum is set then NULL is returned.
  1175. */
  1176. gcstring_t **linebreak_break_from_utf8(linebreak_t * lbobj,
  1177. char *input, size_t len, int check)
  1178. {
  1179. unistr_t unistr = { NULL, 0 };
  1180. gcstring_t **ret;
  1181. if (input == NULL) {
  1182. lbobj->errnum = EINVAL;
  1183. return NULL;
  1184. }
  1185. if (sombok_decode_utf8(&unistr, 0, input, len, check) == NULL)
  1186. return NULL;
  1187. ret = linebreak_break(lbobj, &unistr);
  1188. free(unistr.str);
  1189. return ret;
  1190. }
  1191. void linebreak_free_result(gcstring_t ** result, int deep)
  1192. {
  1193. size_t i;
  1194. if (result == NULL)
  1195. return;
  1196. if (deep)
  1197. for (i = 0; result[i] != NULL; i++)
  1198. gcstring_destroy(result[i]);
  1199. free(result);
  1200. }