pmh_styleparser.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937
  1. /* PEG Markdown Highlight
  2. * Copyright 2011-2016 Ali Rantakari -- http://hasseg.org
  3. * Licensed under the GPL2+ and MIT licenses (see LICENSE for more info).
  4. *
  5. * styleparser.c
  6. *
  7. * Parser for custom syntax highlighting stylesheets.
  8. */
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <stdarg.h>
  13. #include <ctype.h>
  14. #include "pmh_styleparser.h"
  15. #include "pmh_parser.h"
  16. #if pmh_DEBUG_OUTPUT
  17. #define pmhsp_PRINTF(x, ...) fprintf(stderr, x, ##__VA_ARGS__)
  18. #else
  19. #define pmhsp_PRINTF(x, ...)
  20. #endif
  21. // vasprintf is not in the C standard nor in POSIX so we provide our own
  22. static int our_vasprintf(char **strptr, const char *fmt, va_list argptr)
  23. {
  24. int ret;
  25. va_list argptr2;
  26. *strptr = NULL;
  27. va_copy(argptr2, argptr);
  28. ret = vsnprintf(NULL, 0, fmt, argptr2);
  29. if (ret <= 0)
  30. return ret;
  31. *strptr = (char *)malloc(ret+1);
  32. if (*strptr == NULL)
  33. return -1;
  34. va_copy(argptr2, argptr);
  35. ret = vsnprintf(*strptr, ret+1, fmt, argptr2);
  36. return ret;
  37. }
  38. // Parsing context data
  39. typedef struct
  40. {
  41. char *input;
  42. void (*error_callback)(char*,int,void*);
  43. void *error_callback_context;
  44. int styles_pos;
  45. pmh_style_collection *styles;
  46. } style_parser_data;
  47. typedef struct raw_attribute
  48. {
  49. char *name;
  50. char *value;
  51. int line_number;
  52. struct raw_attribute *next;
  53. } raw_attribute;
  54. static raw_attribute *new_raw_attribute(char *name, char *value,
  55. int line_number)
  56. {
  57. raw_attribute *v = (raw_attribute *)malloc(sizeof(raw_attribute));
  58. v->name = name;
  59. v->value = value;
  60. v->line_number = line_number;
  61. v->next = NULL;
  62. return v;
  63. }
  64. static void free_raw_attributes(raw_attribute *list)
  65. {
  66. raw_attribute *cur = list;
  67. while (cur != NULL)
  68. {
  69. raw_attribute *pattr = NULL;
  70. if (cur->name != NULL) free(cur->name);
  71. if (cur->value != NULL) free(cur->value);
  72. pattr = cur;
  73. cur = cur->next;
  74. free(pattr);
  75. }
  76. }
  77. static void report_error(style_parser_data *p_data,
  78. int line_number, char *str, ...)
  79. {
  80. va_list argptr;
  81. if (p_data->error_callback == NULL)
  82. return;
  83. va_start(argptr, str);
  84. char *errmsg;
  85. our_vasprintf(&errmsg, str, argptr);
  86. va_end(argptr);
  87. p_data->error_callback(errmsg, line_number,
  88. p_data->error_callback_context);
  89. free(errmsg);
  90. }
  91. static char *trim_str(char *str)
  92. {
  93. while (isspace(*str))
  94. str++;
  95. if (*str == '\0')
  96. return str;
  97. char *end = str + strlen(str) - 1;
  98. while (end > str && isspace(*end))
  99. end--;
  100. *(end+1) = '\0';
  101. return str;
  102. }
  103. static char *trim_str_dup(char *str)
  104. {
  105. size_t start = 0;
  106. while (isspace(*(str + start)))
  107. start++;
  108. size_t end = strlen(str) - 1;
  109. while (start < end && isspace(*(str + end)))
  110. end--;
  111. size_t len = end - start + 1;
  112. char *ret = (char *)malloc(sizeof(char)*len + 1);
  113. *ret = '\0';
  114. strncat(ret, (str + start), len);
  115. return ret;
  116. }
  117. static char *strcpy_lower(char *str)
  118. {
  119. char *low = strdup(str);
  120. int i;
  121. int len = strlen(str);
  122. for (i = 0; i < len; i++)
  123. *(low+i) = tolower(*(low+i));
  124. return low;
  125. }
  126. static char *standardize_str(char *str)
  127. {
  128. return strcpy_lower(trim_str(str));
  129. }
  130. static pmh_attr_argb_color *new_argb_color(int r, int g, int b, int a)
  131. {
  132. pmh_attr_argb_color *c = (pmh_attr_argb_color *)
  133. malloc(sizeof(pmh_attr_argb_color));
  134. c->red = r; c->green = g; c->blue = b; c->alpha = a;
  135. return c;
  136. }
  137. static pmh_attr_argb_color *new_argb_from_hex(long long hex, bool has_alpha)
  138. {
  139. // 0xaarrggbb
  140. int a = has_alpha ? ((hex >> 24) & 0xFF) : 255;
  141. int r = ((hex >> 16) & 0xFF);
  142. int g = ((hex >> 8) & 0xFF);
  143. int b = (hex & 0xFF);
  144. return new_argb_color(r,g,b,a);
  145. }
  146. static pmh_attr_argb_color *new_argb_from_hex_str(style_parser_data *p_data,
  147. int attr_line_number,
  148. char *str)
  149. {
  150. // "aarrggbb"
  151. int len = strlen(str);
  152. if (len != 6 && len != 8) {
  153. report_error(p_data, attr_line_number,
  154. "Value '%s' is not a valid color value: it should be a "
  155. "hexadecimal number, 6 or 8 characters long.",
  156. str);
  157. return NULL;
  158. }
  159. char *endptr = NULL;
  160. long long num = strtoll(str, &endptr, 16);
  161. if (*endptr != '\0') {
  162. report_error(p_data, attr_line_number,
  163. "Value '%s' is not a valid color value: the character "
  164. "'%c' is invalid. The color value should be a hexadecimal "
  165. "number, 6 or 8 characters long.",
  166. str, *endptr);
  167. return NULL;
  168. }
  169. return new_argb_from_hex(num, (len == 8));
  170. }
  171. static pmh_attr_value *new_attr_value()
  172. {
  173. return (pmh_attr_value *)malloc(sizeof(pmh_attr_value));
  174. }
  175. static pmh_attr_font_styles *new_font_styles()
  176. {
  177. pmh_attr_font_styles *ret = (pmh_attr_font_styles *)
  178. malloc(sizeof(pmh_attr_font_styles));
  179. ret->italic = false;
  180. ret->bold = false;
  181. ret->underlined = false;
  182. ret->strikeout = false;
  183. return ret;
  184. }
  185. static pmh_attr_font_size *new_font_size()
  186. {
  187. pmh_attr_font_size *ret = (pmh_attr_font_size *)
  188. malloc(sizeof(pmh_attr_font_size));
  189. ret->is_relative = false;
  190. ret->size_pt = 0;
  191. return ret;
  192. }
  193. static pmh_style_attribute *new_attr(char *name, pmh_attr_type type)
  194. {
  195. pmh_style_attribute *attr = (pmh_style_attribute *)malloc(sizeof(pmh_style_attribute));
  196. attr->name = strdup(name);
  197. attr->type = type;
  198. attr->next = NULL;
  199. return attr;
  200. }
  201. static void free_style_attributes(pmh_style_attribute *list)
  202. {
  203. pmh_style_attribute *cur = list;
  204. while (cur != NULL)
  205. {
  206. if (cur->name != NULL)
  207. free(cur->name);
  208. if (cur->value != NULL)
  209. {
  210. if (cur->type == pmh_attr_type_foreground_color
  211. || cur->type == pmh_attr_type_background_color
  212. || cur->type == pmh_attr_type_caret_color
  213. || cur->type == pmh_attr_type_strike_color)
  214. free(cur->value->argb_color);
  215. else if (cur->type == pmh_attr_type_font_family)
  216. free(cur->value->font_family);
  217. else if (cur->type == pmh_attr_type_font_style)
  218. free(cur->value->font_styles);
  219. else if (cur->type == pmh_attr_type_font_size_pt)
  220. free(cur->value->font_size);
  221. else if (cur->type == pmh_attr_type_other)
  222. free(cur->value->string);
  223. free(cur->value);
  224. }
  225. pmh_style_attribute *pattr = cur;
  226. cur = cur->next;
  227. free(pattr);
  228. }
  229. }
  230. #define IF_ATTR_NAME(x) if (strcmp(x, name) == 0)
  231. pmh_attr_type pmh_attr_type_from_name(char *name)
  232. {
  233. IF_ATTR_NAME("color") return pmh_attr_type_foreground_color;
  234. else IF_ATTR_NAME("foreground") return pmh_attr_type_foreground_color;
  235. else IF_ATTR_NAME("foreground-color") return pmh_attr_type_foreground_color;
  236. else IF_ATTR_NAME("background") return pmh_attr_type_background_color;
  237. else IF_ATTR_NAME("background-color") return pmh_attr_type_background_color;
  238. else IF_ATTR_NAME("caret") return pmh_attr_type_caret_color;
  239. else IF_ATTR_NAME("caret-color") return pmh_attr_type_caret_color;
  240. else IF_ATTR_NAME("strike") return pmh_attr_type_strike_color;
  241. else IF_ATTR_NAME("strike-color") return pmh_attr_type_strike_color;
  242. else IF_ATTR_NAME("font-size") return pmh_attr_type_font_size_pt;
  243. else IF_ATTR_NAME("font-family") return pmh_attr_type_font_family;
  244. else IF_ATTR_NAME("font-style") return pmh_attr_type_font_style;
  245. return pmh_attr_type_other;
  246. }
  247. char *pmh_attr_name_from_type(pmh_attr_type type)
  248. {
  249. switch (type)
  250. {
  251. case pmh_attr_type_foreground_color:
  252. return "foreground-color"; break;
  253. case pmh_attr_type_background_color:
  254. return "background-color"; break;
  255. case pmh_attr_type_caret_color:
  256. return "caret-color"; break;
  257. case pmh_attr_type_strike_color:
  258. return "strike-color"; break;
  259. case pmh_attr_type_font_size_pt:
  260. return "font-size"; break;
  261. case pmh_attr_type_font_family:
  262. return "font-family"; break;
  263. case pmh_attr_type_font_style:
  264. return "font-style"; break;
  265. default:
  266. return "unknown";
  267. }
  268. }
  269. typedef struct multi_value
  270. {
  271. char *value;
  272. size_t length;
  273. int line_number;
  274. struct multi_value *next;
  275. } multi_value;
  276. static multi_value *split_multi_value(char *input, char separator)
  277. {
  278. multi_value *head = NULL;
  279. multi_value *tail = NULL;
  280. char *c = input;
  281. while (*c != '\0')
  282. {
  283. size_t i;
  284. for (i = 0; (*(c+i) != '\0' && *(c+i) != separator); i++);
  285. multi_value *mv = (multi_value *)malloc(sizeof(multi_value));
  286. mv->value = (char *)malloc(sizeof(char)*i + 1);
  287. mv->length = i;
  288. mv->line_number = 0;
  289. mv->next = NULL;
  290. *mv->value = '\0';
  291. strncat(mv->value, c, i);
  292. if (head == NULL) {
  293. head = mv;
  294. tail = mv;
  295. } else {
  296. tail->next = mv;
  297. tail = mv;
  298. }
  299. if (*(c+i) == separator)
  300. i++;
  301. c += i;
  302. }
  303. return head;
  304. }
  305. static void free_multi_value(multi_value *val)
  306. {
  307. multi_value *cur = val;
  308. while (cur != NULL)
  309. {
  310. multi_value *pvalue = cur;
  311. multi_value *next_cur = cur->next;
  312. free(pvalue->value);
  313. free(pvalue);
  314. cur = next_cur;
  315. }
  316. }
  317. #define EQUALS(a,b) (strcmp(a, b) == 0)
  318. static pmh_style_attribute *interpret_attributes(style_parser_data *p_data,
  319. pmh_element_type lang_element_type,
  320. raw_attribute *raw_attributes)
  321. {
  322. pmh_style_attribute *attrs = NULL;
  323. raw_attribute *cur = raw_attributes;
  324. while (cur != NULL)
  325. {
  326. pmh_attr_type atype = pmh_attr_type_from_name(cur->name);
  327. pmh_style_attribute *attr = new_attr(cur->name, atype);
  328. attr->lang_element_type = lang_element_type;
  329. attr->value = new_attr_value();
  330. if (atype == pmh_attr_type_foreground_color
  331. || atype == pmh_attr_type_background_color
  332. || atype == pmh_attr_type_caret_color
  333. || atype == pmh_attr_type_strike_color)
  334. {
  335. char *hexstr = trim_str(cur->value);
  336. // new_argb_from_hex_str() reports conversion errors
  337. attr->value->argb_color =
  338. new_argb_from_hex_str(p_data, cur->line_number, hexstr);
  339. if (attr->value->argb_color == NULL) {
  340. free_style_attributes(attr);
  341. attr = NULL;
  342. }
  343. }
  344. else if (atype == pmh_attr_type_font_size_pt)
  345. {
  346. pmh_attr_font_size *fs = new_font_size();
  347. attr->value->font_size = fs;
  348. char *trimmed_value = trim_str_dup(cur->value);
  349. fs->is_relative = (*trimmed_value == '+' || *trimmed_value == '-');
  350. char *endptr = NULL;
  351. fs->size_pt = (int)strtol(cur->value, &endptr, 10);
  352. if (endptr == cur->value) {
  353. report_error(p_data, cur->line_number,
  354. "Value '%s' is invalid for attribute '%s'",
  355. cur->value, cur->name);
  356. free_style_attributes(attr);
  357. attr = NULL;
  358. }
  359. free(trimmed_value);
  360. }
  361. else if (atype == pmh_attr_type_font_family)
  362. {
  363. attr->value->font_family = trim_str_dup(cur->value);
  364. }
  365. else if (atype == pmh_attr_type_font_style)
  366. {
  367. attr->value->font_styles = new_font_styles();
  368. multi_value *values = split_multi_value(cur->value, ',');
  369. multi_value *value_cur = values;
  370. while (value_cur != NULL)
  371. {
  372. char *standardized_value = standardize_str(value_cur->value);
  373. if (EQUALS(standardized_value, "italic"))
  374. attr->value->font_styles->italic = true;
  375. else if (EQUALS(standardized_value, "bold"))
  376. attr->value->font_styles->bold = true;
  377. else if (EQUALS(standardized_value, "underlined"))
  378. attr->value->font_styles->underlined = true;
  379. else if (EQUALS(standardized_value, "strikeout"))
  380. attr->value->font_styles->strikeout = true;
  381. else {
  382. report_error(p_data, cur->line_number,
  383. "Value '%s' is invalid for attribute '%s'",
  384. standardized_value, cur->name);
  385. }
  386. free(standardized_value);
  387. value_cur = value_cur->next;
  388. }
  389. free_multi_value(values);
  390. }
  391. else if (atype == pmh_attr_type_other)
  392. {
  393. attr->value->string = trim_str_dup(cur->value);
  394. }
  395. if (attr != NULL) {
  396. // add to linked list
  397. attr->next = attrs;
  398. attrs = attr;
  399. }
  400. cur = cur->next;
  401. }
  402. return attrs;
  403. }
  404. static void interpret_and_add_style(style_parser_data *p_data,
  405. char *style_rule_name,
  406. int style_rule_line_number,
  407. raw_attribute *raw_attributes)
  408. {
  409. bool isEditorType = false;
  410. bool isCurrentLineType = false;
  411. bool isSelectionType = false;
  412. pmh_element_type type = pmh_element_type_from_name(style_rule_name);
  413. if (type == pmh_NO_TYPE)
  414. {
  415. if (EQUALS(style_rule_name, "editor"))
  416. isEditorType = true, type = pmh_NO_TYPE;
  417. else if (EQUALS(style_rule_name, "editor-current-line"))
  418. isCurrentLineType = true, type = pmh_NO_TYPE;
  419. else if (EQUALS(style_rule_name, "editor-selection"))
  420. isSelectionType = true, type = pmh_NO_TYPE;
  421. else {
  422. report_error(p_data, style_rule_line_number,
  423. "Style rule '%s' is not a language element type name or "
  424. "one of the following: 'editor', 'editor-current-line', "
  425. "'editor-selection'",
  426. style_rule_name);
  427. return;
  428. }
  429. }
  430. pmh_style_attribute *attrs = interpret_attributes(p_data, type, raw_attributes);
  431. if (isEditorType)
  432. p_data->styles->editor_styles = attrs;
  433. else if (isCurrentLineType)
  434. p_data->styles->editor_current_line_styles = attrs;
  435. else if (isSelectionType)
  436. p_data->styles->editor_selection_styles = attrs;
  437. else
  438. p_data->styles->element_styles[(p_data->styles_pos)++] = attrs;
  439. }
  440. static bool char_is_whitespace(char c)
  441. {
  442. return (c == ' ' || c == '\t');
  443. }
  444. static bool char_begins_linecomment(char c)
  445. {
  446. return (c == '#');
  447. }
  448. static bool line_is_comment(multi_value *line)
  449. {
  450. char *c;
  451. for (c = line->value; *c != '\0'; c++)
  452. {
  453. if (!char_is_whitespace(*c))
  454. return char_begins_linecomment(*c);
  455. }
  456. return false;
  457. }
  458. static bool line_is_empty(multi_value *line)
  459. {
  460. char *c;
  461. for (c = line->value; *c != '\0'; c++)
  462. {
  463. if (!char_is_whitespace(*c))
  464. return false;
  465. }
  466. return true;
  467. }
  468. typedef struct block
  469. {
  470. multi_value *lines;
  471. struct block *next;
  472. } block;
  473. static block *new_block()
  474. {
  475. block *ret = (block *)malloc(sizeof(block));
  476. ret->next = NULL;
  477. ret->lines = NULL;
  478. return ret;
  479. }
  480. static void free_blocks(block *val)
  481. {
  482. block *cur = val;
  483. while (cur != NULL)
  484. {
  485. block *pblock = cur;
  486. block *next = pblock->next;
  487. free_multi_value(pblock->lines);
  488. free(pblock);
  489. cur = next;
  490. }
  491. }
  492. static block *get_blocks(char *input)
  493. {
  494. block *head = NULL;
  495. block *tail = NULL;
  496. block *current_block = NULL;
  497. multi_value *discarded_lines = NULL;
  498. int line_number_counter = 1;
  499. multi_value *lines = split_multi_value(input, '\n');
  500. multi_value *previous_line = NULL;
  501. multi_value *line_cur = lines;
  502. while (line_cur != NULL)
  503. {
  504. bool discard_line = false;
  505. line_cur->line_number = line_number_counter++;
  506. if (line_is_empty(line_cur))
  507. {
  508. discard_line = true;
  509. if (current_block != NULL)
  510. {
  511. // terminate block
  512. if (tail != current_block)
  513. tail->next = current_block;
  514. tail = current_block;
  515. current_block = NULL;
  516. previous_line->next = NULL;
  517. }
  518. }
  519. else if (line_is_comment(line_cur))
  520. {
  521. // Do not discard (i.e. free()) comment lines within blocks:
  522. if (current_block == NULL)
  523. discard_line = true;
  524. }
  525. else
  526. {
  527. if (current_block == NULL)
  528. {
  529. // start block
  530. current_block = new_block();
  531. current_block->lines = line_cur;
  532. if (previous_line != NULL)
  533. previous_line->next = NULL;
  534. }
  535. if (head == NULL) {
  536. head = current_block;
  537. tail = current_block;
  538. }
  539. }
  540. multi_value *next_cur = line_cur->next;
  541. previous_line = (discard_line) ? NULL : line_cur;
  542. if (discard_line) {
  543. line_cur->next = discarded_lines;
  544. discarded_lines = line_cur;
  545. }
  546. line_cur = next_cur;
  547. }
  548. if (current_block != NULL && tail != current_block)
  549. tail->next = current_block;
  550. free_multi_value(discarded_lines);
  551. return head;
  552. }
  553. #define ASSIGNMENT_OP_UITEXT "':' or '='"
  554. #define IS_ASSIGNMENT_OP(c) ((c) == ':' || (c) == '=')
  555. #define IS_STYLE_RULE_NAME_CHAR(c) \
  556. ( (c) != '\0' && !isspace(c) \
  557. && !char_begins_linecomment(c) && !IS_ASSIGNMENT_OP(c) )
  558. #define IS_ATTRIBUTE_NAME_CHAR(c) \
  559. ( (c) != '\0' && !char_begins_linecomment(c) && !IS_ASSIGNMENT_OP(c) )
  560. #define IS_ATTRIBUTE_VALUE_CHAR(c) \
  561. ( (c) != '\0' && !char_begins_linecomment(c) )
  562. static char *get_style_rule_name(multi_value *line)
  563. {
  564. char *str = line->value;
  565. // Scan past leading whitespace:
  566. size_t start_index;
  567. for (start_index = 0;
  568. (*(str+start_index) != '\0' && isspace(*(str+start_index)));
  569. start_index++);
  570. // Scan until style rule name characters end:
  571. size_t value_end_index;
  572. for (value_end_index = start_index;
  573. IS_STYLE_RULE_NAME_CHAR(*(str + value_end_index));
  574. value_end_index++);
  575. // Copy style rule name:
  576. size_t value_len = value_end_index - start_index;
  577. char *value = (char *)malloc(sizeof(char)*value_len + 1);
  578. *value = '\0';
  579. strncat(value, (str + start_index), value_len);
  580. return value;
  581. }
  582. static bool parse_attribute_line(style_parser_data *p_data, multi_value *line,
  583. char **out_attr_name, char **out_attr_value)
  584. {
  585. char *str = line->value;
  586. // Scan past leading whitespace:
  587. size_t name_start_index;
  588. for (name_start_index = 0;
  589. ( *(str+name_start_index) != '\0' &&
  590. isspace(*(str+name_start_index)) );
  591. name_start_index++);
  592. // Scan until attribute name characters end:
  593. size_t name_end_index;
  594. for (name_end_index = name_start_index;
  595. IS_ATTRIBUTE_NAME_CHAR(*(str + name_end_index));
  596. name_end_index++);
  597. // Scan backwards to trim trailing whitespace off:
  598. while (name_start_index < name_end_index
  599. && isspace(*(str + name_end_index - 1)))
  600. name_end_index--;
  601. // Scan until just after the first assignment operator:
  602. size_t assignment_end_index;
  603. for (assignment_end_index = name_end_index;
  604. ( *(str + assignment_end_index) != '\0' &&
  605. !IS_ASSIGNMENT_OP(*(str + assignment_end_index)) );
  606. assignment_end_index++);
  607. // Scan over the found assignment operator, or report error:
  608. if (IS_ASSIGNMENT_OP(*(str + assignment_end_index)))
  609. assignment_end_index++;
  610. else
  611. {
  612. report_error(p_data, line->line_number,
  613. "Invalid attribute definition: str does not contain "
  614. "an assignment operator (%s): '%s'",
  615. ASSIGNMENT_OP_UITEXT, str);
  616. return false;
  617. }
  618. size_t value_start_index = assignment_end_index;
  619. // Scan until attribute value characters end:
  620. size_t value_end_index;
  621. for (value_end_index = value_start_index;
  622. IS_ATTRIBUTE_VALUE_CHAR(*(str + value_end_index));
  623. value_end_index++);
  624. // Copy attribute name:
  625. size_t name_len = name_end_index - name_start_index;
  626. char *attr_name = (char *)malloc(sizeof(char)*name_len + 1);
  627. *attr_name = '\0';
  628. strncat(attr_name, (str + name_start_index), name_len);
  629. *out_attr_name = attr_name;
  630. // Copy attribute value:
  631. size_t attr_value_len = value_end_index - assignment_end_index;
  632. char *attr_value_str = (char *)malloc(sizeof(char)*attr_value_len + 1);
  633. *attr_value_str = '\0';
  634. strncat(attr_value_str, (str + assignment_end_index), attr_value_len);
  635. *out_attr_value = attr_value_str;
  636. return true;
  637. }
  638. #define HAS_UTF8_BOM(x) ( ((*x & 0xFF) == 0xEF)\
  639. && ((*(x+1) & 0xFF) == 0xBB)\
  640. && ((*(x+2) & 0xFF) == 0xBF) )
  641. // - Removes UTF-8 BOM
  642. // - Standardizes line endings to \n
  643. static char *strcpy_preformat_style(char *str)
  644. {
  645. char *new_str = (char *)malloc(sizeof(char) * strlen(str) + 1);
  646. char *c = str;
  647. int i = 0;
  648. if (HAS_UTF8_BOM(c))
  649. c += 3;
  650. while (*c != '\0')
  651. {
  652. if (*c == '\r' && *(c+1) == '\n')
  653. {
  654. *(new_str+i) = '\n';
  655. i++;
  656. c += 2;
  657. }
  658. else if (*c == '\r')
  659. {
  660. *(new_str+i) = '\n';
  661. i++;
  662. c++;
  663. }
  664. else
  665. {
  666. *(new_str+i) = *c;
  667. i++;
  668. c++;
  669. }
  670. }
  671. *(new_str+i) = '\0';
  672. return new_str;
  673. }
  674. static void _sty_parse(style_parser_data *p_data)
  675. {
  676. // We don't have to worry about leaking the original p_data->input;
  677. // the user of the library is responsible for that:
  678. p_data->input = strcpy_preformat_style(p_data->input);
  679. block *blocks = get_blocks(p_data->input);
  680. block *block_cur = blocks;
  681. while (block_cur != NULL)
  682. {
  683. raw_attribute *attributes_head = NULL;
  684. raw_attribute *attributes_tail = NULL;
  685. pmhsp_PRINTF("Block:\n");
  686. multi_value *header_line = block_cur->lines;
  687. if (header_line == NULL) {
  688. block_cur = block_cur->next;
  689. continue;
  690. }
  691. pmhsp_PRINTF(" Head line (len %ld): '%s'\n",
  692. header_line->length, header_line->value);
  693. char *style_rule_name = get_style_rule_name(header_line);
  694. pmhsp_PRINTF(" Style rule name: '%s'\n", style_rule_name);
  695. multi_value *attr_line_cur = header_line->next;
  696. if (attr_line_cur == NULL)
  697. report_error(p_data, header_line->line_number,
  698. "No style attributes defined for style rule '%s'",
  699. style_rule_name);
  700. while (attr_line_cur != NULL)
  701. {
  702. if (line_is_comment(attr_line_cur))
  703. {
  704. attr_line_cur = attr_line_cur->next;
  705. continue;
  706. }
  707. pmhsp_PRINTF(" Attr line (len %ld): '%s'\n",
  708. attr_line_cur->length, attr_line_cur->value);
  709. char *attr_name_str;
  710. char *attr_value_str;
  711. bool success = parse_attribute_line(p_data,
  712. attr_line_cur,
  713. &attr_name_str,
  714. &attr_value_str);
  715. if (success)
  716. {
  717. pmhsp_PRINTF(" Attr: '%s' Value: '%s'\n",
  718. attr_name_str, attr_value_str);
  719. raw_attribute *attribute =
  720. new_raw_attribute(attr_name_str, attr_value_str,
  721. attr_line_cur->line_number);
  722. if (attributes_head == NULL) {
  723. attributes_head = attribute;
  724. attributes_tail = attribute;
  725. } else {
  726. attributes_tail->next = attribute;
  727. attributes_tail = attribute;
  728. }
  729. }
  730. attr_line_cur = attr_line_cur->next;
  731. }
  732. if (attributes_head != NULL)
  733. {
  734. interpret_and_add_style(p_data, style_rule_name,
  735. header_line->line_number, attributes_head);
  736. free_raw_attributes(attributes_head);
  737. }
  738. free(style_rule_name);
  739. block_cur = block_cur->next;
  740. }
  741. free_blocks(blocks);
  742. free(p_data->input);
  743. }
  744. static pmh_style_collection *new_style_collection()
  745. {
  746. pmh_style_collection *sc = (pmh_style_collection *)
  747. malloc(sizeof(pmh_style_collection));
  748. sc->element_styles = (pmh_style_attribute**)
  749. malloc(sizeof(pmh_style_attribute*)
  750. * pmh_NUM_LANG_TYPES);
  751. int i;
  752. for (i = 0; i < pmh_NUM_LANG_TYPES; i++)
  753. sc->element_styles[i] = NULL;
  754. sc->editor_styles = NULL;
  755. sc->editor_current_line_styles = NULL;
  756. sc->editor_selection_styles = NULL;
  757. return sc;
  758. }
  759. void pmh_free_style_collection(pmh_style_collection *coll)
  760. {
  761. free_style_attributes(coll->editor_styles);
  762. free_style_attributes(coll->editor_current_line_styles);
  763. free_style_attributes(coll->editor_selection_styles);
  764. int i;
  765. for (i = 0; i < pmh_NUM_LANG_TYPES; i++)
  766. free_style_attributes(coll->element_styles[i]);
  767. free(coll->element_styles);
  768. free(coll);
  769. }
  770. static style_parser_data *new_style_parser_data(char *input)
  771. {
  772. style_parser_data *p_data = (style_parser_data*)
  773. malloc(sizeof(style_parser_data));
  774. p_data->input = input;
  775. p_data->styles_pos = 0;
  776. p_data->styles = new_style_collection();
  777. return p_data;
  778. }
  779. pmh_style_collection *pmh_parse_styles(char *input,
  780. void(*error_callback)(char*,int,void*),
  781. void *error_callback_context)
  782. {
  783. style_parser_data *p_data = new_style_parser_data(input);
  784. p_data->error_callback = error_callback;
  785. p_data->error_callback_context = error_callback_context;
  786. _sty_parse(p_data);
  787. pmh_style_collection *ret = p_data->styles;
  788. free(p_data);
  789. return ret;
  790. }