]> granicus.if.org Git - multimarkdown/commitdiff
ADDED: Add opml option to read for MultiMarkdown OPML files
authorFletcher T. Penney <fletcher@fletcherpenney.net>
Tue, 7 Aug 2018 00:10:35 +0000 (19:10 -0500)
committerFletcher T. Penney <fletcher@fletcherpenney.net>
Tue, 7 Aug 2018 00:10:35 +0000 (19:10 -0500)
17 files changed:
CMakeLists.txt
Sources/libMultiMarkdown/include/libMultiMarkdown.h
Sources/libMultiMarkdown/mmd.c
Sources/libMultiMarkdown/opml-lexer.c [new file with mode: 0644]
Sources/libMultiMarkdown/opml-lexer.h [new file with mode: 0644]
Sources/libMultiMarkdown/opml-lexer.re [new file with mode: 0644]
Sources/libMultiMarkdown/opml-parser.c [new file with mode: 0644]
Sources/libMultiMarkdown/opml-parser.h [new file with mode: 0644]
Sources/libMultiMarkdown/opml-parser.out [new file with mode: 0644]
Sources/libMultiMarkdown/opml-parser.y [new file with mode: 0644]
Sources/libMultiMarkdown/opml-reader.c [new file with mode: 0644]
Sources/libMultiMarkdown/opml-reader.h [new file with mode: 0644]
Sources/libMultiMarkdown/parser.c
Sources/libMultiMarkdown/parser.y
Sources/libMultiMarkdown/update
Sources/multimarkdown/main.c
tests/MarkdownTest.pl

