├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── CMakeLists.txt ├── IMPORTANT ├── LICENSE.txt ├── Makefile ├── README.md ├── build └── .gitignore ├── link_git_modules ├── scripts ├── markdown ├── markdown.bat ├── mmd ├── mmd.bat ├── mmd2all ├── mmd2odf ├── mmd2odf.bat ├── mmd2opml ├── mmd2opml.bat ├── mmd2pdf ├── mmd2rtf ├── mmd2rtf.bat ├── mmd2tex └── mmd2tex.bat ├── src ├── GLibFacade.c ├── GLibFacade.h ├── beamer.c ├── beamer.h ├── critic.c ├── critic.h ├── glib.h ├── html.c ├── html.h ├── latex.c ├── latex.h ├── libMultiMarkdown.h ├── lyx.c ├── lyx.h ├── lyxbeamer.c ├── lyxbeamer.h ├── memoir.c ├── memoir.h ├── multimarkdown.c ├── odf.c ├── odf.h ├── opml.c ├── opml.h ├── parse_utilities.c ├── parser.h ├── parser.leg ├── rng.c ├── rtf.c ├── rtf.h ├── strtok.c ├── strtok.h ├── text.c ├── text.h ├── toc.c ├── toc.h ├── transclude.c ├── transclude.h ├── writer.c └── writer.h ├── templates ├── README.md.in ├── doxygen.conf.in ├── template.c.in ├── template.h.in └── version.h.in ├── test ├── CuTest.c ├── CuTest.h └── make-tests.sh ├── tools ├── Toolchain-MinGW-w64-32bit.cmake ├── Toolchain-MinGW-w64-64bit.cmake ├── Toolchain-mingw32.cmake ├── enumsToPerl.pl ├── make_drag_drop_mac.sh └── version.h └── update_git_modules /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | CHANGELOG-UNRELEASED 3 | 4 | # Developer Tools 5 | *.pbxuser 6 | !default.pbxuser 7 | *.mode1v3 8 | !default.mode1v3 9 | *.mode2v3 10 | !default.mode2v3 11 | *.perspectivev3 12 | !default.perspectivev3 13 | *.xcworkspace 14 | !default.xcworkspace 15 | xcuserdata 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | .idea/ 20 | 21 | 22 | # Binaries 23 | *.dmg 24 | *.exe 25 | *.zip 26 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "submodules/greg"] 2 | path = submodules/greg 3 | url = https://github.com/nddrylliog/greg.git 4 | [submodule "submodules/MarkdownTest"] 5 | path = submodules/MarkdownTest 6 | url = https://github.com/fletcher/MMD-Test-Suite.git 7 | [submodule "submodules/cheat-sheet"] 8 | path = submodules/cheat-sheet 9 | url = https://github.com/fletcher/human-markdown-reference.git 10 | [submodule "submodules/documentation"] 11 | path = submodules/documentation 12 | url = https://github.com/fletcher/MultiMarkdown-5.git 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # MultiMarkdown Change Log # 2 | 3 | ## [5.4.0] - 2016-08-05 4 | 5 | * ADDED: Add email address to cpack settings (addresses #31) 6 | * ADDED: Move static library options to separate make target 7 | * CHANGED: Increase list of syntax structures that are evaluated for abbreviations (Thanks, David!) 8 | * CHANGED: Update README 9 | * FIXED: Avoids problem mentioned on github (#30), but doesn't actually solve it. Any help appreciated! 10 | * FIXED: Fix crash with empty table label 11 | 12 | 13 | ## [5.3.0] - 2016-06-08 ## 14 | 15 | * CHANGED: Update test suite 16 | * FIXED: Don't allow math spans inside strong/emph 17 | * FIXED: Fix additional edge case in emph/strong parser 18 | * FIXED: Fix slow parsing of complex strong/emph (Fixes #18) 19 | * FIXED: Improve accuracy and performance of strong/emph 20 | * FIXED: Improve performance 21 | 22 | 23 | ## [5.2.0] - 2016-03-16 ## 24 | 25 | * ADDED: Add additional test cases 26 | * ADDED: Add support for 'HTML FOOTER' metadata -- appended at very end of HTML documents (after footnotes) 27 | * CHANGED: Allow nested strong/emph spans (though effect of output is not defined for all formats) 28 | * CHANGED: Update Math test suite 29 | * CHANGED: Update documentation for 5.1.0 30 | * CHANGED: Update documentation for HTML Footer metadata 31 | * CHANGED: Update test suite 32 | * CHANGED: recursive support for CriticMarkup syntax (e.g. an addition can be included inside a substitution). 33 | * FIXED: Fix bug in handling of '$' delimited math 34 | * FIXED: Improve accuracy of strong/emph parsing; 35 | * FIXED: Remove debugging statement in recent CriticMarkup changes 36 | 37 | 38 | ## [5.1.0] - 2016-02-22 ## 39 | 40 | * ADDED: Add script to build drag and drop apps on OS X 41 | * ADDED: Beginning code for public header file support; ADDED: Beginning configuration for OS X Bundle/Framework targets 42 | * ADDED: Include support for Xcode libraries to be iOS compatible 43 | * CHANGED: Update copyright info for 2016 44 | * CHANGED: Update test suite 45 | * FIXED: Allow ATX Headers inside lists 46 | * FIXED: Allow 'naturally' aligned table cells like MMD 2 allowed 47 | * FIXED: Allow metadata variables inside links (e.g. [[%foo]][bar]) 48 | * FIXED: Allow newline inside strong/emph (Fixes #10) 49 | * FIXED: Change handling of version.h file for deprecated make to keep it separate from cmake alternative 50 | * FIXED: Don't delete src/version.h 51 | * FIXED: Fix problem with strong/emph matching incorrectly 52 | * FIXED: Fix regression in list/heading fix that was overeager 53 | * FIXED: Include 'fake' version.h for make deprecate 54 | * FIXED: Include additional standard metadata keys in the list to *not* be included in HTML headers 55 | * FIXED: Remove unneeded install directive; FIXED: Fix public header install prefix (I think) 56 | * FIXED: Update MMD test suite for recent table alignment change 57 | * FIXED: fix 'make deprecate' so that it truly doesn't require cmake 58 | * FIXED: Improve tight vs loose list detection with unusual setext headers 59 | * NOTE: Fixed git clone instructions in documentation 60 | * NOTE: Update test suite 61 | 62 | 63 | ## [5.0.1] - 2015-12-01 ## 64 | 65 | * IMPORTANT: Fix major error in last Makefile! (Only in build branch for a few minutes) 66 | * ADDED: Improve empty list item detection in ODF output 67 | * CHANGED: Remove unused node creation utilities 68 | * CHANGED: Update documentation 69 | * CHANGED: Use 'const' char * in g_string_new 70 | * CODE: Improve doxygen support in libMultiMarkdown.h 71 | * CODE: Refactor markdown_to_string() to separate parsing input text and writing output text via the intermediate node tree. 72 | * CODE: Use node creation shortcuts for consistency and future flexibility 73 | * FIX: Add 32 bit flag to older MinGW toolchain 74 | * FIX: Add label for 32 bit builds 75 | * FIX: Don't run valgrind tests unless in 'make debug' mode 76 | * FIX: Don't static link when using 'make debug' for valgrind testing 77 | * FIX: Fix memory leaks in transclude_source() 78 | * NOTE: Add developer notes to the README information 79 | * NOTE: Additional doxygen support 80 | * NOTE: Autogenerate changelog since last commit to master 81 | * NOTE: Begin tracking release notes in 'CHANGELOG.md' 82 | * NOTE: Change empty listitem detection logic for ODF output 83 | * NOTE: Change whitespace for easier diffing 84 | * NOTE: Remove redundant developer note in README 85 | * NOTE: Use tab instead of leading spaces in CHANGELOG-UNRELEASED 86 | 87 | 88 | ## [5.0.0] - 2015-11-15 ## 89 | 90 | * The source repository for MultiMarkdown has been completely rebuilt: 91 | * Use my [c-template] to provide the basic structure 92 | * Requires the [CMake] build system 93 | * Has the beginnings of documentation within the code to support 94 | [Doxygen]-generated developer documentation 95 | * Other changes since 4.7.1: 96 | * Improvements from Matthias Lohr for the shell scripts 97 | * Additional documentation in code 98 | * Fix issue with whitespace after footnotes in ODF export 99 | * Improve accuracy of recognizing single line code blocks 100 | * Fix memory leaks 101 | * Fix `mmd2pdf` for certain installs of TeX on El Capitan 102 | * Improve POSIX compliance on shell scripts 103 | * Improve accuracy of strong/emph matching 104 | * Make reference label matching case insensitive 105 | * Fix error if no caption was included on table 106 | * Fix edge case slowdown when lots of HTML included 107 | * Fix error when quotes included in explicit link 108 | * Allow tables inside LaTeX footnotes 109 | * Improved window path separator support 110 | * And other improvements/fixes 111 | 112 | 113 | 114 | [5.0.0]: https://github.com/fletcher/MultiMarkdown-5/releases/tag/5.0 115 | [5.0.1]: https://github.com/fletcher/MultiMarkdown-5/releases/tag/5.0.1 116 | [5.1.0]: https://github.com/fletcher/MultiMarkdown-5/releases/tag/5.1.0 117 | [5.2.0]: https://github.com/fletcher/MultiMarkdown-5/releases/tag/5.2.0 118 | [5.3.0]: https://github.com/fletcher/MultiMarkdown-5/releases/tag/5.3.0 119 | [5.4.0]: https://github.com/fletcher/MultiMarkdown-5/releases/tag/5.4.0 120 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.6) 2 | 3 | 4 | # ================== 5 | # Define Our Project 6 | # ================== 7 | 8 | set (My_Project_Title "MultiMarkdown") 9 | set (My_Project_Description "MultiMarkdown - lightweight markup processor") 10 | set (My_Project_Author "Fletcher T. Penney") 11 | set (My_Project_Revised_Date "2016-08-05") 12 | set (My_Project_Version_Major 5) 13 | set (My_Project_Version_Minor 4) 14 | set (My_Project_Version_Patch 0) 15 | 16 | set (My_Project_Version "${My_Project_Version_Major}.${My_Project_Version_Minor}.${My_Project_Version_Patch}") 17 | 18 | set (My_Project_Copyright_Date "2013-2016") 19 | set (My_Project_Copyright "Copyright © ${My_Project_Copyright_Date} ${My_Project_Author}.") 20 | 21 | project (${My_Project_Title}) 22 | 23 | 24 | # Search for included files here 25 | include_directories(${PROJECT_BINARY_DIR}) 26 | include_directories( ${PROJECT_SOURCE_DIR}/src ) 27 | include_directories( ${PROJECT_SOURCE_DIR}/test ) 28 | 29 | string(TOUPPER ${My_Project_Title} My_Project_Title_Caps ) 30 | string(REGEX REPLACE " " "_" My_Project_Title_Caps ${My_Project_Title_Caps} ) 31 | 32 | 33 | # ================= 34 | # Macro Definitions 35 | # ================= 36 | 37 | MACRO(ADD_PUBLIC_HEADER target filename) 38 | # Add filename to public_header_files list, flag it as 39 | # public header for libraries and OS X Frameworks 40 | 41 | # This will work for creating one library/framework with public headers 42 | # per project. If you need more than one, you will need to customize 43 | # the workflow as appropriate, since there is only one 44 | # public_header_files list. 45 | 46 | SET_TARGET_PROPERTIES(${target} PROPERTIES PUBLIC_HEADER ${filename}) 47 | 48 | LIST(APPEND public_header_files ${filename}) 49 | 50 | SET_SOURCE_FILES_PROPERTIES( 51 | ${filename} 52 | PROPERTIES 53 | MACOSX_PACKAGE_LOCATION 54 | PUBLIC_HEADER 55 | ) 56 | 57 | # Set Xcode project to configure public header location to allow 58 | # use when this project is used in another workspace. 59 | # NOTE: You must manually add a "Headers" build phase and add 60 | # the desired public headers to that list for Xcode to use them. 61 | # 62 | # TODO: If anyone knows how to automate that in Cmake, I would be very 63 | # greatful!! 64 | 65 | SET_TARGET_PROPERTIES(${target} PROPERTIES 66 | XCODE_ATTRIBUTE_PUBLIC_HEADERS_FOLDER_PATH 67 | "include/$(TARGET_NAME)" 68 | ) 69 | 70 | SET_TARGET_PROPERTIES(${target} PROPERTIES 71 | XCODE_ATTRIBUTE_PRIVATE_HEADERS_FOLDER_PATH 72 | "$(PUBLIC_HEADERS_FOLDER_PATH)/Private" 73 | ) 74 | 75 | # Set Xcode target to include settings for OS X and iOS 76 | 77 | SET_TARGET_PROPERTIES(${target} PROPERTIES 78 | XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS 79 | "macos iphonesimulator iphoneos" 80 | ) 81 | 82 | SET_TARGET_PROPERTIES(${target} PROPERTIES 83 | XCODE_ATTRIBUTE_VALID_ARCHITECTURES 84 | "x86_64 i386 armv6 armv7 armv7s arm64" 85 | ) 86 | 87 | ENDMACRO(ADD_PUBLIC_HEADER) 88 | 89 | 90 | # The target should be an OS X Bundle with a PList 91 | 92 | MACRO(MAKE_TARGET_BUNDLE targetname) 93 | 94 | set_target_properties( 95 | ${targetname} 96 | PROPERTIES 97 | MACOSX_BUNDLE_INFO_PLIST 98 | ${PROJECT_SOURCE_DIR}/templates/plist.in 99 | ) 100 | 101 | ENDMACRO(MAKE_TARGET_BUNDLE) 102 | 103 | 104 | MACRO(ADD_LINKED_FRAMEWORK frame) 105 | 106 | SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGES} -framework ${frame}") 107 | 108 | ENDMACRO(ADD_LINKED_FRAMEWORK) 109 | 110 | 111 | # ====================== 112 | # Process Template Files 113 | # ====================== 114 | 115 | file(READ ${PROJECT_SOURCE_DIR}/LICENSE.txt My_Project_License) 116 | 117 | string(REGEX REPLACE "\n" "\n\t" My_Project_License_Indent ${My_Project_License}) 118 | 119 | configure_file ( 120 | "${PROJECT_SOURCE_DIR}/templates/template.c.in" 121 | "${PROJECT_BINARY_DIR}/template.c" 122 | ) 123 | 124 | configure_file ( 125 | "${PROJECT_SOURCE_DIR}/templates/template.h.in" 126 | "${PROJECT_BINARY_DIR}/template.h" 127 | ) 128 | 129 | # Update the project README, to pull in new version #, etc. 130 | configure_file ( 131 | "${PROJECT_SOURCE_DIR}/templates/README.md.in" 132 | "${CMAKE_CURRENT_LIST_DIR}/README.md" 133 | ) 134 | 135 | configure_file ( 136 | "${PROJECT_SOURCE_DIR}/templates/version.h.in" 137 | "${PROJECT_BINARY_DIR}/version.h" 138 | ) 139 | 140 | if (DEFINED VERSION_ONLY) 141 | return () 142 | endif () 143 | 144 | 145 | # ============ 146 | # Source Files 147 | # ============ 148 | 149 | # Need to build parser.c via greg 150 | add_custom_command ( 151 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/parser.c 152 | COMMAND ${PROJECT_SOURCE_DIR}/submodules/greg/greg -o ${CMAKE_CURRENT_BINARY_DIR}/parser.c ${PROJECT_SOURCE_DIR}/src/parser.leg 153 | ) 154 | 155 | # src_files are the primary files, and will be included in doxygen documentation 156 | set(src_files 157 | ) 158 | 159 | # Primary header files, also for doxygen documentation 160 | set(header_files 161 | src/libMultiMarkdown.h 162 | ) 163 | 164 | # Public headers, will be installed in 'include' 165 | # Do not manually add files here, use the ADD_PUBLIC_HEADER() macro 166 | set(public_header_files 167 | ) 168 | 169 | # Utility source files will not be included in doxygen 170 | set(src_utility_files 171 | src/GLibFacade.c 172 | src/beamer.c 173 | src/critic.c 174 | src/html.c 175 | src/latex.c 176 | src/lyx.c 177 | src/lyxbeamer.c 178 | src/memoir.c 179 | src/odf.c 180 | src/opml.c 181 | src/parse_utilities.c 182 | src/rng.c 183 | src/rtf.c 184 | src/text.c 185 | src/toc.c 186 | src/transclude.c 187 | src/writer.c 188 | 189 | ${CMAKE_CURRENT_BINARY_DIR}/parser.c 190 | ) 191 | 192 | set(header_utility_files 193 | src/GLibFacade.h 194 | ${PROJECT_BINARY_DIR}/version.h 195 | src/beamer.h 196 | src/critic.h 197 | src/glib.h 198 | src/html.h 199 | src/latex.h 200 | src/lyx.h 201 | src/lyxbeamer.h 202 | src/memoir.h 203 | src/odf.h 204 | src/opml.h 205 | src/parser.h 206 | src/rtf.h 207 | src/text.h 208 | src/toc.h 209 | src/transclude.h 210 | src/writer.h 211 | ) 212 | 213 | set(scripts 214 | scripts/mmd 215 | scripts/mmd2all 216 | scripts/mmd2opml 217 | scripts/mmd2rtf 218 | scripts/mmd2odf 219 | scripts/mmd2pdf 220 | scripts/mmd2tex 221 | scripts/markdown 222 | ) 223 | 224 | # Generate doxygen configuration file 225 | string(REPLACE ";" " " doxygen_src_files "${src_files} src/multimarkdown.c" ) 226 | string(REPLACE ";" " " doxygen_header_files "${header_files}" ) 227 | 228 | configure_file ( 229 | "${PROJECT_SOURCE_DIR}/templates/doxygen.conf.in" 230 | "${PROJECT_BINARY_DIR}/doxygen.conf" 231 | ) 232 | 233 | 234 | # =========================================== 235 | # Build Test Suite with CuTest (unit testing) 236 | # =========================================== 237 | 238 | set(test_files 239 | test/CuTest.c 240 | test/CuTest.h 241 | ${PROJECT_BINARY_DIR}/AllTests.c 242 | ) 243 | 244 | if (DEFINED TEST) 245 | add_definitions(-DTEST) 246 | 247 | add_executable(run_tests 248 | ${test_files} 249 | ${src_files} 250 | ${header_files} 251 | ${src_utility_files} 252 | ${header_utility_files} 253 | ) 254 | 255 | # Process source files to look for tests to run 256 | add_custom_command ( 257 | OUTPUT ${PROJECT_BINARY_DIR}/AllTests.c 258 | COMMAND sh ${PROJECT_SOURCE_DIR}/test/make-tests.sh ${PROJECT_SOURCE_DIR}/src/*.c > ${PROJECT_BINARY_DIR}/AllTests.c 259 | ) 260 | 261 | enable_testing() 262 | 263 | add_test( test ${PROJECT_BINARY_DIR}/run_tests) 264 | 265 | # valgrind memory testing 266 | find_program (MEMORYCHECK_COMMAND valgrind) 267 | SET (MEMORYCHECK_COMMAND_OPTIONS --leak-check=full --error-exitcode=1) 268 | 269 | add_test( memory_test ${MEMORYCHECK_COMMAND} ${MEMORYCHECK_COMMAND_OPTIONS} ${PROJECT_BINARY_DIR}/run_tests) 270 | 271 | endif() 272 | 273 | 274 | # ======================= 275 | # Configure for Target OS 276 | # ======================= 277 | 278 | # OS X Builds 279 | if (APPLE) 280 | 281 | # Configure backwards-compatible support (if your project allows it) 282 | SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.4" CACHE STRING "Deployment target for OSX" FORCE) 283 | 284 | # Compile for x86_64 and i386. ppc no longer supported 285 | if(CMAKE_BUILD_TYPE MATCHES "Release") 286 | SET (CMAKE_OSX_ARCHITECTURES x86_64;i386) 287 | # set( CMAKE_OSX_ARCHITECTURES "$(ARCHS_STANDARD_32_64_BIT)") 288 | endif(CMAKE_BUILD_TYPE MATCHES "Release") 289 | 290 | # Use PackageMaker for installers? 291 | if (DEFINED ZIP) 292 | set (CPACK_GENERATOR ZIP) 293 | else (DEFINED ZIP) 294 | set (CPACK_GENERATOR PackageMaker) 295 | endif (DEFINED ZIP) 296 | 297 | # Xcode settings 298 | set (XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH "NO") 299 | endif (APPLE) 300 | 301 | # Windows Builds 302 | if (WIN32) 303 | 304 | # We need strtok on Windows 305 | list (APPEND src_files src/strtok.c) 306 | list (APPEND header_files src/strtok.h) 307 | 308 | # Use bat files instead of shell scripts 309 | set(scripts 310 | scripts/mmd.bat 311 | scripts/mmd2odf.bat 312 | scripts/mmd2opml.bat 313 | scripts/mmd2rtf.bat 314 | scripts/mmd2tex.bat 315 | scripts/markdown.bat 316 | ) 317 | 318 | # Use NSIS to generate installers? 319 | if (DEFINED ZIP) 320 | set (CPACK_GENERATOR ZIP) 321 | else (DEFINED ZIP) 322 | set (CPACK_GENERATOR NSIS) 323 | endif (DEFINED ZIP) 324 | 325 | # Linux Builds (not cross-compiling for Windows) 326 | elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") 327 | 328 | # Create zip archive 329 | set (CPACK_GENERATOR ZIP) 330 | 331 | # Some libraries need to be linked on some Linux builds 332 | if (DEFINED TEST) 333 | # target_link_libraries(run_tests m) 334 | endif (DEFINED TEST) 335 | 336 | endif (WIN32) 337 | 338 | 339 | # ======================================= 340 | # Option to link against static libraries 341 | # ======================================= 342 | 343 | if (DEFINED STATICBUILD) 344 | # Statically link libraries -- might make the binary slightly more 345 | # compatible across Linux distributions, for example 346 | # 347 | # It will likely cause large numbers of errors on valgrind, 348 | # so use "make debug" for valgrind testing 349 | # 350 | # You may wish to disable this. 351 | # 352 | 353 | set (CMAKE_FIND_LIBRARY_SUFFIXES ".a") 354 | set (BUILD_SHARED_LIBRARIES OFF) 355 | set (CMAKE_EXE_LINKER_FLAGS "-static") 356 | endif (DEFINED STATICBUILD) 357 | 358 | 359 | # ============== 360 | # Define targets 361 | # ============== 362 | 363 | # Create a library? 364 | add_library(libMultiMarkdown STATIC 365 | ${src_files} 366 | ${src_utility_files} 367 | ${header_files} 368 | ${header_utility_files} 369 | ) 370 | 371 | # remove the extra "lib" from "liblibFOO" 372 | SET_TARGET_PROPERTIES(libMultiMarkdown PROPERTIES PREFIX "") 373 | 374 | ADD_PUBLIC_HEADER(libMultiMarkdown src/libMultiMarkdown.h) 375 | 376 | # Create command-line app 377 | add_executable(multimarkdown 378 | src/multimarkdown.c 379 | src/GLibFacade.c 380 | src/GLibFacade.h 381 | ${header_files} 382 | ) 383 | 384 | # Xcode settings for fat binaries 385 | set_target_properties(libMultiMarkdown PROPERTIES XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH "NO") 386 | set_target_properties(multimarkdown PROPERTIES XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH "NO") 387 | 388 | # Link the library to the app 389 | target_link_libraries(multimarkdown libMultiMarkdown) 390 | 391 | 392 | # ========================== 393 | # Build Installer with CPack 394 | # ========================== 395 | 396 | # Create HTML version of README 397 | 398 | if (DEFINED IS_CROSSCOMPILING) 399 | # Need to use installed MMD when cross compiling 400 | add_custom_command( 401 | TARGET multimarkdown 402 | POST_BUILD 403 | COMMAND multimarkdown -f ${CMAKE_CURRENT_LIST_DIR}/README.md > ${CMAKE_CURRENT_BINARY_DIR}/README.html 404 | ) 405 | else () 406 | # Use mmd binary we just built 407 | add_custom_command( 408 | TARGET multimarkdown 409 | POST_BUILD 410 | COMMAND $ -f ${CMAKE_CURRENT_LIST_DIR}/README.md > ${CMAKE_CURRENT_BINARY_DIR}/README.html 411 | ) 412 | endif() 413 | 414 | set(CPACK_SET_DESTDIR true) 415 | set(CPACK_INSTALL_PREFIX /usr/local) 416 | 417 | install (TARGETS multimarkdown 418 | DESTINATION bin 419 | COMPONENT application 420 | ) 421 | 422 | set (CPACK_COMPONENT_APPLICATION_DISPLAY_NAME "MultiMarkdown") 423 | set (CPACK_COMPONENT_APPLICATION_DESCRIPTION 424 | "Install the actual `multimarkdown` program.") 425 | 426 | 427 | install (FILES ${scripts} 428 | DESTINATION bin 429 | COMPONENT scripts 430 | PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE 431 | ) 432 | 433 | # install (FILES ${PROJECT_SOURCE_DIR}/LICENSE.txt ${CMAKE_CURRENT_BINARY_DIR}/README.html 434 | # DESTINATION notes 435 | # ) 436 | 437 | # Use something like this to install public header files (after adding them 438 | # with the ADD_PUBLIC_HEADER() macro) 439 | 440 | # install (FILES ${public_header_files} DESTINATION local/include/libMultiMarkdown) 441 | 442 | set (CPACK_COMPONENT_SCRIPTS_DISPLAY_NAME "Convenience scripts") 443 | set (CPACK_COMPONENT_SCRIPTS_DESCRIPTION 444 | "Install convenience scripts for common MultiMarkdown shortcuts, e.g. `mmd`, `mmd2tex`, etc.") 445 | 446 | include (InstallRequiredSystemLibraries) 447 | 448 | set (CPACK_COMPONENTS_ALL application scripts) 449 | 450 | 451 | set (CPACK_COMPONENT_APPLICATION_GROUP "MultiMarkdown") 452 | set (CPACK_COMPONENT_SCRIPTS_GROUP "MultiMarkdown") 453 | 454 | set (CPACK_COMPONENT_GROUP_MULTIMARKDOWN_DESCRIPTION 455 | "The basic MultiMarkdown utility and convenience scripts.") 456 | 457 | set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "${My_Project_Description}") 458 | set (CPACK_PACKAGE_VENDOR "${My_Project_Author}") 459 | set (CPACK_PACKAGE_VERSION "${My_Project_Version_Major}.${My_Project_Version_Minor}.${My_Project_Version_Patch}") 460 | set (CPACK_PACKAGE_VERSION_MAJOR "${My_Project_Version_Major}") 461 | set (CPACK_PACKAGE_VERSION_MINOR "${My_Project_Version_Minor}") 462 | set (CPACK_PACKAGE_VERSION_PATCH "My_Project_Version_Patch") 463 | 464 | set (CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_BINARY_DIR}/README.html") 465 | set (CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_BINARY_DIR}/README.html") 466 | set (CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE.txt") 467 | 468 | set (ZIP_NAME "") 469 | 470 | if (DEFINED ZIP) 471 | set (ZIP_NAME "Portable-") 472 | endif (DEFINED ZIP) 473 | 474 | if (APPLE) 475 | set (CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-Mac-${IS_32_BIT}${ZIP_NAME}${CPACK_PACKAGE_VERSION}") 476 | else (APPLE) 477 | set (CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${CMAKE_SYSTEM_NAME}-${IS_32_BIT}${ZIP_NAME}${CPACK_PACKAGE_VERSION}") 478 | endif (APPLE) 479 | 480 | install (TARGETS multimarkdown 481 | DESTINATION bin 482 | COMPONENT application 483 | ) 484 | 485 | # Use some default install locations (if appropriate) 486 | if (WIN32) 487 | set (CPACK_INSTALL_PREFIX "") 488 | set (CPACK_NSIS_MODIFY_PATH ON) 489 | else (WIN32) 490 | set (CPACK_INSTALL_PREFIX /usr/local) 491 | endif (WIN32) 492 | 493 | set (CPACK_PACKAGE_INSTALL_DIRECTORY ${PROJECT}) 494 | 495 | set (CPACK_PACKAGE_CONTACT "fletcher@fletcherpenney.net") 496 | 497 | include (CPack) 498 | 499 | 500 | # ====================== 501 | # Integration Test Suite 502 | # ====================== 503 | 504 | enable_testing() 505 | 506 | function(ADD_MMD_TEST NAME FLAGS FOLDER EXTENSION) 507 | add_test ( ${NAME} 508 | ${PROJECT_SOURCE_DIR}/submodules/MarkdownTest/MarkdownTest.pl 509 | --Script=${CMAKE_CURRENT_BINARY_DIR}/multimarkdown 510 | --testdir=${PROJECT_SOURCE_DIR}/submodules/MarkdownTest/${FOLDER} 511 | "--Flags=${FLAGS}" 512 | --ext=${EXTENSION} 513 | ) 514 | 515 | endfunction(ADD_MMD_TEST) 516 | 517 | 518 | # Original Markdown tests 519 | 520 | # This first test will fail with a single error on Lists, the same as peg-markdown 521 | # https://github.com/jgm/peg-markdown 522 | 523 | add_test( markdown-should-fail ${PROJECT_SOURCE_DIR}/submodules/MarkdownTest/MarkdownTest.pl --Script=${CMAKE_CURRENT_BINARY_DIR}/multimarkdown --Tidy --Flags="--compatibility" --testdir=${PROJECT_SOURCE_DIR}/submodules/MarkdownTest/Test) 524 | 525 | add_test( markdown ${PROJECT_SOURCE_DIR}/submodules/MarkdownTest/MarkdownTest.pl --Script=${CMAKE_CURRENT_BINARY_DIR}/multimarkdown --Tidy --Flags="--compatibility" --testdir=${PROJECT_SOURCE_DIR}/submodules/MarkdownTest/Tests) 526 | 527 | 528 | # Basic MultiMarkdown integration testing 529 | 530 | ADD_MMD_TEST(mmd "-t html" MultiMarkdownTests html) 531 | ADD_MMD_TEST(latex "-t latex" MultiMarkdownTests tex) 532 | ADD_MMD_TEST(beamer "-t beamer" BeamerTests tex) 533 | ADD_MMD_TEST(memoir "-t memoir" MemoirTests tex) 534 | ADD_MMD_TEST(lyx "-t lyx" MultiMarkdownTests lyx) 535 | ADD_MMD_TEST(lyx-beamer "-t lyx" BeamerTests lyx) 536 | ADD_MMD_TEST(opml "-t opml" MultiMarkdownTests opml) 537 | ADD_MMD_TEST(odf "-t odf" MultiMarkdownTests fodt) 538 | 539 | 540 | # CriticMarkup tests 541 | 542 | ADD_MMD_TEST(critic-accept "-a" CriticMarkup htmla) 543 | ADD_MMD_TEST(critic-reject "-r" CriticMarkup htmlr) 544 | ADD_MMD_TEST(critic-highlight "-r -a" CriticMarkup htmlh) 545 | 546 | 547 | # XSLT tests -- obsolete - they won't pass 548 | 549 | # add_test ( xslt-1 ${PROJECT_SOURCE_DIR}/submodules/MarkdownTest/MarkdownTest.pl --Script=/bin/cat --testdir=${PROJECT_SOURCE_DIR}/submodules/MarkdownTest/MultiMarkdownTests --TrailFlags=|\ ${PROJECT_SOURCE_DIR}/Support/bin/mmd2tex-xslt --ext=tex) 550 | # add_test ( xslt-2 ${PROJECT_SOURCE_DIR}/submodules/MarkdownTest/MarkdownTest.pl --Script=/bin/cat --testdir=${PROJECT_SOURCE_DIR}/submodules/MarkdownTest/BeamerTests --TrailFlags=|\ ${PROJECT_SOURCE_DIR}/Support/bin/mmd2tex-xslt --ext=tex) 551 | # add_test ( xslt-3 ${PROJECT_SOURCE_DIR}/submodules/MarkdownTest/MarkdownTest.pl --Script=/bin/cat --testdir=${PROJECT_SOURCE_DIR}/submodules/MarkdownTest/MemoirTests --TrailFlags=|\ ${PROJECT_SOURCE_DIR}/Support/bin/mmd2tex-xslt --ext=tex) 552 | 553 | 554 | # valgrind memory testing 555 | # Only in 'make debug' mode 556 | 557 | if (DEFINED TEST) 558 | find_program ( MEMORYCHECK_COMMAND valgrind) 559 | set ( MEMORYCHECK_COMMAND_OPTIONS --leak-check=full --error-exitcode=1) 560 | 561 | file (GLOB TEST_FILES ${PROJECT_SOURCE_DIR}/submodules/MarkdownTest/Tests/*.text ${PROJECT_SOURCE_DIR}/submodules/MarkdownTest/MultiMarkdownTests/*.text) 562 | 563 | foreach (FORMAT html latex odf beamer memoir opml lyx) 564 | add_test ( NAME valgrind-${FORMAT} COMMAND "${MEMORYCHECK_COMMAND}" ${MEMORYCHECK_COMMAND_OPTIONS} ${PROJECT_BINARY_DIR}/multimarkdown -t ${FORMAT} ${TEST_FILES} ) 565 | endforeach() 566 | endif (DEFINED TEST) 567 | -------------------------------------------------------------------------------- /IMPORTANT: -------------------------------------------------------------------------------- 1 | This project is designed for use with cmake. 2 | 3 | The Makefile controls the overall build: 4 | 5 | make 6 | make release 7 | 8 | Build a version optimized for performance. 9 | 10 | make debug 11 | 12 | Faster compile, but not as high performance. Enables test suite support. 13 | 14 | make xcode 15 | 16 | Create an Xcode project for use on OS X. 17 | 18 | make windows 19 | 20 | Used for cross-compiling for Windows using MinGW. 21 | 22 | make documentation 23 | 24 | Setup a `doxygen` configuration file based on project information, and then 25 | generate HTML and LaTeX documentation. 26 | 27 | make clean 28 | 29 | Clean up the `build` directory. 30 | 31 | These commands control the `build` directory. Everything in this directory is 32 | auto-generated. You should not manually change these files or put anything 33 | else in there. 34 | 35 | 36 | The setup is designed to support unit testing with CuTest. Functions along 37 | the lines of `void Test*` will be located automatically and used to create the 38 | test suite, which is run by: 39 | 40 | ./run_tests 41 | 42 | Or 43 | 44 | make test 45 | 46 | (The `run_tests` approach will probably give more useful feedback if a test fails.) 47 | 48 | 49 | Integration testing must be handled separately. 50 | 51 | You can configure the `CMakeLists.txt` to support creating installers. 52 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The `c-template` project is released under the MIT License. 2 | 3 | 4 | MMD 5 is released under the MIT License. 5 | 6 | 7 | CuTest is released under the zlib/libpng license. See CuTest.c for the text 8 | of the license. 9 | 10 | 11 | ## The MIT License ## 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy 14 | of this software and associated documentation files (the "Software"), to deal 15 | in the Software without restriction, including without limitation the rights 16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | copies of the Software, and to permit persons to whom the Software is 18 | furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in 21 | all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 | THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BUILD_DIR = build 2 | 3 | GREG = submodules/greg/greg 4 | 5 | # The release target will perform additional optimization 6 | .PHONY : release 7 | release: $(BUILD_DIR) $(GREG) 8 | cd $(BUILD_DIR); touch README.html; \ 9 | cmake -DCMAKE_BUILD_TYPE=Release .. 10 | 11 | # Build release linking to static libraries 12 | .PHONY : static 13 | static: $(BUILD_DIR) $(GREG) 14 | cd $(BUILD_DIR); touch README.html; \ 15 | cmake -DCMAKE_BUILD_TYPE=Release -DSTATICBUILD=1 .. 16 | 17 | # Build zip file package 18 | .PHONY : zip 19 | zip: $(BUILD_DIR) $(GREG) 20 | cd $(BUILD_DIR); touch README.html; \ 21 | cmake -DCMAKE_BUILD_TYPE=Release -DZIP=1 .. 22 | 23 | # debug target enables CuTest unit testing 24 | .PHONY : debug 25 | debug: $(BUILD_DIR) $(GREG) 26 | cd $(BUILD_DIR); touch README.html; \ 27 | ../tools/enumsToPerl.pl ../src/libMultiMarkdown.h enumMap.txt; \ 28 | cmake -DTEST=1 .. 29 | 30 | # analyze target enables use of clang's scan-build (if installed) 31 | # will then need to run 'scan-build make' to compile and analyze 32 | # 'scan-build -V make' will show the results graphically in your 33 | # web browser 34 | .PHONY : analyze 35 | analyze: $(BUILD_DIR) $(GREG) 36 | cd $(BUILD_DIR); touch README.html; \ 37 | scan-build cmake -DTEST=1 .. 38 | 39 | # Create xcode project 40 | # You can then build within XCode, or using the commands: 41 | # xcodebuild -configuration Debug 42 | # xcodebuild -configuration Release 43 | .PHONY : xcode 44 | xcode: $(BUILD_DIR) $(GREG) 45 | cd $(BUILD_DIR); touch README.html; \ 46 | cmake -G Xcode .. 47 | 48 | # Cross-compile for Windows using MinGW on *nix 49 | .PHONY : windows 50 | windows: $(BUILD_DIR) $(GREG) 51 | cd $(BUILD_DIR); touch README.html; \ 52 | cmake -DCMAKE_TOOLCHAIN_FILE=../tools/Toolchain-MinGW-w64-64bit.cmake -DCMAKE_BUILD_TYPE=Release .. 53 | 54 | # Build Windows zip file using MinGW on *nix 55 | .PHONY : windows-zip 56 | windows-zip: $(BUILD_DIR) $(GREG) 57 | cd $(BUILD_DIR); touch README.html; \ 58 | cmake -DCMAKE_TOOLCHAIN_FILE=../tools/Toolchain-MinGW-w64-64bit.cmake -DCMAKE_BUILD_TYPE=Release -DZIP=1 .. 59 | 60 | # Cross-compile for Windows using MinGW on *nix (32-bit) 61 | .PHONY : windows-32 62 | windows-32: $(BUILD_DIR) $(GREG) 63 | cd $(BUILD_DIR); touch README.html; \ 64 | cmake -DCMAKE_TOOLCHAIN_FILE=../tools/Toolchain-MinGW-w64-32bit.cmake -DCMAKE_BUILD_TYPE=Release .. 65 | 66 | # Build Windows zip file using MinGW on *nix (32-bit) 67 | .PHONY : windows-zip-32 68 | windows-zip-32: $(BUILD_DIR) $(GREG) 69 | cd $(BUILD_DIR); touch README.html; \ 70 | cmake -DCMAKE_TOOLCHAIN_FILE=../tools/Toolchain-MinGW-w64-32bit.cmake -DCMAKE_BUILD_TYPE=Release -DZIP=1 .. 71 | 72 | # Build the documentation using doxygen 73 | .PHONY : documentation 74 | documentation: $(BUILD_DIR) $(GREG) 75 | cd $(BUILD_DIR); touch README.html; \ 76 | cmake -DDOCUMENTATION=1 ..; cd ..; \ 77 | doxygen build/doxygen.conf 78 | 79 | # Clean out the build directory 80 | .PHONY : clean 81 | clean: 82 | rm -rf $(BUILD_DIR)/* 83 | -rm src/version.h 84 | 85 | # Ensure greg is compiled 86 | $(GREG): 87 | $(MAKE) -C submodules/greg 88 | 89 | # Create build directory if it doesn't exist 90 | $(BUILD_DIR): CHANGELOG 91 | -mkdir $(BUILD_DIR) 2>/dev/null 92 | -cd $(BUILD_DIR); rm -rf * 93 | 94 | # Generate a list of changes since last commit to 'master' branch 95 | .PHONY : CHANGELOG 96 | CHANGELOG: 97 | git log master..develop --format="* %s" | sort | uniq > CHANGELOG-UNRELEASED 98 | 99 | 100 | # =============== 101 | # make deprecated 102 | # =============== 103 | 104 | # This allows you to create a binary of `multimarkdown` for the current machine 105 | # without installing cmake. 106 | # 107 | # I don't recommend this approach, but it should work in a pinch 108 | 109 | CFLAGS ?= -Wall -g -O3 -include src/GLibFacade.h -include src/version.h -include src/parser.h 110 | PROGRAM = multimarkdown 111 | 112 | OBJS = build/multimarkdown.o build/parse_utilities.o build/parser.o build/GLibFacade.o build/writer.o build/text.o build/html.o build/latex.o build/memoir.o build/beamer.o build/lyx.o build/lyxbeamer.o build/opml.o build/odf.o build/critic.o build/rng.o build/rtf.o build/transclude.o build/toc.o 113 | 114 | build/%.o: src/%.c src/parser.h src/version.h $(BUILD_DIR) 115 | $(CC) -c $(CFLAGS) -o $@ $< 116 | 117 | src/version.h: $(BUILD_DIR) 118 | cd $(BUILD_DIR); touch README.html; \ 119 | cp ../tools/version.h ../src/version.h 120 | 121 | build/parser.o: src/parser.c src/parser.h 122 | $(CC) -c $(CFLAGS) -o $@ $< 123 | 124 | src/parser.c: src/parser.leg $(GREG) src/parser.h 125 | $(GREG) -o src/parser.c src/parser.leg 126 | 127 | .PHONY : deprecated 128 | deprecated: $(GREG) build/$(PROGRAM) 129 | 130 | build/$(PROGRAM): $(OBJS) $(BUILD_DIR) src/version.h 131 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS); \ 132 | rm src/parser.c 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## About ## 2 | 3 | | | | 4 | | ---------- | ------------------------- | 5 | | Title: | MultiMarkdown | 6 | | Author: | Fletcher T. Penney | 7 | | Date: | 2016-08-05 | 8 | | Copyright: | Copyright © 2013-2016 Fletcher T. Penney. | 9 | | Version: | 5.4.0 | 10 | 11 | 12 | ## Introduction ## 13 | 14 | [Markdown] is a simple markup language used to convert plain text into HTML. 15 | 16 | [MultiMarkdown] is a derivative of Markdown that adds new syntax features, 17 | such as footnotes, tables, and metadata. Additionally, it offers mechanisms to 18 | convert plain text into LaTeX in addition to HTML. 19 | 20 | 21 | ## Background ## 22 | 23 | MultiMarkdown started as a Perl script, which was modified from the original 24 | Markdown.pl. 25 | 26 | MultiMarkdown v3 (aka 'peg-multimarkdown') was based on John MacFarlane's 27 | [peg-markdown]. It used a parsing expression grammar (PEG), and was written 28 | in C in order to compile on almost any operating system. Thanks to work by 29 | Daniel Jalkut, MMD v3 was built so that it didn't have any external library 30 | requirements. 31 | 32 | MultiMarkdown v4 was basically a complete rewrite of v3. It used the same 33 | basic PEG for parsing (Multi)Markdown text, but otherwise was almost 34 | completely rebuilt. 35 | 36 | MultiMarkdown v5 is basically the same code as v4, but the project has been 37 | restructured: 38 | 39 | * It is built using my [c-template] project boilerplate -- I welcome 40 | suggestions and ideas for improvement about this. 41 | * It is designed with the CMake build system, rather than just 42 | a Makefile 43 | 44 | 45 | 46 | ## Why switch to CMake? ## 47 | 48 | In early 2014, a user of MMD introduced me to the [CMake] build system. I 49 | looked at it briefly, but didn't do anything with it. Later on, I looked 50 | at it more in depth and created a parallel branch after 4.6. This would allow 51 | me to experiment with CMake without breaking anything else in the `master` 52 | branch. 53 | 54 | CMake isn't perfect by any means, but it does allow for some interesting 55 | things: 56 | 57 | * Automatically generate GUI installers for OS X and Windows, as well as zip 58 | files for *nix. I have not looked into using CMake to build `.deb` 59 | packages, but that might be possible as well. My old system could generate 60 | GUI installers for Windows and OS X, but it was a complex process that 61 | required a lot of manual processing. This is much more amenable to 62 | automation. 63 | * An improved organization structure for various tests, including [Valgrind] 64 | testing. The old system was getting rather messy. 65 | * A templating system that better allows me to synchronize version, and 66 | other, information in code, documentation, and READMEs 67 | * Automatic generation of project files for Xcode, Visual Studio, and 68 | alternative build systems beyond `make` 69 | * An opportunity to reorganize my code directory hierarchy 70 | * The option to start adding unit test code to the source. This probably 71 | won't happen, as it would be too much work. But it is possible. 72 | 73 | 74 | The biggest *problem* is that this means that anyone wishing to compile the 75 | source will need to install CMake. This isn't hard, but it is an extra step. 76 | 77 | As a temporary measure, you can use the `make deprecated` command to use a 78 | simplified `make` recipe to compile a binary of MultiMarkdown for the current 79 | machine. I don't recommend this approach, but it should work in a pinch until 80 | you can upgrade your machine to support cmake. 81 | 82 | I welcome feedback on this decision, but please note -- "I don't like it" or 83 | "bring back the old way" comments will be ignored. Please send meaningful 84 | criticism or suggestions. 85 | 86 | Perhaps an approach if others want to contribute will be to do the reverse of 87 | what I did before -- create a `make` branch that includes a modified Makefile 88 | designed to be used without CMake? 89 | 90 | Additionally, the old Makefile had grown over time to include some tricks that 91 | users of various systems required. I have tested the CMake system on OS X, 92 | Ubuntu and Debian Linux, and MinGW on Ubuntu. I welcome suggestions for 93 | improvements to the CMake configuration. 94 | 95 | 96 | ## Download Binary ## 97 | 98 | Binaries for OS X and Windows are available on the github site: 99 | 100 | 101 | 102 | 103 | ## Compile from Source ## 104 | 105 | To compile MultiMarkdown, you will need to have [CMake] installed on your 106 | machine. 107 | 108 | To download the source: 109 | 110 | * Obtain the source from the github repository (Downloading a zipfile of the 111 | source won't allow you to configure the submodules -- it's much better to 112 | use git): 113 | 114 | git clone https://github.com/fletcher/MultiMarkdown-5.git 115 | 116 | * Configure the submodules with two helper scripts (This can be done 117 | manually on Windows systems by looking at the source): 118 | 119 | ./link_git_modules 120 | ./update_git_modules 121 | 122 | * Compile, and (optionally) test: 123 | 124 | make 125 | cd build 126 | make 127 | make test 128 | 129 | Like all versions of MultiMarkdown since v3, there is one test that will fail 130 | (now helpfully called `markdown-should-fail`). The other tests should pass. 131 | The valgrind tests will not work on OS X, but should pass if valgrind is 132 | installed and used on Linux machines. 133 | 134 | If you want to make an installer, after the above, use the `cpack` command 135 | inside the build directory. 136 | 137 | For more information, checkout the `IMPORTANT` file. 138 | 139 | 140 | ## Usage ## 141 | 142 | The [MultiMarkdown User's Guide] has complete instructions on how to use 143 | MultiMarkdown. 144 | 145 | 146 | # LyX Support # 147 | 148 | Charles R. Cowan () added support for conversion 149 | to [LyX](http://www.lyx.org/). Support for this should be considered to be in 150 | alpha/beta, and is not guaranteed. Issues related to LyX can be added to the 151 | MultiMarkdown [issues] page on github, but will need to be answered by 152 | Charles. I am happy to include this code in the main MMD repo, but since I 153 | don't use LyX I can't support it myself. If this arrangement becomes a 154 | problem, then LyX support can be removed and it can be kept as a separate 155 | fork. 156 | 157 | 158 | # More Information # 159 | 160 | To get more information about MultiMarkdown, check out the 161 | [website][MultiMarkdown] or [MultiMarkdown User's Guide]. 162 | 163 | 164 | # Developer Notes # 165 | 166 | Be sure to read the relevant documentation: 167 | 168 | * IMPORTANT 169 | * README.md 170 | * `make documentation` and look at `build/documentation/html/index.html` 171 | * Relevant portions of the User's Guide 172 | 173 | If you wish to submit pull requests, then be sure to work off of the `develop` 174 | branch and configure the pull requests appropriately. I am *trying* to use the 175 | "git flow" workflow described here: 176 | 177 | 178 | 179 | I will not accept pull requests directly into the `master` branch. 180 | 181 | ***NOTE***: Additionally, I am trying to use a consistent convention for 182 | commit messages, so that I can quickly generate the framework for Release 183 | Notes for new versions of MultiMarkdown. For example: 184 | 185 | TAG: Commit message with uppercase first letter and no period at the end 186 | 187 | TAG: Commit message one; TAG2: Commit message two 188 | 189 | The list of TAGs is in flux, but currently includes: 190 | 191 | * ADDED: New features or functionality 192 | * CHANGED: Change to the way a feature works 193 | * CODE: Change the code, but don't change the overall user experience 194 | * FIX: Fix a bug 195 | * IMPORTANT: Something major was fixed 196 | * NOTE: These are mostly changes to the project itself (e.g. Makefile) and 197 | have no impact on the user experience 198 | 199 | These TAGs are still in flux as I develop the system I am using, but this will 200 | allow me to automatically generate most of the Release Notes for each new 201 | version. I'll still need to go over them manually, but this gives me a head 202 | start! (As an aside, any time you use one of the `make` commands, the file 203 | `CHANGELOG-UNRELEASED` will be updated to show you new features in the 204 | `development` branch that have not been pulled into `master` yet.) 205 | 206 | By using the TAGs, I can sort the list of messages and group things into 207 | categories. By consistently using the semi-colon syntax, I can automatically 208 | split commits with multiple notes. 209 | 210 | 211 | ## License ## 212 | 213 | The `c-template` project is released under the MIT License. 214 | 215 | 216 | MMD 5 is released under the MIT License. 217 | 218 | 219 | CuTest is released under the zlib/libpng license. See CuTest.c for the text 220 | of the license. 221 | 222 | 223 | ## The MIT License ## 224 | 225 | Permission is hereby granted, free of charge, to any person obtaining a copy 226 | of this software and associated documentation files (the "Software"), to deal 227 | in the Software without restriction, including without limitation the rights 228 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 229 | copies of the Software, and to permit persons to whom the Software is 230 | furnished to do so, subject to the following conditions: 231 | 232 | The above copyright notice and this permission notice shall be included in 233 | all copies or substantial portions of the Software. 234 | 235 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 236 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 237 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 238 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 239 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 240 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 241 | THE SOFTWARE. 242 | 243 | 244 | 245 | [Markdown]: http://daringfireball.net/projects/markdown/ 246 | [MultiMarkdown]: http://fletcherpenney.net/multimarkdown/ 247 | [MultiMarkdown User's Guide]: http://fletcher.github.io/MultiMarkdown-5/ 248 | [c-template]: https://github.com/fletcher/c-template 249 | [CMake]: https://cmake.org/ 250 | [Doxygen]: http://www.doxygen.org/ 251 | [Valgrind]: http://valgrind.org/ 252 | [peg-markdown]: https://github.com/jgm/peg-markdown 253 | [issues]: https://github.com/fletcher/MultiMarkdown-5/issues 254 | 255 | 256 | -------------------------------------------------------------------------------- /build/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /link_git_modules: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This command sets up any git submodules in the project. 4 | # Generally, this only needs to be run once on a freshly cloned 5 | # project, but won't harm anything if run later, provided 6 | # you modify it to accomodate any submodules that should *NOT* 7 | # be on the `master` branch. 8 | 9 | git submodule init 10 | 11 | git submodule update 12 | 13 | git submodule foreach git branch --set-upstream master origin/master 14 | 15 | git submodule foreach git checkout master 16 | 17 | cd submodules/documentation; git branch --set-upstream gh-pages origin/gh-pages;git checkout gh-pages 18 | 19 | # If one module needs to follow a different branch: 20 | # cd some_module 21 | # git branch --set-upstream our-branch origin/some-branch 22 | # git checkout our-branch 23 | # cd .. 24 | -------------------------------------------------------------------------------- /scripts/markdown: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # markdown --- MultiMarkdown convenience script 4 | # 5 | # Fletcher T. Penney 6 | # 7 | # Pass arguments on to the binary to convert text to XHTML 8 | # 9 | 10 | # Be sure to include multimarkdown in our PATH 11 | export PATH="$PWD:/usr/local/bin:$PATH" 12 | 13 | which multimarkdown > /dev/null 14 | if [ $? = 1 ] 15 | then 16 | echo multimarkdown executable not found! >&2 17 | exit 1 18 | fi 19 | 20 | if [ $# = 0 ] 21 | then 22 | multimarkdown -c 23 | else 24 | until [ "$*" = "" ] 25 | do 26 | multimarkdown -c -b "$1" 27 | shift 28 | done 29 | fi 30 | -------------------------------------------------------------------------------- /scripts/markdown.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | :Loop 3 | IF "%1"=="" GOTO Continue 4 | "%~dp0\multimarkdown" -c -b %1 5 | SHIFT 6 | GOTO Loop 7 | :Continue 8 | 9 | -------------------------------------------------------------------------------- /scripts/mmd: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # mmd --- MultiMarkdown convenience script 4 | # 5 | # Fletcher T. Penney 6 | # 7 | # Pass arguments on to the binary to convert text to XHTML 8 | # 9 | 10 | # Be sure to include multimarkdown in our PATH 11 | export PATH="$PWD:/usr/local/bin:$PATH" 12 | 13 | which multimarkdown > /dev/null 14 | if [ $? = 1 ] 15 | then 16 | echo multimarkdown executable not found! >&2 17 | exit 1 18 | fi 19 | 20 | if [ $# = 0 ] 21 | then 22 | multimarkdown 23 | else 24 | until [ "$*" = "" ] 25 | do 26 | multimarkdown -b "$1" 27 | shift 28 | done 29 | fi 30 | -------------------------------------------------------------------------------- /scripts/mmd.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | :Loop 3 | IF "%1"=="" GOTO Continue 4 | "%~dp0\multimarkdown" -b %1 5 | SHIFT 6 | GOTO Loop 7 | :Continue 8 | 9 | -------------------------------------------------------------------------------- /scripts/mmd2all: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # mmd2all --- MultiMarkdown convenience script 4 | # 5 | # Fletcher T. Penney 6 | # 7 | # Pass arguments on to the binary to convert text to multiple 8 | # formats and open them 9 | # 10 | 11 | # Be sure to include multimarkdown in our PATH 12 | export PATH="$PWD:/usr/local/bin:$PATH" 13 | 14 | which multimarkdown > /dev/null 15 | if [ $? = 1 ] 16 | then 17 | echo multimarkdown executable not found! >&2 18 | exit 1 19 | fi 20 | 21 | if [ $# = 0 ] 22 | then 23 | echo "Can't work on stdin" 24 | else 25 | until [ "$*" = "" ] 26 | do 27 | file_name=`echo $1| sed 's/\.[^.]*$//'` 28 | 29 | multimarkdown -b "$1" 30 | # open "$file_name.html" 31 | 32 | multimarkdown -b -t latex "$1" 33 | # mate "$file_name.tex" 34 | 35 | multimarkdown -b -t odf "$1" 36 | # open "$file_name.fodt" 37 | 38 | multimarkdown -b -t opml "$1" 39 | # open "$file_name.opml" 40 | 41 | shift 42 | done 43 | fi 44 | -------------------------------------------------------------------------------- /scripts/mmd2odf: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # mmd2odf --- MultiMarkdown convenience script 4 | # 5 | # Fletcher T. Penney 6 | # 7 | # Pass arguments on to the binary to convert text to OpenDocument 8 | # 9 | 10 | # Be sure to include multimarkdown in our PATH 11 | export PATH="$PWD:/usr/local/bin:$PATH" 12 | 13 | which multimarkdown > /dev/null 14 | if [ $? = 1 ] 15 | then 16 | echo multimarkdown executable not found! >&2 17 | exit 1 18 | fi 19 | 20 | if [ $# = 0 ] 21 | then 22 | multimarkdown -t odf 23 | else 24 | until [ "$*" = "" ] 25 | do 26 | multimarkdown -b -t odf "$1" 27 | shift 28 | done 29 | fi 30 | -------------------------------------------------------------------------------- /scripts/mmd2odf.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | :Loop 3 | IF "%1"=="" GOTO Continue 4 | "%~dp0\multimarkdown" -b -t odf %1 5 | SHIFT 6 | GOTO Loop 7 | :Continue 8 | 9 | -------------------------------------------------------------------------------- /scripts/mmd2opml: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # mmd2opml --- MultiMarkdown convenience script 4 | # 5 | # Fletcher T. Penney 6 | # 7 | # Pass arguments on to the binary to convert text to OPML 8 | # 9 | 10 | # Be sure to include multimarkdown in our PATH 11 | export PATH="$PWD:/usr/local/bin:$PATH" 12 | 13 | which multimarkdown > /dev/null 14 | if [ $? = 1 ] 15 | then 16 | echo multimarkdown executable not found! >&2 17 | exit 1 18 | fi 19 | 20 | if [ $# = 0 ] 21 | then 22 | multimarkdown -t opml 23 | else 24 | until [ "$*" = "" ] 25 | do 26 | multimarkdown -b -t opml "$1" 27 | shift 28 | done 29 | fi 30 | -------------------------------------------------------------------------------- /scripts/mmd2opml.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | :Loop 3 | IF "%1"=="" GOTO Continue 4 | "%~dp0\multimarkdown" -b -t opml %1 5 | SHIFT 6 | GOTO Loop 7 | :Continue 8 | 9 | -------------------------------------------------------------------------------- /scripts/mmd2pdf: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # mmd2pdf --- MultiMarkdown convenience script 4 | # 5 | # Fletcher T. Penney 6 | # 7 | # Pass arguments on to the binary to convert text to LaTeX 8 | # Then use latexmk to process into PDF. 9 | # Requires a few extra passes of pdflatex to be sure all autorefs 10 | # are managed. 11 | # Then call latexmk with -c option to try and clean up some extra files. 12 | # 13 | # NOTE: This file is included as a convenience for users - it's not 14 | # likely to fail gracefully if there are any issues in your 15 | # LaTeX file. 16 | 17 | # Be sure to include multimarkdown and latex in our PATH 18 | export PATH="$PWD:/usr/local/bin:/usr/texbin:/Library/TeX/texbin:$PATH" 19 | 20 | which multimarkdown > /dev/null 21 | if [ $? = 1 ] 22 | then 23 | echo multimarkdown executable not found! >&2 24 | exit 1 25 | fi 26 | 27 | if [ $# = 0 ] 28 | then 29 | multimarkdown -t latex 30 | else 31 | until [ "$*" = "" ] 32 | do 33 | multimarkdown -b -t latex "$1" 34 | 35 | file_name=`echo $1| sed 's/\.[^.]*$//'` 36 | 37 | # Check for XeLaTeX mode 38 | 39 | xelatex=`multimarkdown -e usexelatex "$1"` 40 | 41 | if [ "$xelatex" != "" ] 42 | then 43 | # Use XeLaTeX 44 | 45 | xelatex "$file_name.tex" 46 | 47 | if [ "$?" = "127" ] 48 | then 49 | echo "It doesn't appear that xelatex is installed properly." 1>&2 50 | echo "Be sure you have a working LaTeX installation." 1>&2 51 | exit 1 52 | fi 53 | 54 | xelatex "$file_name.tex" 55 | xelatex "$file_name.tex" 56 | xelatex "$file_name.tex" 57 | latexmk -c "$file_name.tex" 58 | 59 | else 60 | # Use LaTeX 61 | latexmk "$file_name.tex" 62 | 63 | if [ "$?" = "127" ] 64 | then 65 | echo "It doesn't appear that latexmk is installed properly." 1>&2 66 | echo "Be sure you have a working LaTeX installation." 1>&2 67 | exit 1 68 | fi 69 | 70 | makeglossaries "$file_name" 71 | pdflatex "$file_name.tex" 72 | pdflatex "$file_name.tex" 73 | latexmk -c "$file_name.tex" 74 | 75 | fi 76 | 77 | shift 78 | done 79 | fi 80 | -------------------------------------------------------------------------------- /scripts/mmd2rtf: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # mmd2rtf --- MultiMarkdown convenience script 4 | # 5 | # Fletcher T. Penney 6 | # 7 | # Pass arguments on to the binary to convert text to OpenDocument 8 | # 9 | 10 | # NOTE: MultiMarkdown's RTF support is limited at best. If you find 11 | # something that isn't working properly, submit code for a proposed 12 | # fix via github or email. But don't just send a message letting me 13 | # know it isn't working. ;) 14 | # 15 | # Be sure to include multimarkdown in our PATH 16 | export PATH="$PWD:/usr/local/bin:$PATH" 17 | 18 | which multimarkdown > /dev/null 19 | if [ $? = 1 ] 20 | then 21 | echo multimarkdown executable not found! >&2 22 | exit 1 23 | fi 24 | 25 | if [ $# = 0 ] 26 | then 27 | multimarkdown -t rtf 28 | else 29 | until [ "$*" = "" ] 30 | do 31 | multimarkdown -b -t rtf "$1" 32 | shift 33 | done 34 | fi 35 | -------------------------------------------------------------------------------- /scripts/mmd2rtf.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | :Loop 3 | IF "%1"=="" GOTO Continue 4 | "%~dp0\multimarkdown" -b -t rtf %1 5 | SHIFT 6 | GOTO Loop 7 | :Continue 8 | 9 | -------------------------------------------------------------------------------- /scripts/mmd2tex: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # mmd2tex --- MultiMarkdown convenience script 4 | # 5 | # Fletcher T. Penney 6 | # 7 | # Pass arguments on to the binary to convert text to LaTeX 8 | # 9 | 10 | # Be sure to include multimarkdown in our PATH 11 | export PATH="$PWD:/usr/local/bin:$PATH" 12 | 13 | which multimarkdown > /dev/null 14 | if [ $? = 1 ] 15 | then 16 | echo multimarkdown executable not found! >&2 17 | exit 1 18 | fi 19 | 20 | if [ $# = 0 ] 21 | then 22 | multimarkdown -t latex 23 | else 24 | until [ "$*" = "" ] 25 | do 26 | multimarkdown -b -t latex "$1" 27 | shift 28 | done 29 | fi 30 | -------------------------------------------------------------------------------- /scripts/mmd2tex.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | :Loop 3 | IF "%1"=="" GOTO Continue 4 | "%~dp0\multimarkdown" -b -t latex %1 5 | SHIFT 6 | GOTO Loop 7 | :Continue 8 | 9 | -------------------------------------------------------------------------------- /src/GLibFacade.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GLibFacade.c 3 | * MultiMarkdown 4 | * 5 | * Created by Daniel Jalkut on 7/26/11. 6 | * Modified by Fletcher T. Penney on 9/15/11 and 12/3/13. 7 | * Modified by Dan Lowe on 1/3/12. 8 | * 9 | * License for original code by Daniel Jalkut: 10 | * 11 | * Copyright 2011 Daniel Jalkut. All rights reserved. 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 14 | * this software and associated documentation files (the “Software”), to deal in 15 | * the Software without restriction, including without limitation the rights to 16 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 17 | * of the Software, and to permit persons to whom the Software is furnished to do 18 | * so, subject to the following conditions: 19 | * 20 | * The above copyright notice and this permission notice shall be included in all 21 | * copies or substantial portions of the Software. 22 | * 23 | * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | * SOFTWARE. 30 | */ 31 | 32 | #include "GLibFacade.h" 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | /* 40 | * The following section came from: 41 | * 42 | * http://lists-archives.org/mingw-users/12649-asprintf-missing-vsnprintf- 43 | * behaving-differently-and-_vsncprintf-undefined.html 44 | * 45 | * and 46 | * 47 | * http://groups.google.com/group/jansson-users/browse_thread/thread/ 48 | * 76a88d63d9519978/041a7d0570de2d48?lnk=raot 49 | */ 50 | 51 | /* Solaris and Windows do not provide vasprintf() or asprintf(). */ 52 | #if defined(__WIN32) || (defined(__SVR4) && defined(__sun)) 53 | int vasprintf( char **sptr, char *fmt, va_list argv ) 54 | { 55 | int wanted = vsnprintf( *sptr = NULL, 0, fmt, argv ); 56 | if( (wanted > 0) && ((*sptr = malloc( 1 + wanted )) != NULL) ) 57 | return vsprintf( *sptr, fmt, argv ); 58 | 59 | return wanted; 60 | } 61 | 62 | int asprintf( char **sptr, char *fmt, ... ) 63 | { 64 | int retval; 65 | va_list argv; 66 | va_start( argv, fmt ); 67 | retval = vasprintf( sptr, fmt, argv ); 68 | va_end( argv ); 69 | return retval; 70 | } 71 | #endif 72 | 73 | 74 | /* GString */ 75 | 76 | #define kStringBufferStartingSize 1024 77 | #define kStringBufferGrowthMultiplier 2 78 | 79 | GString* g_string_new(const char *startingString) 80 | { 81 | GString* newString = malloc(sizeof(GString)); 82 | 83 | if (startingString == NULL) startingString = ""; 84 | 85 | size_t startingBufferSize = kStringBufferStartingSize; 86 | size_t startingStringSize = strlen(startingString); 87 | while (startingBufferSize < (startingStringSize + 1)) 88 | { 89 | startingBufferSize *= kStringBufferGrowthMultiplier; 90 | } 91 | 92 | newString->str = malloc(startingBufferSize); 93 | newString->currentStringBufferSize = startingBufferSize; 94 | strncpy(newString->str, startingString, startingStringSize); 95 | newString->str[startingStringSize] = '\0'; 96 | newString->currentStringLength = startingStringSize; 97 | 98 | return newString; 99 | } 100 | 101 | char* g_string_free(GString* ripString, bool freeCharacterData) 102 | { 103 | if (ripString == NULL) 104 | return NULL; 105 | 106 | char* returnedString = ripString->str; 107 | if (freeCharacterData) 108 | { 109 | if (ripString->str != NULL) 110 | { 111 | free(ripString->str); 112 | } 113 | returnedString = NULL; 114 | } 115 | 116 | free(ripString); 117 | 118 | return returnedString; 119 | } 120 | 121 | static void ensureStringBufferCanHold(GString* baseString, size_t newStringSize) 122 | { 123 | size_t newBufferSizeNeeded = newStringSize + 1; 124 | if (newBufferSizeNeeded > baseString->currentStringBufferSize) 125 | { 126 | size_t newBufferSize = baseString->currentStringBufferSize; 127 | 128 | while (newBufferSizeNeeded > newBufferSize) 129 | { 130 | newBufferSize *= kStringBufferGrowthMultiplier; 131 | } 132 | 133 | char *temp; 134 | temp = realloc(baseString->str, newBufferSize); 135 | 136 | if (temp == NULL) { 137 | /* realloc failed */ 138 | fprintf(stderr, "error reallocating memory\n"); 139 | 140 | exit(1); 141 | } 142 | baseString->str = temp; 143 | baseString->currentStringBufferSize = newBufferSize; 144 | } 145 | } 146 | 147 | void g_string_append(GString* baseString, char* appendedString) 148 | { 149 | if ((appendedString != NULL) && (strlen(appendedString) > 0)) 150 | { 151 | size_t appendedStringLength = strlen(appendedString); 152 | size_t newStringLength = baseString->currentStringLength + appendedStringLength; 153 | ensureStringBufferCanHold(baseString, newStringLength); 154 | 155 | /* We already know where the current string ends, so pass that as the starting address for strncat */ 156 | strncat(baseString->str + baseString->currentStringLength, appendedString, appendedStringLength); 157 | baseString->currentStringLength = newStringLength; 158 | } 159 | } 160 | 161 | void g_string_append_c(GString* baseString, char appendedCharacter) 162 | { 163 | size_t newSizeNeeded = baseString->currentStringLength + 1; 164 | ensureStringBufferCanHold(baseString, newSizeNeeded); 165 | 166 | baseString->str[baseString->currentStringLength] = appendedCharacter; 167 | baseString->currentStringLength++; 168 | baseString->str[baseString->currentStringLength] = '\0'; 169 | } 170 | 171 | void g_string_append_printf(GString* baseString, char* format, ...) 172 | { 173 | va_list args; 174 | va_start(args, format); 175 | 176 | char* formattedString = NULL; 177 | vasprintf(&formattedString, format, args); 178 | if (formattedString != NULL) 179 | { 180 | g_string_append(baseString, formattedString); 181 | free(formattedString); 182 | } 183 | va_end(args); 184 | } 185 | 186 | void g_string_prepend(GString* baseString, char* prependedString) 187 | { 188 | if ((prependedString != NULL) && (strlen(prependedString) > 0)) 189 | { 190 | size_t prependedStringLength = strlen(prependedString); 191 | size_t newStringLength = baseString->currentStringLength + prependedStringLength; 192 | ensureStringBufferCanHold(baseString, newStringLength); 193 | 194 | memmove(baseString->str + prependedStringLength, baseString->str, baseString->currentStringLength); 195 | strncpy(baseString->str, prependedString, prependedStringLength); 196 | baseString->currentStringLength = newStringLength; 197 | baseString->str[baseString->currentStringLength] = '\0'; 198 | } 199 | } 200 | 201 | void g_string_insert(GString* baseString, size_t pos, char * insertedString) 202 | { 203 | if ((insertedString != NULL) && (strlen(insertedString) > 0)) 204 | { 205 | if (pos > baseString->currentStringLength) 206 | pos = baseString->currentStringLength; 207 | 208 | size_t insertedStringLength = strlen(insertedString); 209 | size_t newStringLength = baseString->currentStringLength + insertedStringLength; 210 | ensureStringBufferCanHold(baseString, newStringLength); 211 | 212 | /* Shift following string to 'right' */ 213 | memmove(baseString->str + pos + insertedStringLength, baseString->str + pos, baseString->currentStringLength - pos); 214 | strncpy(baseString->str + pos, insertedString, insertedStringLength); 215 | baseString->currentStringLength = newStringLength; 216 | baseString->str[baseString->currentStringLength] = '\0'; 217 | } 218 | } 219 | 220 | void g_string_insert_c(GString* baseString, size_t pos, char insertedCharacter) 221 | { 222 | if (pos > baseString->currentStringLength) 223 | pos = baseString->currentStringLength; 224 | 225 | size_t newSizeNeeded = baseString->currentStringLength + 1; 226 | ensureStringBufferCanHold(baseString, newSizeNeeded); 227 | 228 | /* Shift following string to 'right' */ 229 | memmove(baseString->str + pos + 1, baseString->str + pos, baseString->currentStringLength - pos); 230 | 231 | baseString->str[pos] = insertedCharacter; 232 | baseString->currentStringLength++; 233 | baseString->str[baseString->currentStringLength] = '\0'; 234 | } 235 | 236 | 237 | void g_string_insert_printf(GString* baseString, size_t pos, char* format, ...) 238 | { 239 | va_list args; 240 | va_start(args, format); 241 | 242 | char* formattedString = NULL; 243 | vasprintf(&formattedString, format, args); 244 | if (formattedString != NULL) 245 | { 246 | g_string_insert(baseString, pos, formattedString); 247 | free(formattedString); 248 | } 249 | va_end(args); 250 | } 251 | 252 | void g_string_erase(GString* baseString, size_t pos, size_t len) 253 | { 254 | if ((pos > baseString->currentStringLength) || (len <= 0)) 255 | return; 256 | 257 | if ((pos + len) >= baseString->currentStringLength) 258 | len = -1; 259 | 260 | if (len == -1) { 261 | baseString->currentStringLength = pos; 262 | } else { 263 | memmove(baseString->str + pos, baseString->str + pos + len, baseString->currentStringLength - pos - len); 264 | baseString->currentStringLength -= len; 265 | } 266 | baseString->str[baseString->currentStringLength] = '\0'; 267 | } 268 | 269 | /* GSList */ 270 | 271 | void g_slist_free(GSList* ripList) 272 | { 273 | GSList* thisListItem = ripList; 274 | while (thisListItem != NULL) 275 | { 276 | GSList* nextItem = thisListItem->next; 277 | 278 | /* I guess we don't release the data? Non-retained memory management is hard... let's figure it out later. */ 279 | free(thisListItem); 280 | 281 | thisListItem = nextItem; 282 | } 283 | } 284 | 285 | /* Currently only used for markdown_output.c endnotes printing */ 286 | GSList* g_slist_reverse(GSList* theList) 287 | { 288 | GSList* lastNodeSeen = NULL; 289 | 290 | /* Iterate the list items, tacking them on to our new reversed List as we find them */ 291 | GSList* listWalker = theList; 292 | while (listWalker != NULL) 293 | { 294 | GSList* nextNode = listWalker->next; 295 | listWalker->next = lastNodeSeen; 296 | lastNodeSeen = listWalker; 297 | listWalker = nextNode; 298 | } 299 | 300 | return lastNodeSeen; 301 | } 302 | 303 | GSList* g_slist_prepend(GSList* targetElement, void* newElementData) 304 | { 305 | GSList* newElement = malloc(sizeof(GSList)); 306 | newElement->data = newElementData; 307 | newElement->next = targetElement; 308 | return newElement; 309 | } 310 | 311 | -------------------------------------------------------------------------------- /src/GLibFacade.h: -------------------------------------------------------------------------------- 1 | /* 2 | * GLibFacade.h 3 | * MultiMarkdown 4 | * 5 | * Created by Daniel Jalkut on 7/26/11. 6 | * Modified by Fletcher T. Penney on 9/15/11 and 12/3/13. 7 | * Modified by Dan Lowe on 1/3/12. 8 | * 9 | * License for original code by Daniel Jalkut: 10 | * 11 | * Copyright 2011 Daniel Jalkut. All rights reserved. 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 14 | * this software and associated documentation files (the “Software”), to deal in 15 | * the Software without restriction, including without limitation the rights to 16 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 17 | * of the Software, and to permit persons to whom the Software is furnished to do 18 | * so, subject to the following conditions: 19 | * 20 | * The above copyright notice and this permission notice shall be included in all 21 | * copies or substantial portions of the Software. 22 | * 23 | * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | * SOFTWARE. 30 | */ 31 | 32 | #ifndef __MARKDOWN_GLIB_FACADE__ 33 | #define __MARKDOWN_GLIB_FACADE__ 34 | 35 | /* peg_markdown uses the link symbol for its own purposes */ 36 | #define link MARKDOWN_LINK_IGNORED 37 | #include 38 | #undef link 39 | 40 | #include 41 | #include 42 | 43 | typedef int gboolean; 44 | typedef char gchar; 45 | 46 | /* This style of bool is used in shared source code */ 47 | #if !defined(FALSE) 48 | #define FALSE false 49 | #endif 50 | #if !defined(TRUE) 51 | #define TRUE true 52 | #endif 53 | 54 | /* WE implement minimal mirror implementations of GLib's GString and GSList 55 | * sufficient to cover the functionality required by MultiMarkdown. 56 | * 57 | * NOTE: THese are 100% clean, from-scratch implementations using only the 58 | * GLib function prototype as guide for behavior. 59 | */ 60 | 61 | typedef struct 62 | { 63 | /* Current UTF8 byte stream this string represents */ 64 | char* str; 65 | 66 | /* Where in the str buffer will we add new characters */ 67 | /* or append new strings? */ 68 | unsigned long currentStringBufferSize; 69 | unsigned long currentStringLength; 70 | } GString; 71 | 72 | GString* g_string_new(const char *startingString); 73 | char* g_string_free(GString* ripString, bool freeCharacterData); 74 | 75 | void g_string_append_c(GString* baseString, char appendedCharacter); 76 | void g_string_append(GString* baseString, char *appendedString); 77 | 78 | void g_string_prepend(GString* baseString, char* prependedString); 79 | 80 | void g_string_append_printf(GString* baseString, char* format, ...); 81 | 82 | void g_string_insert(GString* baseString, size_t pos, char * insertedString); 83 | void g_string_insert_c(GString* baseString, size_t pos, char insertedCharacter); 84 | void g_string_insert_printf(GString* baseString, size_t pos, char* format, ...); 85 | 86 | void g_string_erase(GString* baseString, size_t pos, size_t len); 87 | 88 | /* Just implement a very simple singly linked list. */ 89 | 90 | typedef struct _GSList 91 | { 92 | void* data; 93 | struct _GSList* next; 94 | } GSList; 95 | 96 | void g_slist_free(GSList* ripList); 97 | GSList* g_slist_prepend(GSList* targetElement, void* newElementData); 98 | GSList* g_slist_reverse(GSList* theList); 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /src/beamer.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | beamer.c -- Beamer add-on to LaTeX writer 4 | 5 | (c) 2013-2016 Fletcher T. Penney (http://fletcherpenney.net/). 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License or the MIT 9 | license. See LICENSE for details. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | */ 17 | 18 | #include "beamer.h" 19 | 20 | /* print_beamer_node_tree -- convert node tree to LaTeX */ 21 | void print_beamer_node_tree(GString *out, node *list, scratch_pad *scratch) { 22 | while (list != NULL) { 23 | print_beamer_node(out, list, scratch); 24 | list = list->next; 25 | } 26 | } 27 | 28 | /* print_beamer_node -- convert given node to LaTeX and append */ 29 | void print_beamer_node(GString *out, node *n, scratch_pad *scratch) { 30 | int lev; 31 | char *temp; 32 | 33 | /* If we are forcing a complete document, and METADATA isn't the first thing, 34 | we need to close */ 35 | if ((scratch->extensions & EXT_COMPLETE) 36 | && !(scratch->extensions & EXT_HEAD_CLOSED) && 37 | !((n->key == FOOTER) || (n->key == METADATA))) { 38 | pad(out, 2, scratch); 39 | scratch->extensions = scratch->extensions | EXT_HEAD_CLOSED; 40 | } 41 | switch (n->key) { 42 | case FOOTER: 43 | print_beamer_endnotes(out, scratch); 44 | g_string_append_printf(out, "\\mode\n"); 45 | if (scratch->latex_footer != NULL) { 46 | pad(out, 2, scratch); 47 | g_string_append_printf(out,"\\input{%s}\n", scratch->latex_footer); 48 | } 49 | if (scratch->extensions & EXT_COMPLETE) { 50 | g_string_append_printf(out, "\n\\end{document}"); 51 | } 52 | g_string_append_printf(out, "\\mode*\n"); 53 | break; 54 | case LISTITEM: 55 | pad(out, 1, scratch); 56 | g_string_append_printf(out, "\\item<+-> "); 57 | scratch->padded = 2; 58 | print_latex_node_tree(out, n->children, scratch); 59 | g_string_append_printf(out, "\n"); 60 | break; 61 | case HEADINGSECTION: 62 | if (n->children->key -H1 + scratch->baseheaderlevel == 3) { 63 | pad(out, 2, scratch); 64 | g_string_append_printf(out, "\\begin{frame}"); 65 | /* TODO: Fix this */ 66 | if (tree_contains_key(n->children,VERBATIM) || 67 | tree_contains_key(n->children,VERBATIMFENCE)) { 68 | g_string_append_printf(out, "[fragile]"); 69 | } 70 | scratch->padded = 0; 71 | print_beamer_node_tree(out, n->children, scratch); 72 | g_string_append_printf(out, "\n\n\\end{frame}\n\n"); 73 | scratch->padded = 2; 74 | } else if (n->children->key -H1 + scratch->baseheaderlevel == 4) { 75 | pad(out, 1, scratch); 76 | g_string_append_printf(out, "\\mode
{\n"); 77 | scratch->padded = 0; 78 | print_beamer_node_tree(out, n->children->next, scratch); 79 | g_string_append_printf(out, "\n\n}\n\n"); 80 | scratch->padded = 2; 81 | } else { 82 | print_beamer_node_tree(out, n->children, scratch); 83 | } 84 | break; 85 | case H1: case H2: case H3: case H4: case H5: case H6: 86 | pad(out, 2, scratch); 87 | lev = n->key - H1 + scratch->baseheaderlevel; /* assumes H1 ... H6 are in order */ 88 | switch (lev) { 89 | case 1: 90 | g_string_append_printf(out, "\\part{"); 91 | break; 92 | case 2: 93 | g_string_append_printf(out, "\\section{"); 94 | break; 95 | case 3: 96 | g_string_append_printf(out, "\\frametitle{"); 97 | break; 98 | default: 99 | g_string_append_printf(out, "\\emph{"); 100 | break; 101 | } 102 | /* generate a label for each header (MMD); 103 | don't allow footnotes since invalid here */ 104 | scratch->no_latex_footnote = TRUE; 105 | if (n->children->key == AUTOLABEL) { 106 | temp = label_from_string(n->children->str); 107 | print_latex_node_tree(out, n->children->next, scratch); 108 | g_string_append_printf(out, "}\n\\label{%s}", temp); 109 | free(temp); 110 | } else { 111 | print_latex_node_tree(out, n->children, scratch); 112 | temp = label_from_node_tree(n->children); 113 | g_string_append_printf(out, "}\n\\label{%s}", temp); 114 | free(temp); 115 | } 116 | scratch->no_latex_footnote = FALSE; 117 | scratch->padded = 0; 118 | break; 119 | default: 120 | /* most things are not changed for memoir output */ 121 | print_latex_node(out, n, scratch); 122 | } 123 | } 124 | 125 | /* print_beamer_endnotes */ 126 | void print_beamer_endnotes(GString *out, scratch_pad *scratch) { 127 | scratch->used_notes = reverse_list(scratch->used_notes); 128 | scratch->printing_notes = 1; 129 | 130 | node *note = scratch->used_notes; 131 | #ifdef DEBUG_ON 132 | fprintf(stderr, "start endnotes\n"); 133 | #endif 134 | 135 | if ((note == NULL) || ((note->key == KEY_COUNTER) && (note->next == NULL))) 136 | return; 137 | while (note != NULL) { 138 | if (note->key == CITATIONSOURCE) 139 | break; 140 | note = note->next; 141 | } 142 | 143 | if (note == NULL) 144 | return; 145 | 146 | note = scratch->used_notes; 147 | 148 | /* TODO: need CITATIONSOURCE to print bibliography */ 149 | #ifdef DEBUG_ON 150 | fprintf(stderr, "there are endnotes to print\n"); 151 | #endif 152 | 153 | pad(out, 2, scratch); 154 | g_string_append_printf(out, "\\part{Bibliography}\n\\begin{frame}[allowframebreaks]\n\\frametitle{Bibliography}\n\\def\\newblock{}\n\\begin{thebibliography}{0}\n"); 155 | while ( note != NULL) { 156 | if (note->key == KEY_COUNTER) { 157 | note = note->next; 158 | continue; 159 | } 160 | 161 | pad(out, 1, scratch); 162 | 163 | if (note->key == CITATIONSOURCE) { 164 | g_string_append_printf(out, "\\bibitem{%s}\n", note->str); 165 | scratch->padded = 2; 166 | 167 | print_latex_node(out, note, scratch); 168 | pad(out, 1, scratch); 169 | scratch->padded = 1; 170 | } else { 171 | /* footnotes handled elsewhere */ 172 | } 173 | 174 | note = note->next; 175 | } 176 | pad(out,2, scratch); 177 | g_string_append_printf(out, "\\end{thebibliography}\n\\end{frame}\n\n"); 178 | scratch->padded = 0; 179 | #ifdef DEBUG_ON 180 | fprintf(stderr, "finish endnotes\n"); 181 | #endif 182 | } 183 | -------------------------------------------------------------------------------- /src/beamer.h: -------------------------------------------------------------------------------- 1 | #ifndef BEAMER_PARSER_H 2 | #define BEAMER_PARSER_H 3 | 4 | #include "parser.h" 5 | #include "latex.h" 6 | 7 | void print_beamer_node_tree(GString *out, node *list, scratch_pad *scratch); 8 | void print_beamer_node(GString *out, node *n, scratch_pad *scratch); 9 | void print_beamer_endnotes(GString *out, scratch_pad *scratch); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/critic.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | critic.c -- CriticMarkup preprocessor 4 | 5 | 6 | 7 | (c) 2013-2016 Fletcher T. Penney (http://fletcherpenney.net/). 8 | 9 | This program is free software; you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License or the MIT 11 | license. See LICENSE for details. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | */ 19 | 20 | #include "critic.h" 21 | 22 | void print_critic_accept_node_tree(GString *out, node *list, scratch_pad *scratch) { 23 | while (list != NULL) { 24 | print_critic_accept_node(out, list, scratch); 25 | list = list->next; 26 | } 27 | } 28 | 29 | void print_critic_reject_node_tree(GString *out, node *list, scratch_pad *scratch) { 30 | while (list != NULL) { 31 | print_critic_reject_node(out, list, scratch); 32 | list = list->next; 33 | } 34 | } 35 | 36 | void print_critic_html_highlight_node_tree(GString *out, node *list, scratch_pad *scratch) { 37 | while (list != NULL) { 38 | print_critic_html_highlight_node(out, list, scratch); 39 | list = list->next; 40 | } 41 | } 42 | 43 | void print_critic_accept_node(GString *out, node *n, scratch_pad *scratch) { 44 | if (n == NULL) 45 | return; 46 | 47 | switch (n->key) { 48 | case LIST: 49 | case CRITICSUBSTITUTION: 50 | case CRITICADDITION: 51 | case CRITICHIGHLIGHT: 52 | print_critic_accept_node_tree(out, n->children, scratch); 53 | break; 54 | case CRITICDELETION: 55 | case CRITICCOMMENT: 56 | break; 57 | default: 58 | g_string_append_printf(out,"%s",n->str); 59 | break; 60 | } 61 | } 62 | 63 | void print_critic_reject_node(GString *out, node *n, scratch_pad *scratch) { 64 | if (n == NULL) 65 | return; 66 | 67 | switch (n->key) { 68 | case LIST: 69 | case CRITICSUBSTITUTION: 70 | case CRITICDELETION: 71 | case CRITICHIGHLIGHT: 72 | print_critic_reject_node_tree(out, n->children, scratch); 73 | break; 74 | case CRITICADDITION: 75 | case CRITICCOMMENT: 76 | break; 77 | default: 78 | g_string_append_printf(out,"%s",n->str); 79 | break; 80 | } 81 | } 82 | 83 | void print_critic_html_highlight_node(GString *out, node *n, scratch_pad *scratch) { 84 | if (n == NULL) 85 | return; 86 | 87 | switch (n->key) { 88 | case LIST: 89 | print_critic_html_highlight_node_tree(out, n->children, scratch); 90 | break; 91 | case CRITICSUBSTITUTION: 92 | print_critic_html_highlight_node_tree(out, n->children, scratch); 93 | break; 94 | case CRITICADDITION: 95 | g_string_append_printf(out, ""); 96 | print_critic_html_highlight_node_tree(out, n->children, scratch); 97 | g_string_append_printf(out, ""); 98 | break; 99 | case CRITICCOMMENT: 100 | /* Hide comments for now */ 101 | /* g_string_append_printf(out, "%s", n->str); */ 102 | break; 103 | case CRITICHIGHLIGHT: 104 | g_string_append_printf(out, ""); 105 | print_critic_html_highlight_node_tree(out, n->children, scratch); 106 | g_string_append_printf(out, ""); 107 | break; 108 | case CRITICDELETION: 109 | g_string_append_printf(out, ""); 110 | print_critic_html_highlight_node_tree(out, n->children, scratch); 111 | g_string_append_printf(out, ""); 112 | break; 113 | default: 114 | g_string_append_printf(out,"%s",n->str); 115 | break; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/critic.h: -------------------------------------------------------------------------------- 1 | #ifndef CRITIC_PARSER_H 2 | #define CRITIC_PARSER_H 3 | 4 | #include "parser.h" 5 | #include "writer.h" 6 | 7 | 8 | void print_critic_accept_node_tree(GString *out, node *list, scratch_pad *scratch); 9 | void print_critic_reject_node_tree(GString *out, node *list, scratch_pad *scratch); 10 | void print_critic_html_highlight_node_tree(GString *out, node *list, scratch_pad *scratch); 11 | void print_critic_accept_node(GString *out, node *list, scratch_pad *scratch); 12 | void print_critic_reject_node(GString *out, node *list, scratch_pad *scratch); 13 | void print_critic_html_highlight_node(GString *out, node *list, scratch_pad *scratch); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/glib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * glib.h 3 | * MultiMarkdown 4 | * 5 | * Created by Daniel Jalkut on 7/26/11. 6 | * Copyright 2011 __MyCompanyName__. All rights reserved. 7 | * 8 | */ 9 | 10 | /* Just a dummy file to keep the glib-dependent sources compiling as we would hope */ 11 | #include "GLibFacade.h" 12 | -------------------------------------------------------------------------------- /src/html.h: -------------------------------------------------------------------------------- 1 | #ifndef HTML_PARSER_H 2 | #define HTML_PARSER_H 3 | 4 | #include "parser.h" 5 | #include "writer.h" 6 | 7 | 8 | void print_html_node_tree(GString *out, node *list, scratch_pad *scratch); 9 | void print_html_node(GString *out, node *n, scratch_pad *scratch); 10 | void print_html_localized_typography(GString *out, int character, scratch_pad *scratch); 11 | void print_html_string(GString *out, char *str, scratch_pad *scratch); 12 | void print_html_endnotes(GString *out, scratch_pad *scratch); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/latex.h: -------------------------------------------------------------------------------- 1 | #ifndef LATEX_PARSER_H 2 | #define LATEX_PARSER_H 3 | 4 | #include "parser.h" 5 | #include "writer.h" 6 | 7 | 8 | void print_latex_node_tree(GString *out, node *list, scratch_pad *scratch); 9 | void print_latex_node(GString *out, node *n, scratch_pad *scratch); 10 | void print_latex_localized_typography(GString *out, int character, scratch_pad *scratch); 11 | void print_latex_string(GString *out, char *str, scratch_pad *scratch); 12 | void print_latex_url(GString *out, char *str, scratch_pad *scratch); 13 | void print_latex_endnotes(GString *out, scratch_pad *scratch); 14 | int find_latex_mode(int format, node *n); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/libMultiMarkdown.h: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | MultiMarkdown - lightweight markup processor 4 | 5 | @file libMultiMarkdown.h 6 | 7 | @brief MultiMarkdown library header 8 | 9 | libMultiMarkdown.h provides the key commands to convert raw MultiMarkdown 10 | text into a desired export format. 11 | 12 | For most simple uses, the main API commands provided here are sufficient. 13 | For more complex scenarios, however, you may need to handle file 14 | transclusion which requires knowledge about the location of the source 15 | file in order to appropriately handle relative links. In this case, you 16 | may benefit from examining multimarkdown.c. 17 | 18 | @author Fletcher T. Penney 19 | @bug No known bugs 20 | 21 | **/ 22 | 23 | /* 24 | 25 | Copyright © 2013-2016 Fletcher T. Penney. 26 | 27 | 28 | The `c-template` project is released under the MIT License. 29 | 30 | 31 | MMD 5 is released under the MIT License. 32 | 33 | 34 | CuTest is released under the zlib/libpng license. See CuTest.c for the text 35 | of the license. 36 | 37 | 38 | ## The MIT License ## 39 | 40 | Permission is hereby granted, free of charge, to any person obtaining a copy 41 | of this software and associated documentation files (the "Software"), to deal 42 | in the Software without restriction, including without limitation the rights 43 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 44 | copies of the Software, and to permit persons to whom the Software is 45 | furnished to do so, subject to the following conditions: 46 | 47 | The above copyright notice and this permission notice shall be included in 48 | all copies or substantial portions of the Software. 49 | 50 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 51 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 52 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 53 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 54 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 55 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 56 | THE SOFTWARE. 57 | 58 | */ 59 | 60 | 61 | #ifndef LIB_MULTIMARKDOWN_H 62 | #define LIB_MULTIMARKDOWN_H 63 | 64 | 65 | /* Main API commands */ 66 | 67 | /// Convert source string to output string, based on provided `parser_extensions` 68 | /// and requested `export_format`. 69 | /// The returned `char *` will need to be freed after it is no longer needed. 70 | char * markdown_to_string( 71 | const char * source, //!< Pointer to c-string of the source text 72 | unsigned long extensions, //!< Bit field of parser_extensions 73 | int format //!< Specify export_format to be used 74 | ); 75 | 76 | /// Does the source string have metadata, using provided `parser_extensions`? 77 | /// 78 | bool has_metadata( 79 | const char *source, //!< Pointer to c-string of the source text 80 | unsigned long extensions //!< Bit field of parser_extensions 81 | ); 82 | 83 | /// List all metadata keys, using provided `parser_extensions`. 84 | /// The returned `char *` will need to be freed after it is no longer needed. 85 | char * extract_metadata_keys( 86 | const char *source, //!< Pointer to c-string of the source text 87 | unsigned long extensions //!< Bit field of parser_extensions 88 | ); 89 | 90 | /// Extract the value for the specified metadata key, using provided `parser_extensions`. 91 | /// The returned `char *` will need to be freed after it is no longer needed. 92 | char * extract_metadata_value( 93 | const char *source, //!< Pointer to c-string of the source text 94 | unsigned long extensions, //!< Bit field of parser_extensions 95 | char *key //!< C-string of the key we need to find 96 | ); 97 | 98 | /// Return the version string for this build of libMultiMarkdown 99 | /// The returned `char *` will need to be freed after it is no longer needed. 100 | char * mmd_version(void); 101 | 102 | 103 | /// These are the basic extensions that enable or disable MultiMarkdown features 104 | enum parser_extensions { 105 | EXT_COMPATIBILITY = 1 << 0, //!< Markdown compatibility mode 106 | EXT_COMPLETE = 1 << 1, //!< Create complete document 107 | EXT_SNIPPET = 1 << 2, //!< Create snippet only 108 | EXT_HEAD_CLOSED = 1 << 3, //!< for use by parser 109 | EXT_SMART = 1 << 4, //!< Enable Smart quotes 110 | EXT_NOTES = 1 << 5, //!< Enable Footnotes 111 | EXT_NO_LABELS = 1 << 6, //!< Don't add anchors to headers, etc. 112 | EXT_FILTER_STYLES = 1 << 7, //!< Filter out style blocks 113 | EXT_FILTER_HTML = 1 << 8, //!< Filter out raw HTML 114 | EXT_PROCESS_HTML = 1 << 9, //!< Process Markdown inside HTML 115 | EXT_NO_METADATA = 1 << 10, //!< Don't parse Metadata 116 | EXT_OBFUSCATE = 1 << 11, //!< Mask email addresses 117 | EXT_CRITIC = 1 << 12, //!< Critic Markup Support 118 | EXT_CRITIC_ACCEPT = 1 << 13, //!< Accept all proposed changes 119 | EXT_CRITIC_REJECT = 1 << 14, //!< Reject all proposed changes 120 | EXT_RANDOM_FOOT = 1 << 15, //!< Use random numbers for footnote links 121 | EXT_HEADINGSECTION = 1 << 16, //!< Group blocks under parent heading 122 | EXT_ESCAPED_LINE_BREAKS = 1 << 17, //!< Escaped line break 123 | EXT_NO_STRONG = 1 << 18, //!< Don't allow nested \'s 124 | EXT_NO_EMPH = 1 << 19, //!< Don't allow nested \'s 125 | EXT_FAKE = 1 << 31, //!< 31 is highest number allowed 126 | }; 127 | 128 | /// Define the output formats we support -- not all of these should be used 129 | enum export_formats { 130 | ORIGINAL_FORMAT, //!< Transclusion happens, but no parsing 131 | HTML_FORMAT, //!< Well supported 132 | TEXT_FORMAT, //!< Not currently used, may exit host process 133 | LATEX_FORMAT, //!< Well supported 134 | MEMOIR_FORMAT, //!< Well supported 135 | BEAMER_FORMAT, //!< Well supported 136 | OPML_FORMAT, //!< Well supported 137 | ODF_FORMAT, //!< Well supported 138 | RTF_FORMAT, //!< Not recommended for production code, may crash 139 | CRITIC_ACCEPT_FORMAT, //!< Used as a pre-processing step 140 | CRITIC_REJECT_FORMAT, //!< Used as a pre-processing step 141 | CRITIC_HTML_HIGHLIGHT_FORMAT, //!< Used as a pre-processing step 142 | LYX_FORMAT, //!< Developed and maintained by Charles R. Cowan 143 | TOC_FORMAT, //!< Used as a pre-processing step 144 | }; 145 | 146 | /// These are the identifiers for node types 147 | enum keys { 148 | NO_TYPE, 149 | LIST, 150 | STR, 151 | APOSTROPHE, 152 | FOOTER, 153 | PARA, 154 | PLAIN, 155 | LINEBREAK, 156 | SPACE, 157 | HEADINGSECTION, 158 | H1, H2, H3, H4, H5, H6, H7, //!< Keep H1 through H7 in order 159 | METADATA, 160 | METAKEY, 161 | METAVALUE, 162 | MATHSPAN, 163 | STRONG, 164 | EMPH, 165 | LINK, 166 | SOURCE, 167 | TITLE, 168 | REFNAME, 169 | AUTOLABEL, 170 | IMAGE, 171 | IMAGEBLOCK, 172 | NOTEREFERENCE, 173 | CODE, 174 | HTML, 175 | ELLIPSIS, 176 | ENDASH, 177 | EMDASH, 178 | SINGLEQUOTED, 179 | DOUBLEQUOTED, 180 | BLOCKQUOTE, 181 | BLOCKQUOTEMARKER, 182 | RAW, 183 | VERBATIM, 184 | VERBATIMTYPE, 185 | VERBATIMFENCE, 186 | DEFLIST, 187 | TERM, 188 | DEFINITION, 189 | HRULE, 190 | ORDEREDLIST, 191 | BULLETLIST, 192 | LISTITEM, 193 | HTMLBLOCK, 194 | TABLE, 195 | TABLECAPTION, 196 | TABLELABEL, 197 | TABLESEPARATOR, 198 | TABLECELL, 199 | CELLSPAN, 200 | TABLEROW, 201 | TABLEBODY, 202 | TABLEHEAD, 203 | LINKREFERENCE, 204 | NOTESOURCE, 205 | CITATIONSOURCE, 206 | SOURCEBRANCH, 207 | NOTELABEL, 208 | GLOSSARYLABEL, 209 | ATTRVALUE, 210 | ATTRKEY, 211 | GLOSSARYSOURCE, 212 | GLOSSARYSORTKEY, 213 | GLOSSARYTERM, 214 | CITATION, 215 | NOCITATION, 216 | CRITICADDITION, 217 | CRITICDELETION, 218 | CRITICSUBSTITUTION, 219 | CRITICHIGHLIGHT, 220 | CRITICCOMMENT, 221 | SUPERSCRIPT, 222 | SUBSCRIPT, 223 | VARIABLE, 224 | ABBREVIATION, 225 | ABBR, 226 | ABBRSTART, 227 | ABBRSTOP, 228 | TOC, 229 | KEY_COUNTER //!< This *MUST* be the last item in the list 230 | }; 231 | 232 | 233 | /// This is the element used in the resulting parse tree 234 | struct node { 235 | short key; //!< What type of element is this? 236 | char *str; //!< Relevant string from source for this element 237 | struct link_data *link_data; //!< Store link info when relevant to this node 238 | struct node *children; //!< Pointer to child elements 239 | struct node *next; //!< Pointer to next element 240 | }; 241 | 242 | /// This is the element used in the resulting parse tree 243 | typedef struct node node; 244 | 245 | 246 | /// Structure to simplify handling of links 247 | struct link_data { 248 | char *label; //!< Text of the label, if this is a reference link 249 | char *source; //!< Source URL string 250 | char *title; //!< Title string 251 | node *attr; //!< Pointer to tree of attributes, if any 252 | }; 253 | 254 | /// Structure to simplify handling of links 255 | typedef struct link_data link_data; 256 | 257 | #endif 258 | -------------------------------------------------------------------------------- /src/lyx.h: -------------------------------------------------------------------------------- 1 | #ifndef LYX_PARSER_H 2 | #define LYX_PARSER_H 3 | 4 | #include "parser.h" 5 | #include "writer.h" 6 | 7 | /* allow the user to change the heading levels */ 8 | 9 | extern GString *heading_name[7]; 10 | 11 | /* Lyx likes to wrap strings in "environments" */ 12 | enum lyx_environment{ 13 | LYX_NONE, 14 | LYX_STANDARD, 15 | LYX_CODE, 16 | LYX_PLAIN 17 | }; 18 | 19 | 20 | void perform_lyx_output(GString *out, node* list, scratch_pad *scratch); 21 | bool begin_lyx_output(GString *out, node* list, scratch_pad *scratch); 22 | void print_lyx_node_tree(GString *out, node *list, scratch_pad *scratch, bool no_newline); 23 | void end_lyx_output(GString *out, node* list, scratch_pad *scratch); 24 | void print_lyx_node(GString *out, node *n, scratch_pad *scratch, bool no_newline); 25 | void print_lyx_localized_typography(GString *out, unsigned char character, scratch_pad *scratch); 26 | void print_lyx_string(GString *out,char *str, scratch_pad *scratch,short environment); 27 | void print_lyx_url(GString *out, char *str, scratch_pad *scratch); 28 | void print_lyx_endnotes(GString *out, scratch_pad *scratch); 29 | void lyx_get_table_dimensions(node* list, int *rows, int *cols, scratch_pad *scratch); 30 | void add_prefixes(node *list, node *root, scratch_pad *scratch); 31 | void update_links(char *label,char *prefix, scratch_pad *scratch); 32 | char *prefix_label(char *prefix, char *label, bool pound); 33 | void update_link_source(char *source, char *prefix,node *n); 34 | void print_escaped_node_tree(GString *out, node *n); 35 | void print_escaped_node(GString *out, node *n); 36 | char * escape_string(char *str); 37 | #endif 38 | -------------------------------------------------------------------------------- /src/lyxbeamer.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | lyxbeamer.c -- Beamer add-on to LyX writer 4 | 5 | (c) 2013 Charles R. Cowan 6 | (c) 2013 Fletcher T. Penney (http://fletcherpenney.net/). 7 | 8 | Derrived from MultiMarkdown by Fletcher T. Penney - added code to support the LYY format directly 9 | 10 | This program is free software; you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License or the MIT 12 | license. See LICENSE for details. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | */ 20 | 21 | #include "lyxbeamer.h" 22 | #include "lyx.h" 23 | 24 | bool need_fragile; // if the frame needs to be fragile 25 | 26 | /* print_beamer_node_tree -- convert node tree to LyX */ 27 | void print_lyxbeamer_node_tree(GString *out, node *list, scratch_pad *scratch, bool no_newline) { 28 | while (list != NULL) { 29 | print_lyxbeamer_node(out, list, scratch, no_newline); 30 | list = list->next; 31 | } 32 | } 33 | 34 | /* print_beamer_node -- convert given node to LyX and append */ 35 | void print_lyxbeamer_node(GString *out, node *n, scratch_pad *scratch, bool no_newline) { 36 | int lev; 37 | int i; 38 | char *temp; 39 | char *prefixed_label; 40 | node *temp_node; 41 | int old_type; 42 | 43 | /* If we are forcing a complete document, and METADATA isn't the first thing, 44 | we need to close */ 45 | if ((scratch->extensions & EXT_COMPLETE) 46 | && !(scratch->extensions & EXT_HEAD_CLOSED) && 47 | !((n->key == FOOTER) || (n->key == METADATA))) { 48 | scratch->extensions = scratch->extensions | EXT_HEAD_CLOSED; 49 | } 50 | switch (n->key) { 51 | case FOOTER: 52 | if (scratch->lyx_in_frame){ // have an open frame 53 | g_string_append(out,"\n\\end_deeper"); 54 | // g_string_append(out,"\n\\end_layout"); 55 | g_string_append(out, "\n\\begin_layout Separator"); 56 | g_string_append(out, "\n\\end_layout"); 57 | } 58 | scratch->lyx_in_frame = FALSE; 59 | print_lyxbeamer_endnotes(out, scratch); 60 | break; 61 | case BULLETLIST: 62 | case ORDEREDLIST: 63 | scratch -> lyx_beamerbullet = TRUE; 64 | case DEFLIST: 65 | old_type = scratch->lyx_para_type; 66 | scratch->lyx_para_type = n->key; 67 | scratch->lyx_level++; 68 | if (scratch->lyx_level > 1){ 69 | g_string_append(out,"\n\\begin_deeper\n"); 70 | } 71 | print_lyxbeamer_node_tree(out, n->children, scratch, FALSE); 72 | scratch->lyx_level--; 73 | if (scratch->lyx_level > 0){ 74 | g_string_append(out,"\n\\end_deeper\n"); 75 | } 76 | scratch->lyx_para_type = old_type; 77 | if (scratch->lyx_definition_open){ 78 | g_string_append(out,"\n\\end_deeper\n"); 79 | scratch->lyx_definition_open = FALSE; 80 | } 81 | break; 82 | case LISTITEM: 83 | temp_node = n-> children; // should be a list node 84 | if (temp_node->key != LIST){ 85 | fprintf(stderr,"\nUnanticipated List Item Format"); 86 | exit(EXIT_FAILURE); 87 | } else { 88 | temp_node = temp_node-> children; // process the list 89 | i = 0; 90 | while (temp_node != NULL){ 91 | i++; 92 | if (i == 2){ 93 | g_string_append(out,"\n\\begin_deeper\n"); 94 | old_type = scratch->lyx_para_type; 95 | scratch->lyx_para_type = PARA; // and make it a paragraph, not a list item 96 | } 97 | print_lyx_node(out, temp_node, scratch, no_newline); 98 | temp_node = temp_node->next; 99 | 100 | } 101 | if (i>1){ 102 | scratch->lyx_para_type = old_type; // reset the paragraph type 103 | g_string_append(out,"\n\\end_deeper\n"); 104 | } 105 | } 106 | break; 107 | case HEADINGSECTION: 108 | if (scratch->lyx_in_frame){ // have an open frame 109 | g_string_append(out,"\n\\end_deeper"); 110 | // g_string_append(out,"\n\\end_layout"); 111 | g_string_append(out, "\n\\begin_layout Separator"); 112 | g_string_append(out, "\n\\end_layout"); 113 | } 114 | scratch->lyx_in_frame = FALSE; 115 | need_fragile = FALSE; 116 | if (tree_contains_key(n->children,VERBATIM)) { 117 | need_fragile = TRUE; 118 | } 119 | print_lyxbeamer_node_tree(out,n->children,scratch , FALSE); 120 | break; 121 | case H1: case H2: case H3: case H4: case H5: case H6: 122 | lev = n->key - H1 + scratch->baseheaderlevel; /* assumes H1 ... H6 are in order */ 123 | switch (lev) { 124 | case 1: 125 | g_string_append(out, "\n\\begin_layout Part\n"); 126 | break; 127 | case 2: 128 | g_string_append(out, "\n\\begin_layout Section\n"); 129 | break; 130 | case 3: 131 | if (need_fragile) { 132 | g_string_append(out, "\n\\begin_layout FragileFrame"); 133 | } else { 134 | g_string_append(out, "\n\\begin_layout Frame"); 135 | }; 136 | g_string_append(out,"\n\\begin_inset Argument 4"); 137 | g_string_append(out,"\nstatus open\n"); 138 | g_string_append(out,"\n\\begin_layout Plain Layout\n"); 139 | scratch->lyx_in_frame = TRUE; 140 | break; 141 | case 4: 142 | g_string_append(out,"\n\\begin_layout Standard"); 143 | g_string_append(out, "\n\\begin_inset Flex ArticleMode"); 144 | g_string_append(out, "\nstatus open\n\n"); 145 | g_string_append(out,"\n\\begin_layout Plain Layout\n"); 146 | break; 147 | default: 148 | g_string_append(out,"\n\\begin_layout Standard"); 149 | g_string_append(out, "\n\\emph on\n"); 150 | break; 151 | } 152 | /* Don't allow footnotes */ 153 | scratch->no_lyx_footnote = TRUE; 154 | if (n->children->key == AUTOLABEL) { 155 | /* use label for header since one was specified (MMD)*/ 156 | temp = label_from_string(n->children->str); 157 | prefixed_label = prefix_label(heading_name[lev-1]->str,temp,FALSE); 158 | print_lyx_node_tree(out, n->children->next, scratch , FALSE); 159 | g_string_append(out,"\n\\begin_inset CommandInset label\n"); 160 | g_string_append(out,"LatexCommand label\n"); 161 | g_string_append_printf(out, "name \"%s\"",prefixed_label); 162 | g_string_append(out,"\n\\end_inset\n"); 163 | free(prefixed_label); 164 | free(temp); 165 | } else { 166 | /* generate a label by default for MMD */ 167 | temp = label_from_node_tree(n->children); 168 | prefixed_label = prefix_label(heading_name[lev-1]->str,temp,FALSE); 169 | print_lyx_node_tree(out, n->children, scratch, FALSE); 170 | g_string_append(out,"\n\\begin_inset CommandInset label\n"); 171 | g_string_append(out,"LatexCommand label\n"); 172 | g_string_append_printf(out, "name \"%s\"",prefixed_label); 173 | g_string_append(out,"\n\\end_inset\n"); 174 | free(prefixed_label); 175 | free(temp); 176 | } 177 | scratch->no_lyx_footnote = FALSE; 178 | switch(lev){ 179 | case 1: case 2: 180 | g_string_append(out,"\n\\end_layout\n"); 181 | break; 182 | case 3: 183 | g_string_append(out,"\n\\end_layout\n"); 184 | g_string_append(out,"\n\\end_inset\n"); 185 | g_string_append(out,"\n\\end_layout\n"); 186 | g_string_append(out,"\n\\begin_deeper\n"); 187 | break; 188 | case 4: 189 | g_string_append(out,"\n\\end_layout"); 190 | g_string_append(out,"\n\\end_inset"); 191 | g_string_append(out,"\n\\end_layout"); 192 | break; 193 | default: 194 | g_string_append(out, "\n\\emph default\n"); 195 | g_string_append(out,"\n\\end_layout"); 196 | break; 197 | } 198 | 199 | break; 200 | default: 201 | /* most things are not changed for beamer output */ 202 | print_lyx_node(out, n, scratch,no_newline); 203 | } 204 | } 205 | 206 | /* print_beamer_endnotes */ 207 | void print_lyxbeamer_endnotes(GString *out, scratch_pad *scratch) { 208 | node *temp_node; 209 | scratch->used_notes = reverse_list(scratch->used_notes); 210 | node *note = scratch->used_notes; 211 | 212 | // Handle Glossary 213 | temp_node = note; 214 | while (temp_node != NULL){ 215 | if(temp_node->key == GLOSSARYSOURCE){ 216 | g_string_append(out, "\n\\begin_layout BeginFrame\nGlossary\n"); 217 | g_string_append(out,"\n\\begin_layout Standard"); 218 | g_string_append(out,"\n\\begin_inset CommandInset nomencl_print"); 219 | g_string_append(out,"\nLatexCommand printnomenclature"); 220 | g_string_append(out,"\nset_width \"auto\"\n"); 221 | g_string_append(out,"\n\\end_inset\n"); 222 | g_string_append(out,"\n\\end_layout\n"); 223 | g_string_append(out, "\n\\end_layout\n"); 224 | g_string_append(out, "\n\\begin_layout EndFrame"); 225 | g_string_append(out, "\n\\end_layout"); 226 | break; 227 | } 228 | temp_node = temp_node->next; 229 | } 230 | 231 | if (note == NULL) 232 | return; 233 | 234 | note = scratch->used_notes; 235 | 236 | if (tree_contains_key(note,CITATIONSOURCE)){ 237 | g_string_append(out, "\n\\begin_layout BeginFrame\nReferences\n"); 238 | g_string_append(out, "\n\\end_layout"); 239 | } 240 | while ( note != NULL) { 241 | if (note->key == KEY_COUNTER) { 242 | note = note->next; 243 | continue; 244 | } 245 | 246 | 247 | if (note->key == CITATIONSOURCE) { 248 | g_string_append(out, "\n\\begin_layout Bibliography\n"); 249 | g_string_append(out,"\\begin_inset CommandInset bibitem\n"); 250 | g_string_append(out,"LatexCommand bibitem\n"); 251 | g_string_append_printf(out, "key \"%s\"\n", note->str); 252 | g_string_append_printf(out, "label \"%s\"\n", note->str); 253 | g_string_append(out,"\n\\end_inset\n"); 254 | print_lyx_node(out, note, scratch, FALSE); 255 | g_string_append(out,"\n\\end_layout\n"); 256 | } else { 257 | /* footnotes handled elsewhere */ 258 | } 259 | 260 | note = note->next; 261 | } 262 | g_string_append(out, "\n\\begin_layout EndFrame"); // close last frame 263 | g_string_append(out, "\n\\end_layout"); 264 | 265 | } 266 | -------------------------------------------------------------------------------- /src/lyxbeamer.h: -------------------------------------------------------------------------------- 1 | #ifndef LYX_BEAMER_PARSER_H 2 | #define LYX_BEAMER_PARSER_H 3 | 4 | #include "parser.h" 5 | #include "lyx.h" 6 | 7 | void print_lyxbeamer_node_tree(GString *out, node *list, scratch_pad *scratch,bool no_newline); 8 | void print_lyxbeamer_node(GString *out, node *n, scratch_pad *scratch,bool no_newline); 9 | void print_lyxbeamer_endnotes(GString *out, scratch_pad *scratch); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/memoir.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | memoir.c -- Memoir add-on to LaTeX writer 4 | 5 | (c) 2013-2016 Fletcher T. Penney (http://fletcherpenney.net/). 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License or the MIT 9 | license. See LICENSE for details. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | */ 17 | 18 | #include "memoir.h" 19 | 20 | /* print_memoir_node_tree -- convert node tree to LaTeX */ 21 | void print_memoir_node_tree(GString *out, node *list, scratch_pad *scratch) { 22 | while (list != NULL) { 23 | print_memoir_node(out, list, scratch); 24 | list = list->next; 25 | } 26 | } 27 | 28 | /* print_memoir_node -- convert given node to LaTeX and append */ 29 | void print_memoir_node(GString *out, node *n, scratch_pad *scratch) { 30 | 31 | /* If we are forcing a complete document, and METADATA isn't the first thing, 32 | we need to close */ 33 | if ((scratch->extensions & EXT_COMPLETE) 34 | && !(scratch->extensions & EXT_HEAD_CLOSED) && 35 | !((n->key == FOOTER) || (n->key == METADATA))) { 36 | pad(out, 2, scratch); 37 | scratch->extensions = scratch->extensions | EXT_HEAD_CLOSED; 38 | } 39 | switch (n->key) { 40 | case VERBATIM: 41 | case VERBATIMFENCE: 42 | pad(out, 2, scratch); 43 | if ((n->children != NULL) && (n->children->key == VERBATIMTYPE)) { 44 | trim_trailing_whitespace(n->children->str); 45 | if (strlen(n->children->str) > 0) { 46 | g_string_append_printf(out, "\\begin{adjustwidth}{2.5em}{2.5em}\n\\begin{lstlisting}[language=%s]\n", n->children->str); 47 | print_raw_node(out, n); 48 | g_string_append_printf(out, "\n\\end{lstlisting}\n\\end{adjustwidth}"); 49 | scratch->padded = 0; 50 | break; 51 | } 52 | } 53 | g_string_append_printf(out, "\\begin{adjustwidth}{2.5em}{2.5em}\n\\begin{verbatim}\n\n"); 54 | print_raw_node(out, n); 55 | g_string_append_printf(out, "\n\\end{verbatim}\n\\end{adjustwidth}"); 56 | scratch->padded = 0; 57 | break; 58 | case HEADINGSECTION: 59 | print_memoir_node_tree(out, n->children, scratch); 60 | break; 61 | case DEFLIST: 62 | pad(out, 2, scratch); 63 | g_string_append_printf(out, "\\begin{description}"); 64 | scratch->padded = 0; 65 | print_memoir_node_tree(out, n->children, scratch); 66 | pad(out, 1, scratch); 67 | g_string_append_printf(out, "\\end{description}"); 68 | scratch->padded = 0; 69 | break; 70 | case DEFINITION: 71 | pad(out, 2, scratch); 72 | scratch->padded = 2; 73 | print_memoir_node_tree(out, n->children, scratch); 74 | scratch->padded = 0; 75 | break; 76 | default: 77 | /* most things are not changed for memoir output */ 78 | print_latex_node(out, n, scratch); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/memoir.h: -------------------------------------------------------------------------------- 1 | #ifndef MEMOIR_PARSER_H 2 | #define MEMOIR_PARSER_H 3 | 4 | #include "parser.h" 5 | #include "latex.h" 6 | 7 | void print_memoir_node_tree(GString *out, node *list, scratch_pad *scratch); 8 | void print_memoir_node(GString *out, node *n, scratch_pad *scratch); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/multimarkdown.c: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | MultiMarkdown 4 | 5 | @file multimarkdown.c 6 | 7 | @brief MultiMarkdown - lightweight markup processor 8 | 9 | This file provides the command line executable wrapper for the 10 | libMultiMarkdown library. It handles parsing arguments to determine 11 | which options and formats are needed, as well as providing file 12 | locations to support file transclusion. 13 | 14 | @author Fletcher T. Penney 15 | @bug No known bugs 16 | 17 | **/ 18 | 19 | /* 20 | 21 | Copyright © 2013-2016 Fletcher T. Penney. 22 | 23 | MultiMarkdown 4 and 5 are derived from peg-multimarkdown, which was 24 | forked from peg-markdown, which is (c) 2008 John MacFarlane 25 | (jgm at berkeley dot edu), and licensed under GNU GPL or MIT. 26 | 27 | 28 | The `c-template` project is released under the MIT License. 29 | 30 | 31 | MMD 5 is released under the MIT License. 32 | 33 | 34 | CuTest is released under the zlib/libpng license. See CuTest.c for the text 35 | of the license. 36 | 37 | 38 | ## The MIT License ## 39 | 40 | Permission is hereby granted, free of charge, to any person obtaining a copy 41 | of this software and associated documentation files (the "Software"), to deal 42 | in the Software without restriction, including without limitation the rights 43 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 44 | copies of the Software, and to permit persons to whom the Software is 45 | furnished to do so, subject to the following conditions: 46 | 47 | The above copyright notice and this permission notice shall be included in 48 | all copies or substantial portions of the Software. 49 | 50 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 51 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 52 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 53 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 54 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 55 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 56 | THE SOFTWARE. 57 | 58 | */ 59 | 60 | 61 | #include 62 | #include 63 | #include "parser.h" 64 | #include "transclude.h" 65 | #include "version.h" 66 | 67 | /// main() 68 | int main(int argc, char **argv) 69 | { 70 | int numargs; 71 | int c; 72 | int i; 73 | static int batch_flag = 0; 74 | static int complete_flag = 0; 75 | static int snippet_flag = 0; 76 | static int compatibility_flag = 0; 77 | static int notes_flag = 0; 78 | static int no_notes_flag = 0; 79 | static int typography_flag = 0; 80 | static int no_typography_flag = 0; 81 | static int label_flag = 0; 82 | static int no_label_flag = 0; 83 | static int escaped_line_breaks_flag = 0; 84 | static int obfuscate_flag = 0; 85 | static int no_obfuscate_flag = 0; 86 | static int process_html_flag = 0; 87 | static int random_footnotes_flag = 0; 88 | bool list_meta_keys = 0; 89 | bool list_transclude_manifest = 0; 90 | char *target_meta_key = FALSE; 91 | 92 | static struct option long_options[] = { 93 | {"batch", no_argument, &batch_flag, 1}, /* process each file separately */ 94 | {"to", required_argument, 0, 't'}, /* which output format to use */ 95 | {"full", no_argument, &complete_flag, 1}, /* complete document */ 96 | {"snippet", no_argument, &snippet_flag, 1}, /* snippet only */ 97 | {"output", required_argument, 0, 'o'}, /* which output format to use */ 98 | {"notes", no_argument, ¬es_flag, 1}, /* use footnotes */ 99 | {"nonotes", no_argument, &no_notes_flag, 1}, /* don't use footnotes */ 100 | {"smart", no_argument, &typography_flag, 1}, /* use smart typography */ 101 | {"nosmart", no_argument, &no_typography_flag, 1}, /* don't use smart typography */ 102 | {"mask", no_argument, &obfuscate_flag, 1}, /* mask email addresses */ 103 | {"nomask", no_argument, &no_obfuscate_flag, 1}, /* don't mask email addresses */ 104 | {"labels", no_argument, &label_flag, 1}, /* generate labels */ 105 | {"nolabels", no_argument, &no_label_flag, 1}, /* don't generate labels */ 106 | {"escaped-line-breaks", no_argument, &escaped_line_breaks_flag, 1}, /* enable escaped line breaks */ 107 | {"compatibility", no_argument, &compatibility_flag, 1}, /* compatibility mode */ 108 | {"process-html", no_argument, &process_html_flag, 1}, /* process Markdown inside HTML */ 109 | {"random", no_argument, &random_footnotes_flag, 1}, /* Use random numbers for footnote links */ 110 | {"accept", no_argument, 0, 'a'}, /* Accept all proposed CriticMarkup changes */ 111 | {"reject", no_argument, 0, 'r'}, /* Reject all proposed CriticMarkup changes */ 112 | {"metadata-keys", no_argument, 0, 'm'}, /* List all metadata keys */ 113 | {"extract", required_argument, 0, 'e'}, /* show value of specified metadata */ 114 | {"version", no_argument, 0, 'v'}, /* display version information */ 115 | {"help", no_argument, 0, 'h'}, /* display usage information */ 116 | {"manifest", no_argument, 0, 'x'}, /* List all transcluded files */ 117 | {NULL, 0, NULL, 0} 118 | }; 119 | 120 | GString *inputbuf; 121 | GString *manifest; 122 | FILE *input; 123 | FILE *output; 124 | int curchar; 125 | GString *filename = NULL; 126 | 127 | char *out; 128 | 129 | /* set up my data for the parser */ 130 | int output_format = HTML_FORMAT; /* Default output format unless specified otherwise */ 131 | unsigned long extensions = 0; 132 | extensions = extensions | EXT_SMART | EXT_NOTES | EXT_OBFUSCATE; 133 | 134 | /* process options */ 135 | while (1) { 136 | int option_index = 0; 137 | 138 | c = getopt_long (argc, argv, "vhco:bfst:me:arx", long_options, &option_index); 139 | 140 | if (c == -1) 141 | break; 142 | 143 | switch (c) { 144 | case 0: /* handle long_options */ 145 | /* printf ("option %s", long_options[option_index].name); 146 | if (optarg) 147 | printf (" with arg %s", optarg); 148 | printf("\n"); */ 149 | break; 150 | 151 | case 'b': /* batch */ 152 | batch_flag = 1; 153 | break; 154 | 155 | case 'c': /* compatibility */ 156 | compatibility_flag = 1; 157 | break; 158 | 159 | case 'o': /* output filename */ 160 | if (optarg) 161 | filename = g_string_new(optarg); 162 | break; 163 | 164 | case 'v': /* show version */ 165 | printf("\nMultiMarkdown version %s\n%s\n",MULTIMARKDOWN_VERSION, MMD_COPYRIGHT); 166 | return(EXIT_SUCCESS); 167 | 168 | case 'h': /* show usage */ 169 | printf("\nMultiMarkdown version %s\n\n",MULTIMARKDOWN_VERSION); 170 | printf(" %s [OPTION...] [FILE...]\n",argv[0]); 171 | printf("\n" 172 | " Options:\n" 173 | " -h, --help Show help\n" 174 | " -v, --version Show version information\n" 175 | " -o, --output=FILE Send output to FILE\n" 176 | " -t, --to=FORMAT Convert to FORMAT\n" 177 | " -b, --batch Process each file separately\n" 178 | " -c, --compatibility Markdown compatibility mode\n" 179 | " -f, --full Force a complete document\n" 180 | " -s, --snippet Force a snippet\n" 181 | " --process-html Process Markdown inside of raw HTML\n" 182 | " -m, --metadata-keys List all metadata keys\n" 183 | " -e, --extract Extract specified metadata\n" 184 | " -x, --manifest Show manifest of all transcluded files\n" 185 | " --random Use random numbers for footnote anchors\n" 186 | "\n" 187 | " -a, --accept Accept all CriticMarkup changes\n" 188 | " -r, --reject Reject all CriticMarkup changes\n" 189 | "\n" 190 | " --smart, --nosmart Toggle smart typography\n" 191 | " --notes, --nonotes Toggle footnotes\n" 192 | " --labels, --nolabels Disable id attributes for headers\n" 193 | " --mask, --nomask Mask email addresses in HTML\n" 194 | " --escaped-line-breaks Enable escaped line breaks\n" 195 | 196 | "\nAvailable FORMATs: html(default), latex, beamer, memoir, odf, opml, lyx, mmd\n\n" 197 | "NOTE: The lyx output format was created by Charles R. Cowan, and \n\tis provided as is.\n\n\n" 198 | ); 199 | return(EXIT_SUCCESS); 200 | 201 | case 't': /* output format */ 202 | if (strcmp(optarg, "text") == 0) 203 | output_format = TEXT_FORMAT; 204 | else if (strcmp(optarg, "html") == 0) 205 | output_format = HTML_FORMAT; 206 | else if (strcmp(optarg, "latex") == 0) 207 | output_format = LATEX_FORMAT; 208 | else if (strcmp(optarg, "memoir") == 0) 209 | output_format = MEMOIR_FORMAT; 210 | else if (strcmp(optarg, "beamer") == 0) 211 | output_format = BEAMER_FORMAT; 212 | else if (strcmp(optarg, "opml") == 0) 213 | output_format = OPML_FORMAT; 214 | else if (strcmp(optarg, "odf") == 0) 215 | output_format = ODF_FORMAT; 216 | else if (strcmp(optarg, "rtf") == 0) 217 | output_format = RTF_FORMAT; 218 | else if (strcmp(optarg, "lyx") == 0) 219 | output_format = LYX_FORMAT; 220 | else if (strcmp(optarg, "mmd") == 0) 221 | output_format = ORIGINAL_FORMAT; 222 | else { 223 | /* no valid format specified */ 224 | fprintf(stderr, "%s: Unknown output format '%s'\n",argv[0], optarg); 225 | exit(EXIT_FAILURE); 226 | } 227 | break; 228 | 229 | case 'f': /* full doc */ 230 | extensions = extensions | EXT_COMPLETE; 231 | break; 232 | 233 | case 's': /* snippet only */ 234 | extensions = extensions | EXT_SNIPPET; 235 | break; 236 | 237 | case 'm': /* list metadata */ 238 | list_meta_keys = 1; 239 | break; 240 | 241 | case 'e': /* extract metadata */ 242 | target_meta_key = strdup(optarg); 243 | break; 244 | 245 | case '?': /* long handles */ 246 | break; 247 | 248 | case 'a': /* Accept CriticMarkup changes */ 249 | extensions = extensions | EXT_CRITIC_ACCEPT; 250 | break; 251 | 252 | case 'r': /* Reject CriticMarkup changes */ 253 | extensions = extensions | EXT_CRITIC_REJECT; 254 | break; 255 | 256 | case 'x': /* List transcluded files */ 257 | list_transclude_manifest = 1; 258 | break; 259 | 260 | default: 261 | fprintf(stderr,"Error parsing options.\n"); 262 | abort(); 263 | } 264 | } 265 | 266 | /* Compatibility mode emulates the behavior of Markdown.pl */ 267 | if (compatibility_flag) { 268 | extensions = 0x000000; 269 | extensions = extensions | EXT_COMPATIBILITY | EXT_NO_LABELS | EXT_OBFUSCATE; 270 | } 271 | 272 | /* apply extensions from long options*/ 273 | if (complete_flag) 274 | extensions = extensions | EXT_COMPLETE; 275 | 276 | if (snippet_flag) 277 | extensions = extensions | EXT_SNIPPET; 278 | 279 | if (notes_flag) 280 | extensions = extensions | EXT_NOTES; 281 | 282 | if (no_notes_flag) 283 | extensions &= ~EXT_NOTES; 284 | 285 | if (typography_flag) 286 | extensions = extensions | EXT_SMART; 287 | 288 | if (no_typography_flag) 289 | extensions &= ~EXT_SMART; 290 | 291 | if (label_flag) 292 | extensions &= ~EXT_NO_LABELS; 293 | 294 | if (no_label_flag) 295 | extensions = extensions | EXT_NO_LABELS; 296 | 297 | if (obfuscate_flag) 298 | extensions = extensions | EXT_OBFUSCATE; 299 | 300 | if (no_obfuscate_flag) 301 | extensions &= ~EXT_OBFUSCATE; 302 | 303 | if (process_html_flag) 304 | extensions = extensions | EXT_PROCESS_HTML; 305 | 306 | if (random_footnotes_flag) 307 | extensions = extensions | EXT_RANDOM_FOOT; 308 | 309 | if (escaped_line_breaks_flag) 310 | extensions = extensions | EXT_ESCAPED_LINE_BREAKS; 311 | 312 | /* Enable HEADINGSECTION for certain formats */ 313 | if ((output_format == OPML_FORMAT) || (output_format == BEAMER_FORMAT) || (output_format == LYX_FORMAT)) 314 | extensions = extensions | EXT_HEADINGSECTION; 315 | 316 | /* fix numbering to account for options */ 317 | argc -= optind; 318 | argv += optind; 319 | 320 | /* We expect argc and argv to still point just one below the start of remaining args */ 321 | argc++; 322 | argv--; 323 | 324 | /* any filenames */ 325 | numargs = argc -1; 326 | 327 | if (batch_flag && (numargs != 0)) { 328 | /* we have multiple file names -- handle individually */ 329 | 330 | for (i = 0; i < numargs; i++) { 331 | inputbuf = g_string_new(""); 332 | manifest = g_string_new(""); 333 | char *temp = NULL; 334 | char *folder = NULL; 335 | 336 | /* Read file */ 337 | if ((input = fopen(argv[i+1], "r")) == NULL ) { 338 | perror(argv[i+1]); 339 | g_string_free(inputbuf, true); 340 | g_string_free(filename, true); 341 | exit(EXIT_FAILURE); 342 | } 343 | 344 | while ((curchar = fgetc(input)) != EOF) 345 | g_string_append_c(inputbuf, curchar); 346 | fclose(input); 347 | 348 | /* list metadata keys */ 349 | if (list_meta_keys) { 350 | out = extract_metadata_keys(inputbuf->str, extensions); 351 | if (out != NULL) { 352 | fprintf(stdout, "%s", out); 353 | free(out); 354 | g_string_free(inputbuf, true); 355 | free(target_meta_key); 356 | return(EXIT_SUCCESS); 357 | } 358 | } 359 | 360 | /* extract metadata */ 361 | if (target_meta_key) { 362 | out = extract_metadata_value(inputbuf->str, extensions, target_meta_key); 363 | if (out != NULL) 364 | fprintf(stdout, "%s\n", out); 365 | free(out); 366 | g_string_free(inputbuf, true); 367 | free(target_meta_key); 368 | return(EXIT_SUCCESS); 369 | } 370 | 371 | if (!(extensions & EXT_COMPATIBILITY)) { 372 | temp = strdup(argv[i+1]); 373 | folder = dirname(temp); 374 | prepend_mmd_header(inputbuf); 375 | append_mmd_footer(inputbuf); 376 | transclude_source(inputbuf, folder, NULL, output_format, manifest); 377 | free(temp); 378 | // free(folder); 379 | } 380 | 381 | /* list transclude manifest */ 382 | if (list_transclude_manifest) { 383 | fprintf(stdout, "%s\n", manifest->str); 384 | g_string_free(inputbuf, true); 385 | g_string_free(manifest, true); 386 | return(EXIT_SUCCESS); 387 | } else { 388 | g_string_free(manifest, true); 389 | } 390 | 391 | if (output_format == ORIGINAL_FORMAT) { 392 | /* We want the source, don't parse */ 393 | out = (inputbuf->str); 394 | g_string_free(inputbuf, FALSE); 395 | } else { 396 | out = markdown_to_string(inputbuf->str, extensions, output_format); 397 | g_string_free(inputbuf, true); 398 | } 399 | 400 | /* set up for output */ 401 | temp = argv[i+1]; /* get current filename */ 402 | if (strrchr(temp,'.') != NULL) { 403 | long count = strrchr(temp,'.') - temp; 404 | if (count != 0) { 405 | /* truncate string at "." */ 406 | temp[count] = '\0'; 407 | } 408 | } 409 | 410 | filename = g_string_new(temp); 411 | 412 | if (output_format == TEXT_FORMAT) { 413 | g_string_append(filename,".txt"); 414 | } else if (output_format == HTML_FORMAT) { 415 | g_string_append(filename,".html"); 416 | } else if (output_format == LATEX_FORMAT) { 417 | g_string_append(filename,".tex"); 418 | } else if (output_format == BEAMER_FORMAT) { 419 | g_string_append(filename,".tex"); 420 | } else if (output_format == MEMOIR_FORMAT) { 421 | g_string_append(filename,".tex"); 422 | } else if (output_format == ODF_FORMAT) { 423 | g_string_append(filename,".fodt"); 424 | } else if (output_format == OPML_FORMAT) { 425 | g_string_append(filename,".opml"); 426 | } else if (output_format == LYX_FORMAT) { 427 | g_string_append(filename,".lyx"); 428 | } else if (output_format == RTF_FORMAT) { 429 | g_string_append(filename,".rtf"); 430 | } else if (output_format == ORIGINAL_FORMAT) { 431 | g_string_append(filename,".mmd_out"); 432 | } else { 433 | /* default extension -- in this case we only have 1 */ 434 | g_string_append(filename,".txt"); 435 | } 436 | 437 | if (!(output = fopen(filename->str, "w"))) { 438 | perror(filename->str); 439 | } else { 440 | fprintf(output, "%s\n",out); 441 | fclose(output); 442 | } 443 | 444 | g_string_free(filename,true); 445 | 446 | if (out != NULL) 447 | free(out); 448 | } 449 | } else { 450 | /* get input from stdin or concat all files */ 451 | inputbuf = g_string_new(""); 452 | char *folder = NULL; 453 | char *temp = NULL; 454 | GString *manifest = g_string_new(""); 455 | 456 | folder = getcwd(0,0); 457 | 458 | if (numargs == 0) { 459 | /* get stdin */ 460 | while ((curchar = fgetc(stdin)) != EOF) 461 | g_string_append_c(inputbuf, curchar); 462 | fclose(stdin); 463 | } else { 464 | /* get files */ 465 | free(folder); 466 | temp = strdup(argv[1]); 467 | folder = dirname(temp); 468 | 469 | for (i = 0; i < numargs; i++) { 470 | if ((input = fopen(argv[i+1], "r")) == NULL ) { 471 | perror(argv[i+1]); 472 | g_string_free(inputbuf, true); 473 | g_string_free(filename, true); 474 | // free(folder); 475 | free(temp); 476 | exit(EXIT_FAILURE); 477 | } 478 | 479 | while ((curchar = fgetc(input)) != EOF) 480 | g_string_append_c(inputbuf, curchar); 481 | fclose(input); 482 | } 483 | } 484 | 485 | if (!(extensions & EXT_COMPATIBILITY)) { 486 | prepend_mmd_header(inputbuf); 487 | append_mmd_footer(inputbuf); 488 | transclude_source(inputbuf, folder, NULL, output_format, manifest); 489 | } 490 | 491 | free(temp); 492 | 493 | //if (folder != NULL) 494 | // free(folder); 495 | 496 | /* list metadata keys */ 497 | if (list_meta_keys) { 498 | out = extract_metadata_keys(inputbuf->str, extensions); 499 | if (out != NULL) { 500 | fprintf(stdout, "%s", out); 501 | free(out); 502 | g_string_free(inputbuf, true); 503 | free(target_meta_key); 504 | return(EXIT_SUCCESS); 505 | } 506 | } 507 | 508 | /* extract metadata */ 509 | if (target_meta_key) { 510 | out = extract_metadata_value(inputbuf->str, extensions, target_meta_key); 511 | if (out != NULL) 512 | fprintf(stdout, "%s\n", out); 513 | free(out); 514 | g_string_free(inputbuf, true); 515 | free(target_meta_key); 516 | return(EXIT_SUCCESS); 517 | } 518 | 519 | /* list transclude manifest */ 520 | if (list_transclude_manifest) { 521 | fprintf(stdout, "%s\n", manifest->str); 522 | g_string_free(inputbuf, true); 523 | g_string_free(manifest, true); 524 | return(EXIT_SUCCESS); 525 | } else { 526 | g_string_free(manifest, true); 527 | } 528 | 529 | if (output_format == ORIGINAL_FORMAT) { 530 | /* We want the source, don't parse */ 531 | out = (inputbuf->str); 532 | g_string_free(inputbuf, FALSE); 533 | } else { 534 | out = markdown_to_string(inputbuf->str, extensions, output_format); 535 | g_string_free(inputbuf, true); 536 | } 537 | 538 | /* did we specify an output filename; "-" equals stdout */ 539 | if ((filename == NULL) || (strcmp(filename->str, "-") == 0)) { 540 | output = stdout; 541 | } else if (!(output = fopen(filename->str, "w"))) { 542 | perror(filename->str); 543 | if (out != NULL) 544 | free(out); 545 | g_string_free(filename, true); 546 | return 1; 547 | } 548 | 549 | fprintf(output, "%s\n",out); 550 | fclose(output); 551 | 552 | g_string_free(filename, true); 553 | 554 | if (out != NULL) 555 | free(out); 556 | } 557 | 558 | return(EXIT_SUCCESS); 559 | } 560 | -------------------------------------------------------------------------------- /src/odf.h: -------------------------------------------------------------------------------- 1 | #ifndef ODF_PARSER_H 2 | #define ODF_PARSER_H 3 | 4 | #include "parser.h" 5 | #include "html.h" /* Use some of the HTML routines */ 6 | #include "writer.h" 7 | 8 | void begin_odf_output(GString *out, node* list, scratch_pad *scratch); 9 | void print_odf_node_tree(GString *out, node *list, scratch_pad *scratch); 10 | void print_odf_node(GString *out, node *n, scratch_pad *scratch); 11 | void print_odf_section_and_children(GString *out, node *list, scratch_pad *scratch); 12 | void end_odf_output(GString *out, node* list, scratch_pad *scratch); 13 | void print_odf_string(GString *out, char *str); 14 | void print_odf_code_string(GString *out, char *str); 15 | void print_odf_header(GString *out); 16 | void print_odf_footer(GString *out); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/opml.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | opml.c -- OPML add-on to LaTeX writer 4 | 5 | (c) 2013-2016 Fletcher T. Penney (http://fletcherpenney.net/). 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License or the MIT 9 | license. See LICENSE for details. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | */ 17 | 18 | #include "opml.h" 19 | 20 | /* begin_opml_output -- handle the initial prefix, if any */ 21 | void begin_opml_output(GString *out, node* list, scratch_pad *scratch) { 22 | #ifdef DEBUG_ON 23 | fprintf(stderr, "begin_opml_output\n"); 24 | #endif 25 | node *title; 26 | 27 | g_string_append_printf(out, "\n\n"); 28 | 29 | if (tree_contains_key(list, METAKEY)) { 30 | title = metadata_for_key("title", list); 31 | if (title != NULL) { 32 | char *temp_str; 33 | GString *temp = g_string_new(""); 34 | g_string_append_printf(out, ""); 35 | print_raw_node_tree(temp, title->children); 36 | temp_str = strdup(temp->str); 37 | trim_trailing_whitespace(temp_str); 38 | print_opml_string(out, temp_str); 39 | g_string_append_printf(out, "\n",temp_str); 40 | free(temp_str); 41 | g_string_free(temp, true); 42 | } 43 | } 44 | g_string_append_printf(out, "\n"); 45 | } 46 | 47 | /* end_opml_output -- close the document */ 48 | void end_opml_output(GString *out, node* list, scratch_pad *scratch) { 49 | #ifdef DEBUG_ON 50 | fprintf(stderr, "end_opml_output\n"); 51 | #endif 52 | if (tree_contains_key(list, METAKEY)) { 53 | g_string_append_printf(out, "\n"); 54 | print_opml_node_tree(out, list->children, scratch); 55 | g_string_append_printf(out, ""); 56 | } 57 | g_string_append_printf(out, "\n"); 58 | } 59 | 60 | /* print_opml_node_tree -- convert node tree to LaTeX */ 61 | void print_opml_node_tree(GString *out, node *list, scratch_pad *scratch) { 62 | #ifdef DEBUG_ON 63 | fprintf(stderr, "print_opml_node_tree\n"); 64 | #endif 65 | int lev; 66 | while (list != NULL) { 67 | if (list->key == HEADINGSECTION) { 68 | lev = list->children->key; 69 | 70 | print_opml_section_and_children(out, list, scratch); 71 | 72 | while ((list->next != NULL) && (list->next->key == HEADINGSECTION) 73 | && (list->next->children->key > lev)) { 74 | list = list->next; 75 | } 76 | } else { 77 | print_opml_node(out, list, scratch); 78 | } 79 | list = list->next; 80 | } 81 | } 82 | 83 | /* print_opml_section_and_children -- we want to stay inside the outline structure */ 84 | void print_opml_section_and_children(GString *out, node *list, scratch_pad *scratch) { 85 | #ifdef DEBUG_ON 86 | fprintf(stderr, "print_opml_section_and_children: %d\n",list->key); 87 | #endif 88 | int lev = list->children->key; 89 | 90 | /* print current section (parent) */ 91 | print_opml_node(out, list, scratch); 92 | 93 | /* check for child nodes */ 94 | while ((list->next != NULL) && (list->next->key == HEADINGSECTION) && (list->next->children->key > lev)) { 95 | /* next item is also a HEADINGSECTION and is a child */ 96 | if (list->next->children->key - lev == 1) 97 | print_opml_section_and_children(out, list->next, scratch); 98 | list = list->next; 99 | } 100 | g_string_append_printf(out, "\n"); 101 | } 102 | 103 | /* print_opml_node -- convert given node to OPML and append */ 104 | void print_opml_node(GString *out, node *n, scratch_pad *scratch) { 105 | #ifdef DEBUG_ON 106 | fprintf(stderr, "print_opml_node: %d\n",n->key); 107 | #endif 108 | switch (n->key) { 109 | case METADATA: 110 | /* Metadata is present, so will need to be appended later */ 111 | break; 112 | case METAKEY: 113 | g_string_append_printf(out, "str); 115 | g_string_append_printf(out, "\" _note=\""); 116 | trim_trailing_newlines(n->children->str); 117 | print_opml_string(out, n->children->str); 118 | g_string_append_printf(out, "\"/>"); 119 | break; 120 | case HEADINGSECTION: 121 | /* Need to handle "nesting" properly */ 122 | g_string_append_printf(out, "children, scratch); 126 | 127 | /* print remainder of paragraphs as note */ 128 | g_string_append_printf(out, " _note=\""); 129 | print_opml_node_tree(out, n->children->next, scratch); 130 | g_string_append_printf(out, "\">"); 131 | break; 132 | case H1: case H2: case H3: case H4: case H5: case H6: 133 | g_string_append_printf(out, "text=\""); 134 | print_opml_string(out, n->str); 135 | g_string_append_printf(out,"\""); 136 | break; 137 | case VERBATIM: 138 | case VERBATIMFENCE: 139 | print_opml_string(out, n->str); 140 | break; 141 | case SPACE: 142 | print_opml_string(out, n->str); 143 | break; 144 | case STR: 145 | print_opml_string(out, n->str); 146 | break; 147 | case LINEBREAK: 148 | g_string_append_printf(out, " "); 149 | break; 150 | case PLAIN: 151 | print_opml_node_tree(out, n->children, scratch); 152 | if ((n->next != NULL) && (n->next->key == PLAIN)) { 153 | g_string_append_printf(out, " "); 154 | } 155 | break; 156 | default: 157 | fprintf(stderr, "print_opml_node encountered unknown element key = %d\n", n->key); 158 | exit(EXIT_FAILURE); 159 | } 160 | #ifdef DEBUG_ON 161 | fprintf(stderr, "finish print_opml_node: %d\n", n->key); 162 | #endif 163 | } 164 | 165 | /* print_opml_string - print string, escaping for OPML */ 166 | void print_opml_string(GString *out, char *str) { 167 | while (*str != '\0') { 168 | switch (*str) { 169 | case '&': 170 | g_string_append_printf(out, "&"); 171 | break; 172 | case '<': 173 | g_string_append_printf(out, "<"); 174 | break; 175 | case '>': 176 | g_string_append_printf(out, ">"); 177 | break; 178 | case '"': 179 | g_string_append_printf(out, """); 180 | break; 181 | case '\n': case '\r': 182 | g_string_append_printf(out, " "); 183 | break; 184 | default: 185 | g_string_append_c(out, *str); 186 | } 187 | str++; 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/opml.h: -------------------------------------------------------------------------------- 1 | #ifndef OPML_PARSER_H 2 | #define OPML_PARSER_H 3 | 4 | #include "parser.h" 5 | #include "writer.h" 6 | 7 | void begin_opml_output(GString *out, node* list, scratch_pad *scratch); 8 | void print_opml_node_tree(GString *out, node *list, scratch_pad *scratch); 9 | void print_opml_node(GString *out, node *n, scratch_pad *scratch); 10 | void print_opml_section_and_children(GString *out, node *list, scratch_pad *scratch); 11 | void end_opml_output(GString *out, node* list, scratch_pad *scratch); 12 | void print_opml_string(GString *out, char *str); 13 | 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/parser.h: -------------------------------------------------------------------------------- 1 | /* ensure we only load this once */ 2 | 3 | #ifndef PARSER_LIB_H 4 | #define PARSER_LIB_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "glib.h" 13 | #include "libMultiMarkdown.h" 14 | 15 | #define TABSTOP 4 16 | 17 | #define MMD_COPYRIGHT \ 18 | "Copyright (c) 2013-2016 Fletcher T. Penney.\n\n" \ 19 | "LyX export code (c) 2013-2014 Charles R. Cowan,\n" \ 20 | "licensed under the MIT licenses.\n\n" \ 21 | "portions based on peg-markdown - Copyright (c) 2008-2009 John MacFarlane.\n" \ 22 | "peg-markdown is Licensed under either the GPLv2+ or MIT.\n" \ 23 | "portions Copyright (c) 2011 Daniel Jalkut, MIT licensed.\n\n" \ 24 | "This is free software: you are free to change and redistribute it.\n" \ 25 | "There is NO WARRANTY, to the extent permitted by law.\n\n" 26 | 27 | 28 | #define DEBUG_OFF /* Turn on debugging statements (there's a bunch!)*/ 29 | 30 | 31 | /* This is the type used for the $$ pseudovariable passed to parents */ 32 | #define YYSTYPE node * 33 | 34 | /* This is the data we store in the parser context */ 35 | typedef struct { 36 | const char *charbuf; /* Input buffer */ 37 | const char *original; /* Original input buffer */ 38 | node *result; /* Resulting parse tree */ 39 | unsigned long extensions; /* Extension bitfield */ 40 | node *autolabels; /* Store for later retrieval */ 41 | bool parse_aborted; /* We got bogged down - fail parse */ 42 | clock_t stop_time; /* Note the deadline to complete parsing */ 43 | } parser_data; 44 | 45 | /* A "scratch pad" for storing data when writing output 46 | The structure will vary based on what you need */ 47 | typedef struct { 48 | unsigned long extensions; /* Store copy of extensions for retrieval */ 49 | int padded; /* Track newlines */ 50 | int baseheaderlevel; /* Increase header levels when outputting */ 51 | int language; /* For smart quotes */ 52 | char *table_alignment; /* Hold the alignment string while parsing table */ 53 | int table_column; /* Track the current column number */ 54 | bool inside_footnote; /* Are we inside a footnote? */ 55 | char cell_type; /* What sort of cell type are we in? */ 56 | bool header_column; /* Should the first column be treated like a header? */ 57 | bool printing_notes; /* Are we printing notes/glossary/etc.? */ 58 | node *notes; /* Store reference notes */ 59 | node *links; /* ... links */ 60 | node *glossary; /* ... glossary */ 61 | node *citations; /* ... citations */ 62 | node *abbreviations; /* ... abbreviations */ 63 | node *used_notes; /* notes that have been referenced */ 64 | node *result_tree; /* reference to entire result tree */ 65 | int footnote_to_print; /* set while we are printing so we can reverse link */ 66 | int footnote_para_counter; /* so we know which para is last */ 67 | int max_footnote_num; /* so we know if current note is new or repeat */ 68 | char *html_footer; /* store for appending at the end */ 69 | bool obfuscate; /* flag that we need to mask email addresses */ 70 | char *latex_footer; /* store for appending at the end */ 71 | bool no_latex_footnote; /* can't use footnotes in some places */ 72 | int odf_para_type; /* what type of paragraph do we need? */ 73 | bool odf_list_needs_end_p; /* is there a

that need to be closed */ 74 | int random_seed_base; /* Allow random footnotes */ 75 | int toc_level; /* Track depth for TOC */ 76 | int table_row; /* CRC - Track the current row number */ 77 | int lyx_para_type; /* CRC - the type of paragraph being processed */ 78 | int lyx_level; /* CRC - nesting level */ 79 | bool no_lyx_footnote; /* CRC - Can't use footnotes in some places */ 80 | bool lyx_number_headers; /* CRC - Whether to number headers (with or without *) */ 81 | bool lyx_definition_hit; /* CRC - True when a definition has been encountered */ 82 | bool lyx_definition_open; /* CRC - Have not completed a definition list entry */ 83 | bool lyx_in_header; /* CRC - In a table header */ 84 | bool lyx_in_frame; /* CRC - in a beamer frame */ 85 | bool lyx_beamerbullet; /* CRC - beamer bullet list (add <+->) */ 86 | int lyx_debug_nest; /* CRC - nesting level for enhanced debugging */ 87 | bool lyx_table_need_line; /* CRC - need a line at the top */ 88 | int lyx_table_total_rows; /* CRC - The total number of rows in the table */ 89 | int lyx_table_total_cols; /* CRC - The total number of columns in the table */ 90 | node *lyx_table_caption; /* CRC - Hold the table caption */ 91 | GString *lyx_debug_pad; /* CRC - padding to indent debugging informaiton */ 92 | } scratch_pad; 93 | 94 | /* Define smart typography languages -- first in list is default */ 95 | enum language { 96 | ENGLISH, 97 | DUTCH, 98 | FRENCH, 99 | GERMAN, 100 | GERMANGUILL, 101 | SWEDISH, 102 | }; 103 | 104 | /* Character types for smart typography */ 105 | enum smartelements { 106 | LSQUOTE, 107 | RSQUOTE, 108 | LDQUOTE, 109 | RDQUOTE, 110 | NDASH, 111 | MDASH, 112 | ELLIP, 113 | APOS, 114 | }; 115 | 116 | 117 | 118 | /* parser utilities declarations */ 119 | node * mk_node(int key); 120 | node * mk_str(char *string); 121 | node * mk_list(int key, node *list); 122 | node * mk_link(node *text, char *label, char *source, char *title, node *attr); 123 | 124 | void free_node(node *n); 125 | void free_node_tree(node * n); 126 | void print_node_tree(node * n); 127 | node * copy_node(node *n); 128 | node * copy_node_tree(node *n); 129 | 130 | node * cons(node *new, node *list); 131 | node * reverse_list(node *list); 132 | void append_list(node *new, node *list); 133 | 134 | node * mk_str_from_list(node *list, bool extra_newline); 135 | GString * concat_string_list(node *list); 136 | 137 | parser_data * mk_parser_data(const char *charbuf, unsigned long extensions); 138 | void free_parser_data(parser_data *data); 139 | void free_parser_data_preserving_result(parser_data *data); 140 | 141 | char * preformat_text(const char *text); 142 | 143 | scratch_pad * mk_scratch_pad(unsigned long extensions); 144 | void free_scratch_pad(scratch_pad *scratch); 145 | 146 | link_data * mk_link_data(char *label, char *source, char *title, node *attr); 147 | void free_link_data(link_data *l); 148 | link_data * extract_link_data(char *label, scratch_pad *scratch); 149 | node * mk_autolink(char *text); 150 | 151 | void extract_references(node *list, scratch_pad *scratch); 152 | void extract_abbreviations(node *list, scratch_pad *scratch); 153 | 154 | bool extension(int ext, unsigned long extensions); 155 | 156 | /* export utilities */ 157 | void trim_trailing_whitespace(char *str); 158 | void trim_trailing_newlines(char *str); 159 | 160 | /* other utilities */ 161 | char * lower_string(char *str); 162 | char * label_from_string(char *str); 163 | char * ascii_label_from_string(char *str); 164 | char * clean_string(char *str); 165 | char * string_from_node_tree(node *n); 166 | char * label_from_node_tree(node *n); 167 | char * label_from_node(node *n); 168 | char * ascii_label_from_node_tree(node *n); 169 | char * ascii_label_from_node(node *n); 170 | void print_raw_node(GString *out, node *n); 171 | void print_raw_node_tree(GString *out, node*n); 172 | 173 | char * correct_dimension_units(char *original); 174 | char * metadata_keys(node *list); 175 | node * metadata_for_key(char *key, node *list); 176 | char * metavalue_for_key(char *key, node *list); 177 | 178 | bool tree_contains_key(node *list, int key); 179 | int tree_contains_key_count(node *list, int key); 180 | 181 | node * markdown_chunk_to_node(const char * source, unsigned long extensions); 182 | 183 | bool check_timeout(); 184 | 185 | void debug_node(node *n); 186 | void debug_node_tree(node *n); 187 | 188 | char * my_strndup(const char * source, size_t n); 189 | 190 | #endif 191 | -------------------------------------------------------------------------------- /src/rng.c: -------------------------------------------------------------------------------- 1 | /* This program by D E Knuth is in the public domain and freely copyable 2 | * AS LONG AS YOU MAKE ABSOLUTELY NO CHANGES! 3 | * It is explained in Seminumerical Algorithms, 3rd edition, Section 3.6 4 | * (or in the errata to the 2nd edition --- see 5 | * http://www-cs-faculty.stanford.edu/~knuth/taocp.html 6 | * in the changes to Volume 2 on pages 171 and following). */ 7 | 8 | /* N.B. The MODIFICATIONS introduced in the 9th printing (2002) are 9 | included here; there's no backwards compatibility with the original. */ 10 | 11 | /* This version also adopts Brendan McKay's suggestion to 12 | accommodate naive users who forget to call ran_start(seed). */ 13 | 14 | /* If you find any bugs, please report them immediately to 15 | * taocp@cs.stanford.edu 16 | * (and you will be rewarded if the bug is genuine). Thanks! */ 17 | 18 | /************ see the book for explanations and caveats! *******************/ 19 | /************ in particular, you need two's complement arithmetic **********/ 20 | 21 | #define KK 100 /* the long lag */ 22 | #define LL 37 /* the short lag */ 23 | #define MM (1L<<30) /* the modulus */ 24 | #define mod_diff(x,y) (((x)-(y))&(MM-1)) /* subtraction mod MM */ 25 | 26 | long ran_x[KK]; /* the generator state */ 27 | 28 | #ifdef __STDC__ 29 | void ran_array(long aa[],int n) 30 | #else 31 | void ran_array(aa,n) /* put n new random numbers in aa */ 32 | long *aa; /* destination */ 33 | int n; /* array length (must be at least KK) */ 34 | #endif 35 | { 36 | register int i,j; 37 | for (j=0;j=MM) ss-=MM-2; /* cyclic shift 29 bits */ 67 | } 68 | x[1]++; /* make x[1] (and only x[1]) odd */ 69 | for (ss=seed&(MM-1),t=TT-1; t; ) { 70 | for (j=KK-1;j>0;j--) x[j+j]=x[j], x[j+j-1]=0; /* "square" */ 71 | for (j=KK+KK-2;j>=KK;j--) 72 | x[j-(KK-LL)]=mod_diff(x[j-(KK-LL)],x[j]), 73 | x[j-KK]=mod_diff(x[j-KK],x[j]); 74 | if (is_odd(ss)) { /* "multiply by z" */ 75 | for (j=KK;j>0;j--) x[j]=x[j-1]; 76 | x[0]=x[KK]; /* shift the buffer cyclically */ 77 | x[LL]=mod_diff(x[LL],x[KK]); 78 | } 79 | if (ss) ss>>=1; else t--; 80 | } 81 | for (j=0;j=0? *ran_arr_ptr++: ran_arr_cycle()) 88 | long ran_arr_cycle() 89 | { 90 | if (ran_arr_ptr==&ran_arr_dummy) 91 | ran_start(314159L); /* the user forgot to initialize */ 92 | ran_array(ran_arr_buf,QUALITY); 93 | ran_arr_buf[KK]=-1; 94 | ran_arr_ptr=ran_arr_buf+1; 95 | return ran_arr_buf[0]; 96 | } 97 | 98 | /* Tweaked to include as a library - Fletcher T. Penney */ 99 | /*#include 100 | int main() 101 | { 102 | register int m; long a[2009]; 103 | ran_start(310952L); 104 | for (m=0;m<=2009;m++) ran_array(a,1009); 105 | printf("%ld\n", a[0]); *//* 995235265 */ 106 | /* ran_start(310952L); 107 | for (m=0;m<=1009;m++) ran_array(a,2009); 108 | printf("%ld\n", a[0]); *//* 995235265 */ 109 | /* printf("%ld\n",ran_arr_next()); 110 | return 0; 111 | } */ 112 | 113 | long ran_num_next() 114 | { 115 | return ran_arr_next(); 116 | } 117 | 118 | -------------------------------------------------------------------------------- /src/rtf.h: -------------------------------------------------------------------------------- 1 | #ifndef RTF_PARSER_H 2 | #define RTF_PARSER_H 3 | 4 | #include "parser.h" 5 | #include "writer.h" 6 | 7 | void begin_rtf_output(GString *out, node* list, scratch_pad *scratch); 8 | void end_rtf_output(GString *out, node* list, scratch_pad *scratch); 9 | void print_rtf_node_tree(GString *out, node *list, scratch_pad *scratch); 10 | void print_rtf_node(GString *out, node *n, scratch_pad *scratch); 11 | void print_rtf_localized_typography(GString *out, int character, scratch_pad *scratch); 12 | void print_rtf_string(GString *out, char *str, scratch_pad *scratch); 13 | void print_rtf_code_string(GString *out, char *str, scratch_pad *scratch); 14 | void print_rtf_endnotes(GString *out, scratch_pad *scratch); 15 | void pad_rtf(GString *out, int pad, scratch_pad *scratch); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/strtok.c: -------------------------------------------------------------------------------- 1 | /* 2 | * public domain strtok_r() by Charlie Gordon 3 | * 4 | * from comp.lang.c 9/14/2007 5 | * 6 | * http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684 7 | * 8 | * (Declaration that it's public domain): 9 | * http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c 10 | */ 11 | 12 | /* This file is only included since MINGW doesn't have strtok_r, so I can't 13 | compile for Windows without this */ 14 | 15 | /* Also, fixed by Fletcher T. Penney --- added the "return NULL" when *nextp == NULL */ 16 | 17 | /* This fix is also in the public domain */ 18 | 19 | #include "strtok.h" 20 | 21 | char* strtok_r( 22 | char *str, 23 | const char *delim, 24 | char **nextp) 25 | { 26 | char *ret; 27 | 28 | if (str == NULL) 29 | { 30 | str = *nextp; 31 | } 32 | 33 | if (str == NULL) { 34 | return NULL; 35 | } 36 | 37 | str += strspn(str, delim); 38 | 39 | if (*str == '\0') 40 | { 41 | return NULL; 42 | } 43 | 44 | ret = str; 45 | 46 | str += strcspn(str, delim); 47 | 48 | if (*str) 49 | { 50 | *str++ = '\0'; 51 | } 52 | 53 | *nextp = str; 54 | 55 | return ret; 56 | } 57 | -------------------------------------------------------------------------------- /src/strtok.h: -------------------------------------------------------------------------------- 1 | /* This file is only included since MINGW doesn't have strtok_r, so I can't 2 | compile for Windows without this */ 3 | 4 | #include 5 | 6 | char* strtok_r( 7 | char *str, 8 | const char *delim, 9 | char **nextp); 10 | -------------------------------------------------------------------------------- /src/text.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | test.c -- plain text writer function as an example. 4 | Recreates the input source. 5 | 6 | (c) 2013-2016 Fletcher T. Penney (http://fletcherpenney.net/). 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License or the MIT 10 | license. See LICENSE for details. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | 18 | WARNING -- Not complete and not intended for use 19 | 20 | */ 21 | 22 | #include "text.h" 23 | 24 | 25 | /* print_text_node_tree -- convert node tree to plain text */ 26 | void print_text_node_tree(GString *out, node *list, scratch_pad *scratch) { 27 | while (list != NULL) { 28 | print_text_node(out, list, scratch); 29 | list = list->next; 30 | } 31 | } 32 | 33 | /* print_text_node -- convert given node to plain text and append */ 34 | void print_text_node(GString *out, node *n, scratch_pad *scratch) { 35 | switch (n->key) { 36 | case STR: 37 | g_string_append_printf(out,"%s",n->str); 38 | break; 39 | case METADATA: 40 | print_text_node_tree(out,n->children,scratch); 41 | break; 42 | case METAKEY: 43 | g_string_append_printf(out,"%s:\t",n->str); 44 | print_text_node(out,n->children,scratch); 45 | break; 46 | case METAVALUE: 47 | g_string_append_printf(out,"%s",n->str); 48 | pad(out,1, scratch); 49 | break; 50 | case FOOTER: 51 | break; 52 | default: 53 | fprintf(stderr, "print_text_node encountered unknown node key = %d\n",n->key); 54 | exit(EXIT_FAILURE); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/text.h: -------------------------------------------------------------------------------- 1 | #ifndef TEXT_PARSER_H 2 | #define TEXT_PARSER_H 3 | 4 | #include "parser.h" 5 | #include "writer.h" 6 | 7 | void print_text_node_tree(GString *out, node *list, scratch_pad *scratch); 8 | void print_text_node(GString *out, node *n, scratch_pad *scratch); 9 | 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/toc.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | toc.c -- Table of contents 4 | 5 | (c) 2013-2016 Fletcher T. Penney (http://fletcherpenney.net/). 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License or the MIT 9 | license. See LICENSE for details. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | */ 17 | 18 | #include "toc.h" 19 | 20 | 21 | /* print_toc_node_tree -- convert node tree to MultiMarkdown */ 22 | void print_toc_node_tree(GString *out, node *list, scratch_pad *scratch) { 23 | #ifdef DEBUG_ON 24 | fprintf(stderr, "print_toc_node_tree\n"); 25 | #endif 26 | int lev; 27 | while (list != NULL) { 28 | if (list->key == HEADINGSECTION) { 29 | lev = list->children->key; 30 | 31 | print_toc_section_and_children(out, list, scratch); 32 | 33 | while ((list->next != NULL) && (list->next->key == HEADINGSECTION) 34 | && (list->next->children->key > lev)) { 35 | list = list->next; 36 | } 37 | } else { 38 | print_toc_node(out, list, scratch); 39 | } 40 | list = list->next; 41 | } 42 | } 43 | 44 | /* print_toc_section_and_children -- we want to stay inside the outline structure */ 45 | void print_toc_section_and_children(GString *out, node *list, scratch_pad *scratch) { 46 | #ifdef DEBUG_ON 47 | fprintf(stderr, "print_toc_section_and_children: %d\n",list->key); 48 | #endif 49 | int lev = list->children->key; 50 | 51 | /* print current section (parent) */ 52 | print_toc_node(out, list, scratch); 53 | 54 | scratch->toc_level ++; 55 | 56 | /* check for child nodes */ 57 | while ((list->next != NULL) && (list->next->key == HEADINGSECTION) && (list->next->children->key > lev)) { 58 | /* next item is also a HEADINGSECTION and is a child */ 59 | if (list->next->children->key - lev == 1) 60 | print_toc_section_and_children(out, list->next, scratch); 61 | list = list->next; 62 | } 63 | 64 | scratch->toc_level --; 65 | } 66 | 67 | /* print_toc_node -- convert given node to MultiMarkdown and append */ 68 | void print_toc_node(GString *out, node *n, scratch_pad *scratch) { 69 | char *temp; 70 | int i; 71 | 72 | #ifdef DEBUG_ON 73 | fprintf(stderr, "print_toc_node: %d\n",n->key); 74 | #endif 75 | switch (n->key) { 76 | case HEADINGSECTION: 77 | /* Need to handle "nesting" properly */ 78 | for (i = 0; i < scratch->toc_level; ++i) 79 | { 80 | g_string_append_printf(out, "\t"); 81 | } 82 | g_string_append_printf(out, "* "); 83 | 84 | /* Print header */ 85 | print_toc_node(out, n->children, scratch); 86 | 87 | break; 88 | case H1: case H2: case H3: case H4: case H5: case H6: 89 | if ((n->children != NULL) && (n->children->key == AUTOLABEL)) { 90 | temp = label_from_string(n->children->str); 91 | /* use label for header since one was specified (MMD)*/ 92 | g_string_append_printf(out, "["); 93 | print_toc_node_tree(out, n->children, scratch); 94 | g_string_append_printf(out, "][%s]\n", temp); 95 | } else { 96 | temp = label_from_node_tree(n->children); 97 | g_string_append_printf(out, "["); 98 | print_toc_node_tree(out, n->children, scratch); 99 | g_string_append_printf(out, "][%s]\n", temp); 100 | } 101 | free(temp); 102 | break; 103 | case STR: 104 | print_toc_string(out, n->str); 105 | break; 106 | case EMPH: 107 | g_string_append_printf(out, "*"); 108 | print_toc_node_tree(out, n->children, scratch); 109 | g_string_append_printf(out, "*"); 110 | break; 111 | case STRONG: 112 | g_string_append_printf(out, "**"); 113 | print_toc_node_tree(out, n->children, scratch); 114 | g_string_append_printf(out, "**"); 115 | break; 116 | case SPACE: 117 | g_string_append_printf(out, "%s", n->str); 118 | break; 119 | case LINK: 120 | print_toc_node_tree(out, n->children, scratch); 121 | break; 122 | case HTML: 123 | break; 124 | case LINKREFERENCE: 125 | break; 126 | case AUTOLABEL: 127 | break; 128 | case VARIABLE: 129 | g_string_append_printf(out, "[%%%s]",n->str); 130 | break; 131 | case LIST: 132 | print_toc_node_tree(out, n->children, scratch); 133 | break; 134 | default: fprintf(stderr, "print_toc_node encountered unknown node key = %d\n",n->key); 135 | break; 136 | } 137 | #ifdef DEBUG_ON 138 | fprintf(stderr, "finish print_toc_node: %d\n", n->key); 139 | #endif 140 | } 141 | 142 | /* print_toc_string - print string, escaping for MultiMarkdown */ 143 | void print_toc_string(GString *out, char *str) { 144 | while (*str != '\0') { 145 | switch (*str) { 146 | case '[': 147 | g_string_append_printf(out, "\\["); 148 | break; 149 | case ']': 150 | g_string_append_printf(out, "\\]"); 151 | break; 152 | default: 153 | g_string_append_c(out, *str); 154 | } 155 | str++; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/toc.h: -------------------------------------------------------------------------------- 1 | #ifndef TOC_PARSER_H 2 | #define TOC_PARSER_H 3 | 4 | #include "parser.h" 5 | #include "writer.h" 6 | 7 | void begin_toc_output(GString *out, node* list, scratch_pad *scratch); 8 | void print_toc_node_tree(GString *out, node *list, scratch_pad *scratch); 9 | void print_toc_node(GString *out, node *n, scratch_pad *scratch); 10 | void print_toc_section_and_children(GString *out, node *list, scratch_pad *scratch); 11 | void end_toc_output(GString *out, node* list, scratch_pad *scratch); 12 | void print_toc_string(GString *out, char *str); 13 | 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/transclude.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | transclude.c -- miscellaneous support functions 4 | 5 | (c) 2013 Fletcher T. Penney (http://fletcherpenney.net/). 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License or the MIT 9 | license. See LICENSE for details. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | */ 17 | 18 | #include "transclude.h" 19 | #include "parser.h" 20 | #if defined(__WIN32) 21 | #include 22 | #endif 23 | 24 | 25 | /* Windows can use either `\` or `/` as a separator -- thanks to t-beckmann on github 26 | for suggesting a fix for this. */ 27 | 28 | bool is_separator(char c) { 29 | #if defined(__WIN32) 30 | return c == '\\' || c == '/'; 31 | #else 32 | return c == '/'; 33 | #endif 34 | } 35 | 36 | /* Combine directory and filename to create a full path */ 37 | char * path_from_dir_base(char *dir, char *base) { 38 | #if defined(__WIN32) 39 | char sep = '\\'; 40 | #else 41 | char sep = '/'; 42 | #endif 43 | GString *path = NULL; 44 | char *result; 45 | 46 | if ((base != NULL) && (is_separator(base[0]))) { 47 | path = g_string_new(base); 48 | } else { 49 | path = g_string_new(dir); 50 | 51 | /* Ensure that folder ends in "/" */ 52 | if (!is_separator(path->str[strlen(path->str)-1]) ) { 53 | g_string_append_c(path, sep); 54 | } 55 | 56 | g_string_append_printf(path, "%s", base); 57 | } 58 | 59 | result = path->str; 60 | g_string_free(path, false); 61 | 62 | return result; 63 | } 64 | 65 | /* Separate filename and directory from a full path */ 66 | /* See http://stackoverflow.com/questions/1575278/function-to-split-a-filepath-into-path-and-file */ 67 | void split_path_file(char** dir, char** file, char *path) { 68 | char *slash = path, *next; 69 | #if defined(__WIN32) 70 | const char sep[] = "\\/"; // Windows allows either variant 71 | #else 72 | const char sep[] = "/"; 73 | #endif 74 | 75 | while ((next = strpbrk(slash + 1, sep))) slash = next; 76 | if (path != slash) slash++; 77 | *dir = my_strndup(path, slash - path); 78 | *file = strdup(slash); 79 | } 80 | 81 | /* Return pointer to beginning of text without metadata */ 82 | /* NOTE: This is not a new string, and does not need to be freed separately */ 83 | char * source_without_metadata(char * source, unsigned long extensions ) { 84 | char *result; 85 | 86 | if (has_metadata(source, extensions)) { 87 | /* If we have metadata, then return just the body */ 88 | /* TODO: This could miss YAML Metadata that does not contain 89 | blank line afterwards */ 90 | result = strstr(source, "\n\n"); 91 | 92 | if (result != NULL) 93 | return result + 2; 94 | } 95 | 96 | /* No metadata, so return original pointer */ 97 | 98 | /* But check for UTF-8 BOM and skip if present */ 99 | if (strncmp(source, "\xef\xbb\xbf",3) == 0) 100 | return source + 3; 101 | 102 | return source; 103 | } 104 | 105 | /* Given a GString containing MMD source, and optional base directory, 106 | substitute transclusion references in the source 107 | 108 | Pass the path to the current folder if available -- should be a full path. 109 | 110 | Keep track of what we're parsing to prevent recursion using stack. */ 111 | void transclude_source(GString *source, char *basedir, char *stack, int output_format, GString *manifest) { 112 | char *base = NULL; 113 | char *path = NULL; 114 | char *start; 115 | char *stop; 116 | char *temp; 117 | int curchar; 118 | size_t pos; 119 | char real[1000]; 120 | FILE *input; 121 | long offset; 122 | 123 | if (basedir == NULL) { 124 | base = strdup(""); 125 | } else { 126 | base = strdup(basedir); 127 | } 128 | 129 | GString *folder = NULL; 130 | GString *filename = NULL; 131 | GString *filebuffer = NULL; 132 | GString *stackstring = NULL; 133 | 134 | path = strdup(base); 135 | 136 | /* Look for override folder inside document */ 137 | if (has_metadata(source->str, 0x000000)) { 138 | char *meta = extract_metadata_value(source->str, 0x000000, "transcludebase"); 139 | if (meta != NULL) { 140 | free(path); 141 | path = path_from_dir_base(base, meta); 142 | free(meta); 143 | } 144 | } 145 | 146 | if (path == NULL) { 147 | /* We have nowhere to look, so nothing to do */ 148 | free(base); 149 | return; 150 | } 151 | 152 | folder = g_string_new(path); 153 | 154 | /* Ensure that folder ends in "/" */ 155 | /* TODO: adjust for windows */ 156 | if (!(folder->str[strlen(folder->str)-1] == '/') ) { 157 | g_string_append_c(folder, '/'); 158 | } 159 | 160 | /* fprintf(stderr, "Transclude using '%s'\n", folder->str); */ 161 | 162 | /* Iterate through {{foo.txt}} and substitute contents of file without metadata */ 163 | 164 | start = strstr(source->str,"{{"); 165 | 166 | while (start != NULL) { 167 | stop = strstr(start,"}}"); 168 | if (stop == NULL) 169 | break; 170 | 171 | /* Check that we found something reasonable -- we cap at 1000 characters */ 172 | if (stop - start < 1000) { 173 | strncpy(real,start+2,stop-start-2); 174 | real[stop-start-2] = '\0'; 175 | 176 | if (is_separator(real[0])) { 177 | filename = g_string_new(real); 178 | } else { 179 | filename = g_string_new(folder->str); 180 | g_string_append_printf(filename, "%s",real); 181 | } 182 | 183 | if (strcmp(filename->str,"./TOC") == 0) { 184 | pos = stop - source->str; 185 | start = strstr(source->str + pos,"{{"); 186 | g_string_free(filename, true); 187 | continue; 188 | } 189 | 190 | /* Adjust for wildcard extensions */ 191 | /* But not if output_format == 0 */ 192 | if (output_format && strncmp(&filename->str[strlen(filename->str) - 2],".*",2) == 0) { 193 | g_string_erase(filename, strlen(filename->str) - 2, 2); 194 | if (output_format == TEXT_FORMAT) { 195 | g_string_append(filename,".txt"); 196 | } else if (output_format == HTML_FORMAT) { 197 | g_string_append(filename,".html"); 198 | } else if (output_format == LATEX_FORMAT) { 199 | g_string_append(filename,".tex"); 200 | } else if (output_format == BEAMER_FORMAT) { 201 | g_string_append(filename,".tex"); 202 | } else if (output_format == MEMOIR_FORMAT) { 203 | g_string_append(filename,".tex"); 204 | } else if (output_format == ODF_FORMAT) { 205 | g_string_append(filename,".fodt"); 206 | } else if (output_format == OPML_FORMAT) { 207 | g_string_append(filename,".opml"); 208 | } else if (output_format == LYX_FORMAT) { 209 | g_string_append(filename,".lyx"); 210 | } else if (output_format == RTF_FORMAT) { 211 | g_string_append(filename,".rtf"); 212 | } else { 213 | /* default extension -- in this case we only have 1 */ 214 | g_string_append(filename,".txt"); 215 | } 216 | } 217 | 218 | pos = stop - source->str; 219 | 220 | /* Add to the manifest (if not already included) */ 221 | if (manifest != NULL) { 222 | temp = strstr(manifest->str,filename->str); 223 | 224 | offset = temp - manifest->str; 225 | if ((temp != NULL) && 226 | ((temp == manifest->str) || ((manifest->str)[offset - 1] == '\n')) && 227 | (temp[strlen(filename->str)] == '\n') ){ 228 | /* Already on manifest, so don't add again */ 229 | } else { 230 | g_string_append_printf(manifest,"%s\n",filename->str); 231 | } 232 | } 233 | 234 | 235 | /* Don't reparse ourselves */ 236 | if (stack != NULL) { 237 | temp = strstr(stack,filename->str); 238 | 239 | if ((temp != NULL) && (temp[strlen(filename->str)] == '\n')){ 240 | start = strstr(source->str + pos,"{{"); 241 | g_string_free(filename, true); 242 | continue; 243 | } 244 | } 245 | 246 | /* Read file */ 247 | #if defined(__WIN32) 248 | int wchars_num = MultiByteToWideChar(CP_UTF8, 0, filename->str, -1, NULL, 0); 249 | wchar_t wstr[wchars_num]; 250 | MultiByteToWideChar(CP_UTF8, 0, filename->str, -1, wstr, wchars_num); 251 | 252 | if ((input = _wfopen(wstr, L"r")) != NULL ) { 253 | #else 254 | if ((input = fopen(filename->str, "r")) != NULL ) { 255 | #endif 256 | filebuffer = g_string_new(""); 257 | 258 | while ((curchar = fgetc(input)) != EOF) 259 | g_string_append_c(filebuffer, curchar); 260 | 261 | fclose(input); 262 | 263 | pos = start - source->str; 264 | 265 | g_string_erase(source, pos, 2 + stop - start); 266 | 267 | /* Update stack list */ 268 | stackstring = g_string_new(stack); 269 | g_string_append_printf(stackstring,"%s\n",filename->str); 270 | 271 | 272 | /* Recursively transclude files */ 273 | 274 | /* We want to reset the base directory if we enter a subdirectory */ 275 | char * new_dir; 276 | char * file_only; 277 | split_path_file(&new_dir, &file_only, filename->str); 278 | 279 | /* transclude_source(filebuffer, folder->str, stackstring->str, output_format, manifest); */ 280 | transclude_source(filebuffer, new_dir, stackstring->str, output_format, manifest); 281 | 282 | free(new_dir); 283 | free(file_only); 284 | 285 | temp = source_without_metadata(filebuffer->str, 0x000000); 286 | 287 | g_string_insert(source, pos, temp); 288 | 289 | pos += strlen(temp); 290 | g_string_free(filebuffer, true); 291 | g_string_free(stackstring, true); 292 | } else { 293 | /* fprintf(stderr, "error opening file: %s\n", filename->str); */ 294 | } 295 | 296 | g_string_free(filename, true); 297 | } else { 298 | /* Our "match" was > 1000 characters long */ 299 | pos = stop - source->str; 300 | } 301 | start = strstr(source->str + pos,"{{"); 302 | } 303 | 304 | g_string_free(folder, true); 305 | free(path); 306 | free(base); 307 | } 308 | 309 | /* Allow for a footer to specify files to be appended to the end of the text, and then transcluded. 310 | Useful for appending a list of footnotes, citations, abbreviations, etc. to each separate file, 311 | but not including multiple copies when processing the master file. */ 312 | void append_mmd_footer(GString *source) { 313 | /* Look for mmd_footer metadata */ 314 | if (has_metadata(source->str, 0x000000)) { 315 | char *meta = extract_metadata_value(source->str, 0x000000, "mmdfooter"); 316 | if (meta != NULL) 317 | g_string_append_printf(source, "\n\n{{%s}}\n", meta); 318 | } 319 | } 320 | 321 | void prepend_mmd_header(GString *source) { 322 | /* Same thing, but to be inserted after metadata and before content */ 323 | if (has_metadata(source->str, 0x000000)) { 324 | char *meta = extract_metadata_value(source->str, 0x000000, "mmdheader"); 325 | if (meta != NULL) { 326 | char *content = strstr(source->str, "\n\n"); 327 | if (content != NULL) { 328 | size_t pos = content - source->str; 329 | g_string_insert_printf(source, pos, "\n\n{{%s}}", meta); 330 | } else { 331 | g_string_append_printf(source, "\n\n{{%s}}\n", meta); 332 | } 333 | } 334 | } 335 | } 336 | -------------------------------------------------------------------------------- /src/transclude.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | transclude.h -- miscellaneous support functions 4 | 5 | (c) 2013 Fletcher T. Penney (http://fletcherpenney.net/). 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License or the MIT 9 | license. See LICENSE for details. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "GLibFacade.h" 24 | 25 | char * source_without_metadata(char * source, unsigned long extensions); 26 | void transclude_source(GString *source, char *basedir, char *stack, int format, GString *manifest); 27 | void append_mmd_footer(GString *source); 28 | void prepend_mmd_header(GString *source); 29 | -------------------------------------------------------------------------------- /src/writer.h: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | 3 | #include "text.h" 4 | #include "html.h" 5 | #include "latex.h" 6 | #include "memoir.h" 7 | #include "beamer.h" 8 | #include "lyx.h" 9 | #include "lyxbeamer.h" 10 | #include "opml.h" 11 | #include "odf.h" 12 | #include "rtf.h" 13 | #include "critic.h" 14 | #include "toc.h" 15 | 16 | char * export_node_tree(node *list, int format, unsigned long extensions); 17 | 18 | void extract_references(node *list, scratch_pad *scratch); 19 | void extract_abbreviations(node *list, scratch_pad *scratch); 20 | void find_abbreviations(node *list, scratch_pad *scratch); 21 | 22 | link_data * extract_link_data(char *label, scratch_pad *scratch); 23 | 24 | void pad(GString *out, int num, scratch_pad *scratch); 25 | 26 | int note_number_for_label(char *text, scratch_pad *scratch); 27 | int note_number_for_node(node *ref, scratch_pad *scratch); 28 | node * node_matching_label(char *label, node *n); 29 | int count_node_from_end(node *n); 30 | int cite_count_node_from_end(node *n); 31 | node * node_for_count(node *n, int count); 32 | void move_note_to_used(node *list, scratch_pad *scratch); 33 | void use_inline_footnote(node *ref, scratch_pad *scratch); 34 | node * node_for_attribute(char *querystring, node *list); 35 | 36 | char * dimension_for_attribute(char *querystring, node *list); 37 | 38 | link_data * load_link_data(node *n, scratch_pad *scratch); 39 | -------------------------------------------------------------------------------- /templates/README.md.in: -------------------------------------------------------------------------------- 1 | ## About ## 2 | 3 | | | | 4 | | ---------- | ------------------------- | 5 | | Title: | @My_Project_Title@ | 6 | | Author: | @My_Project_Author@ | 7 | | Date: | @My_Project_Revised_Date@ | 8 | | Copyright: | @My_Project_Copyright@ | 9 | | Version: | @My_Project_Version@ | 10 | 11 | 12 | ## Introduction ## 13 | 14 | [Markdown] is a simple markup language used to convert plain text into HTML. 15 | 16 | [MultiMarkdown] is a derivative of Markdown that adds new syntax features, 17 | such as footnotes, tables, and metadata. Additionally, it offers mechanisms to 18 | convert plain text into LaTeX in addition to HTML. 19 | 20 | 21 | ## Background ## 22 | 23 | MultiMarkdown started as a Perl script, which was modified from the original 24 | Markdown.pl. 25 | 26 | MultiMarkdown v3 (aka 'peg-multimarkdown') was based on John MacFarlane's 27 | [peg-markdown]. It used a parsing expression grammar (PEG), and was written 28 | in C in order to compile on almost any operating system. Thanks to work by 29 | Daniel Jalkut, MMD v3 was built so that it didn't have any external library 30 | requirements. 31 | 32 | MultiMarkdown v4 was basically a complete rewrite of v3. It used the same 33 | basic PEG for parsing (Multi)Markdown text, but otherwise was almost 34 | completely rebuilt. 35 | 36 | MultiMarkdown v5 is basically the same code as v4, but the project has been 37 | restructured: 38 | 39 | * It is built using my [c-template] project boilerplate -- I welcome 40 | suggestions and ideas for improvement about this. 41 | * It is designed with the CMake build system, rather than just 42 | a Makefile 43 | 44 | 45 | 46 | ## Why switch to CMake? ## 47 | 48 | In early 2014, a user of MMD introduced me to the [CMake] build system. I 49 | looked at it briefly, but didn't do anything with it. Later on, I looked 50 | at it more in depth and created a parallel branch after 4.6. This would allow 51 | me to experiment with CMake without breaking anything else in the `master` 52 | branch. 53 | 54 | CMake isn't perfect by any means, but it does allow for some interesting 55 | things: 56 | 57 | * Automatically generate GUI installers for OS X and Windows, as well as zip 58 | files for *nix. I have not looked into using CMake to build `.deb` 59 | packages, but that might be possible as well. My old system could generate 60 | GUI installers for Windows and OS X, but it was a complex process that 61 | required a lot of manual processing. This is much more amenable to 62 | automation. 63 | * An improved organization structure for various tests, including [Valgrind] 64 | testing. The old system was getting rather messy. 65 | * A templating system that better allows me to synchronize version, and 66 | other, information in code, documentation, and READMEs 67 | * Automatic generation of project files for Xcode, Visual Studio, and 68 | alternative build systems beyond `make` 69 | * An opportunity to reorganize my code directory hierarchy 70 | * The option to start adding unit test code to the source. This probably 71 | won't happen, as it would be too much work. But it is possible. 72 | 73 | 74 | The biggest *problem* is that this means that anyone wishing to compile the 75 | source will need to install CMake. This isn't hard, but it is an extra step. 76 | 77 | As a temporary measure, you can use the `make deprecated` command to use a 78 | simplified `make` recipe to compile a binary of MultiMarkdown for the current 79 | machine. I don't recommend this approach, but it should work in a pinch until 80 | you can upgrade your machine to support cmake. 81 | 82 | I welcome feedback on this decision, but please note -- "I don't like it" or 83 | "bring back the old way" comments will be ignored. Please send meaningful 84 | criticism or suggestions. 85 | 86 | Perhaps an approach if others want to contribute will be to do the reverse of 87 | what I did before -- create a `make` branch that includes a modified Makefile 88 | designed to be used without CMake? 89 | 90 | Additionally, the old Makefile had grown over time to include some tricks that 91 | users of various systems required. I have tested the CMake system on OS X, 92 | Ubuntu and Debian Linux, and MinGW on Ubuntu. I welcome suggestions for 93 | improvements to the CMake configuration. 94 | 95 | 96 | ## Download Binary ## 97 | 98 | Binaries for OS X and Windows are available on the github site: 99 | 100 | 101 | 102 | 103 | ## Compile from Source ## 104 | 105 | To compile MultiMarkdown, you will need to have [CMake] installed on your 106 | machine. 107 | 108 | To download the source: 109 | 110 | * Obtain the source from the github repository (Downloading a zipfile of the 111 | source won't allow you to configure the submodules -- it's much better to 112 | use git): 113 | 114 | git clone https://github.com/fletcher/MultiMarkdown-5.git 115 | 116 | * Configure the submodules with two helper scripts (This can be done 117 | manually on Windows systems by looking at the source): 118 | 119 | ./link_git_modules 120 | ./update_git_modules 121 | 122 | * Compile, and (optionally) test: 123 | 124 | make 125 | cd build 126 | make 127 | make test 128 | 129 | Like all versions of MultiMarkdown since v3, there is one test that will fail 130 | (now helpfully called `markdown-should-fail`). The other tests should pass. 131 | The valgrind tests will not work on OS X, but should pass if valgrind is 132 | installed and used on Linux machines. 133 | 134 | If you want to make an installer, after the above, use the `cpack` command 135 | inside the build directory. 136 | 137 | For more information, checkout the `IMPORTANT` file. 138 | 139 | 140 | ## Usage ## 141 | 142 | The [MultiMarkdown User's Guide] has complete instructions on how to use 143 | MultiMarkdown. 144 | 145 | 146 | # LyX Support # 147 | 148 | Charles R. Cowan () added support for conversion 149 | to [LyX](http://www.lyx.org/). Support for this should be considered to be in 150 | alpha/beta, and is not guaranteed. Issues related to LyX can be added to the 151 | MultiMarkdown [issues] page on github, but will need to be answered by 152 | Charles. I am happy to include this code in the main MMD repo, but since I 153 | don't use LyX I can't support it myself. If this arrangement becomes a 154 | problem, then LyX support can be removed and it can be kept as a separate 155 | fork. 156 | 157 | 158 | # More Information # 159 | 160 | To get more information about MultiMarkdown, check out the 161 | [website][MultiMarkdown] or [MultiMarkdown User's Guide]. 162 | 163 | 164 | # Developer Notes # 165 | 166 | Be sure to read the relevant documentation: 167 | 168 | * IMPORTANT 169 | * README.md 170 | * `make documentation` and look at `build/documentation/html/index.html` 171 | * Relevant portions of the User's Guide 172 | 173 | If you wish to submit pull requests, then be sure to work off of the `develop` 174 | branch and configure the pull requests appropriately. I am *trying* to use the 175 | "git flow" workflow described here: 176 | 177 | 178 | 179 | I will not accept pull requests directly into the `master` branch. 180 | 181 | ***NOTE***: Additionally, I am trying to use a consistent convention for 182 | commit messages, so that I can quickly generate the framework for Release 183 | Notes for new versions of MultiMarkdown. For example: 184 | 185 | TAG: Commit message with uppercase first letter and no period at the end 186 | 187 | TAG: Commit message one; TAG2: Commit message two 188 | 189 | The list of TAGs is in flux, but currently includes: 190 | 191 | * ADDED: New features or functionality 192 | * CHANGED: Change to the way a feature works 193 | * CODE: Change the code, but don't change the overall user experience 194 | * FIX: Fix a bug 195 | * IMPORTANT: Something major was fixed 196 | * NOTE: These are mostly changes to the project itself (e.g. Makefile) and 197 | have no impact on the user experience 198 | 199 | These TAGs are still in flux as I develop the system I am using, but this will 200 | allow me to automatically generate most of the Release Notes for each new 201 | version. I'll still need to go over them manually, but this gives me a head 202 | start! (As an aside, any time you use one of the `make` commands, the file 203 | `CHANGELOG-UNRELEASED` will be updated to show you new features in the 204 | `development` branch that have not been pulled into `master` yet.) 205 | 206 | By using the TAGs, I can sort the list of messages and group things into 207 | categories. By consistently using the semi-colon syntax, I can automatically 208 | split commits with multiple notes. 209 | 210 | 211 | ## License ## 212 | 213 | @My_Project_License@ 214 | 215 | 216 | 217 | [Markdown]: http://daringfireball.net/projects/markdown/ 218 | [MultiMarkdown]: http://fletcherpenney.net/multimarkdown/ 219 | [MultiMarkdown User's Guide]: http://fletcher.github.io/MultiMarkdown-5/ 220 | [c-template]: https://github.com/fletcher/c-template 221 | [CMake]: https://cmake.org/ 222 | [Doxygen]: http://www.doxygen.org/ 223 | [Valgrind]: http://valgrind.org/ 224 | [peg-markdown]: https://github.com/jgm/peg-markdown 225 | [issues]: https://github.com/fletcher/MultiMarkdown-5/issues 226 | 227 | 228 | -------------------------------------------------------------------------------- /templates/template.c.in: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | @My_Project_Title@ -- @My_Project_Description@ 4 | 5 | @file file.c 6 | 7 | @brief 8 | 9 | 10 | @author @My_Project_Author@ 11 | @bug 12 | 13 | **/ 14 | 15 | /* 16 | 17 | @My_Project_Copyright@ 18 | 19 | 20 | @My_Project_License_Indent@ 21 | 22 | */ 23 | 24 | 25 | #include "file.h" 26 | 27 | int void_function(void) { 28 | return 0; 29 | } 30 | 31 | #ifdef TEST 32 | void Test_void_function(CuTest* tc) { 33 | int test = void_function(); 34 | 35 | CuAssertIntEquals(tc, 0, test); 36 | } 37 | #endif 38 | -------------------------------------------------------------------------------- /templates/template.h.in: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | @My_Project_Title@ -- @My_Project_Description@ 4 | 5 | @file file.h 6 | 7 | @brief 8 | 9 | 10 | @author @My_Project_Author@ 11 | @bug 12 | 13 | **/ 14 | 15 | /* 16 | 17 | @My_Project_Copyright@ 18 | 19 | 20 | @My_Project_License_Indent@ 21 | 22 | */ 23 | 24 | 25 | #ifndef FILE_@My_Project_Title_Caps@_H 26 | #define FILE_@My_Project_Title_Caps@_H 27 | 28 | #ifdef TEST 29 | #include "CuTest.h" 30 | #endif 31 | 32 | 33 | /// This is a sample function with a doxygen description. 34 | // void void_f(void); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /templates/version.h.in: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | version.h -- @My_Project_Title@ 4 | 5 | @My_Project_Copyright@ 6 | 7 | 8 | @My_Project_License_Indent@ 9 | 10 | */ 11 | 12 | /** 13 | 14 | @file 15 | 16 | @brief @My_Project_Description@ - project version header 17 | 18 | **/ 19 | 20 | 21 | #ifndef FILE_@My_Project_Title_Caps@_VERSION_H 22 | #define FILE_@My_Project_Title_Caps@_VERSION_H 23 | 24 | #define @My_Project_Title_Caps@_VERSION "@My_Project_Version@" 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /test/CuTest.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | CuTest 4 | 5 | http://cutest.sourceforge.net/ 6 | 7 | NOTE 8 | 9 | The license is based on the zlib/libpng license. For more details see 10 | http://www.opensource.org/licenses/zlib-license.html. The intent of the 11 | license is to: 12 | 13 | - keep the license as simple as possible 14 | - encourage the use of CuTest in both free and commercial applications 15 | and libraries 16 | - keep the source code together 17 | - give credit to the CuTest contributors for their work 18 | 19 | If you ship CuTest in source form with your source distribution, the 20 | following license document must be included with it in unaltered form. 21 | If you find CuTest useful we would like to hear about it. 22 | 23 | LICENSE 24 | 25 | Copyright (c) 2003 Asim Jalis 26 | 27 | This software is provided 'as-is', without any express or implied 28 | warranty. In no event will the authors be held liable for any damages 29 | arising from the use of this software. 30 | 31 | Permission is granted to anyone to use this software for any purpose, 32 | including commercial applications, and to alter it and redistribute it 33 | freely, subject to the following restrictions: 34 | 35 | 1. The origin of this software must not be misrepresented; you must not 36 | claim that you wrote the original software. If you use this software in 37 | a product, an acknowledgment in the product documentation would be 38 | appreciated but is not required. 39 | 40 | 2. Altered source versions must be plainly marked as such, and must not 41 | be misrepresented as being the original software. 42 | 43 | 3. This notice may not be removed or altered from any source 44 | distribution. 45 | 46 | */ 47 | 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | 55 | #include "CuTest.h" 56 | 57 | /*-------------------------------------------------------------------------* 58 | * CuStr 59 | *-------------------------------------------------------------------------*/ 60 | 61 | char* CuStrAlloc(int size) 62 | { 63 | char* newStr = (char*) malloc( sizeof(char) * (size) ); 64 | return newStr; 65 | } 66 | 67 | char* CuStrCopy(const char* old) 68 | { 69 | int len = strlen(old); 70 | char* newStr = CuStrAlloc(len + 1); 71 | strcpy(newStr, old); 72 | return newStr; 73 | } 74 | 75 | /*-------------------------------------------------------------------------* 76 | * CuString 77 | *-------------------------------------------------------------------------*/ 78 | 79 | void CuStringInit(CuString* str) 80 | { 81 | str->length = 0; 82 | str->size = STRING_MAX; 83 | str->buffer = (char*) malloc(sizeof(char) * str->size); 84 | str->buffer[0] = '\0'; 85 | } 86 | 87 | CuString* CuStringNew(void) 88 | { 89 | CuString* str = (CuString*) malloc(sizeof(CuString)); 90 | str->length = 0; 91 | str->size = STRING_MAX; 92 | str->buffer = (char*) malloc(sizeof(char) * str->size); 93 | str->buffer[0] = '\0'; 94 | return str; 95 | } 96 | 97 | void CuStringDelete(CuString *str) 98 | { 99 | if (!str) return; 100 | free(str->buffer); 101 | free(str); 102 | } 103 | 104 | void CuStringResize(CuString* str, int newSize) 105 | { 106 | str->buffer = (char*) realloc(str->buffer, sizeof(char) * newSize); 107 | str->size = newSize; 108 | } 109 | 110 | void CuStringAppend(CuString* str, const char* text) 111 | { 112 | int length; 113 | 114 | if (text == NULL) { 115 | text = "NULL"; 116 | } 117 | 118 | length = strlen(text); 119 | if (str->length + length + 1 >= str->size) 120 | CuStringResize(str, str->length + length + 1 + STRING_INC); 121 | str->length += length; 122 | strcat(str->buffer, text); 123 | } 124 | 125 | void CuStringAppendChar(CuString* str, char ch) 126 | { 127 | char text[2]; 128 | text[0] = ch; 129 | text[1] = '\0'; 130 | CuStringAppend(str, text); 131 | } 132 | 133 | void CuStringAppendFormat(CuString* str, const char* format, ...) 134 | { 135 | va_list argp; 136 | char buf[HUGE_STRING_LEN]; 137 | va_start(argp, format); 138 | vsprintf(buf, format, argp); 139 | va_end(argp); 140 | CuStringAppend(str, buf); 141 | } 142 | 143 | void CuStringInsert(CuString* str, const char* text, int pos) 144 | { 145 | int length = strlen(text); 146 | if (pos > str->length) 147 | pos = str->length; 148 | if (str->length + length + 1 >= str->size) 149 | CuStringResize(str, str->length + length + 1 + STRING_INC); 150 | memmove(str->buffer + pos + length, str->buffer + pos, (str->length - pos) + 1); 151 | str->length += length; 152 | memcpy(str->buffer + pos, text, length); 153 | } 154 | 155 | /*-------------------------------------------------------------------------* 156 | * CuTest 157 | *-------------------------------------------------------------------------*/ 158 | 159 | void CuTestInit(CuTest* t, const char* name, TestFunction function) 160 | { 161 | t->name = CuStrCopy(name); 162 | t->failed = 0; 163 | t->ran = 0; 164 | t->message = NULL; 165 | t->function = function; 166 | t->jumpBuf = NULL; 167 | } 168 | 169 | CuTest* CuTestNew(const char* name, TestFunction function) 170 | { 171 | CuTest* tc = CU_ALLOC(CuTest); 172 | CuTestInit(tc, name, function); 173 | return tc; 174 | } 175 | 176 | void CuTestDelete(CuTest *t) 177 | { 178 | if (!t) return; 179 | free(t->name); 180 | free(t); 181 | } 182 | 183 | void CuTestRun(CuTest* tc) 184 | { 185 | jmp_buf buf; 186 | tc->jumpBuf = &buf; 187 | if (setjmp(buf) == 0) 188 | { 189 | tc->ran = 1; 190 | (tc->function)(tc); 191 | } 192 | tc->jumpBuf = 0; 193 | } 194 | 195 | static void CuFailInternal(CuTest* tc, const char* file, int line, CuString* string) 196 | { 197 | char buf[HUGE_STRING_LEN]; 198 | 199 | sprintf(buf, "%s:%d: ", file, line); 200 | CuStringInsert(string, buf, 0); 201 | 202 | tc->failed = 1; 203 | tc->message = string->buffer; 204 | if (tc->jumpBuf != 0) longjmp(*(tc->jumpBuf), 0); 205 | } 206 | 207 | void CuFail_Line(CuTest* tc, const char* file, int line, const char* message2, const char* message) 208 | { 209 | CuString string; 210 | 211 | CuStringInit(&string); 212 | if (message2 != NULL) 213 | { 214 | CuStringAppend(&string, message2); 215 | CuStringAppend(&string, ": "); 216 | } 217 | CuStringAppend(&string, message); 218 | CuFailInternal(tc, file, line, &string); 219 | } 220 | 221 | void CuAssert_Line(CuTest* tc, const char* file, int line, const char* message, int condition) 222 | { 223 | if (condition) return; 224 | CuFail_Line(tc, file, line, NULL, message); 225 | } 226 | 227 | void CuAssertStrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, 228 | const char* expected, const char* actual) 229 | { 230 | CuString string; 231 | if ((expected == NULL && actual == NULL) || 232 | (expected != NULL && actual != NULL && 233 | strcmp(expected, actual) == 0)) 234 | { 235 | return; 236 | } 237 | 238 | CuStringInit(&string); 239 | if (message != NULL) 240 | { 241 | CuStringAppend(&string, message); 242 | CuStringAppend(&string, ": "); 243 | } 244 | CuStringAppend(&string, "expected <"); 245 | CuStringAppend(&string, expected); 246 | CuStringAppend(&string, "> but was <"); 247 | CuStringAppend(&string, actual); 248 | CuStringAppend(&string, ">"); 249 | CuFailInternal(tc, file, line, &string); 250 | } 251 | 252 | void CuAssertIntEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, 253 | int expected, int actual) 254 | { 255 | char buf[STRING_MAX]; 256 | if (expected == actual) return; 257 | sprintf(buf, "expected <%d> but was <%d>", expected, actual); 258 | CuFail_Line(tc, file, line, message, buf); 259 | } 260 | 261 | void CuAssertDblEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, 262 | double expected, double actual, double delta) 263 | { 264 | char buf[STRING_MAX]; 265 | if (fabs(expected - actual) <= delta) return; 266 | sprintf(buf, "expected <%f> but was <%f>", expected, actual); 267 | 268 | CuFail_Line(tc, file, line, message, buf); 269 | } 270 | 271 | void CuAssertPtrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, 272 | void* expected, void* actual) 273 | { 274 | char buf[STRING_MAX]; 275 | if (expected == actual) return; 276 | sprintf(buf, "expected pointer <0x%p> but was <0x%p>", expected, actual); 277 | CuFail_Line(tc, file, line, message, buf); 278 | } 279 | 280 | 281 | /*-------------------------------------------------------------------------* 282 | * CuSuite 283 | *-------------------------------------------------------------------------*/ 284 | 285 | void CuSuiteInit(CuSuite* testSuite) 286 | { 287 | testSuite->count = 0; 288 | testSuite->failCount = 0; 289 | memset(testSuite->list, 0, sizeof(testSuite->list)); 290 | } 291 | 292 | CuSuite* CuSuiteNew(void) 293 | { 294 | CuSuite* testSuite = CU_ALLOC(CuSuite); 295 | CuSuiteInit(testSuite); 296 | return testSuite; 297 | } 298 | 299 | void CuSuiteDelete(CuSuite *testSuite) 300 | { 301 | unsigned int n; 302 | for (n=0; n < MAX_TEST_CASES; n++) 303 | { 304 | if (testSuite->list[n]) 305 | { 306 | CuTestDelete(testSuite->list[n]); 307 | } 308 | } 309 | free(testSuite); 310 | 311 | } 312 | 313 | void CuSuiteAdd(CuSuite* testSuite, CuTest *testCase) 314 | { 315 | assert(testSuite->count < MAX_TEST_CASES); 316 | testSuite->list[testSuite->count] = testCase; 317 | testSuite->count++; 318 | } 319 | 320 | void CuSuiteAddSuite(CuSuite* testSuite, CuSuite* testSuite2) 321 | { 322 | int i; 323 | for (i = 0 ; i < testSuite2->count ; ++i) 324 | { 325 | CuTest* testCase = testSuite2->list[i]; 326 | CuSuiteAdd(testSuite, testCase); 327 | } 328 | } 329 | 330 | void CuSuiteRun(CuSuite* testSuite) 331 | { 332 | int i; 333 | for (i = 0 ; i < testSuite->count ; ++i) 334 | { 335 | CuTest* testCase = testSuite->list[i]; 336 | CuTestRun(testCase); 337 | if (testCase->failed) { testSuite->failCount += 1; } 338 | } 339 | } 340 | 341 | void CuSuiteSummary(CuSuite* testSuite, CuString* summary) 342 | { 343 | int i; 344 | for (i = 0 ; i < testSuite->count ; ++i) 345 | { 346 | CuTest* testCase = testSuite->list[i]; 347 | CuStringAppend(summary, testCase->failed ? "F" : "."); 348 | } 349 | CuStringAppend(summary, "\n\n"); 350 | } 351 | 352 | void CuSuiteDetails(CuSuite* testSuite, CuString* details) 353 | { 354 | int i; 355 | int failCount = 0; 356 | 357 | if (testSuite->failCount == 0) 358 | { 359 | int passCount = testSuite->count - testSuite->failCount; 360 | const char* testWord = passCount == 1 ? "test" : "tests"; 361 | CuStringAppendFormat(details, "OK (%d %s)\n", passCount, testWord); 362 | } 363 | else 364 | { 365 | if (testSuite->failCount == 1) 366 | CuStringAppend(details, "There was 1 failure:\n"); 367 | else 368 | CuStringAppendFormat(details, "There were %d failures:\n", testSuite->failCount); 369 | 370 | for (i = 0 ; i < testSuite->count ; ++i) 371 | { 372 | CuTest* testCase = testSuite->list[i]; 373 | if (testCase->failed) 374 | { 375 | failCount++; 376 | CuStringAppendFormat(details, "%d) %s: %s\n", 377 | failCount, testCase->name, testCase->message); 378 | } 379 | } 380 | CuStringAppend(details, "\n!!!FAILURES!!!\n"); 381 | 382 | CuStringAppendFormat(details, "Runs: %d ", testSuite->count); 383 | CuStringAppendFormat(details, "Passes: %d ", testSuite->count - testSuite->failCount); 384 | CuStringAppendFormat(details, "Fails: %d\n", testSuite->failCount); 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /test/CuTest.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | CuTest 4 | 5 | http://cutest.sourceforge.net/ 6 | 7 | NOTE 8 | 9 | The license is based on the zlib/libpng license. For more details see 10 | http://www.opensource.org/licenses/zlib-license.html. The intent of the 11 | license is to: 12 | 13 | - keep the license as simple as possible 14 | - encourage the use of CuTest in both free and commercial applications 15 | and libraries 16 | - keep the source code together 17 | - give credit to the CuTest contributors for their work 18 | 19 | If you ship CuTest in source form with your source distribution, the 20 | following license document must be included with it in unaltered form. 21 | If you find CuTest useful we would like to hear about it. 22 | 23 | LICENSE 24 | 25 | Copyright (c) 2003 Asim Jalis 26 | 27 | This software is provided 'as-is', without any express or implied 28 | warranty. In no event will the authors be held liable for any damages 29 | arising from the use of this software. 30 | 31 | Permission is granted to anyone to use this software for any purpose, 32 | including commercial applications, and to alter it and redistribute it 33 | freely, subject to the following restrictions: 34 | 35 | 1. The origin of this software must not be misrepresented; you must not 36 | claim that you wrote the original software. If you use this software in 37 | a product, an acknowledgment in the product documentation would be 38 | appreciated but is not required. 39 | 40 | 2. Altered source versions must be plainly marked as such, and must not 41 | be misrepresented as being the original software. 42 | 43 | 3. This notice may not be removed or altered from any source 44 | distribution. 45 | 46 | */ 47 | 48 | #ifndef CU_TEST_H 49 | #define CU_TEST_H 50 | 51 | #include 52 | #include 53 | 54 | #define CUTEST_VERSION "CuTest 1.5" 55 | 56 | /* CuString */ 57 | 58 | char* CuStrAlloc(int size); 59 | char* CuStrCopy(const char* old); 60 | 61 | #define CU_ALLOC(TYPE) ((TYPE*) malloc(sizeof(TYPE))) 62 | 63 | #define HUGE_STRING_LEN 8192 64 | #define STRING_MAX 256 65 | #define STRING_INC 256 66 | 67 | typedef struct 68 | { 69 | int length; 70 | int size; 71 | char* buffer; 72 | } CuString; 73 | 74 | void CuStringInit(CuString* str); 75 | CuString* CuStringNew(void); 76 | void CuStringRead(CuString* str, const char* path); 77 | void CuStringAppend(CuString* str, const char* text); 78 | void CuStringAppendChar(CuString* str, char ch); 79 | void CuStringAppendFormat(CuString* str, const char* format, ...); 80 | void CuStringInsert(CuString* str, const char* text, int pos); 81 | void CuStringResize(CuString* str, int newSize); 82 | void CuStringDelete(CuString* str); 83 | 84 | /* CuTest */ 85 | 86 | typedef struct CuTest CuTest; 87 | 88 | typedef void (*TestFunction)(CuTest *); 89 | 90 | struct CuTest 91 | { 92 | char* name; 93 | TestFunction function; 94 | int failed; 95 | int ran; 96 | const char* message; 97 | jmp_buf *jumpBuf; 98 | }; 99 | 100 | void CuTestInit(CuTest* t, const char* name, TestFunction function); 101 | CuTest* CuTestNew(const char* name, TestFunction function); 102 | void CuTestRun(CuTest* tc); 103 | void CuTestDelete(CuTest *t); 104 | 105 | /* Internal versions of assert functions -- use the public versions */ 106 | void CuFail_Line(CuTest* tc, const char* file, int line, const char* message2, const char* message); 107 | void CuAssert_Line(CuTest* tc, const char* file, int line, const char* message, int condition); 108 | void CuAssertStrEquals_LineMsg(CuTest* tc, 109 | const char* file, int line, const char* message, 110 | const char* expected, const char* actual); 111 | void CuAssertIntEquals_LineMsg(CuTest* tc, 112 | const char* file, int line, const char* message, 113 | int expected, int actual); 114 | void CuAssertDblEquals_LineMsg(CuTest* tc, 115 | const char* file, int line, const char* message, 116 | double expected, double actual, double delta); 117 | void CuAssertPtrEquals_LineMsg(CuTest* tc, 118 | const char* file, int line, const char* message, 119 | void* expected, void* actual); 120 | 121 | /* public assert functions */ 122 | 123 | #define CuFail(tc, ms) CuFail_Line( (tc), __FILE__, __LINE__, NULL, (ms)) 124 | #define CuAssert(tc, ms, cond) CuAssert_Line((tc), __FILE__, __LINE__, (ms), (cond)) 125 | #define CuAssertTrue(tc, cond) CuAssert_Line((tc), __FILE__, __LINE__, "assert failed", (cond)) 126 | 127 | #define CuAssertStrEquals(tc,ex,ac) CuAssertStrEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) 128 | #define CuAssertStrEquals_Msg(tc,ms,ex,ac) CuAssertStrEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac)) 129 | #define CuAssertIntEquals(tc,ex,ac) CuAssertIntEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) 130 | #define CuAssertIntEquals_Msg(tc,ms,ex,ac) CuAssertIntEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac)) 131 | #define CuAssertDblEquals(tc,ex,ac,dl) CuAssertDblEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac),(dl)) 132 | #define CuAssertDblEquals_Msg(tc,ms,ex,ac,dl) CuAssertDblEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac),(dl)) 133 | #define CuAssertPtrEquals(tc,ex,ac) CuAssertPtrEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) 134 | #define CuAssertPtrEquals_Msg(tc,ms,ex,ac) CuAssertPtrEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac)) 135 | 136 | #define CuAssertPtrNotNull(tc,p) CuAssert_Line((tc),__FILE__,__LINE__,"null pointer unexpected",(p != NULL)) 137 | #define CuAssertPtrNotNullMsg(tc,msg,p) CuAssert_Line((tc),__FILE__,__LINE__,(msg),(p != NULL)) 138 | 139 | /* CuSuite */ 140 | 141 | #define MAX_TEST_CASES 1024 142 | 143 | #define SUITE_ADD_TEST(SUITE,TEST) CuSuiteAdd(SUITE, CuTestNew(#TEST, TEST)) 144 | 145 | typedef struct 146 | { 147 | int count; 148 | CuTest* list[MAX_TEST_CASES]; 149 | int failCount; 150 | 151 | } CuSuite; 152 | 153 | 154 | void CuSuiteInit(CuSuite* testSuite); 155 | CuSuite* CuSuiteNew(void); 156 | void CuSuiteDelete(CuSuite *testSuite); 157 | void CuSuiteAdd(CuSuite* testSuite, CuTest *testCase); 158 | void CuSuiteAddSuite(CuSuite* testSuite, CuSuite* testSuite2); 159 | void CuSuiteRun(CuSuite* testSuite); 160 | void CuSuiteSummary(CuSuite* testSuite, CuString* summary); 161 | void CuSuiteDetails(CuSuite* testSuite, CuString* details); 162 | 163 | #endif /* CU_TEST_H */ 164 | -------------------------------------------------------------------------------- /test/make-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Auto generate single AllTests file for CuTest. 4 | # Searches through all *.c files in the current directory. 5 | # Prints to stdout. 6 | # Author: Asim Jalis 7 | # Date: 01/08/2003 8 | # 9 | # Modified by Fletcher T. Penney for proper error codes 10 | 11 | if test $# -eq 0 ; then FILES=*.c ; else FILES=$* ; fi 12 | 13 | echo ' 14 | 15 | /* This is auto-generated code. Edit at your own peril. */ 16 | #include 17 | #include 18 | 19 | #include "CuTest.h" 20 | 21 | ' 22 | 23 | cat $FILES | grep '^void Test' | 24 | sed -e 's/(.*$//' \ 25 | -e 's/$/(CuTest*);/' \ 26 | -e 's/^/extern /' 27 | 28 | echo \ 29 | ' 30 | 31 | void RunAllTests(void) 32 | { 33 | CuString *output = CuStringNew(); 34 | CuSuite* suite = CuSuiteNew(); 35 | int failCount = 0; 36 | 37 | ' 38 | cat $FILES | grep '^void Test' | 39 | sed -e 's/^void //' \ 40 | -e 's/(.*$//' \ 41 | -e 's/^/ SUITE_ADD_TEST(suite, /' \ 42 | -e 's/$/);/' 43 | 44 | echo \ 45 | ' 46 | CuSuiteRun(suite); 47 | CuSuiteSummary(suite, output); 48 | CuSuiteDetails(suite, output); 49 | printf("%s\\n", output->buffer); 50 | CuStringDelete(output); 51 | 52 | failCount = suite->failCount; 53 | CuSuiteDelete(suite); 54 | 55 | if (failCount != 0) 56 | exit(EXIT_FAILURE); 57 | 58 | exit(EXIT_SUCCESS); 59 | } 60 | 61 | int main(void) 62 | { 63 | RunAllTests(); 64 | } 65 | ' 66 | -------------------------------------------------------------------------------- /tools/Toolchain-MinGW-w64-32bit.cmake: -------------------------------------------------------------------------------- 1 | # Settings for compiling for Windows 32-bit machines using MinGW-w64 2 | 3 | set (IS_CROSSCOMPILING "YES") 4 | set (IS_32_BIT "32-") 5 | 6 | set (CMAKE_SYSTEM_NAME Windows) 7 | 8 | set (CMAKE_C_COMPILER i686-w64-mingw32-gcc) 9 | set (CMAKE_CXX_COMPILER i686-w64-mingw32-g++) 10 | set (CMAKE_RC_COMPILER i686-w64-mingw32-windres) 11 | 12 | set (CMAKE_FIND_ROOT_PATH /usr/bin) 13 | -------------------------------------------------------------------------------- /tools/Toolchain-MinGW-w64-64bit.cmake: -------------------------------------------------------------------------------- 1 | # Settings for compiling for Windows 64-bit machines using MinGW-w64 2 | 3 | set (IS_CROSSCOMPILING "YES") 4 | 5 | set (CMAKE_SYSTEM_NAME Windows) 6 | 7 | set (CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) 8 | set (CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) 9 | set (CMAKE_RC_COMPILER x86_64-w64-mingw32-windres) 10 | 11 | set (CMAKE_FIND_ROOT_PATH /usr/bin) 12 | -------------------------------------------------------------------------------- /tools/Toolchain-mingw32.cmake: -------------------------------------------------------------------------------- 1 | # Settings for compiling for Windows 32-bit machines 2 | 3 | set (IS_CROSSCOMPILING "YES") 4 | set (IS_32_BIT "32-") 5 | 6 | set (CMAKE_SYSTEM_NAME Windows) 7 | 8 | set (CMAKE_C_COMPILER i586-mingw32msvc-gcc) 9 | set (CMAKE_CXX_COMPILER i586-mingw32msvc-g++) 10 | set (CMAKE_RC_COMPILER i586-mingw32msvc-windres) 11 | 12 | set (CMAKE_FIND_ROOT_PATH /usr/bin) 13 | -------------------------------------------------------------------------------- /tools/enumsToPerl.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # enumsToPerl: 4 | # This Perl script creates a Perl version of the enum's that are in 5 | # the C/C++ header file supplied as the first command-line argument. 6 | # The second command-line argument specifies the filepath to the 7 | # module file that is to be created. 8 | # Sample usage: 9 | # perl enumsToPerl MyEnums.h MyEnums.pm 10 | # 11 | # Cameron Hayne (macdev@hayne.net) May 2009 12 | 13 | use strict; 14 | use warnings; 15 | use File::Basename; 16 | 17 | # getEnumsFromString: 18 | sub getEnumsFromString($) 19 | { 20 | my ($str) = @_; 21 | 22 | # strip C & C++ style comments 23 | # (regex from: http://perldoc.perl.org/perlfaq6.html) 24 | $str =~ s#/\*[^*]*\*+([^/*][^*]*\*+)*/ 25 | |//[^\n]* 26 | |q("(\\.|[^"\\])*" | '(\\.|[^'\\])*' | .[^/"'\\]*) 27 | #defined $2 ? $2 : ""#gsex; 28 | 29 | my @enums = (); 30 | while ($str =~ /(enum\s+(\w+)\s*{([^}]*)}\s*;)/g) 31 | { 32 | my $enumDecl = $1; 33 | my $enumName = $2; 34 | my $enumBody = $3; 35 | 36 | my $enum = {}; 37 | $enum->{name} = $enumName; 38 | $enum->{pairs} = []; 39 | push(@enums, $enum); 40 | 41 | my $prevValue = -1; 42 | while ($enumBody =~ /(\w+)\s*(?:=\s*(.+))?,?/g) 43 | { 44 | my $name = $1; 45 | my $value = $2; 46 | if (defined($value)) 47 | { 48 | $value = eval($value); 49 | } 50 | else 51 | { 52 | $value = $prevValue + 1; 53 | } 54 | $prevValue = $value; 55 | 56 | push(@{$enum->{pairs}}, [$name, $value]); 57 | } 58 | } 59 | 60 | return @enums; 61 | } 62 | 63 | # writePerlCodeForEnums: 64 | sub writePerlCodeForEnums($$@) 65 | { 66 | my ($filehandle, $useHex, @enums) = @_; 67 | 68 | print $filehandle "# Enums:\n"; 69 | print $filehandle "# -----------------------------------------\n"; 70 | 71 | my $format = $useHex ? "\t'%s' => 0x%02x,\n" 72 | : "\t'%s' => %d,\n"; 73 | 74 | foreach my $enum (@enums) 75 | { 76 | my $enumName = $enum->{name}; 77 | print $filehandle "our %$enumName = (\n"; 78 | foreach my $pair (@{$enum->{pairs}}) 79 | { 80 | my $name = $pair->[0]; 81 | my $value = $pair->[1]; 82 | 83 | printf $filehandle ($format, $name, $value); 84 | } 85 | print $filehandle ");\n"; 86 | print $filehandle "\n"; 87 | } 88 | } 89 | 90 | # writePerlCodeForReverseEnums: 91 | sub writePerlCodeForReverseEnums($$@) 92 | { 93 | my ($filehandle, $useHex, @enums) = @_; 94 | 95 | print $filehandle "# Reverse Enums:\n"; 96 | print $filehandle "# -----------------------------------------\n"; 97 | 98 | my $format = $useHex ? "\t0x%02x => '%s',\n" 99 | : "\t%d => '%s',\n"; 100 | 101 | foreach my $enum (@enums) 102 | { 103 | my $enumName = $enum->{name} . "REVERSE"; 104 | print $filehandle "our %$enumName = (\n"; 105 | foreach my $pair (@{$enum->{pairs}}) 106 | { 107 | my $name = $pair->[0]; 108 | my $value = $pair->[1]; 109 | printf $filehandle ($format, $value, $name); 110 | } 111 | print $filehandle ");\n"; 112 | print $filehandle "\n"; 113 | } 114 | } 115 | 116 | sub writePerlCodeForModule($$$@) 117 | { 118 | my ($filehandle, $headerFile, $moduleName, @enums) = @_; 119 | 120 | my $time = localtime(); 121 | my $moduleStart = << "EOT"; 122 | # This Perl module was generated: $time 123 | # by using the 'enumsToPerl' script on the following header file: 124 | # $headerFile 125 | 126 | package $moduleName; 127 | EOT 128 | my $moduleCode = <<'EOT'; 129 | use Exporter; 130 | our @ISA = qw(Exporter); 131 | our @EXPORT = do { 132 | no strict 'refs'; 133 | map { '%' . $_ } keys %{ __PACKAGE__ . '::' }; 134 | }; 135 | 136 | EOT 137 | 138 | print $filehandle "$moduleStart\n"; 139 | print $filehandle "$moduleCode\n"; 140 | } 141 | 142 | # MAIN 143 | { 144 | die "Usage: enumsToPerl headerFile moduleFile\n" unless scalar(@ARGV) == 2; 145 | my $headerFile = $ARGV[0]; 146 | my $moduleFile = $ARGV[1]; 147 | die "Can't read header file '$headerFile'" unless -r $headerFile; 148 | my ($basename, $dirname, $ext) = fileparse($moduleFile, qr/\.[^.]*/); 149 | die "Can't create module file '$moduleFile'" unless -w $dirname; 150 | my $moduleName = $basename; 151 | 152 | undef $/; # slurp mode 153 | open(HEADERFILE, "< $headerFile") 154 | or die "Can't open header file '$headerFile' : $!\n"; 155 | my $contents = ; 156 | close(HEADERFILE) or die "Failed to close header file: $!\n"; 157 | 158 | open(MODULEFILE, "> $moduleFile") 159 | or die "Can't open module file '$moduleFile' for writing: $!\n"; 160 | my @enums = getEnumsFromString($contents); 161 | my $useHex = 0; 162 | writePerlCodeForModule(*MODULEFILE, $headerFile, $moduleName, @enums); 163 | writePerlCodeForEnums(*MODULEFILE, $useHex, @enums); 164 | writePerlCodeForReverseEnums(*MODULEFILE, $useHex, @enums); 165 | close(MODULEFILE) or die "Failed to close module file: $!\n"; 166 | } 167 | 168 | -------------------------------------------------------------------------------- /tools/make_drag_drop_mac.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Run this in terminal (with the Platypus command line tool installed) 4 | # to create drag and drop binaries. NOTE: These rarely need to be 5 | # updated, and rely on whatever version of MultiMarkdown you have 6 | # installed. 7 | 8 | # Modified for Platypus version 5 9 | 10 | /usr/local/bin/platypus --droppable --quit-after-execution \ 11 | --name 'MMD to HTML' --output-type 'Text Window' --interpreter '/bin/sh' \ 12 | --bundle-identifier net.fletcherpenney.MMD2HTML --suffixes '*' \ 13 | '../scripts/mmd' -y MMD2HTML.app 14 | 15 | /usr/local/bin/platypus --droppable --quit-after-execution \ 16 | --name 'MMD to LaTeX' --output-type 'Text Window' --interpreter '/bin/sh' \ 17 | --bundle-identifier net.fletcherpenney.MMD2LaTeX --suffixes '*' \ 18 | '../scripts/mmd2tex' -y MMD2LaTeX.app 19 | 20 | /usr/local/bin/platypus --droppable --quit-after-execution \ 21 | --name 'MMD to OPML' --output-type 'Text Window' --interpreter '/bin/sh' \ 22 | --bundle-identifier net.fletcherpenney.MMD2OPML --suffixes '*' \ 23 | '../scripts/mmd2opml' -y MMD2OPML.app 24 | 25 | /usr/local/bin/platypus --droppable --quit-after-execution \ 26 | --name 'MMD to ODF' --output-type 'Text Window' --interpreter '/bin/sh' \ 27 | --bundle-identifier net.fletcherpenney.MMD2ODF --suffixes '*' \ 28 | '../scripts/mmd2odf' -y MMD2ODF.app 29 | 30 | 31 | -------------------------------------------------------------------------------- /tools/version.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | ***NOTE*** -- this version of the file is only for use with 4 | "make deprecated". It will not be automatically updated with the latest 5 | version information. 6 | 7 | 8 | version_deprecated.h -- MultiMarkdown 9 | 10 | Copyright © 2013-2016 Fletcher T. Penney. 11 | 12 | 13 | The `c-template` project is released under the MIT License. 14 | 15 | 16 | MMD 5 is released under the MIT License. 17 | 18 | 19 | CuTest is released under the zlib/libpng license. See CuTest.c for the text 20 | of the license. 21 | 22 | 23 | ## The MIT License ## 24 | 25 | Permission is hereby granted, free of charge, to any person obtaining a copy 26 | of this software and associated documentation files (the "Software"), to deal 27 | in the Software without restriction, including without limitation the rights 28 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 29 | copies of the Software, and to permit persons to whom the Software is 30 | furnished to do so, subject to the following conditions: 31 | 32 | The above copyright notice and this permission notice shall be included in 33 | all copies or substantial portions of the Software. 34 | 35 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 36 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 37 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 38 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 39 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 40 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 41 | THE SOFTWARE. 42 | 43 | */ 44 | 45 | /** 46 | 47 | @file 48 | 49 | @brief MultiMarkdown - lightweight markup processor - project version header 50 | 51 | **/ 52 | 53 | 54 | #ifndef FILE_MULTIMARKDOWN_VERSION_H 55 | #define FILE_MULTIMARKDOWN_VERSION_H 56 | 57 | #define MULTIMARKDOWN_VERSION "5.4.0-dep" 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /update_git_modules: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This command can be run any time to ensure that all submodules are on 4 | # the most recent commit for the configured branch, as set up 5 | # in the `link_git_modules` script. 6 | 7 | git submodule foreach git pull origin 8 | --------------------------------------------------------------------------------