xs_html.h 4.8 KB

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