xs_html.h 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /* copyright (c) 2022 - 2023 grunfink et al. / MIT license */
  2. #ifndef _XS_HTML_H
  3. #define _XS_HTML_H
  4. typedef struct xs_html xs_html;
  5. xs_str *xs_html_encode(char *str);
  6. xs_html *xs_html_attr(char *key, char *value);
  7. xs_html *xs_html_text(char *content);
  8. xs_html *xs_html_raw(char *content);
  9. xs_html *_xs_html_add(xs_html *tag, xs_html *var[]);
  10. #define xs_html_add(tag, ...) _xs_html_add(tag, (xs_html *[]) { __VA_ARGS__, NULL })
  11. xs_html *_xs_html_tag(char *tag, xs_html *var[]);
  12. #define xs_html_tag(tag, ...) _xs_html_tag(tag, (xs_html *[]) { __VA_ARGS__, NULL })
  13. xs_html *_xs_html_sctag(char *tag, xs_html *var[]);
  14. #define xs_html_sctag(tag, ...) _xs_html_sctag(tag, (xs_html *[]) { __VA_ARGS__, NULL })
  15. xs_str *xs_html_render_s(xs_html *h, xs_str *s);
  16. #define xs_html_render(h) xs_html_render_s(h, xs_str_new(NULL))
  17. #ifdef XS_IMPLEMENTATION
  18. typedef enum {
  19. XS_HTML_TAG,
  20. XS_HTML_SCTAG,
  21. XS_HTML_ATTR,
  22. XS_HTML_TEXT
  23. } xs_html_type;
  24. struct xs_html {
  25. xs_html_type type;
  26. xs_str *content;
  27. xs_html *f_attr;
  28. xs_html *l_attr;
  29. xs_html *f_tag;
  30. xs_html *l_tag;
  31. xs_html *next;
  32. };
  33. xs_str *xs_html_encode(char *str)
  34. /* encodes str using HTML entities */
  35. {
  36. xs_str *s = xs_str_new(NULL);
  37. int o = 0;
  38. char *e = str + strlen(str);
  39. for (;;) {
  40. char *ec = "<>\"'&"; /* characters to escape */
  41. char *q = e;
  42. int z;
  43. /* find the nearest happening of a char */
  44. while (*ec) {
  45. char *m = memchr(str, *ec++, q - str);
  46. if (m)
  47. q = m;
  48. }
  49. /* copy string to here */
  50. z = q - str;
  51. s = xs_insert_m(s, o, str, z);
  52. o += z;
  53. /* if q points to the end, nothing more to do */
  54. if (q == e)
  55. break;
  56. /* insert the escaped char */
  57. char tmp[8];
  58. snprintf(tmp, sizeof(tmp), "&#%d;", *q);
  59. z = strlen(tmp);
  60. s = xs_insert_m(s, o, tmp, z);
  61. o += z;
  62. str = q + 1;
  63. }
  64. return s;
  65. }
  66. #define XS_HTML_NEW() memset(xs_realloc(NULL, sizeof(xs_html)), '\0', sizeof(xs_html))
  67. xs_html *xs_html_attr(char *key, char *value)
  68. /* creates an HTML block with an attribute */
  69. {
  70. xs_html *a = XS_HTML_NEW();
  71. a->type = XS_HTML_ATTR;
  72. if (value) {
  73. xs *ev = xs_html_encode(value);
  74. a->content = xs_fmt("%s=\"%s\"", key, ev);
  75. }
  76. else
  77. a->content = xs_dup(key);
  78. return a;
  79. }
  80. xs_html *xs_html_text(char *content)
  81. /* creates an HTML block of text, escaping it previously */
  82. {
  83. xs_html *a = XS_HTML_NEW();
  84. a->type = XS_HTML_TEXT;
  85. a->content = xs_html_encode(content);
  86. return a;
  87. }
  88. xs_html *xs_html_raw(char *content)
  89. /* creates an HTML block without escaping (for pre-formatted HTML, comments, etc) */
  90. {
  91. xs_html *a = XS_HTML_NEW();
  92. a->type = XS_HTML_TEXT;
  93. a->content = xs_dup(content);
  94. return a;
  95. }
  96. xs_html *_xs_html_add(xs_html *tag, xs_html *var[])
  97. /* add data (attrs, tags or text) to a tag */
  98. {
  99. while (*var) {
  100. xs_html *data = *var++;
  101. xs_html **first;
  102. xs_html **last;
  103. if (data->type == XS_HTML_ATTR) {
  104. first = &tag->f_attr;
  105. last = &tag->l_attr;
  106. }
  107. else {
  108. first = &tag->f_tag;
  109. last = &tag->l_tag;
  110. }
  111. if (*first == NULL)
  112. *first = data;
  113. if (*last != NULL)
  114. (*last)->next = data;
  115. *last = data;
  116. }
  117. return tag;
  118. }
  119. static xs_html *_xs_html_tag_t(xs_html_type type, char *tag, xs_html *var[])
  120. /* creates a tag with a variable list of attributes and subtags */
  121. {
  122. xs_html *a = XS_HTML_NEW();
  123. a->type = type;
  124. a->content = xs_dup(tag);
  125. _xs_html_add(a, var);
  126. return a;
  127. }
  128. xs_html *_xs_html_tag(char *tag, xs_html *var[])
  129. {
  130. return _xs_html_tag_t(XS_HTML_TAG, tag, var);
  131. }
  132. xs_html *_xs_html_sctag(char *tag, xs_html *var[])
  133. {
  134. return _xs_html_tag_t(XS_HTML_SCTAG, tag, var);
  135. }
  136. xs_str *xs_html_render_s(xs_html *h, xs_str *s)
  137. /* renders the tag and its subtags into s */
  138. {
  139. xs_html *st;
  140. switch (h->type) {
  141. case XS_HTML_TAG:
  142. case XS_HTML_SCTAG:
  143. s = xs_str_cat(s, "<", h->content);
  144. /* render the attributes */
  145. st = h->f_attr;
  146. while (st) {
  147. xs_html *nst = st->next;
  148. s = xs_html_render_s(st, s);
  149. st = nst;
  150. }
  151. if (h->type == XS_HTML_SCTAG) {
  152. /* self-closing tags should not have subtags */
  153. s = xs_str_cat(s, "/>\n");
  154. }
  155. else {
  156. s = xs_str_cat(s, ">");
  157. /* render the subtags */
  158. st = h->f_tag;
  159. while (st) {
  160. xs_html *nst = st->next;
  161. s = xs_html_render_s(st, s);
  162. st = nst;
  163. }
  164. s = xs_str_cat(s, "</", h->content, ">");
  165. }
  166. break;
  167. case XS_HTML_ATTR:
  168. s = xs_str_cat(s, " ", h->content);
  169. break;
  170. case XS_HTML_TEXT:
  171. s = xs_str_cat(s, h->content);
  172. break;
  173. }
  174. xs_free(h->content);
  175. xs_free(h);
  176. return s;
  177. }
  178. #endif /* XS_IMPLEMENTATION */
  179. #endif /* _XS_HTML_H */