index d0baeff7e31b6a712fda2d693ec7d4dc9ccf7e11..7ff61ad3eec2a552a4617505902346a67e8dbd38 100644 (file)
@@ -194,6 +194,9 @@ set(src_files
        Sources/libMultiMarkdown/opendocument.c
        Sources/libMultiMarkdown/opendocument-content.c
        Sources/libMultiMarkdown/opml.c
+       Sources/libMultiMarkdown/opml-lexer.c
+       Sources/libMultiMarkdown/opml-parser.c
+       Sources/libMultiMarkdown/opml-reader.c
        Sources/libMultiMarkdown/parser.c
        Sources/libMultiMarkdown/rng.c
        Sources/libMultiMarkdown/scanners.c
@@ -227,6 +230,9 @@ set(header_files
        Sources/libMultiMarkdown/opendocument.h
        Sources/libMultiMarkdown/opendocument-content.h
        Sources/libMultiMarkdown/opml.h
+       Sources/libMultiMarkdown/opml-lexer.h
+       Sources/libMultiMarkdown/opml-parser.h
+       Sources/libMultiMarkdown/opml-reader.h
        Sources/libMultiMarkdown/scanners.h
        Sources/libMultiMarkdown/stack.h
        Sources/libMultiMarkdown/textbundle.c
@@ -672,6 +678,20 @@ ADD_MMD_TEST(mmd-6-critic-accept "-a" CriticMarkup htmla)
 
 ADD_MMD_TEST(mmd-6-critic-reject "-r" CriticMarkup htmlr)
 
+# Some of these will (properly) fail.
+# But it's useful to run manually at times to verify that
+# round-tripping through OPML generally works
+
+# add_test ( mmd-6-opml-parse 
+#              ${PROJECT_SOURCE_DIR}/tests/MarkdownTest.pl
+#              --Script="${CMAKE_CURRENT_BINARY_DIR}/multimarkdown"
+#              --testdir=${PROJECT_SOURCE_DIR}/tests/MMD6Tests
+#              "--Flags=--opml -t mmd"
+#              --ext=text
+#              --source_ext=opml
+# )
+
+
 ADD_MMD_TEST(pathologic-compat "-c" ../build html)
 
 ADD_MMD_TEST(pathologic "" ../build html)
index 20fcbd73d680b6113d6c81ee4a9405fb6acfc22e..cbeefeee38dad5ab5c95201f4cec2d3ad20a96ba 100644 (file)
@@ -555,6 +555,7 @@ enum parser_extensions {
        EXT_CRITIC_REJECT       = 1 << 11,   //!< Reject all proposed changes
        EXT_RANDOM_FOOT         = 1 << 12,   //!< Use random numbers for footnote links
        EXT_TRANSCLUDE          = 1 << 13,   //!< Perform transclusion(s)
+       EXT_PARSE_OPML          = 1 << 14,   //!< Convert from OPML before processing source text
        EXT_FAKE                = 1 << 31,   //!< 31 is highest number allowed
 };
 
index 6a42c463b4c0070f647c6153d0c11dddb9a71ce6..b4b38d6e0ff704c2fb79c7d9ae2cc86180fecb24 100644 (file)
@@ -65,6 +65,7 @@
 #include "mmd.h"
 #include "object_pool.h"
 #include "opendocument.h"
+#include "opml-reader.h"
 #include "parser.h"
 #include "scanners.h"
 #include "stack.h"
@@ -2187,6 +2188,10 @@ token * mmd_engine_parse_substring(mmd_engine * e, size_t byte_start, size_t byt
                e->extensions |= EXT_NO_METADATA;
        }
 
+       if (e->extensions & EXT_PARSE_OPML) {
+               // Convert from OPML first (if not done earlier)
+               mmd_convert_opml_string(e, byte_start, byte_len);
+       }
 
        // Tokenize the string
        token * doc = mmd_tokenize_string(e, byte_start, byte_len, false);
@@ -2759,6 +2764,11 @@ DString * mmd_engine_convert_to_data(mmd_engine * e, short format, const char *
        DString * result = NULL;
 
        if (format == FORMAT_MMD) {
+               if (e->extensions & EXT_PARSE_OPML) {
+                       // Convert from OPML first (if not done earlier)
+                       mmd_convert_opml_string(e, 0, e->dstr->currentStringLength);
+               }
+
                // Simply return text (transclusion is handled externally)
                d_string_append_c_array(output, e->dstr->str, e->dstr->currentStringLength);
 
diff --git a/Sources/libMultiMarkdown/opml-lexer.c b/Sources/libMultiMarkdown/opml-lexer.c
new file mode 100644 (file)
index 0000000..4aa2f69
--- /dev/null
@@ -0,0 +1,1264 @@
+/* Generated by re2c 1.0.3 on Mon Aug  6 17:50:40 2018 */
+/**
+
+       MultiMarkdown -- Lightweight markup processor to produce HTML, LaTeX, and more.
+
+       @file opml-lexer.c
+
+       @brief Tokenize OPML file for parsing
+
+
+       @author Fletcher T. Penney
+       @bug    
+
+**/
+
+/*
+
+       Copyright © 2016 - 2018 Fletcher T. Penney.
+
+
+       The `MultiMarkdown 6` project is released under the MIT License..
+       
+       GLibFacade.c and GLibFacade.h are from the MultiMarkdown v4 project:
+       
+               https://github.com/fletcher/MultiMarkdown-4/
+       
+       MMD 4 is released under both the MIT License and GPL.
+       
+       
+       CuTest is released under the zlib/libpng license. See CuTest.c for the
+       text of the license.
+       
+       uthash library:
+               Copyright (c) 2005-2016, Troy D. Hanson
+       
+               Licensed under Revised BSD license
+       
+       miniz library:
+               Copyright 2013-2014 RAD Game Tools and Valve Software
+               Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
+       
+               Licensed under the MIT license
+       
+       argtable3 library:
+               Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+               <sheitmann@users.sourceforge.net>
+               All rights reserved.
+       
+               Licensed under the Revised BSD License
+       
+       
+       ## The MIT License ##
+       
+       Permission is hereby granted, free of charge, to any person obtaining
+       a copy of this software and associated documentation files (the
+       "Software"), to deal in the Software without restriction, including
+       without limitation the rights to use, copy, modify, merge, publish,
+       distribute, sublicense, and/or sell copies of the Software, and to
+       permit persons to whom the Software is furnished to do so, subject to
+       the following conditions:
+       
+       The above copyright notice and this permission notice shall be
+       included in all copies or substantial portions of the Software.
+       
+       THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+       EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+       MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+       IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+       CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+       TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+       SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+       
+       
+       ## Revised BSD License ##
+       
+       Redistribution and use in source and binary forms, with or without
+       modification, are permitted provided that the following conditions are
+       met:
+           * Redistributions of source code must retain the above copyright
+             notice, this list of conditions and the following disclaimer.
+           * Redistributions in binary form must reproduce the above
+             copyright notice, this list of conditions and the following
+             disclaimer in the documentation and/or other materials provided
+             with the distribution.
+           * Neither the name of the <organization> nor the
+             names of its contributors may be used to endorse or promote
+             products derived from this software without specific prior
+             written permission.
+       
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+       "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+       LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+       A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT
+       HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+       EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+       PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES LOSS OF USE, DATA, OR
+       PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+       LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+       NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+       SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+       
+
+*/
+
+#include <stdlib.h>
+
+#include "opml-lexer.h"
+#include "opml-parser.h"
+
+
+// Basic scanner struct
+
+#define YYCTYPE                unsigned char
+#define YYCURSOR       s->cur
+#define YYMARKER       s->ptr
+#define YYCTXMARKER    s->ctx
+
+int opml_scan(Scanner * s, const char * stop) {
+
+       scan:
+
+       if (s->cur >= stop) {
+               return 0;
+       }
+
+       s->start = s->cur;
+
+       
+{
+       YYCTYPE yych;
+       unsigned int yyaccept = 0;
+       yych = *YYCURSOR;
+       switch (yych) {
+       case '\t':
+       case '\n':
+       case '\r':
+       case ' ':       goto yy4;
+       case '<':       goto yy7;
+       default:        goto yy2;
+       }
+yy2:
+       ++YYCURSOR;
+yy3:
+       { goto scan; }
+yy4:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case '\t':
+       case '\n':
+       case '\r':
+       case ' ':       goto yy4;
+       default:        goto yy6;
+       }
+yy6:
+       { return OPML_WSNL; }
+yy7:
+       yyaccept = 0;
+       yych = *(YYMARKER = ++YYCURSOR);
+       switch (yych) {
+       case '/':       goto yy8;
+       case '?':       goto yy10;
+       case 'B':
+       case 'b':       goto yy11;
+       case 'H':
+       case 'h':       goto yy12;
+       case 'O':
+       case 'o':       goto yy13;
+       case 'T':
+       case 't':       goto yy14;
+       default:        goto yy3;
+       }
+yy8:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'B':
+       case 'b':       goto yy15;
+       case 'H':
+       case 'h':       goto yy16;
+       case 'O':
+       case 'o':       goto yy17;
+       case 'T':
+       case 't':       goto yy18;
+       default:        goto yy9;
+       }
+yy9:
+       YYCURSOR = YYMARKER;
+       switch (yyaccept) {
+       case 0:         goto yy3;
+       case 1:         goto yy89;
+       default:        goto yy94;
+       }
+yy10:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'X':
+       case 'x':       goto yy19;
+       default:        goto yy9;
+       }
+yy11:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'O':
+       case 'o':       goto yy20;
+       default:        goto yy9;
+       }
+yy12:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'E':
+       case 'e':       goto yy21;
+       default:        goto yy9;
+       }
+yy13:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'P':
+       case 'p':       goto yy22;
+       case 'U':
+       case 'u':       goto yy23;
+       default:        goto yy9;
+       }
+yy14:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'I':
+       case 'i':       goto yy24;
+       default:        goto yy9;
+       }
+yy15:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'O':
+       case 'o':       goto yy25;
+       default:        goto yy9;
+       }
+yy16:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'E':
+       case 'e':       goto yy26;
+       default:        goto yy9;
+       }
+yy17:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'P':
+       case 'p':       goto yy27;
+       case 'U':
+       case 'u':       goto yy28;
+       default:        goto yy9;
+       }
+yy18:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'I':
+       case 'i':       goto yy29;
+       default:        goto yy9;
+       }
+yy19:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'M':
+       case 'm':       goto yy30;
+       default:        goto yy9;
+       }
+yy20:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'D':
+       case 'd':       goto yy31;
+       default:        goto yy9;
+       }
+yy21:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'A':
+       case 'a':       goto yy32;
+       default:        goto yy9;
+       }
+yy22:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'M':
+       case 'm':       goto yy33;
+       default:        goto yy9;
+       }
+yy23:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'T':
+       case 't':       goto yy34;
+       default:        goto yy9;
+       }
+yy24:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'T':
+       case 't':       goto yy35;
+       default:        goto yy9;
+       }
+yy25:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'D':
+       case 'd':       goto yy36;
+       default:        goto yy9;
+       }
+yy26:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'A':
+       case 'a':       goto yy37;
+       default:        goto yy9;
+       }
+yy27:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'M':
+       case 'm':       goto yy38;
+       default:        goto yy9;
+       }
+yy28:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'T':
+       case 't':       goto yy39;
+       default:        goto yy9;
+       }
+yy29:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'T':
+       case 't':       goto yy40;
+       default:        goto yy9;
+       }
+yy30:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'L':
+       case 'l':       goto yy41;
+       default:        goto yy9;
+       }
+yy31:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'Y':
+       case 'y':       goto yy43;
+       default:        goto yy9;
+       }
+yy32:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'D':
+       case 'd':       goto yy45;
+       default:        goto yy9;
+       }
+yy33:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'L':
+       case 'l':       goto yy47;
+       default:        goto yy9;
+       }
+yy34:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'L':
+       case 'l':       goto yy49;
+       default:        goto yy9;
+       }
+yy35:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'L':
+       case 'l':       goto yy50;
+       default:        goto yy9;
+       }
+yy36:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'Y':
+       case 'y':       goto yy51;
+       default:        goto yy9;
+       }
+yy37:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'D':
+       case 'd':       goto yy52;
+       default:        goto yy9;
+       }
+yy38:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'L':
+       case 'l':       goto yy53;
+       default:        goto yy9;
+       }
+yy39:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'L':
+       case 'l':       goto yy54;
+       default:        goto yy9;
+       }
+yy40:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'L':
+       case 'l':       goto yy55;
+       default:        goto yy9;
+       }
+yy41:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 0x00:      goto yy9;
+       case '>':       goto yy56;
+       default:        goto yy41;
+       }
+yy43:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 0x00:      goto yy9;
+       case '>':       goto yy58;
+       default:        goto yy43;
+       }
+yy45:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 0x00:      goto yy9;
+       case '>':       goto yy60;
+       default:        goto yy45;
+       }
+yy47:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 0x00:      goto yy9;
+       case '>':       goto yy62;
+       default:        goto yy47;
+       }
+yy49:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'I':
+       case 'i':       goto yy64;
+       default:        goto yy9;
+       }
+yy50:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'E':
+       case 'e':       goto yy65;
+       default:        goto yy9;
+       }
+yy51:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case '>':       goto yy67;
+       default:        goto yy9;
+       }
+yy52:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case '>':       goto yy69;
+       default:        goto yy9;
+       }
+yy53:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case '>':       goto yy71;
+       default:        goto yy9;
+       }
+yy54:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'I':
+       case 'i':       goto yy73;
+       default:        goto yy9;
+       }
+yy55:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'E':
+       case 'e':       goto yy74;
+       default:        goto yy9;
+       }
+yy56:
+       ++YYCURSOR;
+       { return OPML_XML; }
+yy58:
+       ++YYCURSOR;
+       { return OPML_BODY_OPEN; }
+yy60:
+       ++YYCURSOR;
+       { return OPML_HEAD_OPEN; }
+yy62:
+       ++YYCURSOR;
+       { return OPML_OPML_OPEN; }
+yy64:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'N':
+       case 'n':       goto yy75;
+       default:        goto yy9;
+       }
+yy65:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 0x00:      goto yy9;
+       case '>':       goto yy76;
+       default:        goto yy65;
+       }
+yy67:
+       ++YYCURSOR;
+       { return OPML_BODY_CLOSE; }
+yy69:
+       ++YYCURSOR;
+       { return OPML_HEAD_CLOSE; }
+yy71:
+       ++YYCURSOR;
+       { return OPML_OPML_CLOSE; }
+yy73:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'N':
+       case 'n':       goto yy78;
+       default:        goto yy9;
+       }
+yy74:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case '>':       goto yy79;
+       default:        goto yy9;
+       }
+yy75:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'E':
+       case 'e':       goto yy81;
+       default:        goto yy9;
+       }
+yy76:
+       ++YYCURSOR;
+       { return OPML_TITLE_OPEN; }
+yy78:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'E':
+       case 'e':       goto yy83;
+       default:        goto yy9;
+       }
+yy79:
+       ++YYCURSOR;
+       { return OPML_TITLE_CLOSE; }
+yy81:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 0x00:      goto yy9;
+       case '\t':
+       case '\n':
+       case '\r':
+       case ' ':       goto yy81;
+       case '/':       goto yy86;
+       case '>':       goto yy88;
+       case 'T':
+       case 't':       goto yy90;
+       default:        goto yy84;
+       }
+yy83:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case '>':       goto yy91;
+       default:        goto yy9;
+       }
+yy84:
+       yych = *++YYCURSOR;
+yy85:
+       switch (yych) {
+       case 0x00:      goto yy9;
+       case '/':       goto yy86;
+       case '>':       goto yy88;
+       default:        goto yy84;
+       }
+yy86:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 0x00:      goto yy9;
+       case '/':       goto yy86;
+       case '>':       goto yy93;
+       default:        goto yy84;
+       }
+yy88:
+       ++YYCURSOR;
+yy89:
+       { return OPML_OUTLINE_OPEN; }
+yy90:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'E':
+       case 'e':       goto yy95;
+       default:        goto yy85;
+       }
+yy91:
+       ++YYCURSOR;
+       { return OPML_OUTLINE_CLOSE; }
+yy93:
+       ++YYCURSOR;
+yy94:
+       { return OPML_OUTLINE_SELF_CLOSE; }
+yy95:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'X':
+       case 'x':       goto yy96;
+       default:        goto yy85;
+       }
+yy96:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'T':
+       case 't':       goto yy97;
+       default:        goto yy85;
+       }
+yy97:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 0x00:      goto yy9;
+       case '\t':
+       case '\n':
+       case '\r':
+       case ' ':       goto yy97;
+       case '/':       goto yy86;
+       case '=':       goto yy99;
+       case '>':       goto yy88;
+       default:        goto yy84;
+       }
+yy99:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 0x00:      goto yy9;
+       case '\t':
+       case '\n':
+       case '\r':
+       case ' ':       goto yy99;
+       case '"':       goto yy101;
+       case '/':       goto yy86;
+       case '>':       goto yy88;
+       default:        goto yy84;
+       }
+yy101:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case '(':       goto yy102;
+       case 'M':
+       case 'm':       goto yy103;
+       default:        goto yy85;
+       }
+yy102:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'U':
+       case 'u':       goto yy104;
+       default:        goto yy85;
+       }
+yy103:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'E':
+       case 'e':       goto yy105;
+       default:        goto yy85;
+       }
+yy104:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'N':
+       case 'n':       goto yy106;
+       default:        goto yy85;
+       }
+yy105:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'T':
+       case 't':       goto yy107;
+       default:        goto yy85;
+       }
+yy106:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'T':
+       case 't':       goto yy108;
+       default:        goto yy85;
+       }
+yy107:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'A':
+       case 'a':       goto yy109;
+       default:        goto yy85;
+       }
+yy108:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'I':
+       case 'i':       goto yy110;
+       default:        goto yy85;
+       }
+yy109:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'D':
+       case 'd':       goto yy111;
+       default:        goto yy85;
+       }
+yy110:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'T':
+       case 't':       goto yy112;
+       default:        goto yy85;
+       }
+yy111:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'A':
+       case 'a':       goto yy113;
+       default:        goto yy85;
+       }
+yy112:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'L':
+       case 'l':       goto yy114;
+       default:        goto yy85;
+       }
+yy113:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'T':
+       case 't':       goto yy115;
+       default:        goto yy85;
+       }
+yy114:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'E':
+       case 'e':       goto yy116;
+       default:        goto yy85;
+       }
+yy115:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'A':
+       case 'a':       goto yy117;
+       default:        goto yy85;
+       }
+yy116:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'D':
+       case 'd':       goto yy118;
+       default:        goto yy85;
+       }
+yy117:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case '"':       goto yy119;
+       default:        goto yy85;
+       }
+yy118:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case ' ':       goto yy121;
+       default:        goto yy85;
+       }
+yy119:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 0x00:      goto yy9;
+       case '\t':
+       case '\n':
+       case '\r':
+       case ' ':       goto yy119;
+       case '/':       goto yy86;
+       case '>':       goto yy122;
+       default:        goto yy84;
+       }
+yy121:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'P':
+       case 'p':       goto yy124;
+       default:        goto yy85;
+       }
+yy122:
+       ++YYCURSOR;
+       { return OPML_OUTLINE_METADATA; }
+yy124:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'R':
+       case 'r':       goto yy125;
+       default:        goto yy85;
+       }
+yy125:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'E':
+       case 'e':       goto yy126;
+       default:        goto yy85;
+       }
+yy126:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'A':
+       case 'a':       goto yy127;
+       default:        goto yy85;
+       }
+yy127:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'M':
+       case 'm':       goto yy128;
+       default:        goto yy85;
+       }
+yy128:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'B':
+       case 'b':       goto yy129;
+       default:        goto yy85;
+       }
+yy129:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'L':
+       case 'l':       goto yy130;
+       default:        goto yy85;
+       }
+yy130:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'E':
+       case 'e':       goto yy131;
+       default:        goto yy85;
+       }
+yy131:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case ')':       goto yy132;
+       default:        goto yy85;
+       }
+yy132:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case '"':       goto yy133;
+       default:        goto yy85;
+       }
+yy133:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 0x00:      goto yy9;
+       case '\t':
+       case '\n':
+       case '\r':
+       case ' ':       goto yy133;
+       case '/':       goto yy86;
+       case '>':       goto yy88;
+       case '_':       goto yy135;
+       default:        goto yy84;
+       }
+yy135:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'N':
+       case 'n':       goto yy136;
+       default:        goto yy85;
+       }
+yy136:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'O':
+       case 'o':       goto yy137;
+       default:        goto yy85;
+       }
+yy137:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'T':
+       case 't':       goto yy138;
+       default:        goto yy85;
+       }
+yy138:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 'E':
+       case 'e':       goto yy139;
+       default:        goto yy85;
+       }
+yy139:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 0x00:      goto yy9;
+       case '\t':
+       case '\n':
+       case '\r':
+       case ' ':       goto yy139;
+       case '/':       goto yy86;
+       case '=':       goto yy141;
+       case '>':       goto yy88;
+       default:        goto yy84;
+       }
+yy141:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 0x00:      goto yy9;
+       case '\t':
+       case '\n':
+       case '\r':
+       case ' ':       goto yy141;
+       case '"':       goto yy143;
+       case '/':       goto yy86;
+       case '>':       goto yy88;
+       default:        goto yy84;
+       }
+yy143:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 0x00:      goto yy9;
+       case '"':       goto yy145;
+       case '/':       goto yy147;
+       case '>':       goto yy149;
+       default:        goto yy143;
+       }
+yy145:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 0x00:      goto yy9;
+       case '\t':
+       case '\n':
+       case '\r':
+       case ' ':       goto yy145;
+       case '/':       goto yy86;
+       case '>':       goto yy150;
+       default:        goto yy84;
+       }
+yy147:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case 0x00:      goto yy9;
+       case '"':       goto yy145;
+       case '/':       goto yy147;
+       case '>':       goto yy152;
+       default:        goto yy143;
+       }
+yy149:
+       yyaccept = 1;
+       yych = *(YYMARKER = ++YYCURSOR);
+       if (yych <= 0x00) goto yy89;
+       goto yy154;
+yy150:
+       ++YYCURSOR;
+       { return OPML_OUTLINE_PREAMBLE; }
+yy152:
+       yyaccept = 2;
+       yych = *(YYMARKER = ++YYCURSOR);
+       if (yych <= 0x00) goto yy94;
+       goto yy154;
+yy153:
+       yych = *++YYCURSOR;
+yy154:
+       switch (yych) {
+       case 0x00:      goto yy9;
+       case '"':       goto yy155;
+       default:        goto yy153;
+       }
+yy155:
+       yych = *++YYCURSOR;
+       switch (yych) {
+       case '\t':
+       case '\n':
+       case '\r':
+       case ' ':       goto yy155;
+       case '>':       goto yy150;
+       default:        goto yy9;
+       }
+}
+
+}
+
+
+
+
+/// skip through text attribute to find value
+size_t scan_text(const char * c) {
+       const char * marker = NULL;
+       const char * start = c;
+
+
+{
+       unsigned char yych;
+       yych = *(marker = c);
+       switch (yych) {
+       case '\t':
+       case '\r':
+       case ' ':       goto yy161;
+       case '\n':      goto yy162;
+       case 'T':
+       case 't':       goto yy165;
+       default:        goto yy160;
+       }
+yy159:
+       { return 0; }
+yy160:
+       ++c;
+       goto yy159;
+yy161:
+       yych = *(marker = ++c);
+       switch (yych) {
+       case '\t':
+       case '\n':
+       case '\r':
+       case ' ':       goto yy162;
+       case 'T':
+       case 't':       goto yy166;
+       default:        goto yy159;
+       }
+yy162:
+       yych = *++c;
+       switch (yych) {
+       case '\t':
+       case '\n':
+       case '\r':
+       case ' ':       goto yy162;
+       case 'T':
+       case 't':       goto yy166;
+       default:        goto yy164;
+       }
+yy164:
+       c = marker;
+       goto yy159;
+yy165:
+       yych = *(marker = ++c);
+       switch (yych) {
+       case 'E':
+       case 'e':       goto yy167;
+       default:        goto yy159;
+       }
+yy166:
+       yych = *++c;
+       switch (yych) {
+       case 'E':
+       case 'e':       goto yy167;
+       default:        goto yy164;
+       }
+yy167:
+       yych = *++c;
+       switch (yych) {
+       case 'X':
+       case 'x':       goto yy168;
+       default:        goto yy164;
+       }
+yy168:
+       yych = *++c;
+       switch (yych) {
+       case 'T':
+       case 't':       goto yy169;
+       default:        goto yy164;
+       }
+yy169:
+       yych = *++c;
+       switch (yych) {
+       case '\t':
+       case '\n':
+       case '\r':
+       case ' ':       goto yy169;
+       case '=':       goto yy171;
+       default:        goto yy164;
+       }
+yy171:
+       yych = *++c;
+       switch (yych) {
+       case '\t':
+       case '\n':
+       case '\r':
+       case ' ':       goto yy171;
+       case '"':
+               marker = c;
+               goto yy173;
+       default:        goto yy164;
+       }
+yy173:
+       yych = *++c;
+       switch (yych) {
+       case 0x00:      goto yy164;
+       case '"':       goto yy175;
+       default:        goto yy173;
+       }
+yy175:
+       ++c;
+       c = marker;
+       { return (size_t)( c - start ); }
+}
+       
+}
+
+
+/// skip through _note attribute to find value
+size_t scan_note(const char * c) {
+       const char * marker = NULL;
+       const char * start = c;
+
+
+{
+       unsigned char yych;
+       yych = *(marker = c);
+       switch (yych) {
+       case '\t':
+       case '\r':
+       case ' ':       goto yy181;
+       case '\n':      goto yy182;
+       case '_':       goto yy185;
+       default:        goto yy180;
+       }
+yy179:
+       { return 0; }
+yy180:
+       ++c;
+       goto yy179;
+yy181:
+       yych = *(marker = ++c);
+       switch (yych) {
+       case '\t':
+       case '\n':
+       case '\r':
+       case ' ':       goto yy182;
+       case '_':       goto yy186;
+       default:        goto yy179;
+       }
+yy182:
+       yych = *++c;
+       switch (yych) {
+       case '\t':
+       case '\n':
+       case '\r':
+       case ' ':       goto yy182;
+       case '_':       goto yy186;
+       default:        goto yy184;
+       }
+yy184:
+       c = marker;
+       goto yy179;
+yy185:
+       yych = *(marker = ++c);
+       switch (yych) {
+       case 'N':
+       case 'n':       goto yy187;
+       default:        goto yy179;
+       }
+yy186:
+       yych = *++c;
+       switch (yych) {
+       case 'N':
+       case 'n':       goto yy187;
+       default:        goto yy184;
+       }
+yy187:
+       yych = *++c;
+       switch (yych) {
+       case 'O':
+       case 'o':       goto yy188;
+       default:        goto yy184;
+       }
+yy188:
+       yych = *++c;
+       switch (yych) {
+       case 'T':
+       case 't':       goto yy189;
+       default:        goto yy184;
+       }
+yy189:
+       yych = *++c;
+       switch (yych) {
+       case 'E':
+       case 'e':       goto yy190;
+       default:        goto yy184;
+       }
+yy190:
+       yych = *++c;
+       switch (yych) {
+       case '\t':
+       case '\n':
+       case '\r':
+       case ' ':       goto yy190;
+       case '=':       goto yy192;
+       default:        goto yy184;
+       }
+yy192:
+       yych = *++c;
+       switch (yych) {
+       case '\t':
+       case '\n':
+       case '\r':
+       case ' ':       goto yy192;
+       case '"':
+               marker = c;
+               goto yy194;
+       default:        goto yy184;
+       }
+yy194:
+       yych = *++c;
+       switch (yych) {
+       case 0x00:      goto yy184;
+       case '"':       goto yy196;
+       default:        goto yy194;
+       }
+yy196:
+       ++c;
+       c = marker;
+       { return (size_t)( c - start ); }
+}
+       
+}
+
+
+/// find end of double quoted value
+size_t scan_double_quoted(const char * c) {
+       const char * marker = NULL;
+       const char * start = c;
+
+
+{
+       unsigned char yych;
+       yych = *c;
+       switch (yych) {
+       case '\n':      goto yy200;
+       case '"':       goto yy202;
+       default:        goto yy201;
+       }
+yy200:
+       { return 0; }
+yy201:
+       ++c;
+       goto yy200;
+yy202:
+       yych = *(marker = ++c);
+       if (yych <= 0x00) goto yy200;
+       goto yy204;
+yy203:
+       yych = *++c;
+yy204:
+       switch (yych) {
+       case 0x00:      goto yy205;
+       case '"':       goto yy206;
+       default:        goto yy203;
+       }
+yy205:
+       c = marker;
+       goto yy200;
+yy206:
+       ++c;
+       { return (size_t)( c - start ); }
+}
+       
+}
diff --git a/Sources/libMultiMarkdown/opml-lexer.h b/Sources/libMultiMarkdown/opml-lexer.h
new file mode 100644 (file)
index 0000000..482d67e
--- /dev/null
@@ -0,0 +1,153 @@
+/**
+
+       MultiMarkdown -- Lightweight markup processor to produce HTML, LaTeX, and more.
+
+       @file opml-lexer.h
+
+       @brief
+
+
+       @author Fletcher T. Penney
+       @bug
+
+**/
+
+/*
+
+       Copyright © 2016 - 2018 Fletcher T. Penney.
+
+
+       The `MultiMarkdown 6` project is released under the MIT License..
+
+       GLibFacade.c and GLibFacade.h are from the MultiMarkdown v4 project:
+
+               https://github.com/fletcher/MultiMarkdown-4/
+
+       MMD 4 is released under both the MIT License and GPL.
+
+
+       CuTest is released under the zlib/libpng license. See CuTest.c for the
+       text of the license.
+
+       uthash library:
+               Copyright (c) 2005-2016, Troy D. Hanson
+
+               Licensed under Revised BSD license
+
+       miniz library:
+               Copyright 2013-2014 RAD Game Tools and Valve Software
+               Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
+
+               Licensed under the MIT license
+
+       argtable3 library:
+               Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+               <sheitmann@users.sourceforge.net>
+               All rights reserved.
+
+               Licensed under the Revised BSD License
+
+
+       ## The MIT License ##
+
+       Permission is hereby granted, free of charge, to any person obtaining
+       a copy of this software and associated documentation files (the
+       "Software"), to deal in the Software without restriction, including
+       without limitation the rights to use, copy, modify, merge, publish,
+       distribute, sublicense, and/or sell copies of the Software, and to
+       permit persons to whom the Software is furnished to do so, subject to
+       the following conditions:
+
+       The above copyright notice and this permission notice shall be
+       included in all copies or substantial portions of the Software.
+
+       THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+       EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+       MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+       IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+       CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+       TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+       SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+       ## Revised BSD License ##
+
+       Redistribution and use in source and binary forms, with or without
+       modification, are permitted provided that the following conditions are
+       met:
+           * Redistributions of source code must retain the above copyright
+             notice, this list of conditions and the following disclaimer.
+           * Redistributions in binary form must reproduce the above
+             copyright notice, this list of conditions and the following
+             disclaimer in the documentation and/or other materials provided
+             with the distribution.
+           * Neither the name of the <organization> nor the
+             names of its contributors may be used to endorse or promote
+             products derived from this software without specific prior
+             written permission.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+       "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+       LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+       A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT
+       HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+       EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+       PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES LOSS OF USE, DATA, OR
+       PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+       LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+       NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+       SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+*/
+
+
+#ifndef OPML_LEXER_MULTIMARKDOWN_H
+#define OPML_LEXER_MULTIMARKDOWN_H
+
+enum opml_tokens {
+       OPML_XML,
+       OPML_OPML_OPEN,
+       OPML_OPML_CLOSE,
+       OPML_HEAD_OPEN,
+       OPML_HEAD_CLOSE,
+       OPML_TITLE_OPEN,
+       OPML_TITLE_CLOSE,
+       OPML_BODY_OPEN,
+       OPML_BODY_CLOSE,
+       OPML_WSNL,
+};
+
+/// Re2c scanner data -- this structure is used by the re2c
+/// lexer to track progress and offsets within the source
+/// string.  They can be used to create "tokens" that match
+/// sections of the text with an abstract syntax tree.
+struct Scanner {
+       const char *    start;          //!< Start of current token
+       const char *    cur;            //!< Character currently being matched
+       const char *    ptr;            //!< Used for backtracking by re2c
+       const char *    ctx;
+};
+
+typedef struct Scanner Scanner;
+
+
+/// Scan for the next opml token
+int opml_scan(
+       Scanner * s,                    //!< Pointer to Scanner state structure
+       const char * stop               //!< Pointer to position in string at which to stop parsing
+);
+
+
+/// skip through text attribute to find value
+size_t scan_text(const char * c);
+
+
+/// skip through _note attribute to find value
+size_t scan_note(const char * c);
+
+
+/// find end of double quoted value
+size_t scan_double_quoted(const char * c);
+
+#endif
diff --git a/Sources/libMultiMarkdown/opml-lexer.re b/Sources/libMultiMarkdown/opml-lexer.re
new file mode 100644 (file)
index 0000000..3146a2d
--- /dev/null
@@ -0,0 +1,214 @@
+/**
+
+       MultiMarkdown -- Lightweight markup processor to produce HTML, LaTeX, and more.
+
+       @file opml-lexer.c
+
+       @brief Tokenize OPML file for parsing
+
+
+       @author Fletcher T. Penney
+       @bug    
+
+**/
+
+/*
+
+       Copyright © 2016 - 2018 Fletcher T. Penney.
+
+
+       The `MultiMarkdown 6` project is released under the MIT License..
+       
+       GLibFacade.c and GLibFacade.h are from the MultiMarkdown v4 project:
+       
+               https://github.com/fletcher/MultiMarkdown-4/
+       
+       MMD 4 is released under both the MIT License and GPL.
+       
+       
+       CuTest is released under the zlib/libpng license. See CuTest.c for the
+       text of the license.
+       
+       uthash library:
+               Copyright (c) 2005-2016, Troy D. Hanson
+       
+               Licensed under Revised BSD license
+       
+       miniz library:
+               Copyright 2013-2014 RAD Game Tools and Valve Software
+               Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
+       
+               Licensed under the MIT license
+       
+       argtable3 library:
+               Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+               <sheitmann@users.sourceforge.net>
+               All rights reserved.
+       
+               Licensed under the Revised BSD License
+       
+       
+       ## The MIT License ##
+       
+       Permission is hereby granted, free of charge, to any person obtaining
+       a copy of this software and associated documentation files (the
+       "Software"), to deal in the Software without restriction, including
+       without limitation the rights to use, copy, modify, merge, publish,
+       distribute, sublicense, and/or sell copies of the Software, and to
+       permit persons to whom the Software is furnished to do so, subject to
+       the following conditions:
+       
+       The above copyright notice and this permission notice shall be
+       included in all copies or substantial portions of the Software.
+       
+       THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+       EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+       MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+       IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+       CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+       TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+       SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+       
+       
+       ## Revised BSD License ##
+       
+       Redistribution and use in source and binary forms, with or without
+       modification, are permitted provided that the following conditions are
+       met:
+           * Redistributions of source code must retain the above copyright
+             notice, this list of conditions and the following disclaimer.
+           * Redistributions in binary form must reproduce the above
+             copyright notice, this list of conditions and the following
+             disclaimer in the documentation and/or other materials provided
+             with the distribution.
+           * Neither the name of the <organization> nor the
+             names of its contributors may be used to endorse or promote
+             products derived from this software without specific prior
+             written permission.
+       
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+       "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+       LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+       A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT
+       HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+       EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+       PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES LOSS OF USE, DATA, OR
+       PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+       LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+       NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+       SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+       
+
+*/
+
+#include <stdlib.h>
+
+#include "opml-lexer.h"
+#include "opml-parser.h"
+
+
+// Basic scanner struct
+
+#define YYCTYPE                unsigned char
+#define YYCURSOR       s->cur
+#define YYMARKER       s->ptr
+#define YYCTXMARKER    s->ctx
+
+int opml_scan(Scanner * s, const char * stop) {
+
+       scan:
+
+       if (s->cur >= stop) {
+               return 0;
+       }
+
+       s->start = s->cur;
+
+       /*!re2c
+               re2c:yyfill:enable = 0;
+
+               NL                                                                                                              = "\r\n" | '\n' | '\r';
+               WS                                                                                                              = [ \t]+;
+               WSNL                                                                                                    = (NL | WS)+;
+
+               EQUAL                                                                                                   = '=';
+               
+               double_quoted                                                                                   = '"' [^"\x00]* '"';
+
+               text_attribute                                                                                  = WSNL* 'text' WSNL* EQUAL WSNL*;
+               note_attribute                                                                                  = WSNL* '_note' WSNL* EQUAL WSNL*;
+
+               '<?xml' [^>\x00]* '>'                                                                   { return OPML_XML; }
+
+               '<opml' [^>\x00]* '>'                                                                   { return OPML_OPML_OPEN; }
+               '</opml>'                                                                                               { return OPML_OPML_CLOSE; }
+
+               '<head' [^>\x00]* '>'                                                                   { return OPML_HEAD_OPEN; }
+               '</head>'                                                                                               { return OPML_HEAD_CLOSE; }
+
+               '<title' [^>\x00]* '>'                                                                  { return OPML_TITLE_OPEN; }
+               '</title>'                                                                                              { return OPML_TITLE_CLOSE; }
+
+               '<body' [^>\x00]* '>'                                                                   { return OPML_BODY_OPEN; }
+               '</body>'                                                                                               { return OPML_BODY_CLOSE; }
+
+               '<outline' text_attribute '"(Untitled Preamble)"' note_attribute double_quoted WSNL* '>'                { return OPML_OUTLINE_PREAMBLE; }
+               '<outline' text_attribute '"Metadata"' WSNL* '>'                                        { return OPML_OUTLINE_METADATA; }
+
+
+               '<outline' [^>\x00]* '/>'                                                               { return OPML_OUTLINE_SELF_CLOSE; }
+               '<outline' [^>\x00]* '>'                                                                { return OPML_OUTLINE_OPEN; }
+               '</outline>'                                                                                    { return OPML_OUTLINE_CLOSE; }
+
+               WSNL                                                                                                    { return OPML_WSNL; }
+
+               // Skip over anything else - '.' does not include '\n'
+               .                                                                                                               { goto scan; }
+       */
+}
+
+
+/*!re2c
+
+       re2c:define:YYCTYPE = "unsigned char";
+       re2c:define:YYCURSOR = c;
+       re2c:define:YYMARKER = marker;
+       re2c:define:YYCTXMARKER = marker;
+       re2c:yyfill:enable = 0;
+
+*/
+
+/// skip through text attribute to find value
+size_t scan_text(const char * c) {
+       const char * marker = NULL;
+       const char * start = c;
+
+/*!re2c
+       text_attribute / double_quoted          { return (size_t)( c - start ); }
+       .?                                                                              { return 0; }
+*/     
+}
+
+
+/// skip through _note attribute to find value
+size_t scan_note(const char * c) {
+       const char * marker = NULL;
+       const char * start = c;
+
+/*!re2c
+       note_attribute / double_quoted  { return (size_t)( c - start ); }
+       .?                                                                              { return 0; }
+*/     
+}
+
+
+/// find end of double quoted value
+size_t scan_double_quoted(const char * c) {
+       const char * marker = NULL;
+       const char * start = c;
+
+/*!re2c
+       double_quoted                                                   { return (size_t)( c - start ); }
+       .?                                                                              { return 0; }
+*/     
+}
diff --git a/Sources/libMultiMarkdown/opml-parser.c b/Sources/libMultiMarkdown/opml-parser.c
new file mode 100644 (file)
index 0000000..f356bc6
--- /dev/null
@@ -0,0 +1,1081 @@
+/*
+** 2000-05-29
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+*************************************************************************
+** Driver template for the LEMON parser generator.
+**
+** The "lemon" program processes an LALR(1) input grammar file, then uses
+** this template to construct a parser.  The "lemon" program inserts text
+** at each "%%" line.  Also, any "P-a-r-s-e" identifer prefix (without the
+** interstitial "-" characters) contained in this template is changed into
+** the value of the %name directive from the grammar.  Otherwise, the content
+** of this template is copied straight through into the generate parser
+** source file.
+**
+** The following is the concatenation of all %include directives from the
+** input grammar file:
+*/
+#include <stdio.h>
+/************ Begin %include sections from the grammar ************************/
+
+       #include <assert.h>
+       #include <stdio.h>
+       #include <stdlib.h>
+
+       #include "libMultiMarkdown.h"
+       #include "mmd.h"
+       #include "parser.h"
+       #include "token.h"
+/**************** End of %include directives **********************************/
+/* These constants specify the various numeric values for terminal symbols
+** in a format understandable to "makeheaders".  This section is blank unless
+** "lemon" is run with the "-m" command-line option.
+***************** Begin makeheaders token definitions *************************/
+/**************** End makeheaders token definitions ***************************/
+
+/* The next sections is a series of control #defines.
+** various aspects of the generated parser.
+**    YYCODETYPE         is the data type used to store the integer codes
+**                       that represent terminal and non-terminal symbols.
+**                       "unsigned char" is used if there are fewer than
+**                       256 symbols.  Larger types otherwise.
+**    YYNOCODE           is a number of type YYCODETYPE that is not used for
+**                       any terminal or nonterminal symbol.
+**    YYFALLBACK         If defined, this indicates that one or more tokens
+**                       (also known as: "terminal symbols") have fall-back
+**                       values which should be used if the original symbol
+**                       would not parse.  This permits keywords to sometimes
+**                       be used as identifiers, for example.
+**    YYACTIONTYPE       is the data type used for "action codes" - numbers
+**                       that indicate what to do in response to the next
+**                       token.
+**    OPMLTOKENTYPE     is the data type used for minor type for terminal
+**                       symbols.  Background: A "minor type" is a semantic
+**                       value associated with a terminal or non-terminal
+**                       symbols.  For example, for an "ID" terminal symbol,
+**                       the minor type might be the name of the identifier.
+**                       Each non-terminal can have a different minor type.
+**                       Terminal symbols all have the same minor type, though.
+**                       This macros defines the minor type for terminal 
+**                       symbols.
+**    YYMINORTYPE        is the data type used for all minor types.
+**                       This is typically a union of many types, one of
+**                       which is OPMLTOKENTYPE.  The entry in the union
+**                       for terminal symbols is called "yy0".
+**    YYSTACKDEPTH       is the maximum depth of the parser's stack.  If
+**                       zero the stack is dynamically sized using realloc()
+**    OPMLARG_SDECL     A static variable declaration for the %extra_argument
+**    OPMLARG_PDECL     A parameter declaration for the %extra_argument
+**    OPMLARG_STORE     Code to store %extra_argument into yypParser
+**    OPMLARG_FETCH     Code to extract %extra_argument from yypParser
+**    YYERRORSYMBOL      is the code number of the error symbol.  If not
+**                       defined, then do no error processing.
+**    YYNSTATE           the combined number of states.
+**    YYNRULE            the number of rules in the grammar
+**    YY_MAX_SHIFT       Maximum value for shift actions
+**    YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions
+**    YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions
+**    YY_MIN_REDUCE      Maximum value for reduce actions
+**    YY_ERROR_ACTION    The yy_action[] code for syntax error
+**    YY_ACCEPT_ACTION   The yy_action[] code for accept
+**    YY_NO_ACTION       The yy_action[] code for no-op
+*/
+#ifndef INTERFACE
+# define INTERFACE 1
+#endif
+/************* Begin control #defines *****************************************/
+#define YYCODETYPE unsigned char
+#define YYNOCODE 26
+#define YYACTIONTYPE unsigned char
+#define OPMLTOKENTYPE  token * 
+typedef union {
+  int yyinit;
+  OPMLTOKENTYPE yy0;
+} YYMINORTYPE;
+#ifndef YYSTACKDEPTH
+#define YYSTACKDEPTH 100
+#endif
+#define OPMLARG_SDECL  mmd_engine * engine ;
+#define OPMLARG_PDECL , mmd_engine * engine 
+#define OPMLARG_FETCH  mmd_engine * engine  = yypParser->engine 
+#define OPMLARG_STORE yypParser->engine  = engine 
+#define YYNSTATE             18
+#define YYNRULE              17
+#define YY_MAX_SHIFT         17
+#define YY_MIN_SHIFTREDUCE   33
+#define YY_MAX_SHIFTREDUCE   49
+#define YY_MIN_REDUCE        50
+#define YY_MAX_REDUCE        66
+#define YY_ERROR_ACTION      67
+#define YY_ACCEPT_ACTION     68
+#define YY_NO_ACTION         69
+/************* End control #defines *******************************************/
+
+/* Define the yytestcase() macro to be a no-op if is not already defined
+** otherwise.
+**
+** Applications can choose to define yytestcase() in the %include section
+** to a macro that can assist in verifying code coverage.  For production
+** code the yytestcase() macro should be turned off.  But it is useful
+** for testing.
+*/
+#ifndef yytestcase
+# define yytestcase(X)
+#endif
+
+
+/* Next are the tables used to determine what action to take based on the
+** current state and lookahead token.  These tables are used to implement
+** functions that take a state number and lookahead value and return an
+** action integer.  
+**
+** Suppose the action integer is N.  Then the action is determined as
+** follows
+**
+**   0 <= N <= YY_MAX_SHIFT             Shift N.  That is, push the lookahead
+**                                      token onto the stack and goto state N.
+**
+**   N between YY_MIN_SHIFTREDUCE       Shift to an arbitrary state then
+**     and YY_MAX_SHIFTREDUCE           reduce by rule N-YY_MIN_SHIFTREDUCE.
+**
+**   N between YY_MIN_REDUCE            Reduce by rule N-YY_MIN_REDUCE
+**     and YY_MAX_REDUCE
+**
+**   N == YY_ERROR_ACTION               A syntax error has occurred.
+**
+**   N == YY_ACCEPT_ACTION              The parser accepts its input.
+**
+**   N == YY_NO_ACTION                  No such action.  Denotes unused
+**                                      slots in the yy_action[] table.
+**
+** The action table is constructed as a single large table named yy_action[].
+** Given state S and lookahead X, the action is computed as either:
+**
+**    (A)   N = yy_action[ yy_shift_ofst[S] + X ]
+**    (B)   N = yy_default[S]
+**
+** The (A) formula is preferred.  The B formula is used instead if:
+**    (1)  The yy_shift_ofst[S]+X value is out of range, or
+**    (2)  yy_lookahead[yy_shift_ofst[S]+X] is not equal to X, or
+**    (3)  yy_shift_ofst[S] equal YY_SHIFT_USE_DFLT.
+** (Implementation note: YY_SHIFT_USE_DFLT is chosen so that
+** YY_SHIFT_USE_DFLT+X will be out of range for all possible lookaheads X.
+** Hence only tests (1) and (2) need to be evaluated.)
+**
+** The formulas above are for computing the action when the lookahead is
+** a terminal symbol.  If the lookahead is a non-terminal (as occurs after
+** a reduce action) then the yy_reduce_ofst[] array is used in place of
+** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of
+** YY_SHIFT_USE_DFLT.
+**
+** The following are the tables generated in this section:
+**
+**  yy_action[]        A single table containing all actions.
+**  yy_lookahead[]     A table containing the lookahead for each entry in
+**                     yy_action.  Used to detect hash collisions.
+**  yy_shift_ofst[]    For each state, the offset into yy_action for
+**                     shifting terminals.
+**  yy_reduce_ofst[]   For each state, the offset into yy_action for
+**                     shifting non-terminals after a reduce.
+**  yy_default[]       Default action for each state.
+**
+*********** Begin parsing tables **********************************************/
+#define YY_ACTTAB_COUNT (57)
+static const YYACTIONTYPE yy_action[] = {
+ /*     0 */     1,   45,   14,    3,   49,   42,    1,   10,   14,    3,
+ /*    10 */    49,    1,   48,   14,    3,   49,    1,   47,   14,    3,
+ /*    20 */    49,   41,    1,   43,   14,    3,   49,    1,    2,   14,
+ /*    30 */     3,   49,   68,   17,   15,    6,    6,    7,    7,    8,
+ /*    40 */     5,    5,   12,    2,   40,    9,   13,   39,   11,    4,
+ /*    50 */    16,   38,   37,   36,   46,   35,   50,
+};
+static const YYCODETYPE yy_lookahead[] = {
+ /*     0 */    11,   12,   13,   14,   15,   10,   11,    1,   13,   14,
+ /*    10 */    15,   11,   12,   13,   14,   15,   11,   12,   13,   14,
+ /*    20 */    15,   10,   11,   24,   13,   14,   15,   11,    9,   13,
+ /*    30 */    14,   15,   17,   18,   21,   23,   24,   23,   24,    5,
+ /*    40 */    23,   24,   22,    9,    8,   20,   21,    6,    7,    3,
+ /*    50 */    19,    6,    4,    4,   12,    2,    0,
+};
+#define YY_SHIFT_USE_DFLT (57)
+#define YY_SHIFT_COUNT    (17)
+#define YY_SHIFT_MIN      (-11)
+#define YY_SHIFT_MAX      (56)
+static const signed char yy_shift_ofst[] = {
+ /*     0 */     6,  -11,   -5,   16,   34,    0,    5,   11,   41,   19,
+ /*    10 */    46,   36,   45,   48,   42,   49,   53,   56,
+};
+#define YY_REDUCE_USE_DFLT (-2)
+#define YY_REDUCE_COUNT (10)
+#define YY_REDUCE_MIN   (-1)
+#define YY_REDUCE_MAX   (31)
+static const signed char yy_reduce_ofst[] = {
+ /*     0 */    15,   12,   14,   17,   25,   -1,   -1,   -1,   20,   13,
+ /*    10 */    31,
+};
+static const YYACTIONTYPE yy_default[] = {
+ /*     0 */    67,   67,   67,   67,   67,   67,   67,   67,   67,   67,
+ /*    10 */    67,   67,   67,   67,   67,   67,   51,   67,
+};
+/********** End of lemon-generated parsing tables *****************************/
+
+/* The next table maps tokens (terminal symbols) into fallback tokens.  
+** If a construct like the following:
+** 
+**      %fallback ID X Y Z.
+**
+** appears in the grammar, then ID becomes a fallback token for X, Y,
+** and Z.  Whenever one of the tokens X, Y, or Z is input to the parser
+** but it does not parse, the type of the token is changed to ID and
+** the parse is retried before an error is thrown.
+**
+** This feature can be used, for example, to cause some keywords in a language
+** to revert to identifiers if they keyword does not apply in the context where
+** it appears.
+*/
+#ifdef YYFALLBACK
+static const YYCODETYPE yyFallback[] = {
+};
+#endif /* YYFALLBACK */
+
+/* The following structure represents a single element of the
+** parser's stack.  Information stored includes:
+**
+**   +  The state number for the parser at this level of the stack.
+**
+**   +  The value of the token stored at this level of the stack.
+**      (In other words, the "major" token.)
+**
+**   +  The semantic value stored at this level of the stack.  This is
+**      the information used by the action routines in the grammar.
+**      It is sometimes called the "minor" token.
+**
+** After the "shift" half of a SHIFTREDUCE action, the stateno field
+** actually contains the reduce action for the second half of the
+** SHIFTREDUCE.
+*/
+struct yyStackEntry {
+  YYACTIONTYPE stateno;  /* The state-number, or reduce action in SHIFTREDUCE */
+  YYCODETYPE major;      /* The major token value.  This is the code
+                         ** number for the token at this stack level */
+  YYMINORTYPE minor;     /* The user-supplied minor token value.  This
+                         ** is the value of the token  */
+};
+typedef struct yyStackEntry yyStackEntry;
+
+/* The state of the parser is completely contained in an instance of
+** the following structure */
+struct yyParser {
+  yyStackEntry *yytos;          /* Pointer to top element of the stack */
+#ifdef YYTRACKMAXSTACKDEPTH
+  int yyhwm;                    /* High-water mark of the stack */
+#endif
+#ifndef YYNOERRORRECOVERY
+  int yyerrcnt;                 /* Shifts left before out of the error */
+#endif
+  OPMLARG_SDECL                /* A place to hold %extra_argument */
+#if YYSTACKDEPTH<=0
+  int yystksz;                  /* Current side of the stack */
+  yyStackEntry *yystack;        /* The parser's stack */
+  yyStackEntry yystk0;          /* First stack entry */
+#else
+  yyStackEntry yystack[YYSTACKDEPTH];  /* The parser's stack */
+#endif
+};
+typedef struct yyParser yyParser;
+
+#ifndef NDEBUG
+#include <stdio.h>
+static FILE *yyTraceFILE = 0;
+static char *yyTracePrompt = 0;
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* 
+** Turn parser tracing on by giving a stream to which to write the trace
+** and a prompt to preface each trace message.  Tracing is turned off
+** by making either argument NULL 
+**
+** Inputs:
+** <ul>
+** <li> A FILE* to which trace output should be written.
+**      If NULL, then tracing is turned off.
+** <li> A prefix string written at the beginning of every
+**      line of trace output.  If NULL, then tracing is
+**      turned off.
+** </ul>
+**
+** Outputs:
+** None.
+*/
+void OPMLTrace(FILE *TraceFILE, char *zTracePrompt){
+  yyTraceFILE = TraceFILE;
+  yyTracePrompt = zTracePrompt;
+  if( yyTraceFILE==0 ) yyTracePrompt = 0;
+  else if( yyTracePrompt==0 ) yyTraceFILE = 0;
+}
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* For tracing shifts, the names of all terminals and nonterminals
+** are required.  The following table supplies these names */
+static const char *const yyTokenName[] = { 
+  "$",             "OPML_XML",      "OPML_WSNL",     "OPML_OPML_OPEN",
+  "OPML_OPML_CLOSE",  "OPML_HEAD_OPEN",  "OPML_HEAD_CLOSE",  "OPML_TITLE_OPEN",
+  "OPML_TITLE_CLOSE",  "OPML_BODY_OPEN",  "OPML_BODY_CLOSE",  "OPML_OUTLINE_OPEN",
+  "OPML_OUTLINE_CLOSE",  "OPML_OUTLINE_PREAMBLE",  "OPML_OUTLINE_METADATA",  "OPML_OUTLINE_SELF_CLOSE",
+  "error",         "doc",           "doc_xml",       "doc_opml",    
+  "opml_header",   "opml_body",     "opml_title",    "opml_outlines",
+  "opml_outline",
+};
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* For tracing reduce actions, the names of all rules are required.
+*/
+static const char *const yyRuleName[] = {
+ /*   0 */ "doc ::= doc_xml",
+ /*   1 */ "doc_xml ::= OPML_XML doc_opml",
+ /*   2 */ "doc_xml ::= OPML_XML doc_opml OPML_WSNL",
+ /*   3 */ "doc_opml ::= OPML_OPML_OPEN opml_header opml_body OPML_OPML_CLOSE",
+ /*   4 */ "doc_opml ::= OPML_OPML_OPEN opml_body OPML_OPML_CLOSE",
+ /*   5 */ "opml_header ::= OPML_HEAD_OPEN opml_title OPML_HEAD_CLOSE",
+ /*   6 */ "opml_header ::= OPML_HEAD_OPEN OPML_HEAD_CLOSE",
+ /*   7 */ "opml_title ::= OPML_TITLE_OPEN OPML_TITLE_CLOSE",
+ /*   8 */ "opml_body ::= OPML_BODY_OPEN opml_outlines OPML_BODY_CLOSE",
+ /*   9 */ "opml_body ::= OPML_BODY_OPEN OPML_BODY_CLOSE",
+ /*  10 */ "opml_outlines ::= opml_outlines opml_outline",
+ /*  11 */ "opml_outlines ::= opml_outline",
+ /*  12 */ "opml_outline ::= OPML_OUTLINE_OPEN OPML_OUTLINE_CLOSE",
+ /*  13 */ "opml_outline ::= OPML_OUTLINE_PREAMBLE OPML_OUTLINE_CLOSE",
+ /*  14 */ "opml_outline ::= OPML_OUTLINE_OPEN opml_outlines OPML_OUTLINE_CLOSE",
+ /*  15 */ "opml_outline ::= OPML_OUTLINE_METADATA opml_outlines OPML_OUTLINE_CLOSE",
+ /*  16 */ "opml_outline ::= OPML_OUTLINE_SELF_CLOSE",
+};
+#endif /* NDEBUG */
+
+
+#if YYSTACKDEPTH<=0
+/*
+** Try to increase the size of the parser stack.  Return the number
+** of errors.  Return 0 on success.
+*/
+static int yyGrowStack(yyParser *p){
+  int newSize;
+  int idx;
+  yyStackEntry *pNew;
+
+  newSize = p->yystksz*2 + 100;
+  idx = p->yytos ? (int)(p->yytos - p->yystack) : 0;
+  if( p->yystack==&p->yystk0 ){
+    pNew = malloc(newSize*sizeof(pNew[0]));
+    if( pNew ) pNew[0] = p->yystk0;
+  }else{
+    pNew = realloc(p->yystack, newSize*sizeof(pNew[0]));
+  }
+  if( pNew ){
+    p->yystack = pNew;
+    p->yytos = &p->yystack[idx];
+#ifndef NDEBUG
+    if( yyTraceFILE ){
+      fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n",
+              yyTracePrompt, p->yystksz, newSize);
+    }
+#endif
+    p->yystksz = newSize;
+  }
+  return pNew==0; 
+}
+#endif
+
+/* Datatype of the argument to the memory allocated passed as the
+** second argument to OPMLAlloc() below.  This can be changed by
+** putting an appropriate #define in the %include section of the input
+** grammar.
+*/
+#ifndef YYMALLOCARGTYPE
+# define YYMALLOCARGTYPE size_t
+#endif
+
+/* 
+** This function allocates a new parser.
+** The only argument is a pointer to a function which works like
+** malloc.
+**
+** Inputs:
+** A pointer to the function used to allocate memory.
+**
+** Outputs:
+** A pointer to a parser.  This pointer is used in subsequent calls
+** to OPML and OPMLFree.
+*/
+void *OPMLAlloc(void *(*mallocProc)(YYMALLOCARGTYPE)){
+  yyParser *pParser;
+  pParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) );
+  if( pParser ){
+#ifdef YYTRACKMAXSTACKDEPTH
+    pParser->yyhwm = 0;
+#endif
+#if YYSTACKDEPTH<=0
+    pParser->yytos = NULL;
+    pParser->yystack = NULL;
+    pParser->yystksz = 0;
+    if( yyGrowStack(pParser) ){
+      pParser->yystack = &pParser->yystk0;
+      pParser->yystksz = 1;
+    }
+#endif
+#ifndef YYNOERRORRECOVERY
+    pParser->yyerrcnt = -1;
+#endif
+    pParser->yytos = pParser->yystack;
+    pParser->yystack[0].stateno = 0;
+    pParser->yystack[0].major = 0;
+  }
+  return pParser;
+}
+
+/* The following function deletes the "minor type" or semantic value
+** associated with a symbol.  The symbol can be either a terminal
+** or nonterminal. "yymajor" is the symbol code, and "yypminor" is
+** a pointer to the value to be deleted.  The code used to do the 
+** deletions is derived from the %destructor and/or %token_destructor
+** directives of the input grammar.
+*/
+static void yy_destructor(
+  yyParser *yypParser,    /* The parser */
+  YYCODETYPE yymajor,     /* Type code for object to destroy */
+  YYMINORTYPE *yypminor   /* The object to be destroyed */
+){
+  OPMLARG_FETCH;
+  switch( yymajor ){
+    /* Here is inserted the actions which take place when a
+    ** terminal or non-terminal is destroyed.  This can happen
+    ** when the symbol is popped from the stack during a
+    ** reduce or during error processing or when a parser is 
+    ** being destroyed before it is finished parsing.
+    **
+    ** Note: during a reduce, the only symbols destroyed are those
+    ** which appear on the RHS of the rule, but which are *not* used
+    ** inside the C code.
+    */
+/********* Begin destructor definitions ***************************************/
+/********* End destructor definitions *****************************************/
+    default:  break;   /* If no destructor action specified: do nothing */
+  }
+}
+
+/*
+** Pop the parser's stack once.
+**
+** If there is a destructor routine associated with the token which
+** is popped from the stack, then call it.
+*/
+static void yy_pop_parser_stack(yyParser *pParser){
+  yyStackEntry *yytos;
+  assert( pParser->yytos!=0 );
+  assert( pParser->yytos > pParser->yystack );
+  yytos = pParser->yytos--;
+#ifndef NDEBUG
+  if( yyTraceFILE ){
+    fprintf(yyTraceFILE,"%sPopping %s\n",
+      yyTracePrompt,
+      yyTokenName[yytos->major]);
+  }
+#endif
+  yy_destructor(pParser, yytos->major, &yytos->minor);
+}
+
+/* 
+** Deallocate and destroy a parser.  Destructors are called for
+** all stack elements before shutting the parser down.
+**
+** If the YYPARSEFREENEVERNULL macro exists (for example because it
+** is defined in a %include section of the input grammar) then it is
+** assumed that the input pointer is never NULL.
+*/
+void OPMLFree(
+  void *p,                    /* The parser to be deleted */
+  void (*freeProc)(void*)     /* Function used to reclaim memory */
+){
+  yyParser *pParser = (yyParser*)p;
+#ifndef YYPARSEFREENEVERNULL
+  if( pParser==0 ) return;
+#endif
+  while( pParser->yytos>pParser->yystack ) yy_pop_parser_stack(pParser);
+#if YYSTACKDEPTH<=0
+  if( pParser->yystack!=&pParser->yystk0 ) free(pParser->yystack);
+#endif
+  (*freeProc)((void*)pParser);
+}
+
+/*
+** Return the peak depth of the stack for a parser.
+*/
+#ifdef YYTRACKMAXSTACKDEPTH
+int OPMLStackPeak(void *p){
+  yyParser *pParser = (yyParser*)p;
+  return pParser->yyhwm;
+}
+#endif
+
+/*
+** Find the appropriate action for a parser given the terminal
+** look-ahead token iLookAhead.
+*/
+static unsigned int yy_find_shift_action(
+  yyParser *pParser,        /* The parser */
+  YYCODETYPE iLookAhead     /* The look-ahead token */
+){
+  int i;
+  int stateno = pParser->yytos->stateno;
+  if( stateno>=YY_MIN_REDUCE ) return stateno;
+  assert( stateno <= YY_SHIFT_COUNT );
+  do{
+    i = yy_shift_ofst[stateno];
+    assert( iLookAhead!=YYNOCODE );
+    i += iLookAhead;
+    if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
+#ifdef YYFALLBACK
+      YYCODETYPE iFallback;            /* Fallback token */
+      if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
+             && (iFallback = yyFallback[iLookAhead])!=0 ){
+#ifndef NDEBUG
+        if( yyTraceFILE ){
+          fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
+             yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
+        }
+#endif
+        assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */
+        iLookAhead = iFallback;
+        continue;
+      }
+#endif
+#ifdef YYWILDCARD
+      {
+        int j = i - iLookAhead + YYWILDCARD;
+        if( 
+#if YY_SHIFT_MIN+YYWILDCARD<0
+          j>=0 &&
+#endif
+#if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT
+          j<YY_ACTTAB_COUNT &&
+#endif
+          yy_lookahead[j]==YYWILDCARD && iLookAhead>0
+        ){
+#ifndef NDEBUG
+          if( yyTraceFILE ){
+            fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
+               yyTracePrompt, yyTokenName[iLookAhead],
+               yyTokenName[YYWILDCARD]);
+          }
+#endif /* NDEBUG */
+          return yy_action[j];
+        }
+      }
+#endif /* YYWILDCARD */
+      return yy_default[stateno];
+    }else{
+      return yy_action[i];
+    }
+  }while(1);
+}
+
+/*
+** Find the appropriate action for a parser given the non-terminal
+** look-ahead token iLookAhead.
+*/
+static int yy_find_reduce_action(
+  int stateno,              /* Current state number */
+  YYCODETYPE iLookAhead     /* The look-ahead token */
+){
+  int i;
+#ifdef YYERRORSYMBOL
+  if( stateno>YY_REDUCE_COUNT ){
+    return yy_default[stateno];
+  }
+#else
+  assert( stateno<=YY_REDUCE_COUNT );
+#endif
+  i = yy_reduce_ofst[stateno];
+  assert( i!=YY_REDUCE_USE_DFLT );
+  assert( iLookAhead!=YYNOCODE );
+  i += iLookAhead;
+#ifdef YYERRORSYMBOL
+  if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
+    return yy_default[stateno];
+  }
+#else
+  assert( i>=0 && i<YY_ACTTAB_COUNT );
+  assert( yy_lookahead[i]==iLookAhead );
+#endif
+  return yy_action[i];
+}
+
+/*
+** The following routine is called if the stack overflows.
+*/
+static void yyStackOverflow(yyParser *yypParser){
+   OPMLARG_FETCH;
+   yypParser->yytos--;
+#ifndef NDEBUG
+   if( yyTraceFILE ){
+     fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
+   }
+#endif
+   while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
+   /* Here code is inserted which will execute if the parser
+   ** stack every overflows */
+/******** Begin %stack_overflow code ******************************************/
+/******** End %stack_overflow code ********************************************/
+   OPMLARG_STORE; /* Suppress warning about unused %extra_argument var */
+}
+
+/*
+** Print tracing information for a SHIFT action
+*/
+#ifndef NDEBUG
+static void yyTraceShift(yyParser *yypParser, int yyNewState){
+  if( yyTraceFILE ){
+    if( yyNewState<YYNSTATE ){
+      fprintf(yyTraceFILE,"%sShift '%s', go to state %d\n",
+         yyTracePrompt,yyTokenName[yypParser->yytos->major],
+         yyNewState);
+    }else{
+      fprintf(yyTraceFILE,"%sShift '%s'\n",
+         yyTracePrompt,yyTokenName[yypParser->yytos->major]);
+    }
+  }
+}
+#else
+# define yyTraceShift(X,Y)
+#endif
+
+/*
+** Perform a shift action.
+*/
+static void yy_shift(
+  yyParser *yypParser,          /* The parser to be shifted */
+  int yyNewState,               /* The new state to shift in */
+  int yyMajor,                  /* The major token to shift in */
+  OPMLTOKENTYPE yyMinor        /* The minor token to shift in */
+){
+  yyStackEntry *yytos;
+  yypParser->yytos++;
+#ifdef YYTRACKMAXSTACKDEPTH
+  if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
+    yypParser->yyhwm++;
+    assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) );
+  }
+#endif
+#if YYSTACKDEPTH>0 
+  if( yypParser->yytos>=&yypParser->yystack[YYSTACKDEPTH] ){
+    yyStackOverflow(yypParser);
+    return;
+  }
+#else
+  if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz] ){
+    if( yyGrowStack(yypParser) ){
+      yyStackOverflow(yypParser);
+      return;
+    }
+  }
+#endif
+  if( yyNewState > YY_MAX_SHIFT ){
+    yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
+  }
+  yytos = yypParser->yytos;
+  yytos->stateno = (YYACTIONTYPE)yyNewState;
+  yytos->major = (YYCODETYPE)yyMajor;
+  yytos->minor.yy0 = yyMinor;
+  yyTraceShift(yypParser, yyNewState);
+}
+
+/* The following table contains information about every rule that
+** is used during the reduce.
+*/
+static const struct {
+  YYCODETYPE lhs;         /* Symbol on the left-hand side of the rule */
+  unsigned char nrhs;     /* Number of right-hand side symbols in the rule */
+} yyRuleInfo[] = {
+  { 17, 1 },
+  { 18, 2 },
+  { 18, 3 },
+  { 19, 4 },
+  { 19, 3 },
+  { 20, 3 },
+  { 20, 2 },
+  { 22, 2 },
+  { 21, 3 },
+  { 21, 2 },
+  { 23, 2 },
+  { 23, 1 },
+  { 24, 2 },
+  { 24, 2 },
+  { 24, 3 },
+  { 24, 3 },
+  { 24, 1 },
+};
+
+static void yy_accept(yyParser*);  /* Forward Declaration */
+
+/*
+** Perform a reduce action and the shift that must immediately
+** follow the reduce.
+*/
+static void yy_reduce(
+  yyParser *yypParser,         /* The parser */
+  unsigned int yyruleno        /* Number of the rule by which to reduce */
+){
+  int yygoto;                     /* The next state */
+  int yyact;                      /* The next action */
+  yyStackEntry *yymsp;            /* The top of the parser's stack */
+  int yysize;                     /* Amount to pop the stack */
+  OPMLARG_FETCH;
+  yymsp = yypParser->yytos;
+#ifndef NDEBUG
+  if( yyTraceFILE && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
+    yysize = yyRuleInfo[yyruleno].nrhs;
+    fprintf(yyTraceFILE, "%sReduce [%s], go to state %d.\n", yyTracePrompt,
+      yyRuleName[yyruleno], yymsp[-yysize].stateno);
+  }
+#endif /* NDEBUG */
+
+  /* Check that the stack is large enough to grow by a single entry
+  ** if the RHS of the rule is empty.  This ensures that there is room
+  ** enough on the stack to push the LHS value */
+  if( yyRuleInfo[yyruleno].nrhs==0 ){
+#ifdef YYTRACKMAXSTACKDEPTH
+    if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
+      yypParser->yyhwm++;
+      assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack));
+    }
+#endif
+#if YYSTACKDEPTH>0 
+    if( yypParser->yytos>=&yypParser->yystack[YYSTACKDEPTH-1] ){
+      yyStackOverflow(yypParser);
+      return;
+    }
+#else
+    if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){
+      if( yyGrowStack(yypParser) ){
+        yyStackOverflow(yypParser);
+        return;
+      }
+      yymsp = yypParser->yytos;
+    }
+#endif
+  }
+
+  switch( yyruleno ){
+  /* Beginning here are the reduction cases.  A typical example
+  ** follows:
+  **   case 0:
+  **  #line <lineno> <grammarfile>
+  **     { ... }           // User supplied code
+  **  #line <lineno> <thisfile>
+  **     break;
+  */
+/********** Begin reduce actions **********************************************/
+      case 0: /* doc ::= doc_xml */
+{ engine->root = yymsp[0].minor.yy0; }
+        break;
+      default:
+      /* (1) doc_xml ::= OPML_XML doc_opml */ yytestcase(yyruleno==1);
+      /* (2) doc_xml ::= OPML_XML doc_opml OPML_WSNL */ yytestcase(yyruleno==2);
+      /* (3) doc_opml ::= OPML_OPML_OPEN opml_header opml_body OPML_OPML_CLOSE */ yytestcase(yyruleno==3);
+      /* (4) doc_opml ::= OPML_OPML_OPEN opml_body OPML_OPML_CLOSE */ yytestcase(yyruleno==4);
+      /* (5) opml_header ::= OPML_HEAD_OPEN opml_title OPML_HEAD_CLOSE */ yytestcase(yyruleno==5);
+      /* (6) opml_header ::= OPML_HEAD_OPEN OPML_HEAD_CLOSE */ yytestcase(yyruleno==6);
+      /* (7) opml_title ::= OPML_TITLE_OPEN OPML_TITLE_CLOSE */ yytestcase(yyruleno==7);
+      /* (8) opml_body ::= OPML_BODY_OPEN opml_outlines OPML_BODY_CLOSE */ yytestcase(yyruleno==8);
+      /* (9) opml_body ::= OPML_BODY_OPEN OPML_BODY_CLOSE */ yytestcase(yyruleno==9);
+      /* (10) opml_outlines ::= opml_outlines opml_outline */ yytestcase(yyruleno==10);
+      /* (11) opml_outlines ::= opml_outline (OPTIMIZED OUT) */ assert(yyruleno!=11);
+      /* (12) opml_outline ::= OPML_OUTLINE_OPEN OPML_OUTLINE_CLOSE */ yytestcase(yyruleno==12);
+      /* (13) opml_outline ::= OPML_OUTLINE_PREAMBLE OPML_OUTLINE_CLOSE */ yytestcase(yyruleno==13);
+      /* (14) opml_outline ::= OPML_OUTLINE_OPEN opml_outlines OPML_OUTLINE_CLOSE */ yytestcase(yyruleno==14);
+      /* (15) opml_outline ::= OPML_OUTLINE_METADATA opml_outlines OPML_OUTLINE_CLOSE */ yytestcase(yyruleno==15);
+      /* (16) opml_outline ::= OPML_OUTLINE_SELF_CLOSE */ yytestcase(yyruleno==16);
+        break;
+/********** End reduce actions ************************************************/
+  };
+  assert( yyruleno<sizeof(yyRuleInfo)/sizeof(yyRuleInfo[0]) );
+  yygoto = yyRuleInfo[yyruleno].lhs;
+  yysize = yyRuleInfo[yyruleno].nrhs;
+  yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto);
+  if( yyact <= YY_MAX_SHIFTREDUCE ){
+    if( yyact>YY_MAX_SHIFT ){
+      yyact += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
+    }
+    yymsp -= yysize-1;
+    yypParser->yytos = yymsp;
+    yymsp->stateno = (YYACTIONTYPE)yyact;
+    yymsp->major = (YYCODETYPE)yygoto;
+    yyTraceShift(yypParser, yyact);
+  }else{
+    assert( yyact == YY_ACCEPT_ACTION );
+    yypParser->yytos -= yysize;
+    yy_accept(yypParser);
+  }
+}
+
+/*
+** The following code executes when the parse fails
+*/
+#ifndef YYNOERRORRECOVERY
+static void yy_parse_failed(
+  yyParser *yypParser           /* The parser */
+){
+  OPMLARG_FETCH;
+#ifndef NDEBUG
+  if( yyTraceFILE ){
+    fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
+  }
+#endif
+  while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
+  /* Here code is inserted which will be executed whenever the
+  ** parser fails */
+/************ Begin %parse_failure code ***************************************/
+
+       fprintf(stderr, "Parser failed to successfully parse.\n");
+/************ End %parse_failure code *****************************************/
+  OPMLARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+#endif /* YYNOERRORRECOVERY */
+
+/*
+** The following code executes when a syntax error first occurs.
+*/
+static void yy_syntax_error(
+  yyParser *yypParser,           /* The parser */
+  int yymajor,                   /* The major type of the error token */
+  OPMLTOKENTYPE yyminor         /* The minor type of the error token */
+){
+  OPMLARG_FETCH;
+#define TOKEN yyminor
+/************ Begin %syntax_error code ****************************************/
+
+       fprintf(stderr,"Parser syntax error.\n");
+#ifndef NDEBUG
+       fprintf(stderr,"Parser syntax error.\n");
+       int n = sizeof(yyTokenName) / sizeof(yyTokenName[0]);
+       for (int i = 0; i < n; ++i) {
+               int a = yy_find_shift_action(yypParser, (YYCODETYPE)i);
+               if (a < YYNSTATE + YYNRULE) {
+                       fprintf(stderr,"expected token: %s\n", yyTokenName[i]);
+               }
+       }
+#endif
+/************ End %syntax_error code ******************************************/
+  OPMLARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/*
+** The following is executed when the parser accepts
+*/
+static void yy_accept(
+  yyParser *yypParser           /* The parser */
+){
+  OPMLARG_FETCH;
+#ifndef NDEBUG
+  if( yyTraceFILE ){
+    fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
+  }
+#endif
+#ifndef YYNOERRORRECOVERY
+  yypParser->yyerrcnt = -1;
+#endif
+  assert( yypParser->yytos==yypParser->yystack );
+  /* Here code is inserted which will be executed whenever the
+  ** parser accepts */
+/*********** Begin %parse_accept code *****************************************/
+
+//     printf("parsing completed successfully!\n");
+/*********** End %parse_accept code *******************************************/
+  OPMLARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/* The main parser program.
+** The first argument is a pointer to a structure obtained from
+** "OPMLAlloc" which describes the current state of the parser.
+** The second argument is the major token number.  The third is
+** the minor token.  The fourth optional argument is whatever the
+** user wants (and specified in the grammar) and is available for
+** use by the action routines.
+**
+** Inputs:
+** <ul>
+** <li> A pointer to the parser (an opaque structure.)
+** <li> The major token number.
+** <li> The minor token number.
+** <li> An option argument of a grammar-specified type.
+** </ul>
+**
+** Outputs:
+** None.
+*/
+void OPML(
+  void *yyp,                   /* The parser */
+  int yymajor,                 /* The major token code number */
+  OPMLTOKENTYPE yyminor       /* The value for the token */
+  OPMLARG_PDECL               /* Optional %extra_argument parameter */
+){
+  YYMINORTYPE yyminorunion;
+  unsigned int yyact;   /* The parser action. */
+#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
+  int yyendofinput;     /* True if we are at the end of input */
+#endif
+#ifdef YYERRORSYMBOL
+  int yyerrorhit = 0;   /* True if yymajor has invoked an error */
+#endif
+  yyParser *yypParser;  /* The parser */
+
+  yypParser = (yyParser*)yyp;
+  assert( yypParser->yytos!=0 );
+#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
+  yyendofinput = (yymajor==0);
+#endif
+  OPMLARG_STORE;
+
+#ifndef NDEBUG
+  if( yyTraceFILE ){
+    fprintf(yyTraceFILE,"%sInput '%s'\n",yyTracePrompt,yyTokenName[yymajor]);
+  }
+#endif
+
+  do{
+    yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor);
+    if( yyact <= YY_MAX_SHIFTREDUCE ){
+      yy_shift(yypParser,yyact,yymajor,yyminor);
+#ifndef YYNOERRORRECOVERY
+      yypParser->yyerrcnt--;
+#endif
+      yymajor = YYNOCODE;
+    }else if( yyact <= YY_MAX_REDUCE ){
+      yy_reduce(yypParser,yyact-YY_MIN_REDUCE);
+    }else{
+      assert( yyact == YY_ERROR_ACTION );
+      yyminorunion.yy0 = yyminor;
+#ifdef YYERRORSYMBOL
+      int yymx;
+#endif
+#ifndef NDEBUG
+      if( yyTraceFILE ){
+        fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt);
+      }
+#endif
+#ifdef YYERRORSYMBOL
+      /* A syntax error has occurred.
+      ** The response to an error depends upon whether or not the
+      ** grammar defines an error token "ERROR".  
+      **
+      ** This is what we do if the grammar does define ERROR:
+      **
+      **  * Call the %syntax_error function.
+      **
+      **  * Begin popping the stack until we enter a state where
+      **    it is legal to shift the error symbol, then shift
+      **    the error symbol.
+      **
+      **  * Set the error count to three.
+      **
+      **  * Begin accepting and shifting new tokens.  No new error
+      **    processing will occur until three tokens have been
+      **    shifted successfully.
+      **
+      */
+      if( yypParser->yyerrcnt<0 ){
+        yy_syntax_error(yypParser,yymajor,yyminor);
+      }
+      yymx = yypParser->yytos->major;
+      if( yymx==YYERRORSYMBOL || yyerrorhit ){
+#ifndef NDEBUG
+        if( yyTraceFILE ){
+          fprintf(yyTraceFILE,"%sDiscard input token %s\n",
+             yyTracePrompt,yyTokenName[yymajor]);
+        }
+#endif
+        yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion);
+        yymajor = YYNOCODE;
+      }else{
+        while( yypParser->yytos >= yypParser->yystack
+            && yymx != YYERRORSYMBOL
+            && (yyact = yy_find_reduce_action(
+                        yypParser->yytos->stateno,
+                        YYERRORSYMBOL)) >= YY_MIN_REDUCE
+        ){
+          yy_pop_parser_stack(yypParser);
+        }
+        if( yypParser->yytos < yypParser->yystack || yymajor==0 ){
+          yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
+          yy_parse_failed(yypParser);
+#ifndef YYNOERRORRECOVERY
+          yypParser->yyerrcnt = -1;
+#endif
+          yymajor = YYNOCODE;
+        }else if( yymx!=YYERRORSYMBOL ){
+          yy_shift(yypParser,yyact,YYERRORSYMBOL,yyminor);
+        }
+      }
+      yypParser->yyerrcnt = 3;
+      yyerrorhit = 1;
+#elif defined(YYNOERRORRECOVERY)
+      /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to
+      ** do any kind of error recovery.  Instead, simply invoke the syntax
+      ** error routine and continue going as if nothing had happened.
+      **
+      ** Applications can set this macro (for example inside %include) if
+      ** they intend to abandon the parse upon the first syntax error seen.
+      */
+      yy_syntax_error(yypParser,yymajor, yyminor);
+      yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
+      yymajor = YYNOCODE;
+      
+#else  /* YYERRORSYMBOL is not defined */
+      /* This is what we do if the grammar does not define ERROR:
+      **
+      **  * Report an error message, and throw away the input token.
+      **
+      **  * If the input token is $, then fail the parse.
+      **
+      ** As before, subsequent error messages are suppressed until
+      ** three input tokens have been successfully shifted.
+      */
+      if( yypParser->yyerrcnt<=0 ){
+        yy_syntax_error(yypParser,yymajor, yyminor);
+      }
+      yypParser->yyerrcnt = 3;
+      yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
+      if( yyendofinput ){
+        yy_parse_failed(yypParser);
+#ifndef YYNOERRORRECOVERY
+        yypParser->yyerrcnt = -1;
+#endif
+      }
+      yymajor = YYNOCODE;
+#endif
+    }
+  }while( yymajor!=YYNOCODE && yypParser->yytos>yypParser->yystack );
+#ifndef NDEBUG
+  if( yyTraceFILE ){
+    yyStackEntry *i;
+    char cDiv = '[';
+    fprintf(yyTraceFILE,"%sReturn. Stack=",yyTracePrompt);
+    for(i=&yypParser->yystack[1]; i<=yypParser->yytos; i++){
+      fprintf(yyTraceFILE,"%c%s", cDiv, yyTokenName[i->major]);
+      cDiv = ' ';
+    }
+    fprintf(yyTraceFILE,"]\n");
+  }
+#endif
+  return;
+}
diff --git a/Sources/libMultiMarkdown/opml-parser.h b/Sources/libMultiMarkdown/opml-parser.h
new file mode 100644 (file)
index 0000000..d974765
--- /dev/null
@@ -0,0 +1,15 @@
+#define OPML_XML                         1
+#define OPML_WSNL                        2
+#define OPML_OPML_OPEN                   3
+#define OPML_OPML_CLOSE                  4
+#define OPML_HEAD_OPEN                   5
+#define OPML_HEAD_CLOSE                  6
+#define OPML_TITLE_OPEN                  7
+#define OPML_TITLE_CLOSE                 8
+#define OPML_BODY_OPEN                   9
+#define OPML_BODY_CLOSE                 10
+#define OPML_OUTLINE_OPEN               11
+#define OPML_OUTLINE_CLOSE              12
+#define OPML_OUTLINE_PREAMBLE           13
+#define OPML_OUTLINE_METADATA           14
+#define OPML_OUTLINE_SELF_CLOSE         15
diff --git a/Sources/libMultiMarkdown/opml-parser.out b/Sources/libMultiMarkdown/opml-parser.out
new file mode 100644 (file)
index 0000000..7ce1075
--- /dev/null
@@ -0,0 +1,215 @@
+State 0:
+          doc ::= * doc_xml
+          doc_xml ::= * OPML_XML doc_opml
+          doc_xml ::= * OPML_XML doc_opml OPML_WSNL
+
+                      OPML_XML shift        10     
+                           doc accept
+                       doc_xml shift        17     
+
+State 1:
+          opml_outlines ::= * opml_outlines opml_outline
+          opml_outlines ::= * opml_outline
+          opml_outline ::= * OPML_OUTLINE_OPEN OPML_OUTLINE_CLOSE
+          opml_outline ::= OPML_OUTLINE_OPEN * OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_PREAMBLE OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_OPEN opml_outlines OPML_OUTLINE_CLOSE
+          opml_outline ::= OPML_OUTLINE_OPEN * opml_outlines OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_METADATA opml_outlines OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_SELF_CLOSE
+
+             OPML_OUTLINE_OPEN shift        1      
+            OPML_OUTLINE_CLOSE shift-reduce 12     opml_outline ::= OPML_OUTLINE_OPEN OPML_OUTLINE_CLOSE
+         OPML_OUTLINE_PREAMBLE shift        14     
+         OPML_OUTLINE_METADATA shift        3      
+       OPML_OUTLINE_SELF_CLOSE shift-reduce 16     opml_outline ::= OPML_OUTLINE_SELF_CLOSE
+                 opml_outlines shift        6      
+                  opml_outline shift        6        /* because opml_outline==opml_outlines */
+
+State 2:
+          opml_body ::= OPML_BODY_OPEN * opml_outlines OPML_BODY_CLOSE
+          opml_body ::= OPML_BODY_OPEN * OPML_BODY_CLOSE
+          opml_outlines ::= * opml_outlines opml_outline
+          opml_outlines ::= * opml_outline
+          opml_outline ::= * OPML_OUTLINE_OPEN OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_PREAMBLE OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_OPEN opml_outlines OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_METADATA opml_outlines OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_SELF_CLOSE
+
+               OPML_BODY_CLOSE shift-reduce 9      opml_body ::= OPML_BODY_OPEN OPML_BODY_CLOSE
+             OPML_OUTLINE_OPEN shift        1      
+         OPML_OUTLINE_PREAMBLE shift        14     
+         OPML_OUTLINE_METADATA shift        3      
+       OPML_OUTLINE_SELF_CLOSE shift-reduce 16     opml_outline ::= OPML_OUTLINE_SELF_CLOSE
+                 opml_outlines shift        7      
+                  opml_outline shift        7        /* because opml_outline==opml_outlines */
+
+State 3:
+          opml_outlines ::= * opml_outlines opml_outline
+          opml_outlines ::= * opml_outline
+          opml_outline ::= * OPML_OUTLINE_OPEN OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_PREAMBLE OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_OPEN opml_outlines OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_METADATA opml_outlines OPML_OUTLINE_CLOSE
+          opml_outline ::= OPML_OUTLINE_METADATA * opml_outlines OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_SELF_CLOSE
+
+             OPML_OUTLINE_OPEN shift        1      
+         OPML_OUTLINE_PREAMBLE shift        14     
+         OPML_OUTLINE_METADATA shift        3      
+       OPML_OUTLINE_SELF_CLOSE shift-reduce 16     opml_outline ::= OPML_OUTLINE_SELF_CLOSE
+                 opml_outlines shift        5      
+                  opml_outline shift        5        /* because opml_outline==opml_outlines */
+
+State 4:
+          doc_opml ::= OPML_OPML_OPEN * opml_header opml_body OPML_OPML_CLOSE
+          doc_opml ::= OPML_OPML_OPEN * opml_body OPML_OPML_CLOSE
+          opml_header ::= * OPML_HEAD_OPEN opml_title OPML_HEAD_CLOSE
+          opml_header ::= * OPML_HEAD_OPEN OPML_HEAD_CLOSE
+          opml_body ::= * OPML_BODY_OPEN opml_outlines OPML_BODY_CLOSE
+          opml_body ::= * OPML_BODY_OPEN OPML_BODY_CLOSE
+
+                OPML_HEAD_OPEN shift        8      
+                OPML_BODY_OPEN shift        2      
+                   opml_header shift        9      
+                     opml_body shift        13     
+
+State 5:
+          opml_outlines ::= opml_outlines * opml_outline
+          opml_outline ::= * OPML_OUTLINE_OPEN OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_PREAMBLE OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_OPEN opml_outlines OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_METADATA opml_outlines OPML_OUTLINE_CLOSE
+          opml_outline ::= OPML_OUTLINE_METADATA opml_outlines * OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_SELF_CLOSE
+
+             OPML_OUTLINE_OPEN shift        1      
+            OPML_OUTLINE_CLOSE shift-reduce 15     opml_outline ::= OPML_OUTLINE_METADATA opml_outlines OPML_OUTLINE_CLOSE
+         OPML_OUTLINE_PREAMBLE shift        14     
+         OPML_OUTLINE_METADATA shift        3      
+       OPML_OUTLINE_SELF_CLOSE shift-reduce 16     opml_outline ::= OPML_OUTLINE_SELF_CLOSE
+                  opml_outline shift-reduce 10     opml_outlines ::= opml_outlines opml_outline
+
+State 6:
+          opml_outlines ::= opml_outlines * opml_outline
+          opml_outline ::= * OPML_OUTLINE_OPEN OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_PREAMBLE OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_OPEN opml_outlines OPML_OUTLINE_CLOSE
+          opml_outline ::= OPML_OUTLINE_OPEN opml_outlines * OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_METADATA opml_outlines OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_SELF_CLOSE
+
+             OPML_OUTLINE_OPEN shift        1      
+            OPML_OUTLINE_CLOSE shift-reduce 14     opml_outline ::= OPML_OUTLINE_OPEN opml_outlines OPML_OUTLINE_CLOSE
+         OPML_OUTLINE_PREAMBLE shift        14     
+         OPML_OUTLINE_METADATA shift        3      
+       OPML_OUTLINE_SELF_CLOSE shift-reduce 16     opml_outline ::= OPML_OUTLINE_SELF_CLOSE
+                  opml_outline shift-reduce 10     opml_outlines ::= opml_outlines opml_outline
+
+State 7:
+          opml_body ::= OPML_BODY_OPEN opml_outlines * OPML_BODY_CLOSE
+          opml_outlines ::= opml_outlines * opml_outline
+          opml_outline ::= * OPML_OUTLINE_OPEN OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_PREAMBLE OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_OPEN opml_outlines OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_METADATA opml_outlines OPML_OUTLINE_CLOSE
+          opml_outline ::= * OPML_OUTLINE_SELF_CLOSE
+
+               OPML_BODY_CLOSE shift-reduce 8      opml_body ::= OPML_BODY_OPEN opml_outlines OPML_BODY_CLOSE
+             OPML_OUTLINE_OPEN shift        1      
+         OPML_OUTLINE_PREAMBLE shift        14     
+         OPML_OUTLINE_METADATA shift        3      
+       OPML_OUTLINE_SELF_CLOSE shift-reduce 16     opml_outline ::= OPML_OUTLINE_SELF_CLOSE
+                  opml_outline shift-reduce 10     opml_outlines ::= opml_outlines opml_outline
+
+State 8:
+          opml_header ::= OPML_HEAD_OPEN * opml_title OPML_HEAD_CLOSE
+          opml_header ::= OPML_HEAD_OPEN * OPML_HEAD_CLOSE
+          opml_title ::= * OPML_TITLE_OPEN OPML_TITLE_CLOSE
+
+               OPML_HEAD_CLOSE shift-reduce 6      opml_header ::= OPML_HEAD_OPEN OPML_HEAD_CLOSE
+               OPML_TITLE_OPEN shift        11     
+                    opml_title shift        12     
+
+State 9:
+          doc_opml ::= OPML_OPML_OPEN opml_header * opml_body OPML_OPML_CLOSE
+          opml_body ::= * OPML_BODY_OPEN opml_outlines OPML_BODY_CLOSE
+          opml_body ::= * OPML_BODY_OPEN OPML_BODY_CLOSE
+
+                OPML_BODY_OPEN shift        2      
+                     opml_body shift        15     
+
+State 10:
+          doc_xml ::= OPML_XML * doc_opml
+          doc_xml ::= OPML_XML * doc_opml OPML_WSNL
+          doc_opml ::= * OPML_OPML_OPEN opml_header opml_body OPML_OPML_CLOSE
+          doc_opml ::= * OPML_OPML_OPEN opml_body OPML_OPML_CLOSE
+
+                OPML_OPML_OPEN shift        4      
+                      doc_opml shift        16     
+
+State 11:
+          opml_title ::= OPML_TITLE_OPEN * OPML_TITLE_CLOSE
+
+              OPML_TITLE_CLOSE shift-reduce 7      opml_title ::= OPML_TITLE_OPEN OPML_TITLE_CLOSE
+
+State 12:
+          opml_header ::= OPML_HEAD_OPEN opml_title * OPML_HEAD_CLOSE
+
+               OPML_HEAD_CLOSE shift-reduce 5      opml_header ::= OPML_HEAD_OPEN opml_title OPML_HEAD_CLOSE
+
+State 13:
+          doc_opml ::= OPML_OPML_OPEN opml_body * OPML_OPML_CLOSE
+
+               OPML_OPML_CLOSE shift-reduce 4      doc_opml ::= OPML_OPML_OPEN opml_body OPML_OPML_CLOSE
+
+State 14:
+          opml_outline ::= OPML_OUTLINE_PREAMBLE * OPML_OUTLINE_CLOSE
+
+            OPML_OUTLINE_CLOSE shift-reduce 13     opml_outline ::= OPML_OUTLINE_PREAMBLE OPML_OUTLINE_CLOSE
+
+State 15:
+          doc_opml ::= OPML_OPML_OPEN opml_header opml_body * OPML_OPML_CLOSE
+
+               OPML_OPML_CLOSE shift-reduce 3      doc_opml ::= OPML_OPML_OPEN opml_header opml_body OPML_OPML_CLOSE
+
+State 16:
+      (1) doc_xml ::= OPML_XML doc_opml *
+          doc_xml ::= OPML_XML doc_opml * OPML_WSNL
+
+                     OPML_WSNL shift-reduce 2      doc_xml ::= OPML_XML doc_opml OPML_WSNL
+                     {default} reduce       1      doc_xml ::= OPML_XML doc_opml
+
+State 17:
+      (0) doc ::= doc_xml *
+
+                             $ reduce       0      doc ::= doc_xml
+
+----------------------------------------------------
+Symbols:
+    0: $:
+    1: OPML_XML
+    2: OPML_WSNL
+    3: OPML_OPML_OPEN
+    4: OPML_OPML_CLOSE
+    5: OPML_HEAD_OPEN
+    6: OPML_HEAD_CLOSE
+    7: OPML_TITLE_OPEN
+    8: OPML_TITLE_CLOSE
+    9: OPML_BODY_OPEN
+   10: OPML_BODY_CLOSE
+   11: OPML_OUTLINE_OPEN
+   12: OPML_OUTLINE_CLOSE
+   13: OPML_OUTLINE_PREAMBLE
+   14: OPML_OUTLINE_METADATA
+   15: OPML_OUTLINE_SELF_CLOSE
+   16: error:
+   17: doc: OPML_XML
+   18: doc_xml: OPML_XML
+   19: doc_opml: OPML_OPML_OPEN
+   20: opml_header: OPML_HEAD_OPEN
+   21: opml_body: OPML_BODY_OPEN
+   22: opml_title: OPML_TITLE_OPEN
+   23: opml_outlines: OPML_OUTLINE_OPEN OPML_OUTLINE_PREAMBLE OPML_OUTLINE_METADATA OPML_OUTLINE_SELF_CLOSE
+   24: opml_outline: OPML_OUTLINE_OPEN OPML_OUTLINE_PREAMBLE OPML_OUTLINE_METADATA OPML_OUTLINE_SELF_CLOSE
diff --git a/Sources/libMultiMarkdown/opml-parser.y b/Sources/libMultiMarkdown/opml-parser.y
new file mode 100644 (file)
index 0000000..ddd1165
--- /dev/null
@@ -0,0 +1,185 @@
+/**
+
+       MultiMarkdown -- Lightweight markup processor to produce HTML, LaTeX, and more.
+
+       @file opml-parser.c
+
+       @brief 
+
+
+       @author Fletcher T. Penney
+       @bug    
+
+**/
+
+/*
+
+       Copyright © 2016 - 2018 Fletcher T. Penney.
+
+
+       The `MultiMarkdown 6` project is released under the MIT License..
+       
+       GLibFacade.c and GLibFacade.h are from the MultiMarkdown v4 project:
+       
+               https://github.com/fletcher/MultiMarkdown-4/
+       
+       MMD 4 is released under both the MIT License and GPL.
+       
+       
+       CuTest is released under the zlib/libpng license. See CuTest.c for the
+       text of the license.
+       
+       uthash library:
+               Copyright (c) 2005-2016, Troy D. Hanson
+       
+               Licensed under Revised BSD license
+       
+       miniz library:
+               Copyright 2013-2014 RAD Game Tools and Valve Software
+               Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
+       
+               Licensed under the MIT license
+       
+       argtable3 library:
+               Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+               <sheitmann@users.sourceforge.net>
+               All rights reserved.
+       
+               Licensed under the Revised BSD License
+       
+       
+       ## The MIT License ##
+       
+       Permission is hereby granted, free of charge, to any person obtaining
+       a copy of this software and associated documentation files (the
+       "Software"), to deal in the Software without restriction, including
+       without limitation the rights to use, copy, modify, merge, publish,
+       distribute, sublicense, and/or sell copies of the Software, and to
+       permit persons to whom the Software is furnished to do so, subject to
+       the following conditions:
+       
+       The above copyright notice and this permission notice shall be
+       included in all copies or substantial portions of the Software.
+       
+       THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+       EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+       MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+       IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+       CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+       TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+       SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+       
+       
+       ## Revised BSD License ##
+       
+       Redistribution and use in source and binary forms, with or without
+       modification, are permitted provided that the following conditions are
+       met:
+           * Redistributions of source code must retain the above copyright
+             notice, this list of conditions and the following disclaimer.
+           * Redistributions in binary form must reproduce the above
+             copyright notice, this list of conditions and the following
+             disclaimer in the documentation and/or other materials provided
+             with the distribution.
+           * Neither the name of the <organization> nor the
+             names of its contributors may be used to endorse or promote
+             products derived from this software without specific prior
+             written permission.
+       
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+       "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+       LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+       A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT
+       HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+       EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+       PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES LOSS OF USE, DATA, OR
+       PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+       LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+       NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+       SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+       
+
+*/
+
+
+//
+// Language grammar here
+//
+
+%token_type { token * }
+
+%extra_argument { mmd_engine * engine }
+
+%name OPML
+
+
+// Start symbol
+doc                            ::=  doc_xml(B).                                                                                                { engine->root = B; }
+
+doc_xml                        ::= OPML_XML doc_opml.
+doc_xml                        ::= OPML_XML doc_opml OPML_WSNL.
+
+doc_opml               ::= OPML_OPML_OPEN opml_header opml_body OPML_OPML_CLOSE.
+doc_opml               ::= OPML_OPML_OPEN opml_body OPML_OPML_CLOSE.
+
+opml_header            ::= OPML_HEAD_OPEN opml_title OPML_HEAD_CLOSE.
+opml_header            ::= OPML_HEAD_OPEN OPML_HEAD_CLOSE.
+
+opml_title             ::= OPML_TITLE_OPEN OPML_TITLE_CLOSE.
+
+opml_body              ::= OPML_BODY_OPEN opml_outlines OPML_BODY_CLOSE.
+opml_body              ::= OPML_BODY_OPEN OPML_BODY_CLOSE.
+
+opml_outlines  ::= opml_outlines opml_outline.
+opml_outlines  ::= opml_outline.
+
+opml_outline   ::= OPML_OUTLINE_OPEN OPML_OUTLINE_CLOSE.
+opml_outline   ::= OPML_OUTLINE_PREAMBLE OPML_OUTLINE_CLOSE.
+
+opml_outline   ::= OPML_OUTLINE_OPEN opml_outlines OPML_OUTLINE_CLOSE.
+opml_outline   ::= OPML_OUTLINE_METADATA opml_outlines OPML_OUTLINE_CLOSE.
+
+opml_outline   ::= OPML_OUTLINE_SELF_CLOSE.
+
+
+
+//
+// Additional Configuration
+//
+
+%include {
+       #include <assert.h>
+       #include <stdio.h>
+       #include <stdlib.h>
+
+       #include "libMultiMarkdown.h"
+       #include "mmd.h"
+       #include "parser.h"
+       #include "token.h"
+}
+
+
+// Improved error messages for debugging:
+//     http://stackoverflow.com/questions/11705737/expected-token-using-lemon-parser-generator
+
+%syntax_error {
+       fprintf(stderr,"Parser syntax error.\n");
+#ifndef NDEBUG
+       fprintf(stderr,"Parser syntax error.\n");
+       int n = sizeof(yyTokenName) / sizeof(yyTokenName[0]);
+       for (int i = 0; i < n; ++i) {
+               int a = yy_find_shift_action(yypParser, (YYCODETYPE)i);
+               if (a < YYNSTATE + YYNRULE) {
+                       fprintf(stderr,"expected token: %s\n", yyTokenName[i]);
+               }
+       }
+#endif
+}
+
+%parse_accept {
+//     printf("parsing completed successfully!\n");
+}
+
+%parse_failure {
+       fprintf(stderr, "Parser failed to successfully parse.\n");
+}
diff --git a/Sources/libMultiMarkdown/opml-reader.c b/Sources/libMultiMarkdown/opml-reader.c
new file mode 100644 (file)
index 0000000..db1385c
--- /dev/null
@@ -0,0 +1,418 @@
+/**
+
+       MultiMarkdown -- Lightweight markup processor to produce HTML, LaTeX, and more.
+
+       @file opml-reader.c
+
+       @brief
+
+
+       @author Fletcher T. Penney
+       @bug
+
+**/
+
+/*
+
+       Copyright © 2016 - 2018 Fletcher T. Penney.
+
+
+       The `MultiMarkdown 6` project is released under the MIT License..
+
+       GLibFacade.c and GLibFacade.h are from the MultiMarkdown v4 project:
+
+               https://github.com/fletcher/MultiMarkdown-4/
+
+       MMD 4 is released under both the MIT License and GPL.
+
+
+       CuTest is released under the zlib/libpng license. See CuTest.c for the
+       text of the license.
+
+       uthash library:
+               Copyright (c) 2005-2016, Troy D. Hanson
+
+               Licensed under Revised BSD license
+
+       miniz library:
+               Copyright 2013-2014 RAD Game Tools and Valve Software
+               Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
+
+               Licensed under the MIT license
+
+       argtable3 library:
+               Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+               <sheitmann@users.sourceforge.net>
+               All rights reserved.
+
+               Licensed under the Revised BSD License
+
+
+       ## The MIT License ##
+
+       Permission is hereby granted, free of charge, to any person obtaining
+       a copy of this software and associated documentation files (the
+       "Software"), to deal in the Software without restriction, including
+       without limitation the rights to use, copy, modify, merge, publish,
+       distribute, sublicense, and/or sell copies of the Software, and to
+       permit persons to whom the Software is furnished to do so, subject to
+       the following conditions:
+
+       The above copyright notice and this permission notice shall be
+       included in all copies or substantial portions of the Software.
+
+       THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+       EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+       MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+       IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+       CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+       TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+       SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+       ## Revised BSD License ##
+
+       Redistribution and use in source and binary forms, with or without
+       modification, are permitted provided that the following conditions are
+       met:
+           * Redistributions of source code must retain the above copyright
+             notice, this list of conditions and the following disclaimer.
+           * Redistributions in binary form must reproduce the above
+             copyright notice, this list of conditions and the following
+             disclaimer in the documentation and/or other materials provided
+             with the distribution.
+           * Neither the name of the <organization> nor the
+             names of its contributors may be used to endorse or promote
+             products derived from this software without specific prior
+             written permission.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+       "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+       LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+       A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT
+       HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+       EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+       PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES LOSS OF USE, DATA, OR
+       PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+       LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+       NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+       SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "mmd.h"
+#include "opml-reader.h"
+#include "opml-lexer.h"
+#include "opml-parser.h"
+#include "token.h"
+
+
+// Basic parser function declarations
+void * OPMLAlloc();
+void OPML();
+void OPMLFree();
+void OPMLTrace();
+
+
+#define print(x) d_string_append(out, x)
+#define print_const(x) d_string_append_c_array(out, x, sizeof(x) - 1)
+#define print_char(x) d_string_append_c(out, x)
+#define printf(...) d_string_append_printf(out, __VA_ARGS__)
+
+
+/// Create a token chain from source OPML string
+token * tokenize_opml_string(mmd_engine * e, size_t start, size_t len) {
+
+       // Create a scanner (for re2c)
+       Scanner s;
+       s.start = &e->dstr->str[start];
+       s.cur = s.start;
+
+       // Where do we stop parsing?
+       const char * stop = &e->dstr->str[start] + len;
+
+       int type;                                                               // TOKEN type
+       token * t;                                                              // Create tokens for incorporation
+
+       token * root = token_new(0, start, 0);          // Store the final parse tree here
+
+       const char * last_stop = &e->dstr->str[start];  // Remember where last token ended
+
+       do {
+               // Scan for next token (type of 0 means there is nothing left);
+               type = opml_scan(&s, stop);
+
+               //if (type && s.start != last_stop) {
+               if (s.start != last_stop) {
+                       // We skipped characters between tokens
+
+                       if (type) {
+                               // Create a default token type for the skipped characters
+                               //                      t = token_new(TEXT_PLAIN, (size_t)(last_stop - e->dstr->str), (size_t)(s.start - last_stop));
+                       } else {
+                               if (stop > last_stop) {
+                                       // Source text ends without newline
+                                       //                              t = token_new(TEXT_PLAIN, (size_t)(last_stop - e->dstr->str), (size_t)(stop - last_stop));
+                               }
+                       }
+               } else if (type == 0 && stop > last_stop) {
+                       // Source text ends without newline
+                       //              t = token_new(TEXT_PLAIN, (size_t)(last_stop - e->dstr->str), (size_t)(stop - last_stop));
+               }
+
+
+               switch (type) {
+                       case 0:
+                               // 0 means we finished with input
+                               break;
+
+                       case OPML_WSNL:
+                               // Ignore for now
+                               break;
+
+                       default:
+                               t = token_new(type, (size_t)(s.start - e->dstr->str), (size_t)(s.cur - s.start));
+                               token_chain_append(root, t);
+                               break;
+               }
+
+               // Remember where token ends to detect skipped characters
+               last_stop = s.cur;
+       } while (type != 0);
+
+       return root;
+}
+
+
+void print_opml_text(DString * out, const char * source, size_t start, size_t len) {
+       const char * s_start = &source[start];
+       const char * s_stop = &source[start + len];
+
+       char * c = (char *) s_start;
+
+       while (c < s_stop) {
+               switch (*c) {
+                       case '&':
+                               switch (*++c) {
+                                       case '#':
+                                               if (strncmp(c, "#10;", 4) == 0) {
+                                                       print_char('\n');
+                                                       c += 4;
+                                                       continue;
+                                               }
+
+                                               if (strncmp(c, "#9;", 3) == 0) {
+                                                       print_char('\t');
+                                                       c += 3;
+                                                       continue;
+                                               }
+
+                                               if (strncmp(c, "#13;", 4) == 0) {
+                                                       print_char('\r');
+                                                       c += 4;
+                                                       continue;
+                                               }
+
+                                               break;
+
+                                       case 'a':
+                                               if (strncmp(c, "amp;", 4) == 0) {
+                                                       print_char('&');
+                                                       c += 4;
+                                                       continue;
+                                               }
+
+                                               if (strncmp(c, "apos;", 5) == 0) {
+                                                       print_char('\'');
+                                                       c += 5;
+                                                       continue;
+                                               }
+
+                                               break;
+
+                                       case 'l':
+                                               if (strncmp(c, "lt;", 3) == 0) {
+                                                       print_char('<');
+                                                       c += 3;
+                                                       continue;
+                                               }
+
+                                               break;
+
+                                       case 'g':
+                                               if (strncmp(c, "gt;", 3) == 0) {
+                                                       print_char('>');
+                                                       c += 3;
+                                                       continue;
+                                               }
+
+                                               break;
+
+                                       case 'q':
+                                               if (strncmp(c, "quot;", 5) == 0) {
+                                                       print_char('"');
+                                                       c += 5;
+                                                       continue;
+                                               }
+
+                                               break;
+
+                                       default:
+                                               break;
+                               }
+
+                               print_char('&');
+                               continue;
+                               break;
+
+                       default:
+                               print_char(*c);
+                               break;
+               }
+
+               c++;
+       }
+}
+
+
+
+void parse_opml_token_chain(mmd_engine * e, token * chain) {
+
+       void* pParser = OPMLAlloc (malloc);             // Create a parser (for lemon)
+       token * walker = chain->next;                           // Walk the existing tree
+       token * remainder;                                                      // Hold unparsed tail of chain
+
+       #ifndef NDEBUG
+       OPMLTrace(stderr, "parser >>");
+       #endif
+
+       // Remove existing token tree
+       e->root = NULL;
+
+       while (walker != NULL) {
+               remainder = walker->next;
+
+               OPML(pParser, walker->type, walker, e);
+
+               walker = remainder;
+       }
+
+       // Signal finish to parser
+       #ifndef NDEBUG
+       fprintf(stderr, "\nFinish parse\n");
+       #endif
+       OPML(pParser, 0, NULL, e);
+
+       if (e->root) {
+               // Successful parse -- process to new source document
+               DString * final = d_string_new("");
+               DString * metadata = d_string_new("");
+               DString * out = final;
+
+               size_t header_level = 0;
+               size_t start, len;
+
+               walker = chain->next;
+
+               while (walker) {
+                       switch (walker->type) {
+                               case OPML_OUTLINE_PREAMBLE:
+                               case OPML_OUTLINE_OPEN:
+                               case OPML_OUTLINE_SELF_CLOSE:
+                                       header_level++;
+
+                                       // Advance over `<outline`
+                                       start = walker->start + 8;
+
+                                       start += scan_text(&(e->dstr->str[start]));
+                                       len = scan_double_quoted(&(e->dstr->str[start]));
+
+                                       if (strncmp(&(e->dstr->str[start + 1]), "(Untitled Preamble)", 19) != 0) {
+                                               if (out == metadata) {
+                                                       print_opml_text(out, e->dstr->str, start + 1, len - 2);
+                                                       print_const(":\t");
+                                               } else {
+                                                       // Print header
+                                                       for (int i = 0; i < header_level; ++i) {
+                                                               print_char('#');
+                                                       }
+
+                                                       print_char(' ');
+
+                                                       print_opml_text(out, e->dstr->str, start + 1, len - 2);
+
+                                                       print_char(' ');
+
+                                                       for (int i = 0; i < header_level; ++i) {
+                                                               print_char('#');
+                                                       }
+
+                                                       print_const("\n");
+                                               }
+                                       }
+
+                                       // Print contents
+                                       start += len;
+                                       start += scan_note(&(e->dstr->str[start]));
+                                       len = scan_double_quoted(&(e->dstr->str[start]));
+
+                                       print_opml_text(out, e->dstr->str, start + 1, len - 2);
+
+                                       if (out == metadata) {
+                                               print_char('\n');
+                                       }
+
+                                       if (walker->type == OPML_OUTLINE_SELF_CLOSE) {
+                                               header_level--;
+                                       }
+
+                                       break;
+
+                               case OPML_OUTLINE_METADATA:
+                                       // Now handle metadata
+                                       out = metadata;
+                                       header_level++;
+                                       break;
+
+                               case OPML_OUTLINE_CLOSE:
+                                       header_level--;
+                                       break;
+
+                               default:
+                                       break;
+                       }
+
+                       walker = walker->next;
+               }
+
+               // Append body to metadata
+               d_string_append_c_array(metadata, final->str, final->currentStringLength);
+
+               // TODO: How to safely swap the new text, given that we might not own e->dstr->str?
+
+               free(e->dstr->str);
+               e->dstr->str = metadata->str;
+               e->dstr->currentStringLength = metadata->currentStringLength;
+
+               d_string_free(metadata, false);
+               d_string_free(final, true);
+       } else {
+               // Unsuccessful parse -- free token chain
+       }
+
+       // Clean up token chain
+       token_tree_free(chain);
+
+       OPMLFree(pParser, free);
+}
+
+
+/// Create a token chain from source OPML string
+void mmd_convert_opml_string(mmd_engine * e, size_t start, size_t len) {
+       token * chain = tokenize_opml_string(e, start, len);
+       parse_opml_token_chain(e, chain);
+}
diff --git a/Sources/libMultiMarkdown/opml-reader.h b/Sources/libMultiMarkdown/opml-reader.h
new file mode 100644 (file)
index 0000000..f867efa
--- /dev/null
@@ -0,0 +1,111 @@
+/**
+
+       MultiMarkdown -- Lightweight markup processor to produce HTML, LaTeX, and more.
+
+       @file opml-reader.h
+
+       @brief
+
+
+       @author Fletcher T. Penney
+       @bug
+
+**/
+
+/*
+
+       Copyright © 2016 - 2018 Fletcher T. Penney.
+
+
+       The `MultiMarkdown 6` project is released under the MIT License..
+
+       GLibFacade.c and GLibFacade.h are from the MultiMarkdown v4 project:
+
+               https://github.com/fletcher/MultiMarkdown-4/
+
+       MMD 4 is released under both the MIT License and GPL.
+
+
+       CuTest is released under the zlib/libpng license. See CuTest.c for the
+       text of the license.
+
+       uthash library:
+               Copyright (c) 2005-2016, Troy D. Hanson
+
+               Licensed under Revised BSD license
+
+       miniz library:
+               Copyright 2013-2014 RAD Game Tools and Valve Software
+               Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
+
+               Licensed under the MIT license
+
+       argtable3 library:
+               Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+               <sheitmann@users.sourceforge.net>
+               All rights reserved.
+
+               Licensed under the Revised BSD License
+
+
+       ## The MIT License ##
+
+       Permission is hereby granted, free of charge, to any person obtaining
+       a copy of this software and associated documentation files (the
+       "Software"), to deal in the Software without restriction, including
+       without limitation the rights to use, copy, modify, merge, publish,
+       distribute, sublicense, and/or sell copies of the Software, and to
+       permit persons to whom the Software is furnished to do so, subject to
+       the following conditions:
+
+       The above copyright notice and this permission notice shall be
+       included in all copies or substantial portions of the Software.
+
+       THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+       EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+       MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+       IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+       CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+       TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+       SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+       ## Revised BSD License ##
+
+       Redistribution and use in source and binary forms, with or without
+       modification, are permitted provided that the following conditions are
+       met:
+           * Redistributions of source code must retain the above copyright
+             notice, this list of conditions and the following disclaimer.
+           * Redistributions in binary form must reproduce the above
+             copyright notice, this list of conditions and the following
+             disclaimer in the documentation and/or other materials provided
+             with the distribution.
+           * Neither the name of the <organization> nor the
+             names of its contributors may be used to endorse or promote
+             products derived from this software without specific prior
+             written permission.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+       "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+       LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+       A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT
+       HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+       EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+       PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES LOSS OF USE, DATA, OR
+       PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+       LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+       NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+       SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+*/
+
+
+#ifndef OPML_READER_MULTIMARKDOWN_H
+#define OPML_READER_MULTIMARKDOWN_H
+
+/// Create a token chain from source OPML string
+void mmd_convert_opml_string(mmd_engine * e, size_t start, size_t len);
+
+#endif
index cc5a01ac8269502afcef8795fc53561591e754a5..db335c5211ba391341d5499e687232f9b1e07909 100644 (file)
@@ -1579,6 +1579,8 @@ static void yy_accept(
   /* Here code is inserted which will be executed whenever the
   ** parser accepts */
 /*********** Begin %parse_accept code *****************************************/
+
+//     printf("parsing completed successfully!\n");
 /*********** End %parse_accept code *******************************************/
   ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
 }
