diff --git a/.gitignore b/.gitignore index f73aae0..ed8eeb0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .DS_Store +CHANGELOG-UNRELEASED # Developer Tools *.pbxuser @@ -16,3 +17,9 @@ profile *.moved-aside DerivedData .idea/ + + +# Binaries +*.dmg +*.exe +*.zip diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..efbb396 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,56 @@ +# MultiMarkdown Change Log # + + +## [5.0.1] - 2015-12-01 ## + +* IMPORTANT: Fix major error in last Makefile! (Only in build branch for a few minutes) +* ADDED: Improve empty list item detection in ODF output +* CHANGED: Remove unused node creation utilities +* CHANGED: Update documentation +* CHANGED: Use 'const' char * in g_string_new +* CODE: Improve doxygen support in libMultiMarkdown.h +* CODE: Refactor markdown_to_string() to separate parsing input text and writing output text via the intermediate node tree. +* CODE: Use node creation shortcuts for consistency and future flexibility +* FIX: Add 32 bit flag to older MinGW toolchain +* FIX: Add label for 32 bit builds +* FIX: Don't run valgrind tests unless in 'make debug' mode +* FIX: Don't static link when using 'make debug' for valgrind testing +* FIX: Fix memory leaks in transclude_source() +* NOTE: Add developer notes to the README information +* NOTE: Additional doxygen support +* NOTE: Autogenerate changelog since last commit to master +* NOTE: Begin tracking release notes in 'CHANGELOG.md' +* NOTE: Change empty listitem detection logic for ODF output +* NOTE: Change whitespace for easier diffing +* NOTE: Remove redundant developer note in README +* NOTE: Use tab instead of leading spaces in CHANGELOG-UNRELEASED + + +## [5.0.0] - 2015-11-15 ## + +* The source repository for MultiMarkdown has been completely rebuilt: + * Use my [c-template] to provide the basic structure + * Requires the [CMake] build system + * Has the beginnings of documentation within the code to support + [Doxygen]-generated developer documentation +* Other changes since 4.7.1: + * Improvements from Matthias Lohr for the shell scripts + * Additional documentation in code + * Fix issue with whitespace after footnotes in ODF export + * Improve accuracy of recognizing single line code blocks + * Fix memory leaks + * Fix `mmd2pdf` for certain installs of TeX on El Capitan + * Improve POSIX compliance on shell scripts + * Improve accuracy of strong/emph matching + * Make reference label matching case insensitive + * Fix error if no caption was included on table + * Fix edge case slowdown when lots of HTML included + * Fix error when quotes included in explicit link + * Allow tables inside LaTeX footnotes + * Improved window path separator support + * And other improvements/fixes + + + +[5.0.0]: https://github.com/fletcher/MultiMarkdown-5/releases/tag/v5.0 +[5.0.1]: https://github.com/fletcher/MultiMarkdown-5/releases/tag/v5.0.1 diff --git a/CMakeLists.txt b/CMakeLists.txt index 23f31bb..bdc4b51 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,10 +8,10 @@ cmake_minimum_required (VERSION 2.6) set (My_Project_Title "MultiMarkdown") set (My_Project_Description "MultiMarkdown - lightweight markup processor") set (My_Project_Author "Fletcher T. Penney") -set (My_Project_Revised_Date "2015-11-12") +set (My_Project_Revised_Date "2015-12-01") set (My_Project_Version_Major 5) set (My_Project_Version_Minor 0) -set (My_Project_Version_Patch 0) +set (My_Project_Version_Patch 1) set (My_Project_Version "${My_Project_Version_Major}.${My_Project_Version_Minor}.${My_Project_Version_Patch}") @@ -245,19 +245,22 @@ elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") # Create zip archive set (CPACK_GENERATOR ZIP) - # Statically link libraries -- might make the binary slightly more - # compatible across Linux distributions, for example - # - # You may wish to disable this. - # - - set (CMAKE_FIND_LIBRARY_SUFFIXES ".a") - set (BUILD_SHARED_LIBRARIES OFF) - set (CMAKE_EXE_LINKER_FLAGS "-static") - # Some libraries need to be linked on some Linux builds if (DEFINED TEST) # target_link_libraries(run_tests m) + else (DEFINE TEST) + # Statically link libraries -- might make the binary slightly more + # compatible across Linux distributions, for example + # + # It will likely cause large numbers of errors on valgrind, + # so use "make debug" for valgrind testing + # + # You may wish to disable this. + # + + set (CMAKE_FIND_LIBRARY_SUFFIXES ".a") + set (BUILD_SHARED_LIBRARIES OFF) + set (CMAKE_EXE_LINKER_FLAGS "-static") endif (DEFINED TEST) endif (WIN32) @@ -374,9 +377,9 @@ if (DEFINED ZIP) endif (DEFINED ZIP) if (APPLE) - set (CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-Mac-${ZIP_NAME}${CPACK_PACKAGE_VERSION}") + set (CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-Mac-${IS_32_BIT}${ZIP_NAME}${CPACK_PACKAGE_VERSION}") else (APPLE) - set (CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${CMAKE_SYSTEM_NAME}-${ZIP_NAME}${CPACK_PACKAGE_VERSION}") + set (CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${CMAKE_SYSTEM_NAME}-${IS_32_BIT}${ZIP_NAME}${CPACK_PACKAGE_VERSION}") endif (APPLE) install (TARGETS multimarkdown @@ -452,12 +455,15 @@ ADD_MMD_TEST(critic-highlight "-r -a" CriticMarkup htmlh) # valgrind memory testing +# Only in 'make debug' mode -find_program ( MEMORYCHECK_COMMAND valgrind) -set ( MEMORYCHECK_COMMAND_OPTIONS --leak-check=full --error-exitcode=1) +if (DEFINED TEST) + find_program ( MEMORYCHECK_COMMAND valgrind) + set ( MEMORYCHECK_COMMAND_OPTIONS --leak-check=full --error-exitcode=1) -file (GLOB TEST_FILES ${PROJECT_SOURCE_DIR}/submodules/MarkdownTest/Tests/*.text ${PROJECT_SOURCE_DIR}/submodules/MarkdownTest/MultiMarkdownTests/*.text) + file (GLOB TEST_FILES ${PROJECT_SOURCE_DIR}/submodules/MarkdownTest/Tests/*.text ${PROJECT_SOURCE_DIR}/submodules/MarkdownTest/MultiMarkdownTests/*.text) -foreach (FORMAT html latex odf beamer memoir opml lyx) - add_test ( NAME valgrind-${FORMAT} COMMAND "${MEMORYCHECK_COMMAND}" ${MEMORYCHECK_COMMAND_OPTIONS} ${PROJECT_BINARY_DIR}/multimarkdown -t ${FORMAT} ${TEST_FILES} ) -endforeach() + foreach (FORMAT html latex odf beamer memoir opml lyx) + add_test ( NAME valgrind-${FORMAT} COMMAND "${MEMORYCHECK_COMMAND}" ${MEMORYCHECK_COMMAND_OPTIONS} ${PROJECT_BINARY_DIR}/multimarkdown -t ${FORMAT} ${TEST_FILES} ) + endforeach() +endif (DEFINED TEST) diff --git a/Makefile b/Makefile index 2fc1f87..aea54ff 100644 --- a/Makefile +++ b/Makefile @@ -61,7 +61,7 @@ windows-32: $(BUILD_DIR) $(GREG) .PHONY : windows-zip-32 windows-zip-32: $(BUILD_DIR) $(GREG) cd $(BUILD_DIR); touch README.html; \ - cmake -DCMAKE_TOOLCHAIN_FILE=../tools/Toolchain-mingw32.cmake -DCMAKE_BUILD_TYPE=Release -DZIP=1 .. + cmake -DCMAKE_TOOLCHAIN_FILE=../tools/Toolchain-MinGW-w64-32bit.cmake -DCMAKE_BUILD_TYPE=Release -DZIP=1 .. # Build the documentation using doxygen .PHONY : documentation @@ -80,10 +80,14 @@ $(GREG): $(MAKE) -C submodules/greg # Create build directory if it doesn't exist -$(BUILD_DIR): +$(BUILD_DIR): CHANGELOG -mkdir $(BUILD_DIR) 2>/dev/null -cd $(BUILD_DIR); rm -rf * +# Generate a list of changes since last commit to 'master' branch +.PHONY : CHANGELOG +CHANGELOG: + git log master..develop --format="* %s" | sort | uniq > CHANGELOG-UNRELEASED # =============== diff --git a/README.md b/README.md index 6eea4cd..f105199 100644 --- a/README.md +++ b/README.md @@ -4,27 +4,37 @@ | ---------- | ------------------------- | | Title: | MultiMarkdown | | Author: | Fletcher T. Penney | -| Date: | 2015-11-12 | +| Date: | 2015-12-01 | | Copyright: | Copyright © 2013-2015 Fletcher T. Penney. | -| Version: | 5.0.0 | +| Version: | 5.0.1 | ## Introduction ## [Markdown] is a simple markup language used to convert plain text into HTML. -[MultiMarkdown] is a derivative of Markdown that adds new syntax features, such as footnotes, tables, and metadata. Additionally, it offers mechanisms to convert plain text into LaTeX in addition to HTML. +[MultiMarkdown] is a derivative of Markdown that adds new syntax features, +such as footnotes, tables, and metadata. Additionally, it offers mechanisms to +convert plain text into LaTeX in addition to HTML. ## Background ## -MultiMarkdown started as a Perl script, which was modified from the original Markdown.pl. +MultiMarkdown started as a Perl script, which was modified from the original +Markdown.pl. -MultiMarkdown v3 (aka 'peg-multimarkdown') was based on John MacFarlane's [peg-markdown]. It used a parsing expression grammar (PEG), and was written in C in order to compile on almost any operating system. Thanks to work by Daniel Jalkut, MMD v3 was built so that it didn't have any external library requirements. +MultiMarkdown v3 (aka 'peg-multimarkdown') was based on John MacFarlane's +[peg-markdown]. It used a parsing expression grammar (PEG), and was written +in C in order to compile on almost any operating system. Thanks to work by +Daniel Jalkut, MMD v3 was built so that it didn't have any external library +requirements. -MultiMarkdown v4 was basically a complete rewrite of v3. It used the same basic PEG for parsing (Multi)Markdown text, but otherwise was almost completely rebuilt. +MultiMarkdown v4 was basically a complete rewrite of v3. It used the same +basic PEG for parsing (Multi)Markdown text, but otherwise was almost +completely rebuilt. -MultiMarkdown v5 is basically the same code as v4, but the project has been restructured: +MultiMarkdown v5 is basically the same code as v4, but the project has been +restructured: * It is built using my [c-template] project boilerplate -- I welcome suggestions and ideas for improvement about this. @@ -92,15 +102,19 @@ Binaries for OS X and Windows are available on the github site: ## Compile from Source ## -To compile MultiMarkdown, you will need to have [CMake] installed on your machine. +To compile MultiMarkdown, you will need to have [CMake] installed on your +machine. To download the source: -* Obtain the source from the github repository (Download a zipfile of the source won't allow you to configure the submodules -- it's much better to use git): +* Obtain the source from the github repository (Downloading a zipfile of the + source won't allow you to configure the submodules -- it's much better to + use git): git clone https://github.com/fletcher/MultiMarkdown-5.git -* Configure the submodules with two helper scripts (This can be done manually on Windows systems by looking at the source): +* Configure the submodules with two helper scripts (This can be done + manually on Windows systems by looking at the source): ./link_git_modules ./update_git_modules @@ -112,35 +126,87 @@ To download the source: make make test -Like all versions of MultiMarkdown since v3, there is one test that will fail (now helpfully called `markdown-should-fail`). The other tests should pass. The valgrind tests will not work on OS X, but should pass if valgrind is installed and used on Linux machines. +Like all versions of MultiMarkdown since v3, there is one test that will fail +(now helpfully called `markdown-should-fail`). The other tests should pass. +The valgrind tests will not work on OS X, but should pass if valgrind is +installed and used on Linux machines. -If you want to make an installer, after the above, use the `cpack` command inside the build directory. +If you want to make an installer, after the above, use the `cpack` command +inside the build directory. For more information, checkout the `IMPORTANT` file. ## Usage ## -The [MultiMarkdown User's Guide] has complete instructions on how to use MultiMarkdown. +The [MultiMarkdown User's Guide] has complete instructions on how to use +MultiMarkdown. -## Developer's Notes ## +# LyX Support # -The documentation, created by doxygen, has information for developers: +Charles R. Cowan () added support for conversion +to [LyX](http://www.lyx.org/). Support for this should be considered to be in +alpha/beta, and is not guaranteed. Issues related to LyX can be added to the +MultiMarkdown [issues] page on github, but will need to be answered by +Charles. I am happy to include this code in the main MMD repo, but since I +don't use LyX I can't support it myself. If this arrangement becomes a +problem, then LyX support can be removed and it can be kept as a separate +fork. - make documentation -You can then view `build/documentation/html/index.html` for some developer's notes. There's not a lot there yet. There is also a LaTeX version created if you want a PDF. Just use latexmk in the latex directory. +# More Information # +To get more information about MultiMarkdown, check out the +[website][MultiMarkdown] or [MultiMarkdown User's Guide]. -# LyX Support # -Charles R. Cowan () added support for conversion to [LyX](http://www.lyx.org/). Support for this should be considered to be in alpha/beta, and is not guaranteed. Issues related to LyX can be added to the MultiMarkdown [issues] page on github, but will need to be answered by Charles. I am happy to include this code in the main MMD repo, but since I don't use LyX I can't support it myself. If this arrangement becomes a problem, then LyX support can be removed and it can be kept as a separate fork. +# Developer Notes # +Be sure to read the relevant documentation: -# More Information # +* IMPORTANT +* README.md +* `make documentation` and look at `build/documentation/html/index.html` +* Relevant portions of the User's Guide + +If you wish to submit pull requests, then be sure to work off of the `develop` +branch and configure the pull requests appropriately. I am *trying* to use the +"git flow" workflow described here: + + + +I will not accept pull requests directly into the `master` branch. + +***NOTE***: Additionally, I am trying to use a consistent convention for +commit messages, so that I can quickly generate the framework for Release +Notes for new versions of MultiMarkdown. For example: + + TAG: Commit message with uppercase first letter and no period at the end + + TAG: Commit message one; TAG2: Commit message two + +The list of TAGs is in flux, but currently includes: + +* ADDED: New features or functionality +* CHANGED: Change to the way a feature works +* CODE: Change the code, but don't change the overall user experience +* FIX: Fix a bug +* IMPORTANT: Something major was fixed +* NOTE: These are mostly changes to the project itself (e.g. Makefile) and + have no impact on the user experience + +These TAGs are still in flux as I develop the system I am using, but this will +allow me to automatically generate most of the Release Notes for each new +version. I'll still need to go over them manually, but this gives me a head +start! (As an aside, any time you use one of the `make` commands, the file +`CHANGELOG-UNRELEASED` will be updated to show you new features in the +`development` branch that have not been pulled into `master` yet.) + +By using the TAGs, I can sort the list of messages and group things into +categories. By consistently using the semi-colon syntax, I can automatically +split commits with multiple notes. -To get more information about MultiMarkdown, check out the [website][MultiMarkdown] or [MultiMarkdown User's Guide]. ## License ## diff --git a/src/GLibFacade.c b/src/GLibFacade.c index 4d74802..e56a71e 100644 --- a/src/GLibFacade.c +++ b/src/GLibFacade.c @@ -76,7 +76,7 @@ int asprintf( char **sptr, char *fmt, ... ) #define kStringBufferStartingSize 1024 #define kStringBufferGrowthMultiplier 2 -GString* g_string_new(char *startingString) +GString* g_string_new(const char *startingString) { GString* newString = malloc(sizeof(GString)); diff --git a/src/GLibFacade.h b/src/GLibFacade.h index b2075fa..352010a 100644 --- a/src/GLibFacade.h +++ b/src/GLibFacade.h @@ -69,7 +69,7 @@ typedef struct unsigned long currentStringLength; } GString; -GString* g_string_new(char *startingString); +GString* g_string_new(const char *startingString); char* g_string_free(GString* ripString, bool freeCharacterData); void g_string_append_c(GString* baseString, char appendedCharacter); diff --git a/src/libMultiMarkdown.h b/src/libMultiMarkdown.h index 8b2c73f..b4093ca 100644 --- a/src/libMultiMarkdown.h +++ b/src/libMultiMarkdown.h @@ -67,69 +67,83 @@ /// Convert source string to output string, based on provided `parser_extensions` /// and requested `export_format`. /// The returned `char *` will need to be freed after it is no longer needed. -char * markdown_to_string(const char * source, unsigned long extensions, int format); +char * markdown_to_string( + const char * source, //!< Pointer to c-string of the source text + unsigned long extensions, //!< Bit field of parser_extensions + int format //!< Specify export_format to be used +); /// Does the source string have metadata, using provided `parser_extensions`? /// -bool has_metadata(const char *source, unsigned long extensions); +bool has_metadata( + const char *source, //!< Pointer to c-string of the source text + unsigned long extensions //!< Bit field of parser_extensions +); /// List all metadata keys, using provided `parser_extensions`. /// The returned `char *` will need to be freed after it is no longer needed. -char * extract_metadata_keys(const char *source, unsigned long extensions); +char * extract_metadata_keys( + const char *source, //!< Pointer to c-string of the source text + unsigned long extensions //!< Bit field of parser_extensions +); /// Extract the value for the specified metadata key, using provided `parser_extensions`. /// The returned `char *` will need to be freed after it is no longer needed. -char * extract_metadata_value(const char *source, unsigned long extensions, char *key); +char * extract_metadata_value( + const char *source, //!< Pointer to c-string of the source text + unsigned long extensions, //!< Bit field of parser_extensions + char *key //!< C-string of the key we need to find +); /// Return the version string for this build of libMultiMarkdown /// The returned `char *` will need to be freed after it is no longer needed. char * mmd_version(void); -/* These are the basic extensions */ +/// These are the basic extensions that enable or disable MultiMarkdown features enum parser_extensions { - EXT_COMPATIBILITY = 1 << 0, /* Markdown compatibility mode */ - EXT_COMPLETE = 1 << 1, /* Create complete document */ - EXT_SNIPPET = 1 << 2, /* Create snippet only */ - EXT_HEAD_CLOSED = 1 << 3, /* for use by parser */ - EXT_SMART = 1 << 4, /* Enable Smart quotes */ - EXT_NOTES = 1 << 5, /* Enable Footnotes */ - EXT_NO_LABELS = 1 << 6, /* Don't add anchors to headers, etc. */ - EXT_FILTER_STYLES = 1 << 7, /* Filter out style blocks */ - EXT_FILTER_HTML = 1 << 8, /* Filter out raw HTML */ - EXT_PROCESS_HTML = 1 << 9, /* Process Markdown inside HTML */ - EXT_NO_METADATA = 1 << 10, /* Don't parse Metadata */ - EXT_OBFUSCATE = 1 << 11, /* Mask email addresses */ - EXT_CRITIC = 1 << 12, /* Critic Markup Support */ - EXT_CRITIC_ACCEPT = 1 << 13, /* Accept all proposed changes */ - EXT_CRITIC_REJECT = 1 << 14, /* Reject all proposed changes */ - EXT_RANDOM_FOOT = 1 << 15, /* Use random numbers for footnote links */ - EXT_HEADINGSECTION = 1 << 16, /* Group blocks under parent heading */ - EXT_ESCAPED_LINE_BREAKS = 1 << 17, /* Escaped line break */ - EXT_NO_STRONG = 1 << 18, /* Don't allow nested 's */ - EXT_NO_EMPH = 1 << 19, /* Don't allow nested 's */ - EXT_FAKE = 1 << 31, /* 31 is highest number allowed */ + EXT_COMPATIBILITY = 1 << 0, //!< Markdown compatibility mode + EXT_COMPLETE = 1 << 1, //!< Create complete document + EXT_SNIPPET = 1 << 2, //!< Create snippet only + EXT_HEAD_CLOSED = 1 << 3, //!< for use by parser + EXT_SMART = 1 << 4, //!< Enable Smart quotes + EXT_NOTES = 1 << 5, //!< Enable Footnotes + EXT_NO_LABELS = 1 << 6, //!< Don't add anchors to headers, etc. + EXT_FILTER_STYLES = 1 << 7, //!< Filter out style blocks + EXT_FILTER_HTML = 1 << 8, //!< Filter out raw HTML + EXT_PROCESS_HTML = 1 << 9, //!< Process Markdown inside HTML + EXT_NO_METADATA = 1 << 10, //!< Don't parse Metadata + EXT_OBFUSCATE = 1 << 11, //!< Mask email addresses + EXT_CRITIC = 1 << 12, //!< Critic Markup Support + EXT_CRITIC_ACCEPT = 1 << 13, //!< Accept all proposed changes + EXT_CRITIC_REJECT = 1 << 14, //!< Reject all proposed changes + EXT_RANDOM_FOOT = 1 << 15, //!< Use random numbers for footnote links + EXT_HEADINGSECTION = 1 << 16, //!< Group blocks under parent heading + EXT_ESCAPED_LINE_BREAKS = 1 << 17, //!< Escaped line break + EXT_NO_STRONG = 1 << 18, //!< Don't allow nested \'s + EXT_NO_EMPH = 1 << 19, //!< Don't allow nested \'s + EXT_FAKE = 1 << 31, //!< 31 is highest number allowed }; -/* Define output formats we support -- first in list is default */ +/// Define the output formats we support -- not all of these should be used enum export_formats { - ORIGINAL_FORMAT, /* Transclusion happens, but no parsing */ - HTML_FORMAT, /* Well supported */ - TEXT_FORMAT, /* Not currently used, may exit host process */ - LATEX_FORMAT, - MEMOIR_FORMAT, - BEAMER_FORMAT, - OPML_FORMAT, - ODF_FORMAT, - RTF_FORMAT, /* Not recommended for production code, may crash */ - CRITIC_ACCEPT_FORMAT, - CRITIC_REJECT_FORMAT, - CRITIC_HTML_HIGHLIGHT_FORMAT, - LYX_FORMAT, /* Not actively developed */ - TOC_FORMAT, + ORIGINAL_FORMAT, //!< Transclusion happens, but no parsing + HTML_FORMAT, //!< Well supported + TEXT_FORMAT, //!< Not currently used, may exit host process + LATEX_FORMAT, //!< Well supported + MEMOIR_FORMAT, //!< Well supported + BEAMER_FORMAT, //!< Well supported + OPML_FORMAT, //!< Well supported + ODF_FORMAT, //!< Well supported + RTF_FORMAT, //!< Not recommended for production code, may crash + CRITIC_ACCEPT_FORMAT, //!< Used as a pre-processing step + CRITIC_REJECT_FORMAT, //!< Used as a pre-processing step + CRITIC_HTML_HIGHLIGHT_FORMAT, //!< Used as a pre-processing step + LYX_FORMAT, //!< Developed and maintained by Charles R. Cowan + TOC_FORMAT, //!< Used as a pre-processing step }; -/* These are the identifiers for node types */ +/// These are the identifiers for node types enum keys { NO_TYPE, LIST, @@ -141,7 +155,7 @@ enum keys { LINEBREAK, SPACE, HEADINGSECTION, - H1, H2, H3, H4, H5, H6, H7, /* Keep these in order */ + H1, H2, H3, H4, H5, H6, H7, //!< Keep H1 through H7 in order METADATA, METAKEY, METAVALUE, @@ -212,30 +226,32 @@ enum keys { ABBRSTART, ABBRSTOP, TOC, - KEY_COUNTER /* This *MUST* be the last item in the list */ + KEY_COUNTER //!< This *MUST* be the last item in the list }; -/* This is the element used in the resulting parse tree */ +/// This is the element used in the resulting parse tree struct node { - short key; /* what type of element are we? */ - char *str; /* relevant string from source for element */ - struct link_data *link_data; /* store link info when relevant */ - struct node *children; /* child elements */ - struct node *next; /* next element */ + short key; //!< What type of element is this? + char *str; //!< Relevant string from source for this element + struct link_data *link_data; //!< Store link info when relevant to this node + struct node *children; //!< Pointer to child elements + struct node *next; //!< Pointer to next element }; +/// This is the element used in the resulting parse tree typedef struct node node; -/* Define a structure to simplify handling of links */ +/// Structure to simplify handling of links struct link_data { - char *label; /* if this is a reference link */ - char *source; /* source URL */ - char *title; /* title string */ - node *attr; /* attribute tree */ + char *label; //!< Text of the label, if this is a reference link + char *source; //!< Source URL string + char *title; //!< Title string + node *attr; //!< Pointer to tree of attributes, if any }; +/// Structure to simplify handling of links typedef struct link_data link_data; #endif diff --git a/src/odf.c b/src/odf.c index c58288a..ad090f7 100644 --- a/src/odf.c +++ b/src/odf.c @@ -199,7 +199,9 @@ void print_odf_node(GString *out, node *n, scratch_pad *scratch) { break; } scratch->odf_list_needs_end_p = true; - } else if ((n->children != NULL) && (n->children->key == LIST) && (n->children->children == NULL)) { + } else if ((n->children != NULL) && + (((n->children->key == LIST) && (n->children->children == NULL)) || + ((n->children->key == STR) && (strlen(n->children->str) == 0)))) { /* This is an empty list item. ODF apparently requires something for the empty item to appear */ switch (scratch->odf_para_type) { case BULLETLIST: diff --git a/src/parse_utilities.c b/src/parse_utilities.c index b29f76b..6f85935 100644 --- a/src/parse_utilities.c +++ b/src/parse_utilities.c @@ -103,29 +103,6 @@ node * mk_list(int key, node *list) { result->children = reverse_list(list); return result; } - -/* Create a new node with position information */ -node * mk_pos_node(int key, char *string, unsigned int start, unsigned int stop) { - node *result = mk_node(key); - if (string != NULL) - result->str = strdup(string); - - return result; -} - -/* Create a new string node with position information */ -node * mk_pos_str(char *string, unsigned int start, unsigned int stop) { - node *result = mk_str(string); - - return result; -} - -/* Create a new list node with position information */ -node * mk_pos_list(int key, node *list, unsigned int start, unsigned int stop) { - node *result = mk_list(key, list); - - return result; -} /* free just the current node and children*/ void free_node(node *n) { @@ -240,6 +217,15 @@ parser_data * mk_parser_data(const char *charbuf, unsigned long extensions) { return result; } +void free_parser_data_preserving_result(parser_data *data) { + free_node_tree(data->autolabels); +/* don't do this - it's owned by someone else -- free(data->original); */ + data->original = NULL; + data->charbuf = NULL; + + free(data); +} + void free_parser_data(parser_data *data) { free_node_tree(data->result); free_node_tree(data->autolabels); diff --git a/src/parser.h b/src/parser.h index d04d63f..3f51a1a 100644 --- a/src/parser.h +++ b/src/parser.h @@ -118,9 +118,6 @@ node * mk_node(int key); node * mk_str(char *string); node * mk_list(int key, node *list); node * mk_link(node *text, char *label, char *source, char *title, node *attr); -node * mk_pos_node(int key, char *string, unsigned int start, unsigned int stop); -node * mk_pos_str(char *string, unsigned int start, unsigned int stop); -node * mk_pos_list(int key, node *list, unsigned int start, unsigned int stop); void free_node(node *n); void free_node_tree(node * n); @@ -137,6 +134,7 @@ GString * concat_string_list(node *list); parser_data * mk_parser_data(const char *charbuf, unsigned long extensions); void free_parser_data(parser_data *data); +void free_parser_data_preserving_result(parser_data *data); char * preformat_text(const char *text); diff --git a/src/parser.leg b/src/parser.leg index a50700b..6bfb5db 100644 --- a/src/parser.leg +++ b/src/parser.leg @@ -25,9 +25,9 @@ /* Define shortcuts to adding nodes, etc. */ -#define node(x) mk_pos_node(x, NULL, thunk->begin, thunk->end) -#define str(x) mk_pos_str(x, thunk->begin, thunk->end) -#define list(x,y) mk_pos_list(x, y, thunk->begin, thunk->end) +#define node(x) mk_node(x) +#define str(x) mk_str(x) +#define list(x,y) mk_list(x, y) #define ext(x) extension(x,((parser_data *)G->data)->extensions) @@ -66,7 +66,9 @@ MetaData = a:StartList ) (MetaDataKeyValue { a = cons($$, a); })+ (YAMLStop)?) - { $$ = list(METADATA, a); } + { + $$ = list(METADATA, a); + } MetaDataKeyValue = a:MetaDataKey Sp ':' Sp b:MetaDataValue { @@ -157,25 +159,36 @@ HeadingSectionBlock = BlankLine* !Heading TOC = "{{TOC}}" Sp Newline { - $$ = mk_node(TOC); - $$->children = mk_node(RAW); + $$ = node(TOC); + $$->children = node(RAW); $$->children->str = markdown_to_string(((parser_data *)G->data)->original, 0, TOC_FORMAT); } Heading = SetextHeading | AtxHeading -HeadingSection = a:StartList Heading { a = cons($$, a); } +HeadingSection = a:StartList Heading { + a = cons($$, a); + } (HeadingSectionBlock {a = cons($$, a); })* - { $$ = list(HEADINGSECTION, a); } + { + $$ = list(HEADINGSECTION, a); + } AtxInline = !Newline !( &{ !ext(EXT_COMPATIBILITY) } Sp AutoLabel Sp '#'* Sp Newline) !(Sp '#'* Sp Newline) Inline AtxStart = NonindentSpace < ( "######" | "#####" | "####" | "###" | "##" | "#" ) > { $$ = node(H1 + ((int)strlen(yytext) - 1)); } -AtxHeading = s:AtxStart Sp a:StartList ( AtxInline { a = cons($$, a); } )+ ( Sp b:AutoLabel { append_list(b,a);})? (Sp '#'* Sp)? Sp < Newline > -# ensures that we count characters all the way to the end - { $$ = list(s->key,a); free(s); } +AtxHeading = s:AtxStart Sp a:StartList + ( AtxInline { a = cons($$, a); } )+ + ( Sp b:AutoLabel { append_list(b,a);})? + (Sp '#'* Sp)? + Sp < Newline > + # ensures that we count characters all the way to the end + { + $$ = list(s->key,a); + free(s); + } SetextHeading = NonindentSpace (SetextHeading1 | SetextHeading2) @@ -185,20 +198,28 @@ SetextBottom2 = NonindentSpace '-'+ Sp Newline SetextHeading1 = &(RawLine SetextBottom1) a:StartList ( !Endline !( &{ !ext(EXT_COMPATIBILITY) } Sp AutoLabel ) Inline { a = cons($$, a); } )+ ( Sp b:AutoLabel { append_list(b,a);} Sp )? Sp Newline - { $$ = list(H1, a); } + + { + $$ = list(H1, a); + } SetextHeading2 = &(RawLine SetextBottom2) a:StartList ( !Endline !( &{ !ext(EXT_COMPATIBILITY) } Sp AutoLabel ) Inline { a = cons($$, a); } )+ ( Sp b:AutoLabel { append_list(b,a)} Sp )? Sp Newline - { $$ = list(H2, a); } + + { + $$ = list(H2, a); + } BlockQuote = a:BlockQuoteRaw - { $$ = list(BLOCKQUOTE,a); } + { + $$ = list(BLOCKQUOTE,a); + } BlockQuoteRaw = a:StartList x:StartList (( NonindentSpace b:BlockQuoteMarker Line { a = cons($$, a); x = cons(b, x); } ) ( !(NonindentSpace '>') !BlankLine Line { a = cons($$, a); } )* - ( BlankLine { a = cons(mk_str("\n"), a); } )* + ( BlankLine { a = cons(str("\n"), a); } )* )+ { $$ = x; @@ -274,14 +295,22 @@ Entity = ( HexEntity | DecEntity | CharEntity ) { $$ = str(yytext); $$->key = HTML; } Para = NonindentSpace a:Inlines BlankLine+ - { $$ = a; $$->key = PARA; } + { + $$ = a; + $$->key = PARA; + } Plain = a:Inlines - { $$ = a; $$->key = PLAIN; } + { + $$ = a; + $$->key = PLAIN; + } Inlines = a:StartList ( !Endline Inline { a = cons($$, a); } | c:Endline &Inline { a = cons(c, a); } )+ voidEndline? - { $$ = list(LIST, a); } + { + $$ = list(LIST, a); + } Inline = #&{ check_timeout((parser_data *)G->data) } # TODO: the check_timeout function still slows us down -- do we still need it?? @@ -333,7 +362,10 @@ InlineNoEmph = #&{ check_timeout((parser_data *)G->data) } Space = Spacechar+ - { $$ = str(" "); $$->key = SPACE; } + { + $$ = str(" "); + $$->key = SPACE; + } Endline = LineBreak | TerminalEndline | NormalEndline @@ -348,7 +380,10 @@ TerminalEndline = Sp Newline Eof { $$ = NULL; } LineBreak = (" " | &{ !ext(EXT_COMPATIBILITY) && ext(EXT_ESCAPED_LINE_BREAKS) } Sp "\\") a:NormalEndline - { $$ = a; $$->key = LINEBREAK; } + { + $$ = a; + $$->key = LINEBREAK; + } voidEndline = ((" " | &{ !ext(EXT_COMPATIBILITY) && ext(EXT_ESCAPED_LINE_BREAKS) } Sp "\\") voidNormalEndline) | (Sp Newline Eof) | voidNormalEndline @@ -358,7 +393,13 @@ voidNormalEndline = Sp Newline !BlankLine !'>' !AtxStart Str = a:StartList < NormalChar+ > { a = cons(str(yytext), a); } ( &{ !ext(EXT_COMPATIBILITY) } StrChunk { a = cons($$, a); } )* ( &{ !ext(EXT_COMPATIBILITY) } ( Superscript | Subscript) {a = cons($$, a); } )* - { if (a->next == NULL) { $$ = a; } else { $$ = list(LIST, a); } } + { + if (a->next == NULL) { + $$ = a; + } else { + $$ = list(LIST, a); + } + } StrChunk = < (NormalChar | '_'+ !Punctuation &Alphanumeric)+ > { $$ = str(yytext); } | AposChunk @@ -378,13 +419,13 @@ StartList = &. MathSpan = '\\' < ( ('\\[' (!'\\\\]' .)* '\\\\]') | ('\\(' (!'\\\\)' .)* '\\\\)') ) > -{ - /* Basically, these delimiters indicate math in LaTeX syntax, and the - delimiters are compatible with MathJax and LaTeX - ASCIIMathML is *not* supported */ - $$ = str(yytext); - $$->key = MATHSPAN; -} + { + /* Basically, these delimiters indicate math in LaTeX syntax, and the + delimiters are compatible with MathJax and LaTeX + ASCIIMathML is *not* supported */ + $$ = str(yytext); + $$->key = MATHSPAN; + } SingleDollarMathStart = '$' !(Spacechar | Newline | '$' ) @@ -420,8 +461,9 @@ Emph = < EmphMatch > */ $$ = markdown_chunk_to_node(&yytext[1], ((parser_data *)G->data)->extensions); - if ($$ != NULL) + if ($$ != NULL) { $$->key = EMPH; + } } @@ -447,8 +489,9 @@ Strong = < StrongMatch > */ $$ = markdown_chunk_to_node(&yytext[2], ((parser_data *)G->data)->extensions ); - if ($$ != NULL) + if ($$ != NULL) { $$->key = STRONG; + } } StrongMatch = &{ !ext(EXT_NO_STRONG) } (StrongStar | StrongUl) @@ -531,7 +574,9 @@ CitationReferenceSingle = < (( "[]" Spnl b:RawCitationReference ) } RawCitationReference = "[#" < ( !Newline !']' . )+ > ']' - { $$ = str(yytext); } + { + $$ = str(yytext); + } Variable = "[%" < (!Newline !']' .)+ > ']' { @@ -612,7 +657,7 @@ AttrKey = < AlphanumericAscii+ > { char *lab; lab = label_from_string(yytext); - $$ = mk_str(lab); + $$ = str(lab); $$->key = ATTRKEY; free(lab); } @@ -662,17 +707,24 @@ Abbreviation = '*' l:Label Sp ':' Sp RawLine } ImageBlock = Image Sp Newline BlankLine+ - { if ($$->key == IMAGE) $$->key = IMAGEBLOCK; } + { + if ($$->key == IMAGE) + $$->key = IMAGEBLOCK; + } Image = '!' ( !AutoLink Link ) - { $$->key = IMAGE; } + { + $$->key = IMAGE; + } Label = < "[" !'[' ( !'^' !'#' &{ ext(EXT_NOTES) } | &. &{ !ext(EXT_NOTES) } ) a:StartList ( !']' Inline { a = cons($$, a); } )* ']'> - { $$ = list(LIST, a); } + { + $$ = list(LIST, a); + } AutoLabel = '[' < (!Newline !'^' !'#' !'%' . )( !Newline !']' !'[' . )+ > ']' &(!(Sp ('(' | '['))) { @@ -736,14 +788,17 @@ Glossary = &{ ext(EXT_NOTES) } GlossaryTerm = < (!Newline !'(' .)+ > { - $$ = mk_list(LIST, NULL); + $$ = list(LIST, NULL); $$->str = 0; - $$->children = mk_str(yytext); + $$->children = str(yytext); $$->key = GLOSSARYTERM; } GlossarySortKey = '(' < (!')' !Newline .)* > ')' - { $$ = mk_str(yytext); $$->key = GLOSSARYSORTKEY; } + { + $$ = str(yytext); + $$->key = GLOSSARYSORTKEY; + } Ticks1 = "`" !'`' Ticks2 = "``" !'`' @@ -757,7 +812,10 @@ Code = ( Ticks1 Sp < ( ( !'`' Nonspacechar )+ | !Ticks1 '`'+ | !( Sp Ticks1 ) ( | Ticks4 Sp < ( ( !'`' Nonspacechar )+ | !Ticks4 '`'+ | !( Sp Ticks4 ) ( Spacechar | Newline !BlankLine ) )+ > Sp Ticks4 | Ticks5 Sp < ( ( !'`' Nonspacechar )+ | !Ticks5 '`'+ | !( Sp Ticks5 ) ( Spacechar | Newline !BlankLine ) )+ > Sp Ticks5 ) - { $$ = str(yytext); $$->key = CODE; } + { + $$ = str(yytext); + $$->key = CODE; + } FenceType = Sp &((!'\n' !'\r' !'`' .)* Newline) RawLine { $$ = str(yytext); $$->key = VERBATIMTYPE; } @@ -766,7 +824,8 @@ Fenced = NonindentSpace (( Ticks3 a:FenceType < ( (!(NonindentSpace Ticks3) NonM ( Ticks4 a:FenceType < ( (!(NonindentSpace Ticks4) NonMatchingRawLine)* ) > NonindentSpace Ticks4 Sp Newline ) | ( Ticks5 a:FenceType < ( (!(NonindentSpace Ticks5) NonMatchingRawLine)* ) > NonindentSpace Ticks5 Sp Newline ) ) { - $$ = str(yytext); $$->key = VERBATIMFENCE; + $$ = str(yytext); + $$->key = VERBATIMFENCE; $$->children = a; } @@ -797,7 +856,9 @@ SingleQuoted = SingleQuoteStart a:StartList ( !SingleQuoteEnd b:Inline { a = cons(b, a); } )* SingleQuoteEnd - { $$ = mk_list(SINGLEQUOTED, a); } + { + $$ = list(SINGLEQUOTED, a); + } DoubleQuoteStart = '"' @@ -807,7 +868,7 @@ DoubleQuoted = DoubleQuoteStart a:StartList ( !DoubleQuoteEnd !BackTickEnd !BackTickStart b:Inline { a = cons(b, a); } )* DoubleQuoteEnd - { $$ = mk_list(DOUBLEQUOTED, a); } + { $$ = list(DOUBLEQUOTED, a); } BackTickStart = '``' @@ -817,18 +878,25 @@ BackTickQuoted = BackTickStart a:StartList ( !DoubleQuoteEnd !BackTickEnd b:Inline { a = cons(b, a); } )* BackTickEnd - { $$ = mk_list(DOUBLEQUOTED, a); } + { + $$ = list(DOUBLEQUOTED, a); + } NonblankIndentedLine = !BlankLine IndentedLine VerbatimChunk = a:StartList - ( BlankLine { a = cons(mk_str("\n"), a); } )* + ( BlankLine { a = cons(str("\n"), a); } )* ( NonblankIndentedLine { a = cons($$, a); } )+ - { $$ = mk_str_from_list(a, false); } + { + $$ = mk_str_from_list(a, false); + } Verbatim = BlankLine* a:StartList ( VerbatimChunk { a = cons($$, a); } )+ BlankLine* - { $$ = mk_str_from_list(a, false); $$->key = VERBATIM; } + { + $$ = mk_str_from_list(a, false); + $$->key = VERBATIM; + } HorizontalRule = NonindentSpace @@ -836,7 +904,9 @@ HorizontalRule = NonindentSpace | '-' Sp '-' Sp '-' (Sp '-')* | '_' Sp '_' Sp '_' (Sp '_')*) Sp Newline BlankLine* - { $$ = mk_node(HRULE); } + { + $$ = node(HRULE); + } DefinitionList = a:StartList &(TermLine+ Newline? NonindentSpace ':') ( (Term { a = cons($$, a); } )+ @@ -844,21 +914,26 @@ DefinitionList = a:StartList &(TermLine+ Newline? NonindentSpace ':') (Definition { a = cons($$, a);})+ BlankLine* )+ - { $$ = mk_list(LIST, a); $$->key = DEFLIST; } + { + $$ = list(LIST, a); + $$->key = DEFLIST; + } TermLine = !':' !BlankLine (!Newline .)* Newline Term = a:StartList !BlankLine !':' (!Newline !Endline Inline {a = cons($$, a);} )+ Sp Newline - { $$ = mk_list(TERM,a); } + { + $$ = list(TERM,a); + } Definition = (a:StartList b:StartList - (BlankLine { b = cons(mk_str("\n"),b); } )? - ( NonindentSpace ':' Sp RawLine { a = cons(mk_str(yytext), a);}) - ( !':' !BlankLine RawLine { a = cons(mk_str(yytext), a);})* - ( BlankLine {a = cons(mk_str("\n"),a);} + (BlankLine { b = cons(str("\n"),b); } )? + ( NonindentSpace ':' Sp RawLine { a = cons(str(yytext), a);}) + ( !':' !BlankLine RawLine { a = cons(str(yytext), a);})* + ( BlankLine {a = cons(str("\n"),a);} (IndentedLine { a = cons($$,a);})+ - { a = cons(mk_str("\n"),a);} + { a = cons(str("\n"),a);} )* ) { if (b != NULL) { a = cons(b,a);} @@ -877,7 +952,9 @@ BulletList = &Bullet (ListTight | ListLoose) ListTight = a:StartList ( ListItemTight { a = cons($$, a); } )+ BlankLine* !(Bullet | Enumerator | BulletNoSpace &EmptyList | EnumeratorNoSpace &EmptyList ) - { $$ = list(LIST, a); } + { + $$ = list(LIST, a); + } ListLoose = a:StartList ( b:ListItem BlankLine* @@ -889,7 +966,9 @@ ListLoose = a:StartList strncat(li->str + size, "\n\n", 2); /* In loose list, \n\n added to end of each element */ a = cons(b, a); } )+ - { $$ = list(LIST, a); } + { + $$ = list(LIST, a); + } ListItem = < ( Bullet | Enumerator | BulletNoSpace &EmptyList | EnumeratorNoSpace &EmptyList )> a:StartList @@ -918,12 +997,16 @@ ListItemTight = ( Bullet | Enumerator | BulletNoSpace &EmptyList | EnumeratorNoS } EmptyList = BlankLine - { $$ = mk_str(""); } + { + $$ = str(""); + } ListBlock = a:StartList ( EmptyList | !Heading Line ) { a = cons($$, a); } ( ListBlockLine { a = cons($$, a); } )* - { $$ = mk_str_from_list(a, false); } + { + $$ = mk_str_from_list(a, false); + } ListContinuationBlock = a:StartList ( < BlankLine* > @@ -934,7 +1017,9 @@ ListContinuationBlock = a:StartList a = cons(str(yytext), a); } ) ( Indent !BlankLine ListBlock { a = cons($$, a); } )+ - { $$ = mk_str_from_list(a, false); } + { + $$ = mk_str_from_list(a, false); + } Enumerator = NonindentSpace [0-9]+ '.' Spacechar+ @@ -1203,7 +1288,7 @@ HtmlBlock = !MarkdownHtmlTagOpen < ( HtmlBlockInTags | HtmlComment | HtmlBlockSe BlankLine+ { if (ext(EXT_FILTER_HTML)) { - $$ = mk_list(LIST, NULL); + $$ = list(LIST, NULL); } else { $$ = str(yytext); if ( ext(EXT_PROCESS_HTML)) $$->key = RAW; @@ -1218,16 +1303,19 @@ MarkdownHtmlBlock = &MarkdownHtmlTagOpen < ( HtmlBlockInTags | HtmlComment | Htm MarkdownHtmlAttribute = ("markdown" | "MARKDOWN") Spnl '=' Spnl ('"' Spnl)? "1" (Spnl '"')? Spnl -MarkdownHtmlTagOpen = a:StartList '<' {a = cons(mk_str("<"),a);} - Spnl {a = cons(mk_str(yytext),a);} &Spacechar Spnl +MarkdownHtmlTagOpen = a:StartList '<' {a = cons(str("<"),a);} + Spnl {a = cons(str(yytext),a);} &Spacechar Spnl (!MarkdownHtmlAttribute - {a = cons(mk_str(" "),a); - a = cons(mk_str(yytext),a);})* + {a = cons(str(" "),a); + a = cons(str(yytext),a);})* MarkdownHtmlAttribute - ( {a = cons(mk_str(" "),a); - a = cons(mk_str(yytext),a);})* - '>' { a = cons(mk_str(">"),a);} - { $$ = mk_str_from_list(a,false); $$->key = HTML; } + ( {a = cons(str(" "),a); + a = cons(str(yytext),a);})* + '>' { a = cons(str(">"),a);} + { + $$ = mk_str_from_list(a,false); + $$->key = HTML; + } HtmlBlockSelfClosing = '<' Spnl HtmlBlockType Spnl HtmlAttribute* '/' Spnl '>' @@ -1246,9 +1334,9 @@ StyleBlock = < InStyleTags > BlankLine* { if (ext(EXT_FILTER_STYLES)) { - $$ = mk_list(LIST, NULL); + $$ = list(LIST, NULL); } else { - $$ = mk_str(yytext); + $$ = str(yytext); $$->key = HTMLBLOCK; } } @@ -1272,13 +1360,17 @@ Table = a:StartList b:StartList (TableCaption { b = cons($$, b);})? } TableBody = a:StartList (TableRow {a = cons($$, a);})+ - { $$ = list(TABLEBODY, a);} + { + $$ = list(TABLEBODY, a); + } TableRow = a:StartList (!SeparatorLine &(TableLine) CellDivider? (TableCell { a = cons($$, a); })+ ) Sp - { $$ = list(TABLEROW, a); } + { + $$ = list(TABLEROW, a); + } TableLine = (!Newline !CellDivider .)* CellDivider @@ -1295,10 +1387,14 @@ ExtendedCell = (EmptyCell | FullCell) FullCell = Sp a:StartList ((!Newline !Endline !CellDivider !(Sp &CellDivider) Inline ) { a = cons($$,a)})+ ( CellDivider )? - { $$ = list(TABLECELL,a); } + { + $$ = list(TABLECELL,a); + } EmptyCell = CellDivider - { $$ = node(TABLECELL); } + { + $$ = node(TABLECELL); + } SeparatorLine = a:StartList &(TableLine) @@ -1316,22 +1412,22 @@ AlignmentCell = Sp (!CellDivider ( LeftAlignWrap | CenterAlignWrap | RightAlignW Sp ( CellDivider )? LeftAlignWrap = ':'? ('-'+ | '='+) '+' &(!'-' !'=' !':') - { $$ = mk_str("L");} + { $$ = str("L");} LeftAlign = ':'? ('-'+ | '='+) &(!'-' !':') - { $$ = mk_str("l");} + { $$ = str("l");} CenterAlignWrap = ':' ('-'+ | '='+)? '+' ':' &(!'-' !'=' !':') - { $$ = mk_str("C");} + { $$ = str("C");} CenterAlign = ':' ('-'+ | '='+)? ':' &(!'-' !'=' !':') - { $$ = mk_str("c");} + { $$ = str("c");} RightAlignWrap = ('-'+ | '='+) ':' '+' &(!'-' !'=' !':') - { $$ = mk_str("R");} + { $$ = str("R");} RightAlign = ('-'+ | '='+)':' &(!'-' !'=' !':') - { $$ = mk_str("r");} + { $$ = str("r");} CellDivider = '|' @@ -1354,7 +1450,9 @@ TableCaption = b:StartList a:StartList (< BracketedText > } RawNoteReference = ( "[^" | "[#" ) < ( !Newline !']' . )+ > ']' - { $$ = str(yytext); } + { + $$ = str(yytext); + } Note = &{ ext(EXT_NOTES) } NonindentSpace ref:RawNoteReference ':' Sp @@ -1375,7 +1473,10 @@ Note = &{ ext(EXT_NOTES) } RawNoteBlock = a:StartList ( !BlankLine !(NonindentSpace RawNoteReference ':') OptionallyIndentedLine { a = cons($$, a); } )+ ( < BlankLine* > { a = cons(str(yytext), a); } ) - { $$ = mk_str_from_list(a, true); $$->key = RAW; } + { + $$ = mk_str_from_list(a, true); + $$->key = RAW; + } DocForOPML = BOM? a:StartList ( &{ !ext(EXT_COMPATIBILITY) } @@ -1388,7 +1489,7 @@ OPMLBlock = BlankLine* ( OPMLHeadingSection | OPMLPlain ) OPMLHeadingSection = a:StartList OPMLHeading { a = cons($$, a); } (OPMLSectionBlock {a = cons($$, a); })* - { $$ = mk_list(HEADINGSECTION, a);} + { $$ = list(HEADINGSECTION, a);} OPMLHeading = OPMLAtxHeading | OPMLSetextHeading @@ -1412,7 +1513,7 @@ OPMLSetextHeading2 = < (!'\r' !'\n' .)* > Newline SetextBottom2 OPMLSectionBlock = BlankLine* !OPMLHeading OPMLPlain OPMLPlain = a:StartList (!BlankLine !Heading Line { a = cons($$,a); })+ - { $$ = mk_list(PLAIN, a); } + { $$ = list(PLAIN, a); } DocForTOC = BOM? a: StartList (&( (YAMLStart)? MetaDataKey Sp ':' Sp (!Newline)) z:MetaData { free_node_tree(z); } )? @@ -1424,7 +1525,7 @@ TOCBlock = BlankLine* TOCHeadingSection TOCHeadingSection = a:StartList Heading { a = cons($$, a); } (TOCSectionBlock )* - { $$ = mk_list(HEADINGSECTION, a); } + { $$ = list(HEADINGSECTION, a); } TOCSectionBlock = BlankLine* !Heading TOCPlain @@ -1436,25 +1537,45 @@ TOCPlain = ( (y:Fenced { free_node_tree(y); } ) | (!BlankLine !Heading z:Line { CriticMarkup = CriticAddition | CriticDeletion | CriticSubstitution | CriticHighlight | CriticComment CriticAddition = ('{++' < (!'++}' .)* > '++}') - { $$ = str(yytext); $$->key = CRITICADDITION; } + { + $$ = str(yytext); + $$->key = CRITICADDITION; + } CriticDeletion = ('{--' < (!'--}' .)* > '--}') - { $$ = str(yytext); $$->key = CRITICDELETION; } + { + $$ = str(yytext); + $$->key = CRITICDELETION; + } CriticSubstitution = a:StartList ( '{~~' CriticSubstDel { a = cons($$,a); } '~>' CriticSubstAdd { a = cons($$,a); } '~~}') - { $$ = list(CRITICSUBSTITUTION, a); } + { + $$ = list(CRITICSUBSTITUTION, a); + } CriticSubstDel = < (!'~>' .)* > - { $$ = str(yytext); $$->key = CRITICDELETION; } + { + $$ = str(yytext); + $$->key = CRITICDELETION; + } CriticSubstAdd = < (!'~~}' .)* > - { $$ = str(yytext); $$->key = CRITICADDITION; } + { + $$ = str(yytext); + $$->key = CRITICADDITION; + } CriticHighlight = ('{==' < (!'==}' .)* > '==}') - { $$ = str(yytext); $$->key = CRITICHIGHLIGHT; } + { + $$ = str(yytext); + $$->key = CRITICHIGHLIGHT; + } CriticComment = ('{>>' < (!'<<}' .)* > '<<}') - { $$ = str(yytext); $$->key = CRITICCOMMENT; } + { + $$ = str(yytext); + $$->key = CRITICCOMMENT; + } DocForCritic = BOM? a:StartList ( CriticString { a = cons($$, a); } | RawString { a = cons($$, a); } | FalseMatch { a = cons($$, a); } )* @@ -1524,6 +1645,7 @@ node * process_raw_blocks(node * n, unsigned long extensions) { return n; } + node * markdown_chunk_to_node(const char * source, unsigned long extensions) { /* Designed for parsing 'chunks' of markdown from inside a specific range, @@ -1553,8 +1675,8 @@ node * markdown_chunk_to_node(const char * source, unsigned long extensions) { return result; } -char * markdown_to_string(const char * source, unsigned long extensions, int format) { - char *out; + +node * parse_markdown(const char * source, unsigned long extensions, int format) { char *formatted; char *critic_resolved; char *target_meta_key = FALSE; @@ -1610,14 +1732,18 @@ char * markdown_to_string(const char * source, unsigned long extensions, int for } if (((parser_data *)g.data)->parse_aborted) { + /* + 2015-11-28 - I don't believe this error had occurred in years. More recently, + however, the check_timeout() function has been disabled. - FTP + */ + /* clean up */ free_parser_data((parser_data *)g.data); yydeinit(&g); free(formatted); - out = strdup("MultiMarkdown was unable to parse this file."); - return out; + return NULL; } refined = process_raw_blocks(((parser_data *)g.data)->result, extensions); /* iteratively parse RAW bits */ @@ -1631,17 +1757,36 @@ char * markdown_to_string(const char * source, unsigned long extensions, int for // fprintf(stderr, "No autolabels\n"); } - /* Show what we got */ - out = export_node_tree(refined, format, extensions); - /* clean up */ - free_parser_data((parser_data *)g.data); + free_parser_data_preserving_result((parser_data *)g.data); yydeinit(&g); free(formatted); - return out; + + return refined; } + +char * markdown_to_string(const char * source, unsigned long extensions, int format) { + char * output; + node * parse_tree; + + /* Parse source (Multi)Markdown text */ + parse_tree = parse_markdown(source, extensions, format); + + if (parse_tree == NULL) { + output = strdup(""); + return output; + } + + /* Convert to desired output format */ + output = export_node_tree(parse_tree, format, extensions); + + free_node_tree(parse_tree); + return output; +} + + /* has_metadata -- determine whether metadata exists or not */ bool has_metadata(const char *source, unsigned long extensions) { char *formatted; @@ -1668,6 +1813,7 @@ bool has_metadata(const char *source, unsigned long extensions) { return answer; } + /* extract_metadata_keys -- return list of metadata keys as "\n" separated list */ char * extract_metadata_keys(const char *source, unsigned long extensions) { char *out; @@ -1694,6 +1840,7 @@ char * extract_metadata_keys(const char *source, unsigned long extensions) { return out; } + /* extract_metadata_value -- find the value and return it */ char * extract_metadata_value(const char *source, unsigned long extensions, char *key) { char *out; @@ -1720,4 +1867,3 @@ char * extract_metadata_value(const char *source, unsigned long extensions, char free(formatted); return out; } - diff --git a/src/transclude.c b/src/transclude.c index 09bc3ab..85090f6 100644 --- a/src/transclude.c +++ b/src/transclude.c @@ -136,13 +136,15 @@ void transclude_source(GString *source, char *basedir, char *stack, int output_f /* Look for override folder inside document */ if (has_metadata(source->str, 0x000000)) { char *meta = extract_metadata_value(source->str, 0x000000, "transcludebase"); - if (meta != NULL) + if (meta != NULL) { + free(path); path = path_from_dir_base(base, meta); + free(meta); + } } if (path == NULL) { /* We have nowhere to look, so nothing to do */ - free(path); free(base); return; } diff --git a/submodules/documentation b/submodules/documentation index 6c29c33..1242835 160000 --- a/submodules/documentation +++ b/submodules/documentation @@ -1 +1 @@ -Subproject commit 6c29c33eb84cee7df657219b3e738248b31dd261 +Subproject commit 124283537509815bfc083f1b02c323bf8c4d782b diff --git a/templates/README.md.in b/templates/README.md.in index 8ffb132..544a1b1 100644 --- a/templates/README.md.in +++ b/templates/README.md.in @@ -13,18 +13,28 @@ [Markdown] is a simple markup language used to convert plain text into HTML. -[MultiMarkdown] is a derivative of Markdown that adds new syntax features, such as footnotes, tables, and metadata. Additionally, it offers mechanisms to convert plain text into LaTeX in addition to HTML. +[MultiMarkdown] is a derivative of Markdown that adds new syntax features, +such as footnotes, tables, and metadata. Additionally, it offers mechanisms to +convert plain text into LaTeX in addition to HTML. ## Background ## -MultiMarkdown started as a Perl script, which was modified from the original Markdown.pl. +MultiMarkdown started as a Perl script, which was modified from the original +Markdown.pl. -MultiMarkdown v3 (aka 'peg-multimarkdown') was based on John MacFarlane's [peg-markdown]. It used a parsing expression grammar (PEG), and was written in C in order to compile on almost any operating system. Thanks to work by Daniel Jalkut, MMD v3 was built so that it didn't have any external library requirements. +MultiMarkdown v3 (aka 'peg-multimarkdown') was based on John MacFarlane's +[peg-markdown]. It used a parsing expression grammar (PEG), and was written +in C in order to compile on almost any operating system. Thanks to work by +Daniel Jalkut, MMD v3 was built so that it didn't have any external library +requirements. -MultiMarkdown v4 was basically a complete rewrite of v3. It used the same basic PEG for parsing (Multi)Markdown text, but otherwise was almost completely rebuilt. +MultiMarkdown v4 was basically a complete rewrite of v3. It used the same +basic PEG for parsing (Multi)Markdown text, but otherwise was almost +completely rebuilt. -MultiMarkdown v5 is basically the same code as v4, but the project has been restructured: +MultiMarkdown v5 is basically the same code as v4, but the project has been +restructured: * It is built using my [c-template] project boilerplate -- I welcome suggestions and ideas for improvement about this. @@ -92,15 +102,19 @@ Binaries for OS X and Windows are available on the github site: ## Compile from Source ## -To compile MultiMarkdown, you will need to have [CMake] installed on your machine. +To compile MultiMarkdown, you will need to have [CMake] installed on your +machine. To download the source: -* Obtain the source from the github repository (Download a zipfile of the source won't allow you to configure the submodules -- it's much better to use git): +* Obtain the source from the github repository (Downloading a zipfile of the + source won't allow you to configure the submodules -- it's much better to + use git): git clone https://github.com/fletcher/MultiMarkdown-5.git -* Configure the submodules with two helper scripts (This can be done manually on Windows systems by looking at the source): +* Configure the submodules with two helper scripts (This can be done + manually on Windows systems by looking at the source): ./link_git_modules ./update_git_modules @@ -112,35 +126,87 @@ To download the source: make make test -Like all versions of MultiMarkdown since v3, there is one test that will fail (now helpfully called `markdown-should-fail`). The other tests should pass. The valgrind tests will not work on OS X, but should pass if valgrind is installed and used on Linux machines. +Like all versions of MultiMarkdown since v3, there is one test that will fail +(now helpfully called `markdown-should-fail`). The other tests should pass. +The valgrind tests will not work on OS X, but should pass if valgrind is +installed and used on Linux machines. -If you want to make an installer, after the above, use the `cpack` command inside the build directory. +If you want to make an installer, after the above, use the `cpack` command +inside the build directory. For more information, checkout the `IMPORTANT` file. ## Usage ## -The [MultiMarkdown User's Guide] has complete instructions on how to use MultiMarkdown. +The [MultiMarkdown User's Guide] has complete instructions on how to use +MultiMarkdown. -## Developer's Notes ## +# LyX Support # -The documentation, created by doxygen, has information for developers: +Charles R. Cowan () added support for conversion +to [LyX](http://www.lyx.org/). Support for this should be considered to be in +alpha/beta, and is not guaranteed. Issues related to LyX can be added to the +MultiMarkdown [issues] page on github, but will need to be answered by +Charles. I am happy to include this code in the main MMD repo, but since I +don't use LyX I can't support it myself. If this arrangement becomes a +problem, then LyX support can be removed and it can be kept as a separate +fork. - make documentation -You can then view `build/documentation/html/index.html` for some developer's notes. There's not a lot there yet. There is also a LaTeX version created if you want a PDF. Just use latexmk in the latex directory. +# More Information # +To get more information about MultiMarkdown, check out the +[website][MultiMarkdown] or [MultiMarkdown User's Guide]. -# LyX Support # -Charles R. Cowan () added support for conversion to [LyX](http://www.lyx.org/). Support for this should be considered to be in alpha/beta, and is not guaranteed. Issues related to LyX can be added to the MultiMarkdown [issues] page on github, but will need to be answered by Charles. I am happy to include this code in the main MMD repo, but since I don't use LyX I can't support it myself. If this arrangement becomes a problem, then LyX support can be removed and it can be kept as a separate fork. +# Developer Notes # +Be sure to read the relevant documentation: -# More Information # +* IMPORTANT +* README.md +* `make documentation` and look at `build/documentation/html/index.html` +* Relevant portions of the User's Guide + +If you wish to submit pull requests, then be sure to work off of the `develop` +branch and configure the pull requests appropriately. I am *trying* to use the +"git flow" workflow described here: + + + +I will not accept pull requests directly into the `master` branch. + +***NOTE***: Additionally, I am trying to use a consistent convention for +commit messages, so that I can quickly generate the framework for Release +Notes for new versions of MultiMarkdown. For example: + + TAG: Commit message with uppercase first letter and no period at the end + + TAG: Commit message one; TAG2: Commit message two + +The list of TAGs is in flux, but currently includes: + +* ADDED: New features or functionality +* CHANGED: Change to the way a feature works +* CODE: Change the code, but don't change the overall user experience +* FIX: Fix a bug +* IMPORTANT: Something major was fixed +* NOTE: These are mostly changes to the project itself (e.g. Makefile) and + have no impact on the user experience + +These TAGs are still in flux as I develop the system I am using, but this will +allow me to automatically generate most of the Release Notes for each new +version. I'll still need to go over them manually, but this gives me a head +start! (As an aside, any time you use one of the `make` commands, the file +`CHANGELOG-UNRELEASED` will be updated to show you new features in the +`development` branch that have not been pulled into `master` yet.) + +By using the TAGs, I can sort the list of messages and group things into +categories. By consistently using the semi-colon syntax, I can automatically +split commits with multiple notes. -To get more information about MultiMarkdown, check out the [website][MultiMarkdown] or [MultiMarkdown User's Guide]. ## License ## diff --git a/tools/Toolchain-MinGW-w64-32bit.cmake b/tools/Toolchain-MinGW-w64-32bit.cmake index 7e55aed..db5c427 100644 --- a/tools/Toolchain-MinGW-w64-32bit.cmake +++ b/tools/Toolchain-MinGW-w64-32bit.cmake @@ -1,6 +1,7 @@ # Settings for compiling for Windows 32-bit machines using MinGW-w64 set (IS_CROSSCOMPILING "YES") +set (IS_32_BIT "32-") set (CMAKE_SYSTEM_NAME Windows) diff --git a/tools/Toolchain-mingw32.cmake b/tools/Toolchain-mingw32.cmake index 2e4546f..3e807ba 100644 --- a/tools/Toolchain-mingw32.cmake +++ b/tools/Toolchain-mingw32.cmake @@ -1,6 +1,7 @@ # Settings for compiling for Windows 32-bit machines set (IS_CROSSCOMPILING "YES") +set (IS_32_BIT "32-") set (CMAKE_SYSTEM_NAME Windows)