Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package lowdown for openSUSE:Factory checked 
in at 2024-12-08 11:37:16
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/lowdown (Old)
 and      /work/SRC/openSUSE:Factory/.lowdown.new.21547 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "lowdown"

Sun Dec  8 11:37:16 2024 rev:4 rq:1228812 version:1.3.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/lowdown/lowdown.changes  2024-12-02 
16:59:34.358304519 +0100
+++ /work/SRC/openSUSE:Factory/.lowdown.new.21547/lowdown.changes       
2024-12-08 11:38:18.356551662 +0100
@@ -1,0 +2,14 @@
+Fri Dec 06 15:58:50 UTC 2024 - scott.bradn...@suse.com
+
+- Update to version 1.3.1:
+  * Version bump.
+  * Internal template documentation.
+  * Add --template functionality to -tlatex.
+  * Only use --template along with -s.
+  * Update docs.
+  * Add regressions and fix corner case.
+  * Add boolean operators and() and or().
+  * Add latex escaping to template.
+  * Rename hesc and move latex escape.
+
+-------------------------------------------------------------------

Old:
----
  lowdown-1.3.0.tar.gz

New:
----
  lowdown-1.3.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ lowdown.spec ++++++
--- /var/tmp/diff_new_pack.HBy9N9/_old  2024-12-08 11:38:18.780569278 +0100
+++ /var/tmp/diff_new_pack.HBy9N9/_new  2024-12-08 11:38:18.780569278 +0100
@@ -20,7 +20,7 @@
 #%%global version_string VERSION_1_1_0
 
 Name:           lowdown
-Version:        1.3.0
+Version:        1.3.1
 Release:        0
 Summary:        Simple markdown translator
 License:        ISC

++++++ _service ++++++
--- /var/tmp/diff_new_pack.HBy9N9/_old  2024-12-08 11:38:18.808570441 +0100
+++ /var/tmp/diff_new_pack.HBy9N9/_new  2024-12-08 11:38:18.812570607 +0100
@@ -2,7 +2,7 @@
   <service name="obs_scm" mode="manual">
     <param name="url">https://github.com/kristapsdz/lowdown</param>
     <param name="scm">git</param>
-    <param name="revision">VERSION_1_3_0</param>
+    <param name="revision">VERSION_1_3_1</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <!-- If we ever have to package a snapshot, use this format pattern:
     <param name="versionformat">@PARENT_TAG@+git@TAG_OFFSET@.%h</param>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.HBy9N9/_old  2024-12-08 11:38:18.840571770 +0100
+++ /var/tmp/diff_new_pack.HBy9N9/_new  2024-12-08 11:38:18.840571770 +0100
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param name="url">https://github.com/kristapsdz/lowdown</param>
-              <param 
name="changesrevision">7f0c33949a20c7fbdad3a8f479ca28c17b5f7b25</param></service></servicedata>
+              <param 
name="changesrevision">6c42fe9655f5db3a0d7eb1ccaa3c1830f211481a</param></service></servicedata>
 (No newline at EOF)
 

++++++ lowdown-1.3.0.tar.gz -> lowdown-1.3.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/Makefile new/lowdown-1.3.1/Makefile
--- old/lowdown-1.3.0/Makefile  2024-12-01 08:26:15.000000000 +0100
+++ new/lowdown-1.3.1/Makefile  2024-12-06 04:08:10.000000000 +0100
@@ -10,7 +10,7 @@
 # while libraries have well-defined semantics of semver change, programs
 # do not.  Let the library guide our versioning until a better way is
 # thought out.
-VERSION                 = 1.3.0
+VERSION                 = 1.3.1
 # This is the major number of VERSION.  It might later become
 # MAJOR.MINOR, if the library moves a lot.
 LIBVER          = 1
@@ -23,6 +23,7 @@
                   html.o \
                   html_escape.o \
                   latex.o \
+                  latex_escape.o \
                   library.o \
                   libdiff.o \
                   nroff.o \
@@ -86,6 +87,7 @@
                   html.c \
                   html_escape.c \
                   latex.c \
+                  latex_escape.c \
                   libdiff.c \
                   library.c \
                   main.c \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/extern.h new/lowdown-1.3.1/extern.h
--- old/lowdown-1.3.0/extern.h  2024-12-01 08:26:15.000000000 +0100
+++ new/lowdown-1.3.1/extern.h  2024-12-06 04:08:10.000000000 +0100
@@ -70,9 +70,14 @@
 ssize_t                 halink_url(size_t *, struct lowdown_buf *, char *, 
size_t, size_t);
 ssize_t                 halink_www(size_t *, struct lowdown_buf *, char *, 
size_t, size_t);
 
-int             hesc_attr(struct lowdown_buf *, const char *, size_t);
-int             hesc_href(struct lowdown_buf *, const char *, size_t);
-int             hesc_html(struct lowdown_buf *, const char *, size_t, int, 
int, int);
+int             lowdown_html_esc(struct lowdown_buf *,
+                       const char *, size_t, int, int, int);
+int             lowdown_html_esc_attr(struct lowdown_buf *,
+                       const char *, size_t);
+int             lowdown_html_esc_href(struct lowdown_buf *,
+                       const char *, size_t);
+int             lowdown_latex_esc(struct lowdown_buf *,
+                       const char *, size_t);
 
 char           *rcsdate2str(const char *);
 char           *rcsauthor2str(const char *);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/html.c new/lowdown-1.3.1/html.c