index 870c01adec60030e8dcf110982a0f4449515a3d0..4bdf0e3f09fba815648df3a9b18c12d63a6e9196 100644 (file)
@@ -406,6 +406,10 @@ para                               ::= defs.
 #endif
 }
 
+%parse_accept {
+//     printf("parsing completed successfully!\n");
+}
+
 %parse_failure {
        fprintf(stderr, "Parser failed to successfully parse.\n");
 }
index 65a0d98c5795be1664b6131f3e32cc7b0fca836b..ec5482fbf642b66caeb8c70dbc452ba1dd8ab3fa 100755 (executable)
@@ -8,7 +8,10 @@ re2c -i -8 lexer.re > lexer.c
 
 re2c -i -8 scanners.re > scanners.c
 
+re2c -i  opml-lexer.re > opml-lexer.c
+
 # It seems that some other versions of lemon don't create valid 
 # parsers?? Using the version included here works
 # lemon -l parser.y
 ../../lemon/build/lemon -l parser.y
+../../lemon/build/lemon -l opml-parser.y
index 962814a8ed5486ca356c97fe9d49b25cef3cbf8e..e6d2022a64e5ebf8993762fa7550514e129cf63f 100644 (file)
@@ -76,7 +76,7 @@
 // argtable structs
 struct arg_lit *a_help, *a_version, *a_compatibility, *a_nolabels, *a_batch,
                   *a_accept, *a_reject, *a_full, *a_snippet, *a_random, *a_meta,
