pmh_styleparser.c 27 KB

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