--- old/lowdown-1.3.0/html.c    2024-12-01 08:26:15.000000000 +0100
+++ new/lowdown-1.3.1/html.c    2024-12-06 04:08:10.000000000 +0100
@@ -55,7 +55,7 @@
     const struct html *st)
 {
 
-       return hesc_html(ob, source, length, 
+       return lowdown_html_esc(ob, source, length,
                st->flags & LOWDOWN_HTML_OWASP, 0,
                st->flags & LOWDOWN_HTML_NUM_ENT);
 }
@@ -64,7 +64,7 @@
  * See escape_html().
  */
 static int
-escape_htmlb(struct lowdown_buf *ob, 
+escape_htmlb(struct lowdown_buf *ob,
     const struct lowdown_buf *in, const struct html *st)
 {
 
@@ -77,11 +77,11 @@
  * Return zero on failure, non-zero on success.
  */
 static int
-escape_literal(struct lowdown_buf *ob, 
+escape_literal(struct lowdown_buf *ob,
     const struct lowdown_buf *in, const struct html *st)
 {
 
-       return hesc_html(ob, in->data, in->size, 
+       return lowdown_html_esc(ob, in->data, in->size,
                st->flags & LOWDOWN_HTML_OWASP, 1,
                st->flags & LOWDOWN_HTML_NUM_ENT);
 }
@@ -95,7 +95,7 @@
     const struct html *st)
 {
 
-       return hesc_href(ob, in->data, in->size);
+       return lowdown_html_esc_href(ob, in->data, in->size);
 }
 
 /*
@@ -106,7 +106,7 @@
 escape_attr(struct lowdown_buf *ob, const struct lowdown_buf *in)
 {
 
-       return hesc_attr(ob, in->data, in->size);
+       return lowdown_html_esc_attr(ob, in->data, in->size);
 }
 
 static int
@@ -119,7 +119,7 @@
 }
 
 static int
-rndr_autolink(struct lowdown_buf *ob, 
+rndr_autolink(struct lowdown_buf *ob,
     const struct rndr_autolink *parm, const struct html *st)
 {
 
@@ -142,8 +142,8 @@
         */
 
        if (hbuf_strprefix(&parm->link, "mailto:";)) {
-               if (!escape_html(ob, 
-                   parm->link.data + 7, 
+               if (!escape_html(ob,
+                   parm->link.data + 7,
                    parm->link.size - 7, st))
                        return 0;
        } else {
@@ -155,7 +155,7 @@
 }
 
 static int
-rndr_blockcode(struct lowdown_buf *ob, 
+rndr_blockcode(struct lowdown_buf *ob,
     const struct rndr_blockcode *parm, const struct html *st)
 {
        if (!newline(ob))
@@ -230,7 +230,7 @@
 
        if (!newline(ob))
                return 0;
-       if (param->type == BLOCKQUOTE_REGULAR || !(st->flags & 
+       if (param->type == BLOCKQUOTE_REGULAR || !(st->flags &
            (LOWDOWN_HTML_CALLOUT_GFM|LOWDOWN_HTML_CALLOUT_MDN)))
                return HBUF_PUTSL(ob, "<blockquote>\n") &&
                    hbuf_putb(ob, content) &&
@@ -454,7 +454,7 @@
                return 0;
        if (param->flags & HLIST_FL_ORDERED) {
                if (param->start > 1) {
-                       if (!hbuf_printf(ob, 
+                       if (!hbuf_printf(ob,
                            "<ol start=\"%zu\">\n", param->start))
                                return 0;
                } else {
@@ -489,7 +489,7 @@
             n->parent != NULL &&
             n->parent->parent != NULL &&
             n->parent->parent->type == LOWDOWN_DEFINITION &&
-            (n->parent->parent->rndr_definition.flags & 
+            (n->parent->parent->rndr_definition.flags &
              HLIST_FL_BLOCK)) ||
            (!(n->rndr_listitem.flags & HLIST_FL_DEF) &&
             n->parent != NULL &&
@@ -552,7 +552,7 @@
        if (content->size == 0)
                return 1;
        while (i < content->size &&
-              isspace((unsigned char)content->data[i])) 
+              isspace((unsigned char)content->data[i]))
                i++;
        if (i == content->size)
                return 1;
@@ -565,7 +565,7 @@
        if (st->flags & LOWDOWN_HTML_HARD_WRAP) {
                for ( ; i < content->size; i++) {
                        org = i;
-                       while (i < content->size && 
+                       while (i < content->size &&
                               content->data[i] != '\n')
                                i++;
 
@@ -584,7 +584,7 @@
                                return 0;
                }
        } else {
-               if (!hbuf_put(ob, 
+               if (!hbuf_put(ob,
                    content->data + i, content->size - i))
                        return 0;
        }
@@ -603,9 +603,9 @@
        if ((st->flags & LOWDOWN_HTML_ESCAPE))
                return escape_htmlb(ob, &param->text, st);
 
-       /* 
+       /*
         * FIXME: Do we *really* need to trim the HTML? How does that
-        * make a difference? 
+        * make a difference?
         */
 
        sz = param->text.size;
@@ -662,7 +662,7 @@
         * that as a cap to the size.
         */
 
-       if (param->dims.size && 
+       if (param->dims.size &&
            param->dims.size < sizeof(dimbuf) - 1) {
                memset(dimbuf, 0, sizeof(dimbuf));
                memcpy(dimbuf, param->dims.data, param->dims.size);
@@ -861,14 +861,14 @@
        /* Insert anchor at the end of first paragraph block. */
 
        while ((i + 3) < content->size) {
-               if (content->data[i++] != '<') 
+               if (content->data[i++] != '<')
                        continue;
-               if (content->data[i++] != '/') 
+               if (content->data[i++] != '/')
                        continue;
-               if (content->data[i++] != 'p' && 
-                   content->data[i] != 'P') 
+               if (content->data[i++] != 'p' &&
+                   content->data[i] != 'P')
                        continue;
-               if (content->data[i] != '>') 
+               if (content->data[i] != '>')
                        continue;
                i -= 3;
                pfound = 1;
@@ -886,7 +886,7 @@
                    "&#8617;"
                    "</a>", num))
                        return 0;
-               if (!hbuf_put(ob, 
+               if (!hbuf_put(ob,
                    content->data + i, content->size - i))
                        return 0;
        } else {
@@ -918,14 +918,14 @@
        if ((st->foots[st->footsz++] = hbuf_dup(content)) == NULL)
                return 0;
 
-       return hbuf_printf(ob, 
+       return hbuf_printf(ob,
                "<sup id=\"fnref%zu\">"
                "<a href=\"#fn%zu\" rel=\"footnote\">"
                "%zu</a></sup>", num, num, num);
 }
 
 static int
-rndr_math(struct lowdown_buf *ob, const struct rndr_math *param, 
+rndr_math(struct lowdown_buf *ob, const struct rndr_math *param,
     const struct html *st)
 {
 
@@ -1005,12 +1005,12 @@
 
                if (!hbuf_puts(ob, starttag))
                        return 0;
-               if (attr && !hesc_attr(ob, start, sz))
+               if (attr && !lowdown_html_esc_attr(ob, start, sz))
                        return 0;
-               else if (href && !hesc_href(ob, start, sz))
+               else if (href && !lowdown_html_esc_href(ob, start, sz))
                        return 0;
-               else if (!attr && !href && !hesc_html(ob, start, sz,
-                   st->flags & LOWDOWN_HTML_OWASP, 0,
+               else if (!attr && !href && !lowdown_html_esc(ob, start,
+                   sz, st->flags & LOWDOWN_HTML_OWASP, 0,
                    st->flags & LOWDOWN_HTML_NUM_ENT))
                        return 0;
                if (!hbuf_puts(ob, endtag))
@@ -1029,15 +1029,15 @@
        const struct lowdown_meta       *m;
        const char                      *author = NULL, *title = NULL,
                                        *affil = NULL, *date = NULL,
-                                       *copy = NULL, *rcsauthor = NULL, 
+                                       *copy = NULL, *rcsauthor = NULL,
                                        *rcsdate = NULL, *css = NULL,
                                        *script = NULL, *header = NULL,
                                        *lang = NULL;
 
-       if (st->templ != NULL) 
-               return lowdown_template(st->templ, content, ob, mq);
-       else if (!(st->flags & LOWDOWN_STANDALONE))
+       if (!(st->flags & LOWDOWN_STANDALONE))
                return hbuf_putb(ob, content);
+       if (st->templ != NULL)
+               return lowdown_template(st->templ, content, ob, mq);
 
        TAILQ_FOREACH(m, mq, entries)
                if (strcasecmp(m->key, "author") == 0)
@@ -1074,13 +1074,13 @@
                return 0;
        if (lang != NULL &&
            (!HBUF_PUTSL(ob, "<html lang=\"") ||
-            !hesc_attr(ob, lang, strlen(lang)) ||
+            !lowdown_html_esc_attr(ob, lang, strlen(lang)) ||
             !HBUF_PUTSL(ob, "\">\n")))
                return 0;
        else if (lang == NULL &&
            !HBUF_PUTSL(ob, "<html>\n"))
                return 0;
-       if (!HBUF_PUTSL(ob, 
+       if (!HBUF_PUTSL(ob,
            "<head>\n"
            "<meta charset=\"utf-8\" />\n"
            "<meta name=\"viewport\" content=\""
@@ -1117,7 +1117,7 @@
                        return 0;
                if (!HBUF_PUTSL(ob, "content=\""))
                        return 0;
-               if (!hesc_attr(ob, date, strlen(date)))
+               if (!lowdown_html_esc_attr(ob, date, strlen(date)))
                        return 0;
                if (!HBUF_PUTSL(ob, "\" />\n"))
                        return 0;
@@ -1134,7 +1134,7 @@
        if (!HBUF_PUTSL(ob, "<title>"))
                return 0;
        if (title != NULL &&
-           !hesc_html(ob, title, strlen(title),
+           !lowdown_html_esc(ob, title, strlen(title),
                    st->flags & LOWDOWN_HTML_OWASP, 0,
                    st->flags & LOWDOWN_HTML_NUM_ENT))
                return 0;
@@ -1164,7 +1164,7 @@
  * Return zero on failure, non-zero on success.
  */
 static int
-rndr_meta(struct html *st, const struct lowdown_node *n, 
+rndr_meta(struct html *st, const struct lowdown_node *n,
     struct lowdown_metaq *mq)
 {
        struct lowdown_meta     *m;
@@ -1197,7 +1197,7 @@
 {
        const struct lowdown_meta       *m;
        const char                      *author = NULL, *title = NULL,
-                                       *date = NULL, *rcsauthor = NULL, 
+                                       *date = NULL, *rcsauthor = NULL,
                                        *rcsdate = NULL;
 
        if (!(st->flags & LOWDOWN_HTML_TITLEBLOCK))
@@ -1231,7 +1231,7 @@
                return 0;
        if (title != NULL &&
            (!HBUF_PUTSL(ob, "<h1 class=\"title\">") ||
-            !hesc_html(ob, title, strlen(title), 
+            !lowdown_html_esc(ob, title, strlen(title),
                     st->flags & LOWDOWN_HTML_OWASP, 0,
                     st->flags & LOWDOWN_HTML_NUM_ENT) ||
             !HBUF_PUTSL(ob, "</h1>\n")))
@@ -1242,7 +1242,7 @@
                return 0;
        if (date != NULL &&
            (!HBUF_PUTSL(ob, "<p class=\"date\">") ||
-            !hesc_html(ob, date, strlen(date), 
+            !lowdown_html_esc(ob, date, strlen(date),
                     st->flags & LOWDOWN_HTML_OWASP, 0,
                     st->flags & LOWDOWN_HTML_NUM_ENT) ||
             !HBUF_PUTSL(ob, "</p>\n")))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/html_escape.c 
new/lowdown-1.3.1/html_escape.c
--- old/lowdown-1.3.0/html_escape.c     2024-12-01 08:26:15.000000000 +0100
+++ new/lowdown-1.3.1/html_escape.c     2024-12-06 04:08:10.000000000 +0100
@@ -22,7 +22,6 @@
 # include <sys/queue.h>
 #endif
 
-#include <assert.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
@@ -143,7 +142,8 @@
  * This is modelled after the main Markdown parser.
  */
 int
-hesc_attr(struct lowdown_buf *ob, const char *data, size_t size)
+lowdown_html_esc_attr(struct lowdown_buf *ob, const char *data,
+    size_t size)
 {
        size_t           i, mark;
        int              rc;
@@ -183,7 +183,8 @@
  * Return zero on failure (memory), non-zero otherwise.
  */
 int
-hesc_href(struct lowdown_buf *ob, const char *data, size_t size)
+lowdown_html_esc_href(struct lowdown_buf *ob, const char *data,
+    size_t size)
 {
        static const char       hex_chars[] = "0123456789ABCDEF";
        size_t                  i, mark;
@@ -251,16 +252,17 @@
 }
 
 /* 
- * Escape HTML.
- * If "literal", we also want to escape some extra characters.
+ * Escape HTML (really XML).
+ * If "literal", also escape some extra characters.
  * If "secure", also escape characters as suggested by OWASP rules.
+ * FIXME: literal and secure do the same thing.
  * If "num", use only numeric escapes.
  * Does nothing if "size" is zero.
  * Return zero on failure (memory), non-zero otherwise.
  */
 int
-hesc_html(struct lowdown_buf *ob, const char *data,
-       size_t size, int secure, int literal, int num)
+lowdown_html_esc(struct lowdown_buf *ob, const char *data,
+    size_t size, int secure, int literal, int num)
 {
        size_t          i, mark;
        int             max = 0, rc;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/latex.c new/lowdown-1.3.1/latex.c
--- old/lowdown-1.3.0/latex.c   2024-12-01 08:26:15.000000000 +0100
+++ new/lowdown-1.3.1/latex.c   2024-12-06 04:08:10.000000000 +0100
@@ -32,77 +32,33 @@
 #include "extern.h"
 
 struct latex {
-       unsigned int    oflags; /* same as in lowdown_opts */
-       struct hentryq  headers_used; /* headers we've seen */
-       ssize_t         headers_offs; /* header offset */
-       size_t          footsz; /* current footnote */
+       unsigned int     oflags; /* same as in lowdown_opts */
+       struct hentryq   headers_used; /* headers we've seen */
+       ssize_t          headers_offs; /* header offset */
+       size_t           footsz; /* current footnote */
+       const char      *templ; /* output template */
 };
 
 /*
- * Escape LaTeX special characters.
- * Return zero on failure, non-zero on success.
- */
-static int
-rndr_escape_text(const struct latex *st, struct lowdown_buf *ob,
-    const char *data, size_t sz)
-{
-       size_t   i;
-
-       for (i = 0; i < sz; i++)
-               switch (data[i]) {
-               case '&':
-               case '%':
-               case '$':
-               case '#':
-               case '_':
-               case '{':
-               case '}':
-                       if (!hbuf_putc(ob, '\\'))
-                               return 0;
-                       if (!hbuf_putc(ob, data[i]))
-                               return 0;
-                       break;
-               case '~':
-                       if (!HBUF_PUTSL(ob, "\\textasciitilde{}"))
-                               return 0;
-                       break;
-               case '^':
-                       if (!HBUF_PUTSL(ob, "\\textasciicircum{}"))
-                               return 0;
-                       break;
-               case '\\':
-                       if (!HBUF_PUTSL(ob, "\\textbackslash{}"))
-                               return 0;
-                       break;
-               default:
-                       if (!hbuf_putc(ob, data[i]))
-                               return 0;
-                       break;
-               }
-
-       return 1;
-}
-
-/*
- * Like rndr_escape_text() but with a NUL-terminated string.
+ * Like lowdown_latex_esc() but with a NUL-terminated string.
  */
 static int
 rndr_escape_string(const struct latex *st, struct lowdown_buf *ob,
     const char *data)
 {
 
-       return rndr_escape_text(st, ob, data, strlen(data));
+       return lowdown_latex_esc(ob, data, strlen(data));
 }
 
 /*
- * Like rndr_escape_text() but with a buffer.
+ * Like lowdown_latex_esc() but with a buffer.
  */
 static int
 rndr_escape(const struct latex *st, struct lowdown_buf *ob,
     const struct lowdown_buf *dat)
 {
        
-       return rndr_escape_text(st, ob, dat->data, dat->size);
+       return lowdown_latex_esc(ob, dat->data, dat->size);
 }
 
 static int
@@ -377,8 +333,8 @@
        else if (!loc && !HBUF_PUTSL(ob, "\\href{"))
                return 0;
 
-       if (loc && !rndr_escape_text
-           (st, ob, &param->link.data[1], param->link.size - 1))
+       if (loc && !lowdown_latex_esc
+           (ob, &param->link.data[1], param->link.size - 1))
                return 0;
        else if (!loc && !rndr_escape(st, ob, &param->link))
                return 0;
@@ -575,13 +531,13 @@
        if (cp != NULL) {
                if (!HBUF_PUTSL(ob, "{"))
                        return 0;
-               if (!rndr_escape_text
-                   (st, ob, param->link.data, cp - param->link.data))
+               if (!lowdown_latex_esc
+                   (ob, param->link.data, cp - param->link.data))
                        return 0;
                if (!HBUF_PUTSL(ob, "}"))
                        return 0;
-               if (!rndr_escape_text(st, ob, cp, 
-                   param->link.size - (cp - param->link.data)))
+               if (!lowdown_latex_esc
+                   (ob, cp, param->link.size - (cp - param->link.data)))
                        return 0;
        } else {
                if (!rndr_escape(st, ob, &param->link))
@@ -708,17 +664,8 @@
 }
 
 static int
-rndr_doc_footer(struct lowdown_buf *ob, const struct latex *st)
-{
-
-       if (st->oflags & LOWDOWN_STANDALONE)
-               return HBUF_PUTSL(ob, "\\end{document}\n");
-       return 1;
-}
-
-static int
-rndr_doc_header(const struct latex *st, struct lowdown_buf *ob,
-    const struct lowdown_metaq *mq)
+rndr_root(const struct latex *st, struct lowdown_buf *ob,
+    const struct lowdown_metaq *mq, const struct lowdown_buf *content)
 {
        const struct lowdown_meta       *m;
        const char                      *author = NULL, *title = NULL,
@@ -727,7 +674,9 @@
                                        *rcsdate = NULL, *header = NULL;
 
        if (!(st->oflags & LOWDOWN_STANDALONE))
-               return 1;
+               return hbuf_putb(ob, content);
+       if (st->templ != NULL)
+               return lowdown_template(st->templ, content, ob, mq);
 
        TAILQ_FOREACH(m, mq, entries)
                if (strcasecmp(m->key, "author") == 0)
@@ -834,7 +783,10 @@
            !HBUF_PUTSL(ob, "\\maketitle\n"))
                return 0;
 
-       return 1;
+       if (!hbuf_putb(ob, content))
+               return 0;
+
+       return HBUF_PUTSL(ob, "\\end{document}\n");
 }
 
 static int
@@ -895,6 +847,9 @@
                if (!rndr_blockcode(ob, &n->rndr_blockcode))
                        return 0;
                break;
+       case LOWDOWN_DOC_HEADER:
+               /* Don't output anything for this. */
+               break;
        case LOWDOWN_BLOCKQUOTE:
                if (!rndr_blockquote(ob, tmp))
                        return 0;
@@ -907,10 +862,6 @@
                if (!rndr_definition_title(ob, tmp))
                        return 0;
                break;
-       case LOWDOWN_DOC_HEADER:
-               if (!rndr_doc_header(st, ob, mq))
-                       return 0;
-               break;
        case LOWDOWN_META:
                if (n->chng != LOWDOWN_CHNG_DELETE &&
                    !rndr_meta(st, n, mq))
@@ -1017,9 +968,7 @@
                        return 0;
                break;
        case LOWDOWN_ROOT:
-               if (!hbuf_putb(ob, tmp))
-                       return 0;
-               if (!rndr_doc_footer(ob, st))
+               if (!rndr_root(st, ob, mq, tmp))
                        return 0;
                break;
        default:
@@ -1067,6 +1016,7 @@
                return NULL;
 
        p->oflags = opts == NULL ? 0 : opts->oflags;
+       p->templ = opts == NULL ? NULL : opts->templ;
        return p;
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/latex_escape.c 
new/lowdown-1.3.1/latex_escape.c
--- old/lowdown-1.3.0/latex_escape.c    1970-01-01 01:00:00.000000000 +0100
+++ new/lowdown-1.3.1/latex_escape.c    2024-12-06 04:08:10.000000000 +0100
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) Kristaps Dzonsons <krist...@bsd.lv>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include "config.h"
+
+#if HAVE_SYS_QUEUE
+# include <sys/queue.h>
+#endif
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "lowdown.h"
+#include "extern.h"
+
+/*
+ * Escape LaTeX special characters.
+ * Return zero on failure (memory), non-zero on success.
+ */
+int
+lowdown_latex_esc(struct lowdown_buf *ob, const char *data, size_t sz)
+{
+       size_t   i;
+
+       for (i = 0; i < sz; i++)
+               switch (data[i]) {
+               case '&':
+               case '%':
+               case '$':
+               case '#':
+               case '_':
+               case '{':
+               case '}':
+                       if (!hbuf_putc(ob, '\\'))
+                               return 0;
+                       if (!hbuf_putc(ob, data[i]))
+                               return 0;
+                       break;
+               case '~':
+                       if (!HBUF_PUTSL(ob, "\\textasciitilde{}"))
+                               return 0;
+                       break;
+               case '^':
+                       if (!HBUF_PUTSL(ob, "\\textasciicircum{}"))
+                               return 0;
+                       break;
+               case '\\':
+                       if (!HBUF_PUTSL(ob, "\\textbackslash{}"))
+                               return 0;
+                       break;
+               default:
+                       if (!hbuf_putc(ob, data[i]))
+                               return 0;
+                       break;
+               }
+
+       return 1;
+}
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/man/lowdown-diff.1 
new/lowdown-1.3.1/man/lowdown-diff.1
--- old/lowdown-1.3.0/man/lowdown-diff.1        2024-12-01 08:26:15.000000000 
+0100
+++ new/lowdown-1.3.1/man/lowdown-diff.1        2024-12-06 04:08:10.000000000 
+0100
@@ -200,7 +200,9 @@
 .Sx Templates .
 This is experimental functionality.
 Currently only for
-.Fl t Ns Ar html .
+.Fl t Ns Ar html
+and
+.Fl t Ns Ar latex .
 .El
 .Pp
 What follows are per-output long options.
@@ -853,21 +855,28 @@
 The
 .Li initial
 value is one of the following:
-.Bl -bullet
-.It
-a canonicalised metadata key
-.It
-.Li this
-.Pq value of a current loop context
-.It
-.Li body
-.Pq parsed input document body
-.It
-.Li meta(val)
-to force accepting
+.Bl -tag -width Ds
+.It Li and(expression[,expression]*)
+A non-empty list containing the value
+.Li true
+if all expressions evaluate to non-empty lists, otherwise an empty list.
+An empty expression evaluates to an empty list.
+.It canonicalised metadata key
+The value for the given metadata key, if found, otherwise an empty list.
+.It Li body
+The parsed input document body.
+.It Li meta(val)
+Evaluate
 .Li val
 as a metadata key, even if it's a special variable like
-.Li body
+.Li body .
+.It Li or(expression[,expression]*)
+A non-empty list containing the value
+.Li true
+if one expression evaluates to non-empty lists, otherwise an empty list.
+An empty expression evaluates to an empty list.
+.It Li this
+The value of a current loop context or an empty list.
 .El
 .Pp
 If a metadata key is not specified in the input, or if the anaphoric
@@ -920,6 +929,11 @@
 .Fl -template
 is not provided to
 .Fl t Ns Ar html .
+.It Pa share/latex/default.latex
+The default template used if
+.Fl -template
+is not provided to
+.Fl t Ns Ar latex .
 .It Pa share/odt/styles.xml
 Default styles used when generating standalone
 .Fl t Ns Ar fodt
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/man/lowdown.1 
new/lowdown-1.3.1/man/lowdown.1
--- old/lowdown-1.3.0/man/lowdown.1     2024-12-01 08:26:15.000000000 +0100
+++ new/lowdown-1.3.1/man/lowdown.1     2024-12-06 04:08:10.000000000 +0100
@@ -218,7 +218,9 @@
 .Sx Templates .
 This is experimental functionality.
 Currently only for
-.Fl t Ns Ar html .
+.Fl t Ns Ar html
+and
+.Fl t Ns Ar latex .
 .El
 .Pp
 What follows are per-output long options.
@@ -846,21 +848,28 @@
 The
 .Li initial
 value is one of the following:
-.Bl -bullet
-.It
-a canonicalised metadata key
-.It
-.Li this
-.Pq value of a current loop context
-.It
-.Li body
-.Pq parsed input document body
-.It
-.Li meta(val)
-to force accepting
+.Bl -tag -width Ds
+.It Li and(expression[,expression]*)
+A non-empty list containing the value
+.Li true
+if all expressions evaluate to non-empty lists, otherwise an empty list.
+An empty expression evaluates to an empty list.
+.It canonicalised metadata key
+The value for the given metadata key, if found, otherwise an empty list.
+.It Li body
+The parsed input document body.
+.It Li meta(val)
+Evaluate
 .Li val
 as a metadata key, even if it's a special variable like
-.Li body
+.Li body .
+.It Li or(expression[,expression]*)
+A non-empty list containing the value
+.Li true
+if one expression evaluates to non-empty lists, otherwise an empty list.
+An empty expression evaluates to an empty list.
+.It Li this
+The value of a current loop context or an empty list.
 .El
 .Pp
 If a metadata key is not specified in the input, or if the anaphoric
@@ -913,6 +922,11 @@
 .Fl -template
 is not provided to
 .Fl t Ns Ar html .
+.It Pa share/latex/default.latex
+The default template used if
+.Fl -template
+is not provided to
+.Fl t Ns Ar latex .
 .It Pa share/odt/styles.xml
 Default styles used when generating standalone
 .Fl t Ns Ar fodt
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/man/lowdown.3 
new/lowdown-1.3.1/man/lowdown.3
--- old/lowdown-1.3.0/man/lowdown.3     2024-12-01 08:26:15.000000000 +0100
+++ new/lowdown-1.3.1/man/lowdown.3     2024-12-06 04:08:10.000000000 +0100
@@ -554,6 +554,14 @@
 .It Va size_t metaovrsz
 Number of pairs in
 .Va metaovr .
+.It Va const char *templ
+If
+.Dv LOWDOWN_STANDALONE
+is specified, this is set to the external template file or
+.Dv NULL
+to use internal templating.
+This is only valid for output media supporting external templates;
+otherwise, it may be ignored.
 .El
 .Pp
 Parsed metadata is held in key-value
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/odt.c new/lowdown-1.3.1/odt.c
--- old/lowdown-1.3.0/odt.c     2024-12-01 08:26:15.000000000 +0100
+++ new/lowdown-1.3.1/odt.c     2024-12-06 04:08:10.000000000 +0100
@@ -909,7 +909,7 @@
                if (author != NULL) {
                        if (!HBUF_PUTSL(ob, "<dc:creator>"))
                                return 0;
-                       if (!hesc_html(ob, author,
+                       if (!lowdown_html_esc(ob, author,
                            strlen(author), 1, 0, 1))
                                return 0;
                        if (!HBUF_PUTSL(ob, "</dc:creator>\n"))
@@ -917,7 +917,7 @@
                }
                if (!HBUF_PUTSL(ob, "<dc:date>"))
                        return 0;
-               if (!hesc_html(ob, date, strlen(date), 1, 0, 1))
+               if (!lowdown_html_esc(ob, date, strlen(date), 1, 0, 1))
                        return 0;
                if (!HBUF_PUTSL(ob, "</dc:date>\n"))
                        return 0;
@@ -974,7 +974,7 @@
        if (title != NULL) {
                if (!HBUF_PUTSL(ob, "<dc:title>"))
                        return 0;
-               if (!hesc_html(ob, title, strlen(title), 1, 0, 1))
+               if (!lowdown_html_esc(ob, title, strlen(title), 1, 0, 1))
                        return 0;
                if (!HBUF_PUTSL(ob, "</dc:title>\n"))
                        return 0;
@@ -983,13 +983,13 @@
        if (author != NULL) {
                if (!HBUF_PUTSL(ob, "<dc:creator>"))
                        return 0;
-               if (!hesc_html(ob, author, strlen(author), 1, 0, 1))
+               if (!lowdown_html_esc(ob, author, strlen(author), 1, 0, 1))
                        return 0;
                if (!HBUF_PUTSL(ob, "</dc:creator>\n"))
                        return 0;
                if (!HBUF_PUTSL(ob, "<meta:initial-creator>"))
                        return 0;
-               if (!hesc_html(ob, author, strlen(author), 1, 0, 1))
+               if (!lowdown_html_esc(ob, author, strlen(author), 1, 0, 1))
                        return 0;
                if (!HBUF_PUTSL(ob, "</meta:initial-creator>\n"))
                        return 0;
@@ -998,13 +998,13 @@
        if (date != NULL) {
                if (!HBUF_PUTSL(ob, "<dc:date>"))
                        return 0;
-               if (!hesc_html(ob, date, strlen(date), 1, 0, 1))
+               if (!lowdown_html_esc(ob, date, strlen(date), 1, 0, 1))
                        return 0;
                if (!HBUF_PUTSL(ob, "</dc:date>\n"))
                        return 0;
                if (!HBUF_PUTSL(ob, "<meta:creation-date>"))
                        return 0;
-               if (!hesc_html(ob, date, strlen(date), 1, 0, 1))
+               if (!lowdown_html_esc(ob, date, strlen(date), 1, 0, 1))
                        return 0;
                if (!HBUF_PUTSL(ob, "</meta:creation-date>\n"))
                        return 0;
@@ -1022,7 +1022,7 @@
        size_t length, const struct odt *st)
 {
 
-       return hesc_html(ob, source, length, 1, 0, 1);
+       return lowdown_html_esc(ob, source, length, 1, 0, 1);
 }
 
 /*
@@ -1044,14 +1044,14 @@
        const struct odt *st)
 {
 
-       return hesc_href(ob, in->data, in->size);
+       return lowdown_html_esc_href(ob, in->data, in->size);
 }
 
 static int
 escape_attr(struct lowdown_buf *ob, const struct lowdown_buf *in)
 {
 
-       return hesc_attr(ob, in->data, in->size);
+       return lowdown_html_esc_attr(ob, in->data, in->size);
 }
 
 /*
@@ -1145,7 +1145,7 @@
                        if (parm->text.data[i] == ' ' &&
                            i < parm->text.size - 1 &&
                            parm->text.data[i + 1] == ' ') {
-                               if (!hesc_html(ob,
+                               if (!lowdown_html_esc(ob,
                                    &parm->text.data[j], sz, 1, 1, 1))
                                        return 0;
                                sz = 0;
@@ -1162,7 +1162,7 @@
                            parm->text.data[i] == '\n')
                                break;
                }
-               if (!hesc_html(ob, &parm->text.data[j], sz, 1, 1, 1))
+               if (!lowdown_html_esc(ob, &parm->text.data[j], sz, 1, 1, 1))
                        return 0;
                if (!HBUF_PUTSL(ob, "</text:p>\n"))
                        return 0;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/regress/diff/metadata-add.latex 
new/lowdown-1.3.1/regress/diff/metadata-add.latex
--- old/lowdown-1.3.0/regress/diff/metadata-add.latex   2024-12-01 
08:26:15.000000000 +0100
+++ new/lowdown-1.3.1/regress/diff/metadata-add.latex   2024-12-06 
04:08:10.000000000 +0100
@@ -23,7 +23,6 @@
 \title{}
 \author{kristaps}
 \maketitle
-
 \hypertarget{section}{%
 \section{section}\label{section}}
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/regress/diff/metadata-change.latex 
new/lowdown-1.3.1/regress/diff/metadata-change.latex
--- old/lowdown-1.3.0/regress/diff/metadata-change.latex        2024-12-01 
08:26:15.000000000 +0100
+++ new/lowdown-1.3.1/regress/diff/metadata-change.latex        2024-12-06 
04:08:10.000000000 +0100
@@ -23,7 +23,6 @@
 \title{}
 \author{shmistaps}
 \maketitle
-
 \hypertarget{section}{%
 \section{section}\label{section}}
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/regress/diff/metadata-remove.latex 
new/lowdown-1.3.1/regress/diff/metadata-remove.latex
--- old/lowdown-1.3.0/regress/diff/metadata-remove.latex        2024-12-01 
08:26:15.000000000 +0100
+++ new/lowdown-1.3.1/regress/diff/metadata-remove.latex        2024-12-06 
04:08:10.000000000 +0100
@@ -20,7 +20,6 @@
 \usepackage{longtable}
 \usepackage{hyperref}
 \begin{document}
-
 \hypertarget{section}{%
 \section{section}\label{section}}
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/regress/template/and-mismatch.xml 
new/lowdown-1.3.1/regress/template/and-mismatch.xml
--- old/lowdown-1.3.0/regress/template/and-mismatch.xml 1970-01-01 
01:00:00.000000000 +0100
+++ new/lowdown-1.3.1/regress/template/and-mismatch.xml 2024-12-06 
04:08:10.000000000 +0100
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta name="viewport" content="width=device-width, 
initial-scale=1" />
+               <meta charset="utf-8" /> 
+               <title>foo</title>
+       </head>
+       <body>
+               empty=[$ifdef(and(title,nope))$no$endif$]
+               empty=[$ifdef(and(title,))$no$endif$]
+               has=[$ifdef(and(title,authors))$yes$endif$]
+               has=[$ifdef(and(title))$yes$endif$]
+               empty=[$ifdef(and())$no$endif$]
+               empty=[$ifdef(and(,,))$no$endif$]
+       </body>
+</html>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/regress/template/and-nested.html 
new/lowdown-1.3.1/regress/template/and-nested.html
--- old/lowdown-1.3.0/regress/template/and-nested.html  1970-01-01 
01:00:00.000000000 +0100
+++ new/lowdown-1.3.1/regress/template/and-nested.html  2024-12-06 
04:08:10.000000000 +0100
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta name="viewport" content="width=device-width, 
initial-scale=1" />
+               <meta charset="utf-8" /> 
+               <title>foo</title>
+       </head>
+       <body>
+               [ YES ]
+               empty=[]
+               [ YES ]
+               [ YES ]
+               empty=[]
+       </body>
+</html>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/regress/template/and-nested.xml 
new/lowdown-1.3.1/regress/template/and-nested.xml
--- old/lowdown-1.3.0/regress/template/and-nested.xml   1970-01-01 
01:00:00.000000000 +0100
+++ new/lowdown-1.3.1/regress/template/and-nested.xml   2024-12-06 
04:08:10.000000000 +0100
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta name="viewport" content="width=device-width, 
initial-scale=1" />
+               <meta charset="utf-8" /> 
+               <title>foo</title>
+       </head>
+       <body>
+               [$ifdef(and(and(title,authors)))$ YES $endif$]
+               empty=[$ifdef(and(and(title,authors), and(title,)))$ NO $endif$]
+               [$ifdef(and(and(and(title))))$ YES $endif$]
+               [$ifdef(and(and(and(title),and(authors))))$ YES $endif$]
+               empty=[$ifdef(and(and(and(title,),and(authors))))$ YES $endif$]
+       </body>
+</html>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/regress/template/and-or.html 
new/lowdown-1.3.1/regress/template/and-or.html
--- old/lowdown-1.3.0/regress/template/and-or.html      1970-01-01 
01:00:00.000000000 +0100
+++ new/lowdown-1.3.1/regress/template/and-or.html      2024-12-06 
04:08:10.000000000 +0100
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta name="viewport" content="width=device-width, 
initial-scale=1" />
+               <meta charset="utf-8" /> 
+               <title>foo</title>
+       </head>
+       <body>
+               has=[yes]
+               empty=[]
+       </body>
+</html>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/regress/template/and-or.xml 
new/lowdown-1.3.1/regress/template/and-or.xml
--- old/lowdown-1.3.0/regress/template/and-or.xml       1970-01-01 
01:00:00.000000000 +0100
+++ new/lowdown-1.3.1/regress/template/and-or.xml       2024-12-06 
04:08:10.000000000 +0100
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta name="viewport" content="width=device-width, 
initial-scale=1" />
+               <meta charset="utf-8" /> 
+               <title>foo</title>
+       </head>
+       <body>
+               has=[$ifdef(and(title,or(authors,nope)))$yes$endif$]
+               empty=[$ifdef(and(title,or(authors,nope),no))$yes$endif$]
+       </body>
+</html>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/regress/template/and.html 
new/lowdown-1.3.1/regress/template/and.html
--- old/lowdown-1.3.0/regress/template/and.html 1970-01-01 01:00:00.000000000 
+0100
+++ new/lowdown-1.3.1/regress/template/and.html 2024-12-06 04:08:10.000000000 
+0100
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta name="viewport" content="width=device-width, 
initial-scale=1" />
+               <meta charset="utf-8" /> 
+               <title>foo</title>
+       </head>
+       <body>
+               empty=[]
+               empty=[]
+               has=[yes]
+               has=[yes]
+               empty=[]
+               empty=[]
+       </body>
+</html>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/regress/template/and.xml 
new/lowdown-1.3.1/regress/template/and.xml
--- old/lowdown-1.3.0/regress/template/and.xml  1970-01-01 01:00:00.000000000 
+0100
+++ new/lowdown-1.3.1/regress/template/and.xml  2024-12-06 04:08:10.000000000 
+0100
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta name="viewport" content="width=device-width, 
initial-scale=1" />
+               <meta charset="utf-8" /> 
+               <title>foo</title>
+       </head>
+       <body>
+               empty=[$ifdef(and(title,nope))$no$endif$]
+               empty=[$ifdef(and(title,))$no$endif$]
+               has=[$ifdef(and(title,authors))$yes$endif$]
+               has=[$ifdef(and(title))$yes$endif$]
+               empty=[$ifdef(and())$no$endif$]
+               empty=[$ifdef(and(,,))$no$endif$]
+       </body>
+</html>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/regress/template/or-nested.html 
new/lowdown-1.3.1/regress/template/or-nested.html
--- old/lowdown-1.3.0/regress/template/or-nested.html   1970-01-01 
01:00:00.000000000 +0100
+++ new/lowdown-1.3.1/regress/template/or-nested.html   2024-12-06 
04:08:10.000000000 +0100
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta name="viewport" content="width=device-width, 
initial-scale=1" />
+               <meta charset="utf-8" /> 
+               <title>foo</title>
+       </head>
+       <body>
+               [ YES ]
+               [ YES ]
+               [ YES ]
+               []
+       </body>
+</html>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/regress/template/or-nested.xml 
new/lowdown-1.3.1/regress/template/or-nested.xml
--- old/lowdown-1.3.0/regress/template/or-nested.xml    1970-01-01 
01:00:00.000000000 +0100
+++ new/lowdown-1.3.1/regress/template/or-nested.xml    2024-12-06 
04:08:10.000000000 +0100
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta name="viewport" content="width=device-width, 
initial-scale=1" />
+               <meta charset="utf-8" /> 
+               <title>foo</title>
+       </head>
+       <body>
+               [$ifdef(or(or(title,authors)))$ YES $endif$]
+               [$ifdef(or(or(title,authors),))$ YES $endif$]
+               [$ifdef(or(or(no,alsono),title))$ YES $endif$]
+               [$ifdef(or(or(no,alsono),asdfsadf))$ NO $endif$]
+       </body>
+</html>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/regress/template/or.html 
new/lowdown-1.3.1/regress/template/or.html
--- old/lowdown-1.3.0/regress/template/or.html  1970-01-01 01:00:00.000000000 
+0100
+++ new/lowdown-1.3.1/regress/template/or.html  2024-12-06 04:08:10.000000000 
+0100
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta name="viewport" content="width=device-width, 
initial-scale=1" />
+               <meta charset="utf-8" /> 
+               <title>foo</title>
+       </head>
+       <body>
+               empty=[]
+               has=[yes]
+               has=[yes]
+               has=[yes]
+       </body>
+</html>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/regress/template/or.xml 
new/lowdown-1.3.1/regress/template/or.xml
--- old/lowdown-1.3.0/regress/template/or.xml   1970-01-01 01:00:00.000000000 
+0100
+++ new/lowdown-1.3.1/regress/template/or.xml   2024-12-06 04:08:10.000000000 
+0100
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta name="viewport" content="width=device-width, 
initial-scale=1" />
+               <meta charset="utf-8" /> 
+               <title>foo</title>
+       </head>
+       <body>
+               empty=[$ifdef(or(alsonope,nope))$no$endif$]
+               has=[$ifdef(or(alsonope,nope,authors))$yes$endif$]
+               has=[$ifdef(or(,,,,authors))$yes$endif$]
+               has=[$ifdef(or(title,asdf,fdsa))$yes$endif$]
+       </body>
+</html>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/share/latex/default.latex 
new/lowdown-1.3.1/share/latex/default.latex
--- old/lowdown-1.3.0/share/latex/default.latex 1970-01-01 01:00:00.000000000 
+0100
+++ new/lowdown-1.3.1/share/latex/default.latex 2024-12-06 04:08:10.000000000 
+0100
@@ -0,0 +1,31 @@
+% Options for packages loaded elsewhere
+\PassOptionsToPackage{unicode}{hyperref}
+\PassOptionsToPackage{hyphens}{url}
+%
+\documentclass[11pt,a4paper]{article}
+\usepackage{amsmath,amssymb}
+\usepackage{lmodern}
+\usepackage{iftex}
+\ifPDFTeX
+  \usepackage[T1]{fontenc}
+  \usepackage[utf8]{inputenc}
+  \usepackage{textcomp} % provide euro and other symbols
+\else % if luatex or xetex
+  \usepackage{unicode-math}
+  \defaultfontfeatures{Scale=MatchLowercase}
+  \defaultfontfeatures[\\rmfamily]{Ligatures=TeX,Scale=1}
+\fi
+\usepackage{xcolor}
+\usepackage{graphicx}
+\usepackage{longtable}
+\usepackage{hyperref}
+$latexheader$
+\begin{document}
+$ifdef(or(title,author,date))$\title{$title.escapelatex$}$endif$
+$ifdef(author)$
+\author{$author.escapelatex$$ifdef(affiliation)$ \\ 
$affiliation.escapelatex$$endif$}
+$endif$
+$ifdef(date)$\date{$date.escapelatex$}$endif$
+$ifdef(or(title,author,date))$\maketitle$endif$
+$body$
+\end{document}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/template.c new/lowdown-1.3.1/template.c
--- old/lowdown-1.3.0/template.c        2024-12-01 08:26:15.000000000 +0100
+++ new/lowdown-1.3.1/template.c        2024-12-06 04:08:10.000000000 +0100
@@ -102,6 +102,9 @@
 static int op_exec(const struct op *, struct lowdown_buf *,
     const struct lowdown_metaq *, const struct lowdown_buf *,
     const char *);
+static struct op_resq *op_eval(const char *, size_t,
+    const struct lowdown_metaq *, const char *, const struct op_resq *,
+    const struct lowdown_buf *);
 
 /*
  * Allocate the generic members of "struct op".  The caller should be
@@ -426,7 +429,7 @@
 
        TAILQ_FOREACH(res, input, entries) {
                hbuf_truncate(buf);
-               if (!hesc_href(buf, res->res, strlen(res->res)))
+               if (!lowdown_html_esc_href(buf, res->res, strlen(res->res)))
                        goto err;
                if ((nres = calloc(1, sizeof(struct op_res))) == NULL)
                        goto err;
@@ -464,7 +467,45 @@
 
        TAILQ_FOREACH(res, input, entries) {
                hbuf_truncate(buf);
-               if (!hesc_attr(buf, res->res, strlen(res->res)))
+               if (!lowdown_html_esc_attr(buf, res->res, strlen(res->res)))
+                       goto err;
+               if ((nres = calloc(1, sizeof(struct op_res))) == NULL)
+                       goto err;
+               TAILQ_INSERT_TAIL(nq, nres, entries);
+               nres->res = strndup(buf->data, buf->size);
+               if (nres->res == NULL)
+                       goto err;
+       }
+       hbuf_free(buf);
+       return nq;
+err:
+       hbuf_free(buf);
+       op_resq_free(nq);
+       return NULL;
+}
+
+/*
+ * LaTeX-escape (for general content) all characters in all list items.
+ * Returns NULL on allocation failure.
+ */
+static struct op_resq *
+op_eval_function_escape_latex(const struct lowdown_metaq *mq,
+    const struct op_resq *input)
+{
+       struct op_resq          *nq = NULL;
+       struct op_res           *nres;
+       const struct op_res     *res;
+       struct lowdown_buf      *buf;
+
+       if ((buf = hbuf_new(32)) == NULL)
+               goto err;
+       if ((nq = malloc(sizeof(struct op_resq))) == NULL)
+               goto err;
+       TAILQ_INIT(nq);
+
+       TAILQ_FOREACH(res, input, entries) {
+               hbuf_truncate(buf);
+               if (!lowdown_latex_esc(buf, res->res, strlen(res->res)))
                        goto err;
                if ((nres = calloc(1, sizeof(struct op_res))) == NULL)
                        goto err;
@@ -502,7 +543,7 @@
 
        TAILQ_FOREACH(res, input, entries) {
                hbuf_truncate(buf);
-               if (!hesc_html(buf, res->res, strlen(res->res),
+               if (!lowdown_html_esc(buf, res->res, strlen(res->res),
                    1, 0, 0))
                        goto err;
                if ((nres = calloc(1, sizeof(struct op_res))) == NULL)
@@ -629,6 +670,8 @@
                nq = op_eval_function_join(mq, input);
        else if (exprsz == 4 && strncasecmp(expr, "trim", 4) == 0)
                nq = op_resq_clone(input, 1);
+       else if (exprsz == 11 && strncasecmp(expr, "escapelatex", 11) == 0)
+               nq = op_eval_function_escape_latex(mq, input);
        else if (exprsz == 10 && strncasecmp(expr, "escapehtml", 10) == 0)
                nq = op_eval_function_escape_html(mq, input);
        else if (exprsz == 14 && strncasecmp(expr, "escapehtmlattr", 14) == 0)
@@ -652,11 +695,12 @@
     size_t argsz, const char *this, const struct lowdown_metaq *mq,
     const struct lowdown_buf *content)
 {
-       struct op_resq                  *q;
+       struct op_resq                  *q, *resq;
        struct op_res                   *res;
        const struct lowdown_meta       *m;
        const char                      *v = NULL;
-       size_t                           vsz;
+       size_t                           i, vsz, stack = 0, start;
+       int                              rc;
 
        if ((q = malloc(sizeof(struct op_resq))) == NULL)
                return NULL;
@@ -671,6 +715,103 @@
                /* Body of HTML document. */
                v = content->data;
                vsz = content->size;
+       } else if (exprsz == 2 && strncasecmp(expr, "or", exprsz) == 0) {
+               /* "OR" of all arguments. */
+               for (rc = 0, start = i = 0; rc == 0 && i < argsz; i++) {
+                       /*
+                        * Read until next comma, then evaluate its
+                        * arguments.  Short-circuit if arguments
+                        * evaluate to true.  Check stack to pass over
+                        * sub-arguments.
+                        */
+
+                       if (args[i] == '(') {
+                               stack++;
+                               continue;
+                       } else if (args[i] == ')') {
+                               stack--;
+                               continue;
+                       } else if (args[i] != ',' || stack > 0)
+                               continue;
+
+                       /* Evaluate or empty evaluates to false. */
+
+                       if (i > start) {
+                               resq = op_eval(&args[start], i - start,
+                                       mq, this, NULL, content);
+                               if (resq == NULL)
+                                       return 0;
+                               rc = !TAILQ_EMPTY(resq);
+                               op_resq_free(resq);
+                       } else
+                               rc = 0;
+                       start = i + 1;
+               }
+
+               /* Catch remaining arguments. */
+
+               if (i > start && rc == 0) {
+                       resq = op_eval(&args[start], i - start, mq,
+                               this, NULL, content);
+                       if (resq == NULL)
+                               return 0;
+                       rc = !TAILQ_EMPTY(resq);
+                       op_resq_free(resq);
+               }
+
+               if (rc == 1) {
+                       v = "true";
+                       vsz = 4;
+               }
+       } else if (exprsz == 3 && strncasecmp(expr, "and", exprsz) == 0) {
+               /* "And" of all arguments. */
+               for (rc = 1, start = i = 0; rc == 1 && i < argsz; i++) {
+                       /*
+                        * Read until next comma, then evaluate its
+                        * arguments.  Short-circuit if arguments
+                        * evaluate to false.  Check stack to pass over
+                        * sub-arguments.
+                        */
+
+                       if (args[i] == '(') {
+                               stack++;
+                               continue;
+                       } else if (args[i] == ')') {
+                               stack--;
+                               continue;
+                       } else if (args[i] != ',' || stack > 0)
+                               continue;
+
+                       /* Evaluate or empty evaluates to false. */
+
+                       if (i > start) {
+                               resq = op_eval(&args[start], i - start,
+                                       mq, this, NULL, content);
+                               if (resq == NULL)
+                                       return 0;
+                               rc = !TAILQ_EMPTY(resq);
+                               op_resq_free(resq);
+                       } else
+                               rc = 0;
+                       start = i + 1;
+               }
+
+               /* Catch remaining arguments. */
+
+               if (i > start && rc == 1) {
+                       resq = op_eval(&args[start], i - start, mq,
+                               this, NULL, content);
+                       if (resq == NULL)
+                               return 0;
+                       rc = !TAILQ_EMPTY(resq);
+                       op_resq_free(resq);
+               } else if (i == start)
+                       rc = 0;
+
+               if (rc == 1) {
+                       v = "true";
+                       vsz = 4;
+               }
        } else {
                /*
                 * If "meta", interpret argument as being a metadata
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lowdown-1.3.0/versions.xml 
new/lowdown-1.3.1/versions.xml
--- old/lowdown-1.3.0/versions.xml      2024-12-01 08:26:15.000000000 +0100
+++ new/lowdown-1.3.1/versions.xml      2024-12-06 04:08:10.000000000 +0100
@@ -1643,4 +1643,21 @@
                        </p>
                </aside>
        </article>
+       <article data-sblg-article="1" data-sblg-tags="version">
+               <header>
+                       <h1>1.3.1</h1>
+                       <address>Kristaps Dzonsons</address>
+                       <time datetime="2024-12-05">2024-12-05</time>
+               </header>
+               <aside>
+                       <p>
+                               Add <b>--template</b> functionality to 
<b>-tlatex</b>.
+                       </p>
+                       <p>
+                               Add <code>and()</code> and <code>or()</code> as 
initial
+                               values to template expressions.  These allow 
for 
+                               <code>ifdef</code> to test for multiple values 
at once.
+                       </p>
+               </aside>
+       </article>
 </articles>

++++++ lowdown.obsinfo ++++++
--- /var/tmp/diff_new_pack.HBy9N9/_old  2024-12-08 11:38:19.064581076 +0100
+++ /var/tmp/diff_new_pack.HBy9N9/_new  2024-12-08 11:38:19.068581243 +0100
@@ -1,5 +1,5 @@
 name: lowdown
-version: 1.3.0
-mtime: 1733037975
-commit: 7f0c33949a20c7fbdad3a8f479ca28c17b5f7b25
+version: 1.3.1
+mtime: 1733454490
+commit: 6c42fe9655f5db3a0d7eb1ccaa3c1830f211481a
 

Reply via email to