-                  *a_notransclude, *a_nosmart;
+                  *a_notransclude, *a_nosmart, *a_opml;
 struct arg_str *a_format, *a_lang, *a_extract;
 struct arg_file *a_file, *a_o;
 struct arg_end *a_end;
@@ -150,6 +150,7 @@ int main(int argc, char** argv) {
                a_nosmart               = arg_lit0(NULL, "nosmart", "Disable smart typography"),
                a_nolabels              = arg_lit0(NULL, "nolabels", "Disable id attributes for headers"),
                a_notransclude  = arg_lit0(NULL, "notransclude", "Disable file transclusion"),
+               a_opml                  = arg_lit0(NULL, "opml", "Convert OPML source to plain text before processing"),
 
                a_rem2                  = arg_rem("", ""),
 
@@ -235,6 +236,11 @@ int main(int argc, char** argv) {
                extensions &= ~EXT_TRANSCLUDE;
        }
 
+       if (a_opml->count > 0) {
+               // Attempt to convert from OPML
+               extensions |= EXT_PARSE_OPML;
+       }
+
        if (a_accept->count > 0) {
                // Accept CriticMarkup changes
                extensions |= EXT_CRITIC_ACCEPT | EXT_CRITIC;
index fd4c87cbcc652e76bc250c2ed66a43bd46d723ef..36b25d7612c6aaf3c449762d8bc33cdb0dc789a9 100755 (executable)
@@ -24,6 +24,8 @@ my $flags = "";
 my $file_ext = "html";
 my $trail = "";
 
+my $source_ext = "text";
+
 GetOptions (
                        "script=s"   => \$script,
                        "testdir=s"  => \$test_dir,
@@ -32,6 +34,7 @@ GetOptions (
                        "flags=s"        => \$flags,
                        "ext=s"          => \$file_ext,
                        "trailflags=s" => \$trail,
+                       "source_ext=s" => \$source_ext,
                        );
 
 if($flag_version) {
@@ -57,14 +60,14 @@ my $tests_failed = 0;
 TEST:
 $test_dir =~ s/ /\\ /g;
 
-foreach my $testfile (glob "$test_dir/*.text") {
+foreach my $testfile (glob "$test_dir/*.$source_ext") {
        my $testname = $testfile;
-       $testname =~ s{.*/(.+)\.text$}{$1}i; 
+       $testname =~ s{.*/(.+)\.$source_ext$}{$1}i; 
        print "$testname ... ";
 
        # Look for a corresponding .html file for each .text file:
        my $resultfile = $testfile;
-       $resultfile =~ s{\.text$}{\.$file_ext}i;
+       $resultfile =~ s{\.$source_ext$}{\.$file_ext}i;
        unless (-f $resultfile) {
                print "'$resultfile' does not exist.\n\n";
                $tests_failed++;