├── strings.h ├── 3.png ├── 4.png ├── 5.png ├── .gitmodules ├── cpp.hint ├── .gitignore ├── StringUtils.hpp ├── include └── console-markdown │ ├── CPP.hpp │ ├── Pawn.hpp │ └── CMDMD.hpp ├── README.md ├── StringUtils.cpp ├── libsoldout ├── LICENSE ├── expanded_markdown.sh ├── mkd2latex.1 ├── CHANGES ├── make-amal ├── renderers.h ├── GNUmakefile ├── mkd2man.1 ├── BSDmakefile ├── soldout.3 ├── benchmark.c ├── mkd2html.1 ├── soldout_renderers.3 ├── array.h ├── markdown.h ├── mkd2html.c ├── buffer.h ├── soldout_buffer.3 ├── soldout_array.3 ├── array.c ├── buffer.c ├── soldout_markdown.3 ├── mkd2man.c ├── mkd2latex.c └── renderers.c ├── CMakeLists.txt ├── console-colour.sln ├── main.cpp ├── console-colour.h ├── parsers ├── pawn.cpp └── cpp.cpp ├── ColouredBuffer.hpp ├── dllmain.cpp ├── console-colour.vcxproj ├── ConsoleRenderer.cpp └── console-colour.c /strings.h: -------------------------------------------------------------------------------- 1 | #define strncasecmp(x,y,z) _strnicmp(x,y,z) 2 | 3 | -------------------------------------------------------------------------------- /3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y-Less/console-markdown/HEAD/3.png -------------------------------------------------------------------------------- /4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y-Less/console-markdown/HEAD/4.png -------------------------------------------------------------------------------- /5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y-Less/console-markdown/HEAD/5.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "subhook"] 2 | path = subhook 3 | url = git@github.com:Zeex/subhook.git 4 | -------------------------------------------------------------------------------- /cpp.hint: -------------------------------------------------------------------------------- 1 | #define CONSOLECOLOUR_API __declspec(dllexport) 2 | #define CONSOLECOLOUR_API __declspec(dllimport) 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /Debug 2 | /*.vcxproj.filters 3 | /*.vcxproj.user 4 | /*.vcxproj 5 | /*.sln 6 | /.vs 7 | /CMakeCache.txt 8 | /CMakeFiles 9 | /Release 10 | /Win32 11 | /console-markdown.dir 12 | /console-markdown-test.dir 13 | /cmake_install.cmake 14 | /Makefile 15 | build/ 16 | 17 | -------------------------------------------------------------------------------- /StringUtils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace cmdmd 6 | { 7 | // https://stackoverflow.com/questions/2896600/how-to-replace-all-occurrences-of-a-character-in-string 8 | void ReplaceAll(std::string & source, std::string const & from, std::string const & to); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /include/console-markdown/CPP.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace cmdmd 6 | { 7 | std::string 8 | CPP(std::string const & s); 9 | 10 | inline namespace Literals 11 | { 12 | std::string 13 | operator "" _cpp_syntax(char const * s, size_t len); 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # console-markdown 2 | Render markdown from C++ strings in the console. 3 | 4 | ![All the abilities](3.png) 5 | 6 | Alternate code formatting/highlighting, and the `_cmdmd` string literal operator: 7 | 8 | ![String literal](4.png) 9 | 10 | Windows vs. Linux: 11 | 12 | ![Windows vs. Linux](5.png) 13 | 14 | -------------------------------------------------------------------------------- /include/console-markdown/Pawn.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace cmdmd 6 | { 7 | std::string 8 | Pawn(std::string const & s, bool ysi = true); 9 | 10 | inline namespace Literals 11 | { 12 | std::string 13 | operator "" _pawn_syntax(char const * s, size_t len); 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /include/console-markdown/CMDMD.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace cmdmd 6 | { 7 | void Init(); 8 | 9 | std::string 10 | Render(std::string const & input); 11 | 12 | std::string 13 | Render(char const * input); 14 | 15 | std::string 16 | Render(char const * input, size_t len); 17 | 18 | inline namespace Literals 19 | { 20 | std::string 21 | operator "" _cmdmd(char const * s, size_t len); 22 | 23 | std::string 24 | operator "" _md(char const * s, size_t len); 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /StringUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "StringUtils.hpp" 2 | 3 | namespace cmdmd 4 | { 5 | void ReplaceAll(std::string & source, std::string const & from, std::string const & to) 6 | { 7 | std::string newString; 8 | newString.reserve(source.length()); // Avoids a few memory allocations. 9 | 10 | std::string::size_type lastPos = 0; 11 | std::string::size_type findPos; 12 | 13 | while(std::string::npos != (findPos = source.find(from, lastPos))) 14 | { 15 | newString.append(source, lastPos, findPos - lastPos); 16 | newString += to; 17 | lastPos = findPos + from.length(); 18 | } 19 | 20 | // Care for the rest after last occurrence. 21 | newString += source.substr(lastPos); 22 | 23 | source.swap(newString); 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /libsoldout/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009, Natacha Porté 2 | 3 | Permission to use, copy, modify, and distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /libsoldout/expanded_markdown.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright (c) 2009, Natacha Porté 4 | # 5 | # Permission to use, copy, modify, and distribute this software for any 6 | # purpose with or without fee is hereby granted, provided that the above 7 | # copyright notice and this permission notice appear in all copies. 8 | # 9 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | # This script is used for markdown validation: 18 | # The original Markdown.pl expand tabs into spaces, which is something 19 | # I don't want to do, and it cannot be easily fixed in the renderer, 20 | # so has to be preprocessed. expand(1) is used for that preprocessing, 21 | # feeding tab-free data into my markdown. 22 | 23 | expand -t 4 "$@" | $(dirname $0)/lace --markdown --xhtml 24 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.9) 2 | 3 | project(console-markdown LANGUAGES C CXX VERSION 1.0 DESCRIPTION "Render markdown in the console") 4 | 5 | if(MSVC) 6 | set(CMAKE_CXX_STANDARD 17) 7 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 8 | else() 9 | set(CMAKE_CXX_FLAGS "-std=gnu++17") 10 | endif() 11 | 12 | add_library(console-markdown STATIC 13 | ColouredBuffer.cpp 14 | ColouredBuffer.hpp 15 | StringUtils.cpp 16 | StringUtils.hpp 17 | ConsoleRenderer.cpp 18 | console-colour.h 19 | console-colour.c 20 | parsers/pawn.cpp 21 | parsers/cpp.cpp 22 | include/console-markdown/CPP.hpp 23 | include/console-markdown/Pawn.hpp 24 | include/console-markdown/CMDMD.hpp 25 | libsoldout/array.c 26 | libsoldout/array.h 27 | libsoldout/buffer.c 28 | libsoldout/buffer.h 29 | libsoldout/markdown.c 30 | libsoldout/markdown.h 31 | ) 32 | 33 | set_target_properties(console-markdown PROPERTIES VERSION ${PROJECT_VERSION}) 34 | set_target_properties(console-markdown PROPERTIES SOVERSION 1) 35 | 36 | if(MSVC) 37 | # Include in Windows from our sources. 38 | target_include_directories(console-markdown PRIVATE .) 39 | endif() 40 | 41 | add_executable(console-markdown-test main.cpp) 42 | target_link_libraries(console-markdown-test console-markdown) 43 | 44 | target_include_directories(console-markdown-test PRIVATE include) 45 | 46 | -------------------------------------------------------------------------------- /console-colour.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29806.167 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "console-colour", "console-colour.vcxproj", "{FE74624B-9FAB-4D3D-B7B8-649885FBFA16}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {FE74624B-9FAB-4D3D-B7B8-649885FBFA16}.Debug|x64.ActiveCfg = Debug|x64 17 | {FE74624B-9FAB-4D3D-B7B8-649885FBFA16}.Debug|x64.Build.0 = Debug|x64 18 | {FE74624B-9FAB-4D3D-B7B8-649885FBFA16}.Debug|x86.ActiveCfg = Debug|Win32 19 | {FE74624B-9FAB-4D3D-B7B8-649885FBFA16}.Debug|x86.Build.0 = Debug|Win32 20 | {FE74624B-9FAB-4D3D-B7B8-649885FBFA16}.Release|x64.ActiveCfg = Release|x64 21 | {FE74624B-9FAB-4D3D-B7B8-649885FBFA16}.Release|x64.Build.0 = Release|x64 22 | {FE74624B-9FAB-4D3D-B7B8-649885FBFA16}.Release|x86.ActiveCfg = Release|Win32 23 | {FE74624B-9FAB-4D3D-B7B8-649885FBFA16}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {F32D4B3F-2B93-4044-A209-2E0EC6059720} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /libsoldout/mkd2latex.1: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (c) 2009 - 2016 Natacha Porté 3 | .\" 4 | .\" Permission to use, copy, modify, and distribute this software for any 5 | .\" purpose with or without fee is hereby granted, provided that the above 6 | .\" copyright notice and this permission notice appear in all copies. 7 | .\" 8 | .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | .\" 16 | .Dd March 1, 2016 17 | .Dt MKD2LATEX 1 18 | .Os 19 | .Sh NAME 20 | .Nm mkd2latex 21 | .Nd convert a markdown document into LaTex 22 | .Sh SYNOPSIS 23 | .Nm 24 | .Op Ar file 25 | .Sh DESCRIPTION 26 | .Nm 27 | utility reads 28 | .Ar file 29 | and generates LaTex input from markdown source. 30 | If unspecified, 31 | .Ar file 32 | is taken to be standard input. 33 | .Sh EXIT STATUS 34 | .Ex -std 35 | .Sh SEE ALSO 36 | .Xr mkd2html 1 , 37 | .Xr mkd2man 1 38 | .Sh AUTHORS 39 | .An -nosplit 40 | .Nm 41 | was written by 42 | .An Natasha Qo Kerensikova Qc Porte Aq Mt natacha@instinctive.eu , 43 | .An Baptiste Daroussin Aq Mt bapt@freebsd.org 44 | and 45 | .An Michael Huang . 46 | Manual page was originally written by 47 | .An Massimo Manghi Aq Mt mxmanghi@apache.org , 48 | and rewritten to mdoc format by 49 | .An Svyatoslav Mishyn Aq Mt juef@openmailbox.org . 50 | -------------------------------------------------------------------------------- /libsoldout/CHANGES: -------------------------------------------------------------------------------- 1 | This is the changelog for libsoldout project. 2 | Please note that this is a user-centered condensed changelog; for an 3 | exhaustive list of changes, please consult the repository, I don't see the 4 | point of duplicating this information here, while on the other hand I have 5 | often missed a simple change list containing only what affect users of 6 | release versions. 7 | 8 | 9 | ## Changes from v1.3 to v1.4 ## 10 | 11 | * Fix a bug in rendering nested block lists 12 | * Fix build with GCC 6 and other minor warnings 13 | * Build a static library too 14 | * Significantly improve documentation 15 | * Improvement of mkd2man 16 | * Add an amalgamation (C sources to include directly in a project) 17 | 18 | 19 | ## Changes from v1.2 to v1.3 ## 20 | 21 | * New example renderer that outputs LaTeX. 22 | 23 | 24 | ## Changes from v1.1 to v1.2 ## 25 | 26 | * Name change to libsoldout 27 | 28 | * Bug fixes: 29 | + various issues with matching emphasis delimiter have been fixed 30 | + better compatibility with non-gcc compilers 31 | + GNU make now uses correct compiler options to generate PIC code 32 | 33 | 34 | ## Changes from v1.0 to v1.1 ## 35 | 36 | * New features: 37 | + new callbacks for document prolog and epilog, 38 | + support of PHP-Markdown-like tables, through 3 new callbacks. 39 | 40 | * Several bug fixes, most notably: 41 | + span-level entities are now correctly recognized in headers, 42 | + empty ATX-styles header (i.e. lines containing only '#' charcters) 43 | no longer crash the library 44 | + empty documents are now correctly handled 45 | + blackslash escapes work in inline URIs, thereby allowing to include 46 | ')' characters in such URIs. 47 | -------------------------------------------------------------------------------- /libsoldout/make-amal: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # make-amal, generate the libsoldout amalgamation 3 | # inspired by make-bootstrap-jim (https://github.com/msteveb/jimtcl/blob/master/make-bootstrap-jim) 4 | # 5 | # Copyright (c) 2016 Svyatoslav Mishyn 6 | # 7 | # Permission to use, copy, modify, and/or distribute this software for 8 | # any purpose with or without fee is hereby granted, provided that the 9 | # above copyright notice and this permission notice appear in all 10 | # copies. 11 | # 12 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | # WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 14 | # WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 15 | # AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 16 | # DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 17 | # PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 18 | # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 19 | # PERFORMANCE OF THIS SOFTWARE. 20 | 21 | outputsource() { 22 | sed -e '/#include.*\".*\"/d' \ 23 | -e 's/\/\*.*\*\///' \ 24 | -e '/^[ ]*\/\*/,/\*\//d' \ 25 | -e 's/[ ]*$//' $1 26 | } 27 | 28 | header="/* Amalgamation version of libsoldout. See https://github.com/faelys/libsoldout */" 29 | license=`sed -n -e '3,17p' markdown.c` 30 | 31 | mkdir -p amalgamation 32 | 33 | 34 | # soldout.h 35 | 36 | echo "$header" > amalgamation/soldout.h 37 | exec >> amalgamation/soldout.h 38 | 39 | echo 40 | echo "$license" 41 | echo 42 | echo '#ifndef SOLDOUT_H' 43 | echo '#define SOLDOUT_H' 44 | echo 45 | 46 | for f in array.h buffer.h markdown.h renderers.h; do 47 | outputsource $f 48 | done 49 | 50 | echo 51 | echo '#endif /* SOLDOUT_H */' 52 | 53 | 54 | # soldout.c 55 | 56 | echo "$header" > amalgamation/soldout.c 57 | exec >> amalgamation/soldout.c 58 | 59 | echo 60 | echo "$license" 61 | echo 62 | echo '#include "soldout.h"' 63 | echo 64 | 65 | for f in array.c buffer.c markdown.c renderers.c; do 66 | outputsource $f 67 | done 68 | -------------------------------------------------------------------------------- /libsoldout/renderers.h: -------------------------------------------------------------------------------- 1 | /* renderers.h - example markdown renderers */ 2 | 3 | /* 4 | * Copyright (c) 2009, Natacha Porté 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef MARKDOWN_RENDERERS_H 20 | #define MARKDOWN_RENDERERS_H 21 | 22 | #include "markdown.h" 23 | 24 | 25 | /***************************** 26 | * EXPORTED HELPER FUNCTIONS * 27 | *****************************/ 28 | 29 | /* lus_attr_escape • copy the buffer entity-escaping '<', '>', '&' and '"' */ 30 | void 31 | lus_attr_escape(struct buf *ob, const char *src, size_t size); 32 | 33 | /* lus_body_escape • copy the buffer entity-escaping '<', '>' and '&' */ 34 | void 35 | lus_body_escape(struct buf *ob, const char *src, size_t size); 36 | 37 | 38 | 39 | /*********************** 40 | * RENDERER STRUCTURES * 41 | ***********************/ 42 | 43 | /* original markdown renderers */ 44 | extern const struct mkd_renderer mkd_html; /* HTML 4 renderer */ 45 | extern const struct mkd_renderer mkd_xhtml; /* XHTML 1.0 renderer */ 46 | 47 | /* renderers with some discount extensions */ 48 | extern const struct mkd_renderer discount_html; 49 | extern const struct mkd_renderer discount_xhtml; 50 | 51 | /* renderers with Natasha's own extensions */ 52 | extern const struct mkd_renderer nat_html; 53 | extern const struct mkd_renderer nat_xhtml; 54 | 55 | #endif /* ndef MARKDOWN_RENDERERS_H */ 56 | -------------------------------------------------------------------------------- /libsoldout/GNUmakefile: -------------------------------------------------------------------------------- 1 | # Makefile 2 | 3 | # Copyright (c) 2009, Natacha Porté 4 | # 5 | # Permission to use, copy, modify, and distribute this software for any 6 | # purpose with or without fee is hereby granted, provided that the above 7 | # copyright notice and this permission notice appear in all copies. 8 | # 9 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | DEPDIR = depends 18 | ALLDEPS = $(DEPDIR)/all 19 | 20 | AR ?= ar 21 | CC ?= cc 22 | CFLAGS ?= -g -O3 -Wall -Werror 23 | LDFLAGS ?= 24 | 25 | all: libsoldout.a libsoldout.so mkd2html mkd2latex mkd2man 26 | 27 | .PHONY: all amal clean 28 | 29 | 30 | # amalgamation 31 | amal: 32 | @./make-amal 33 | 34 | 35 | # libraries 36 | 37 | libsoldout.a: markdown.o array.o buffer.o renderers.o 38 | $(AR) rs $@ $^ 39 | 40 | libsoldout.so: libsoldout.so.1 41 | ln -s $^ $@ 42 | 43 | libsoldout.so.1: markdown.o array.o buffer.o renderers.o 44 | $(CC) $(LDFLAGS) -shared -Wl,-soname=$@ \ 45 | $^ -o $@ 46 | 47 | 48 | # executables 49 | 50 | mkd2html: mkd2html.o libsoldout.so 51 | $(CC) $(LDFLAGS) $^ -o $@ 52 | 53 | mkd2latex: mkd2latex.o libsoldout.so 54 | $(CC) $(LDFLAGS) $^ -o $@ 55 | 56 | mkd2man: mkd2man.o libsoldout.so 57 | $(CC) $(LDFLAGS) $^ -o $@ 58 | 59 | 60 | # Housekeeping 61 | 62 | benchmark: benchmark.o libsoldout.so 63 | $(CC) $(LDFLAGS) $^ -o $@ 64 | 65 | clean: 66 | rm -f *.o 67 | rm -f libsoldout.a libsoldout.so libsoldout.so.* 68 | rm -f mkd2html mkd2latex mkd2man benchmark 69 | rm -rf $(DEPDIR) 70 | 71 | 72 | # dependencies 73 | 74 | -include "$(ALLDEPS)" 75 | 76 | 77 | # generic object compilations 78 | 79 | .c.o: 80 | @mkdir -p $(DEPDIR) 81 | @touch $(ALLDEPS) 82 | @$(CC) -MM $< > $(DEPDIR)/$*.d 83 | @grep -q "$*.d" $(ALLDEPS) \ 84 | || echo "include \"$*.d\"" >> $(ALLDEPS) 85 | $(CC) $(CFLAGS) -std=c99 -fPIC -c -o $@ $< 86 | -------------------------------------------------------------------------------- /libsoldout/mkd2man.1: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (c) 2009 - 2016 Natacha Porté 3 | .\" 4 | .\" Permission to use, copy, modify, and distribute this software for any 5 | .\" purpose with or without fee is hereby granted, provided that the above 6 | .\" copyright notice and this permission notice appear in all copies. 7 | .\" 8 | .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | .\" 16 | .Dd March 30, 2016 17 | .Dt MKD2MAN 1 18 | .Os 19 | .Sh NAME 20 | .Nm mkd2man 21 | .Nd convert a markdown document into an mdoc manual page 22 | .Sh SYNOPSIS 23 | .Nm 24 | .Op Fl h 25 | .Op Fl d Ar date 26 | .Op Fl s Ar section 27 | .Op Fl t Ar title 28 | .Op Ar file 29 | .Sh DESCRIPTION 30 | .Nm 31 | utility reads 32 | .Ar file 33 | and generates 34 | .Xr mdoc 7 35 | input from markdown source. 36 | If unspecified, 37 | .Ar file 38 | is taken to be standard input. 39 | .Pp 40 | The options are as follows: 41 | .Bl -tag -width Ds 42 | .It Fl d , Fl Fl date 43 | set the document date 44 | .Pq Sq \&Dd 45 | to 46 | .Ar date 47 | .Po 48 | preferrably in 49 | .Qq Month Day, Year 50 | format 51 | .Pc . 52 | If unspecified, 53 | .Nm 54 | uses the file modification date or current date 55 | if reading is from standard input or if 56 | .Xr stat 2 57 | fails. 58 | .It Fl h , Fl Fl help 59 | display help text. 60 | .It Fl s , Fl Fl section 61 | set the document section 62 | .Pq Sq \&Dt 63 | to 64 | .Ar section . 65 | If unspecified, 66 | .Nm 67 | uses 68 | .Ar 1 . 69 | .It Fl t , Fl Fl title 70 | set the document title 71 | .Pq Sq \&Dt 72 | to 73 | .Ar title . 74 | If unspecified, 75 | .Nm 76 | uses the suffix-stripped filename part of 77 | .Ar file . 78 | When reading is from stdin the title must be specified. 79 | .El 80 | .Sh EXIT STATUS 81 | .Ex -std 82 | .Sh SEE ALSO 83 | .Xr mkd2html 1 , 84 | .Xr mkd2latex 1 , 85 | .Xr mdoc 7 86 | .Sh AUTHORS 87 | .An -nosplit 88 | .Nm 89 | was written by 90 | .An Natasha Qo Kerensikova Qc Porte Aq Mt natacha@instinctive.eu 91 | and 92 | .An Baptiste Daroussin Aq Mt bapt@freebsd.org . 93 | Manual page was originally written by 94 | .An Massimo Manghi Aq Mt mxmanghi@apache.org , 95 | and rewritten to mdoc format by 96 | .An Svyatoslav Mishyn Aq Mt juef@openmailbox.org . 97 | -------------------------------------------------------------------------------- /libsoldout/BSDmakefile: -------------------------------------------------------------------------------- 1 | # Makefile 2 | 3 | # Copyright (c) 2009, Natacha Porté 4 | # 5 | # Permission to use, copy, modify, and distribute this software for any 6 | # purpose with or without fee is hereby granted, provided that the above 7 | # copyright notice and this permission notice appear in all copies. 8 | # 9 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | DEPDIR = depends 18 | ALLDEPS = $(DEPDIR)/all 19 | 20 | AR ?= ar 21 | CC ?= cc 22 | CFLAGS ?= -g -O3 -Wall -Werror 23 | LDFLAGS ?= 24 | 25 | all: libsoldout.a libsoldout.so mkd2html mkd2latex mkd2man 26 | 27 | .PHONY: all amal clean 28 | 29 | 30 | # amalgamation 31 | amal: 32 | @./make-amal 33 | 34 | 35 | # libraries 36 | 37 | libsoldout.a: markdown.o array.o buffer.o renderers.o 38 | $(AR) rs $(.TARGET) $(.ALLSRC) 39 | 40 | libsoldout.so: libsoldout.so.1 41 | ln -s $(.ALLSRC) $(.TARGET) 42 | 43 | libsoldout.so.1: markdown.o array.o buffer.o renderers.o 44 | $(CC) $(LDFLAGS) -shared -Wl,-soname=$(.TARGET) \ 45 | $(.ALLSRC) -o $(.TARGET) 46 | 47 | 48 | # executables 49 | 50 | mkd2html: mkd2html.o libsoldout.so 51 | $(CC) $(LDFLAGS) $(.ALLSRC) -o $(.TARGET) 52 | 53 | mkd2latex: mkd2latex.o libsoldout.so 54 | $(CC) $(LDFLAGS) $(.ALLSRC) -o $(.TARGET) 55 | 56 | mkd2man: mkd2man.o libsoldout.so 57 | $(CC) $(LDFLAGS) $(.ALLSRC) -o $(.TARGET) 58 | 59 | 60 | # Housekeeping 61 | 62 | GNUmakefile: BSDmakefile 63 | @sed -e 's/^\(all:.*\)GNUmakefile /\1/' \ 64 | -e 's/\(rm .*\)GNUmakefile /\1/' \ 65 | -e '/^GNUmakefile:/,/^$$/d' \ 66 | -e 's/\$$(\.ALLSRC)/$$^/g' \ 67 | -e 's/\$$(\.IMPSRC)/$$ $(.TARGET) 74 | 75 | benchmark: benchmark.o libsoldout.so 76 | $(CC) $(LDFLAGS) $(.ALLSRC) -o $(.TARGET) 77 | 78 | clean: 79 | rm -f *.o 80 | rm -f libsoldout.a libsoldout.so libsoldout.so.* 81 | rm -f mkd2html mkd2latex mkd2man benchmark 82 | rm -rf $(DEPDIR) 83 | 84 | 85 | # dependencies 86 | 87 | .sinclude "$(ALLDEPS)" 88 | 89 | 90 | # generic object compilations 91 | 92 | .c.o: 93 | @mkdir -p $(DEPDIR) 94 | @touch $(ALLDEPS) 95 | @$(CC) -MM $(.IMPSRC) > $(DEPDIR)/$(.PREFIX).d 96 | @grep -q "$(.PREFIX).d" $(ALLDEPS) \ 97 | || echo ".include \"$(.PREFIX).d\"" >> $(ALLDEPS) 98 | $(CC) $(CFLAGS) -std=c99 -fPIC -c -o $(.TARGET) $(.IMPSRC) 99 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifdef CONMD_WINDOWS 5 | #include 6 | #endif 7 | 8 | #include "subhook/subhook.h" 9 | 10 | using namespace cmdmd::Literals; 11 | 12 | int main() 13 | { 14 | cmdmd::Init(); 15 | 16 | std::string output = R"( 17 | ____ 18 | 19 | # Heading 1 20 | **hello** _there_ 21 | 22 | ## Heading 2 23 | ### Heading 3 24 | #### Heading 4 25 | 26 | 27 | ____ 28 | How are you? 29 | And is this a new paragraph? 30 | 31 | You should need two new lines to make a new paragraph, 32 | so this should all be wrapped on one line. Of course that does depend on the width of the console (and not the line length) so check that as well. We may need more code for wrapping text. 33 | 34 | ```pawn 35 | hook OnPlayerConnect(playerid) 36 | { 37 | for (new i = 0; i != 10; ++i) 38 | { 39 | printf("Markdown!"); 40 | } 41 | } 42 | ``` 43 | 44 | ```cpp 45 | class MyClass : public BaseClass 46 | { 47 | private: 48 | virtual unsigned int Method(float a, Data const & b) override = 0; 49 | } 50 | ``` 51 | 52 | Output code with: 53 | 54 | \`\`\`cpp 55 | 56 | // Code goes here. 57 | 58 | \`\`\` 59 | 60 | 61 | 0x02000700, 0x00000000, 0x00FFFE00, 0x00000007, 0x00000000, 0x00000000, 62 | 0x02000700, 0x00000000, 0x00FFFE00, 0x00000007, 0x00000000, 0x00000000, 63 | 0x031D2775, 0x00000000, 0x00FFFE00, 0x00000007, 0x00000000, 0x00000000, 64 | 0x02000700, 0x00000000, 0x00FFFE00, 0x00000007, 0x00000000, 0x00000000, 65 | 0x02BE4788, 0x00000000, 0x00FFFE00, 0x00000007, 0x0003C000, 0x00000000, 66 | 0x02010771, 0x00000000, 0x00FFFE00, 0x00000007, 0x00000000, 0x00000000, 67 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 68 | 0x02000700, 0x00000000, 0x00FFFE00, 0x00000007, 0x00000000, 0x00000000, 69 | 0x02000700, 0x00000000, 0x00FFFE00, 0x00000007, 0x00000000, 0x00000000, 70 | 0x029A0FCE, 0x00000000, 0x00FFFE00, 0x00000007, 0x0000C000, 0x00000000, 71 | 72 | ``` 73 | 0x02000700, 0x00000000, 0x00FFFE00, 0x00000007, 74 | 0x02000700, 0x00000000, 0x00FFFE00, 0x00000007, 75 | 0x031D2775, 0x00000000, 0x00FFFE00, 0x00000007, 76 | 0x02000700, 0x00000000, 0x00FFFE00, 0x00000007, 77 | 0x02BE4788, 0x00000000, 0x00FFFE00, 0x00000007, 78 | 0x02010771, 0x00000000, 0x00FFFE00, 0x00000007, 79 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 80 | 0x02000700, 0x00000000, 0x00FFFE00, 0x00000007, 81 | 0x02000700, 0x00000000, 0x00FFFE00, 0x00000007, 82 | 0x029A0FCE, 0x00000000, 0x00FFFE00, 0x00000007, 83 | ``` 84 | 85 | * One 86 | * Two 87 | * Three 88 | * Four 89 | 90 | I hope you are OK, like `someCode()` is. 91 | 92 | 1. One 93 | 2. One 94 | 3. One1 95 | 96 | )"_cmdmd; 97 | 98 | //WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), output.c_str(), output.length(), NULL, NULL); 99 | std::cout << output; 100 | 101 | return 0; 102 | } 103 | 104 | -------------------------------------------------------------------------------- /console-colour.h: -------------------------------------------------------------------------------- 1 | #ifndef _CONSOLE_COLOUR 2 | #define _CONSOLE_COLOUR 3 | 4 | #if defined _WIN32 || defined WIN32 5 | #define CONMD_WINDOWS 1 6 | #endif 7 | 8 | #ifdef __cplusplus 9 | extern "C" 10 | { 11 | #endif 12 | 13 | #include 14 | 15 | enum STATE_E 16 | { 17 | STATE_NONE, 18 | STATE_ESC, // Saw `\x1B`. 19 | STATE_START, // Saw `[`. 20 | STATE_A00, // Saw a number. 21 | STATE_S0, // Saw a semi-colon. 22 | STATE_A10, // Saw a number. 23 | STATE_S1, // Saw a semi-colon. 24 | STATE_A20, // Saw a number. 25 | STATE_S2, // Saw a semi-colon. 26 | STATE_A30, // Saw a number. 27 | STATE_S3, // Saw a semi-colon. 28 | STATE_A40, // Saw a number. 29 | STATE_DONE, // Complete. 30 | STATE_EXTRA_NL, // Insert one extra new line. 31 | STATE_EXTRA_2NL, // Insert two extra new lines. 32 | STATE_EXTRA_SPACE, // Insert one extra space. 33 | STATE_SKIP, // Multi-byte character. 34 | }; 35 | 36 | #ifdef CONMD_WINDOWS 37 | 38 | #include 39 | #include 40 | 41 | struct console_colour_stream_s; 42 | 43 | typedef int (*OutputC_)(void * data, wchar_t c, struct console_colour_stream_s* const stream); 44 | typedef int (*OutputA_)(void * data, char const* c, int len, struct console_colour_stream_s* const stream); 45 | typedef int (*OutputW_)(void * data, wchar_t const* c, int len, struct console_colour_stream_s* const stream); 46 | typedef void (*OutputColour_)(void * data, unsigned short colour, struct console_colour_stream_s* const stream); 47 | 48 | struct console_colour_call_s 49 | { 50 | OutputC_ 51 | OutputC; 52 | 53 | OutputA_ 54 | OutputA; 55 | 56 | OutputW_ 57 | OutputW; 58 | 59 | OutputColour_ 60 | OutputColour; 61 | 62 | void* 63 | Data; 64 | }; 65 | 66 | struct console_colour_state_s 67 | { 68 | wchar_t 69 | UnicodeMask; 70 | 71 | enum STATE_E 72 | State; 73 | 74 | //bool 75 | // Coloured; 76 | 77 | WORD 78 | DefaultStyle, 79 | CurrentStyle; 80 | 81 | unsigned char 82 | Attr0, 83 | Attr1, 84 | Attr2, 85 | Attr3, 86 | Attr4; 87 | }; 88 | 89 | struct console_colour_stream_s 90 | { 91 | struct console_colour_call_s * 92 | Call; 93 | 94 | struct console_colour_state_s * 95 | State; 96 | }; 97 | 98 | #else 99 | 100 | struct console_colour_state_s 101 | { 102 | wchar_t 103 | UnicodeMask; 104 | 105 | enum STATE_E 106 | State; 107 | }; 108 | 109 | // Empty, just to exist. 110 | struct console_colour_stream_s 111 | { 112 | }; 113 | 114 | #endif 115 | 116 | extern struct console_colour_state_s 117 | gConsoleStreamState; 118 | 119 | void ReaddStreamHooks(); 120 | void RemoveStreamHooks(); 121 | 122 | int WriteColouredW(wchar_t const* s, int n, struct console_colour_stream_s* const stream); 123 | int WriteColouredA(char const* s, int n, struct console_colour_stream_s* const stream); 124 | void Backout(struct console_colour_stream_s* const stream); 125 | 126 | #ifdef __cplusplus 127 | } 128 | #endif 129 | 130 | #endif 131 | 132 | -------------------------------------------------------------------------------- /libsoldout/soldout.3: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (c) 2009 - 2016 Natacha Porté 3 | .\" 4 | .\" Permission to use, copy, modify, and distribute this software for any 5 | .\" purpose with or without fee is hereby granted, provided that the above 6 | .\" copyright notice and this permission notice appear in all copies. 7 | .\" 8 | .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | .\" 16 | .Dd March 29, 2016 17 | .Dt SOLDOUT 3 18 | .Os 19 | .Sh NAME 20 | .Nm soldout 21 | .Nd markdown parser library 22 | .Sh DESCRIPTION 23 | The 24 | .Nm 25 | library only performs the parsing of markdown input, the construction of 26 | the output is left to a renderer, which is a set of callback functions 27 | called when markdown elements are encountered. 28 | Pointers to these functions are gathered into a 29 | .Vt "struct mkd_renderer" 30 | along with some renderer-related data. 31 | .Pp 32 | Basic usage will be: 33 | .Bl -enum 34 | .It 35 | Create output, input buffers by 36 | .Fn bufnew 37 | function. 38 | .It 39 | Fill input buffer by 40 | .Fn bufput 41 | function. 42 | .It 43 | Create 44 | .Vt "struct mkd_renderer" 45 | or use provided renderer. 46 | .It 47 | Call 48 | .Fn markdown 49 | function. 50 | .It 51 | Process output buffer. 52 | .It 53 | Call 54 | .Fn bufrelease 55 | function to clean up buffers. 56 | .El 57 | .Sh EXAMPLES 58 | Simple example that uses first argument as a markdown string, 59 | converts it to an HTML and outputs it to stdout. 60 | .Bd -literal 61 | #include 62 | 63 | #include 64 | #include 65 | #include 66 | 67 | #define INPUT_UNIT 1024 68 | #define OUTPUT_UNIT 64 69 | 70 | int 71 | main(int argc, char *argv[]) 72 | { 73 | struct buf *ib, *ob; 74 | 75 | /* Make sure we have enough arguments. */ 76 | if (argc != 2) { 77 | return 1; 78 | } 79 | 80 | ib = bufnew(INPUT_UNIT); 81 | ob = bufnew(OUTPUT_UNIT); 82 | 83 | /* bufputs() is a wrapper over bufput() for NUL-terminated string. */ 84 | bufputs(ib, argv[1]); 85 | 86 | markdown(ob, ib, &mkd_html); 87 | 88 | /* Note the resulted data is not NUL-terminated string; 89 | * to make it use bufnullterm(). */ 90 | printf("%.*s", (int)ob->size, ob->data); 91 | 92 | bufrelease(ib); 93 | bufrelease(ob); 94 | return 0; 95 | } 96 | .Ed 97 | .Sh SEE ALSO 98 | .Xr soldout_array 3 , 99 | .Xr soldout_buffer 3 , 100 | .Xr soldout_markdown 3 , 101 | .Xr soldout_renderers 3 , 102 | .Lk http://daringfireball.net/projects/markdown/ John Gruber's markdown format 103 | .Sh AUTHORS 104 | .An -nosplit 105 | The 106 | .Nm 107 | library 108 | was written by 109 | .An Natasha Qo Kerensikova Qc Porte Aq Mt natacha@instinctive.eu . 110 | Manual page was originally written by 111 | .An Massimo Manghi Aq Mt mxmanghi@apache.org , 112 | and rewritten to mdoc format by 113 | .An Svyatoslav Mishyn Aq Mt juef@openmailbox.org . 114 | -------------------------------------------------------------------------------- /parsers/pawn.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "../include/console-markdown/Pawn.hpp" 7 | #include "../StringUtils.hpp" 8 | 9 | // This parser is NOT comprehensive. If you do silly things like escaped quotes with comments in 10 | // strings, it will probably break... That isn't the point - it's just a simple debugging aid. 11 | static std::regex 12 | sMatchers[] = { 13 | std::regex("/\\*(.|\\r|\\n)*?\\*/"), 14 | std::regex("//.*"), 15 | // Can do mutli-line pre-processor directives (ones with `\` at the end). 16 | std::regex("^\\s*#(.*?\\\\\\n)*.*"), 17 | std::regex("(!|!\\\\|\\\\!|\\\\)?\"(\"|.*?[^\\\\]\")|\\'\\\\?.\\'"), 18 | // Will detect most hex, binary, decimal, and floats. Won't detect complex things like 19 | // `.5e5`. Again, this is for SIMPLE examples... 20 | std::regex("\\b(0x[0-9a-fA-F]+|0b[01]+|\\-?[0-9]+(\\.[0-9]+)?)\\b"), 21 | // Now colours multiple symbols separated by spaces with the same code, just for efficiency. 22 | std::regex("([\\!\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\]\\{\\}\\~\\,\\<\\>\\/\\?\\|\\;\\:\\#]+[\\s\\r\\n]*)+"), 23 | std::regex("\\b(true|false|public|forward|native|const|static|stock|assert|break|case|continue|default|do|else|for|goto|if|return|sleep|state|switch|while|char|enum|new)\\b"), 24 | std::regex("\\b(hook|inline|foreign|global|timer|remotefunc|iterfunc|uvar|svar|__declspec|master_hook|master_task|master_ptask|group_hook|master_func|task|ptask|@foreign|@global|mhook|foreach|defer|repeat|stop|call|using|loadtext|yield|localfunc|broadcastfunc|targetfunc|final)\\b"), 25 | }; 26 | 27 | static char const * const 28 | sColours[] = { 29 | "\x1B[32m", // Line comment. 30 | "\x1B[32m", // Block comment. 31 | "\x1B[35m", // Pre-processor. 32 | "\x1B[31;1m", // Strings. 33 | "\x1B[35;1m", // Numbers. 34 | "\x1B[30;1m", // Symbols. 35 | "\x1B[36;1m", // Keyword. 36 | "\x1B[36;1m", // YSI. 37 | }; 38 | 39 | static char const * const 40 | sDefault = "\x1B[37;1m"; 41 | 42 | static size_t 43 | gYSI; 44 | 45 | static void 46 | DoRegex(std::stringstream & highlighted, std::string::const_iterator start, std::string::const_iterator end, size_t idx) 47 | { 48 | if (start == end) 49 | return; 50 | if (idx == gYSI) 51 | { 52 | // Out of regexes to check. 53 | highlighted << sDefault << std::string_view(&*start, std::distance(start, end)); 54 | return; 55 | } 56 | std::smatch m; 57 | while (std::regex_search(start, end, m, sMatchers[idx])) 58 | { 59 | size_t 60 | pos = m.position(), 61 | len = m.length(); 62 | std::string::const_iterator 63 | mid = start + pos; 64 | DoRegex(highlighted, start, mid, idx + 1); 65 | highlighted << sColours[idx] << std::string_view(&*mid, len); 66 | start = mid + len; 67 | } 68 | DoRegex(highlighted, start, end, idx + 1); 69 | } 70 | 71 | std::string 72 | cmdmd:: 73 | Pawn(std::string const & s, bool ysi) 74 | { 75 | gYSI = ysi ? 8 : 7; 76 | std::stringstream 77 | highlighted; 78 | highlighted << " "; 79 | DoRegex(highlighted, s.begin(), s.end(), 0); 80 | std::string 81 | ret = highlighted.str(); 82 | ReplaceAll(ret, "\t", " "); 83 | ReplaceAll(ret, "\n", "\n "); 84 | // Reset the colour. 85 | return ret + "\x1B[0m"; 86 | } 87 | 88 | std::string 89 | cmdmd:: 90 | Literals:: 91 | operator "" _pawn_syntax(char const * s, size_t len) 92 | { 93 | return Pawn(std::string(s, len)); 94 | } 95 | 96 | -------------------------------------------------------------------------------- /libsoldout/benchmark.c: -------------------------------------------------------------------------------- 1 | /* benchmark.c - main function for markdown module benchmarking */ 2 | 3 | /* 4 | * Copyright (c) 2009, Natacha Porté 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "markdown.h" 20 | #include "renderers.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #define READ_UNIT 1024 28 | #define OUTPUT_UNIT 64 29 | 30 | 31 | /* markdown_file • performs markdown transformation on FILE* */ 32 | static void 33 | benchmark(FILE *in, int nb) { 34 | struct buf *ib, *ob; 35 | size_t ret, i, n; 36 | if (!in) return; 37 | n = (nb <= 1) ? 1 : nb; 38 | 39 | /* reading everything */ 40 | ib = bufnew(READ_UNIT); 41 | bufgrow(ib, READ_UNIT); 42 | while ((ret = fread(ib->data + ib->size, 1, 43 | ib->asize - ib->size, in)) > 0) { 44 | ib->size += ret; 45 | bufgrow(ib, ib->size + READ_UNIT); } 46 | 47 | /* performing markdown parsing */ 48 | for (i = 0; i < n; i += 1) { 49 | ob = bufnew(OUTPUT_UNIT); 50 | ob->size = 0; 51 | markdown(ob, ib, &mkd_xhtml); 52 | bufrelease(ob); } 53 | 54 | /* cleanup */ 55 | bufrelease(ib); } 56 | 57 | 58 | 59 | /* main • main function, interfacing STDIO with the parser */ 60 | int 61 | main(int argc, char **argv) { 62 | int nb = 1, i, j, f, files = 0; 63 | FILE *in = 0; 64 | 65 | /* looking for a count number */ 66 | if (argc > 1) { 67 | for (i = 1; i < argc; i += 1) 68 | if (argv[i][0] == '-' && argv[i][1] == '-') 69 | nb = atoi(argv[i] + 2); 70 | else files += 1; 71 | if (nb < 1) { 72 | fprintf(stderr, "Usage: %s [--] " 73 | "[file] [file] ...\n", argv[0]); 74 | return 2; } } 75 | 76 | /* if no file is given, using stdin as the only file */ 77 | if (files <= 0) { 78 | in = stdin; 79 | files = 1; } 80 | 81 | /* performing the markdown */ 82 | f = 0; 83 | for (j = 0; j < files; j += 1) { 84 | if (in != stdin) { 85 | f += 1; 86 | while (f < argc 87 | && argv[f][0] == '-' && argv[f][1] == '-') 88 | f += 1; 89 | if (f >= argc) break; 90 | in = fopen(argv[f], "r"); 91 | if (!in) { 92 | fprintf(stderr, "Unable to open \"%s\": %s\n", 93 | argv[f], strerror(errno)); 94 | continue; } } 95 | benchmark(in, nb); 96 | if (in != stdin) fclose(in); } 97 | 98 | #ifdef BUFFER_STATS 99 | /* memory checks */ 100 | if (buffer_stat_nb) 101 | fprintf(stderr, "Warning: %ld buffers still active\n", 102 | buffer_stat_nb); 103 | if (buffer_stat_alloc_bytes) 104 | fprintf(stderr, "Warning: %zu bytes still allocated\n", 105 | buffer_stat_alloc_bytes); 106 | #endif 107 | return 0; } 108 | 109 | /* vim: set filetype=c: */ 110 | -------------------------------------------------------------------------------- /libsoldout/mkd2html.1: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (c) 2009 - 2016 Natacha Porté 3 | .\" 4 | .\" Permission to use, copy, modify, and distribute this software for any 5 | .\" purpose with or without fee is hereby granted, provided that the above 6 | .\" copyright notice and this permission notice appear in all copies. 7 | .\" 8 | .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | .\" 16 | .Dd March 1, 2016 17 | .Dt MKD2HTML 1 18 | .Os 19 | .Sh NAME 20 | .Nm mkd2html 21 | .Nd convert a markdown document into (X)HTML 22 | .Sh SYNOPSIS 23 | .Nm 24 | .Op Fl dHhmnx 25 | .Op Ar file 26 | .Sh DESCRIPTION 27 | .Nm 28 | utility reads 29 | .Ar file 30 | and generates (X)HTML input from markdown source. 31 | If unspecified, 32 | .Ar file 33 | is taken to be standard input. 34 | .Pp 35 | By default, 36 | .Nm 37 | implies 38 | .Fl H 39 | and 40 | .Fl m 41 | options. 42 | .Pp 43 | The options are as follows: 44 | .Bl -tag -width Ds 45 | .It Fl d , Fl Fl discount 46 | enable Discount extensions: 47 | .Bl -bullet -width 1m 48 | .It 49 | image size specification, by appending "=(width)x(height)" to the link 50 | .It 51 | pseudo-protocols in links: 52 | .Bl -bullet -width 1m 53 | .It 54 | abbr:description for ... 55 | .It 56 | class:name for ... 57 | .It 58 | id:name for ... 59 | .It 60 | raw:text for verbatim unprocessed text inclusion 61 | .El 62 | .It 63 | class blocks: blockquotes beginning with %class% will be rendered as a div of 64 | the given class(es) 65 | .El 66 | .Pp 67 | and PHP-Markdown-like tables. 68 | .It Fl H , Fl Fl html 69 | output HTML (self-closing tags like:
). 70 | .It Fl h , Fl Fl help 71 | display help text. 72 | .It Fl m , Fl Fl markdown 73 | disable all extensions and use strict markdown syntax. 74 | .It Fl n , Fl Fl natext 75 | enable Discount extensions and Natasha's own extensions: 76 | .Bl -bullet -width 1m 77 | .It 78 | id attribute for headers, using the syntax id#Header text 79 | .It 80 | class attribute for paragraphs, by putting class name(s) between parenthesis 81 | at the very beginning of the paragraph 82 | .It 83 | and spans, using respectively ++ and -- as delimiters (with 84 | emphasis-like restrictions, i.e. an opening delimiter cannot be followed by a 85 | whitespace, and a closing delimiter cannot be preceded by a whitespace) 86 | .It 87 | plain without attribute, using emphasis-like delimiter | 88 | .El 89 | .It Fl x , Fl Fl xhtml 90 | output XHTML (self-closing tags like:
). 91 | .El 92 | .Sh EXIT STATUS 93 | .Ex -std 94 | .Sh SEE ALSO 95 | .Xr mkd2latex 1 , 96 | .Xr mkd2man 1 97 | .Sh AUTHORS 98 | .An -nosplit 99 | .Nm 100 | was written by 101 | .An Natasha Qo Kerensikova Qc Porte Aq Mt natacha@instinctive.eu . 102 | Manual page was originally written by 103 | .An Massimo Manghi Aq Mt mxmanghi@apache.org , 104 | and rewritten to mdoc format by 105 | .An Svyatoslav Mishyn Aq Mt juef@openmailbox.org . 106 | -------------------------------------------------------------------------------- /parsers/cpp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "../include/console-markdown/CPP.hpp" 7 | #include "../StringUtils.hpp" 8 | 9 | // This parser is NOT comprehensive. If you do silly things like escaped quotes with comments in 10 | // strings, it will probably break... That isn't the point - it's just a simple debugging aid. 11 | static std::regex 12 | sMatchers[] = { 13 | std::regex("/\\*(.|\\r|\\n)*?\\*/"), 14 | std::regex("//.*"), 15 | // Can do mutli-line pre-processor directives (ones with `\` at the end). 16 | std::regex("^\\s*#(.*?\\\\\\n)*.*"), 17 | // Now handles multi-line raw strings. 18 | std::regex("R\"\\((.|\\r|\\n)*?\\)\"|\"\"|\".*?[^\\\\]\"|\\'\\\\?.\\'"), 19 | // Will detect most hex, decimal, and floats. Won't detect complex things like `.5e5` or 20 | // `0ULL`. Again, this is for SIMPLE examples... 21 | std::regex("\\b(0x[0-9a-fA-F]+|\\-?[0-9]+(\\.[0-9]+f?)?)\\b"), 22 | // Now colours multiple symbols separated by spaces with the same code, just for efficiency. 23 | std::regex("([\\!\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\]\\{\\}\\~\\,\\<\\>\\/\\?\\|\\;\\:\\#]+[\\s\\r\\n]*)+"), 24 | // The last bit detects types of the form `name_t` - the `_t` suffix is technically reserved by 25 | // the C++ spec. 26 | std::regex("\\b(auto|bool|break|case|catch|char|class|const|const_cast|continue|default|delete|do|double|dynamic_cast|else|enum|explicit|extern|false|float|for|friend|goto|if|inline|int|long|mutable|namespace|new|nullptr|operator|override|constexpr|decltype|private|protected|public|reinterpret_cast|return|short|signed|sizeof|static|static_cast|struct|switch|template|this|throw|true|try|typedef|typename|union|unsigned|virtual|void|while|[a-zA-Z_][a-zA-Z_0-9]*_t)\\b"), 27 | // alignas|alignof|and|and_eq|asm|atomic_cancel|atomic_commit|atomic_noexcept|audit|axiom|bitand|bitor|char16_t|char32_t|compl|concept|constexpr|co_await|co_return|co_yield|export|final|import|module|noexcept|not|not_eq|or|or_eq|reflexpr|register|requires|static_assert|synchronized|thread_local|transaction_safe|transaction_safe_dynamic|typeid|using|volatile|wchar_t|xor|xor_eq 28 | }; 29 | 30 | static char const * const 31 | sColours[] = { 32 | "\x1B[32m", // Line comment. 33 | "\x1B[32m", // Block comment. 34 | "\x1B[35m", // Pre-processor. 35 | "\x1B[31;1m", // Strings. 36 | "\x1B[35;1m", // Numbers. 37 | "\x1B[30;1m", // Symbols. 38 | "\x1B[36;1m", // Keyword. 39 | }; 40 | 41 | static char const * const 42 | sDefault = "\x1B[37;1m"; 43 | 44 | static void 45 | DoRegex(std::stringstream & highlighted, std::string::const_iterator start, std::string::const_iterator end, size_t idx) 46 | { 47 | if (start == end) 48 | return; 49 | if (idx == 7) 50 | { 51 | // Out of regexes to check. 52 | highlighted << sDefault << std::string_view(&*start, std::distance(start, end)); 53 | return; 54 | } 55 | std::smatch m; 56 | while (std::regex_search(start, end, m, sMatchers[idx])) 57 | { 58 | size_t 59 | pos = m.position(), 60 | len = m.length(); 61 | std::string::const_iterator 62 | mid = start + pos; 63 | DoRegex(highlighted, start, mid, idx + 1); 64 | highlighted << sColours[idx] << std::string_view(&*mid, len); 65 | start = mid + len; 66 | } 67 | DoRegex(highlighted, start, end, idx + 1); 68 | } 69 | 70 | std::string 71 | cmdmd:: 72 | CPP(std::string const & s) 73 | { 74 | std::stringstream 75 | highlighted; 76 | highlighted << " "; 77 | DoRegex(highlighted, s.begin(), s.end(), 0); 78 | std::string 79 | ret = highlighted.str(); 80 | ReplaceAll(ret, "\t", " "); 81 | ReplaceAll(ret, "\n", "\n "); 82 | // Reset the colour. 83 | return ret + "\x1B[0m"; 84 | } 85 | 86 | std::string 87 | cmdmd:: 88 | Literals:: 89 | operator "" _cpp_syntax(char const * s, size_t len) 90 | { 91 | return CPP(std::string(s, len)); 92 | } 93 | 94 | -------------------------------------------------------------------------------- /libsoldout/soldout_renderers.3: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (c) 2009 - 2016 Natacha Porté 3 | .\" 4 | .\" Permission to use, copy, modify, and distribute this software for any 5 | .\" purpose with or without fee is hereby granted, provided that the above 6 | .\" copyright notice and this permission notice appear in all copies. 7 | .\" 8 | .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | .\" 16 | .Dd April 13, 2016 17 | .Dt SOLDOUT_RENDERERS 3 18 | .Os 19 | .Sh NAME 20 | .Nm soldout_renderers , 21 | .Nm lus_attr_escape , 22 | .Nm lus_body_escape 23 | .Nd various markdown to HTML renderers for soldout 24 | .Sh SYNOPSIS 25 | .In renderers.h 26 | .Ft void 27 | .Fo lus_attr_escape 28 | .Fa "struct buf *ob" 29 | .Fa "const char *str" 30 | .Fa "size_t len" 31 | .Fc 32 | .Ft void 33 | .Fo lus_body_escape 34 | .Fa "struct buf *ob" 35 | .Fa "const char *str" 36 | .Fa "size_t len" 37 | .Fc 38 | .Vt extern const struct mkd_renderer mkd_html; 39 | .Vt extern const struct mkd_renderer mkd_xhtml; 40 | .Vt extern const struct mkd_renderer discount_html; 41 | .Vt extern const struct mkd_renderer discount_xhtml; 42 | .Vt extern const struct mkd_renderer nat_html; 43 | .Vt extern const struct mkd_renderer nat_xhtml; 44 | .Sh DESCRIPTION 45 | The 46 | .Fn lus_attr_escape 47 | and 48 | .Fn lus_body_escape 49 | functions escape all problematic characters in (X)HTML: 50 | .Ql < , 51 | .Ql > , 52 | .Ql & , 53 | .Ql \(dq ; 54 | and 55 | .Ql < , 56 | .Ql > , 57 | .Ql & 58 | respectively. 59 | They accept a string 60 | .Va str 61 | of the length 62 | .Va len 63 | and output into 64 | .Va ob 65 | buffer. 66 | .Pp 67 | All provided renderers come with two flavors, 68 | .Va _html 69 | producing HTML code (self-closing tags are rendered like this:
), 70 | and 71 | .Va _xhtml 72 | producing XHTML code (self-closing tags like
). 73 | .Pp 74 | .Va mkd_html 75 | and 76 | .Va mkd_xhtml 77 | implement standard markdown to (X)HTML translation without any extension. 78 | .Pp 79 | .Va discount_html 80 | and 81 | .Va discount_xhtml 82 | implement on top of the standard markdown some of the extensions 83 | found in Discount and PHP-Markdown-like tables. 84 | Here is a list of all extensions included in these renderers: 85 | .Bl -bullet -width 1m 86 | .It 87 | image size specification, by appending "=(width)x(height)" to the link 88 | .It 89 | pseudo-protocols in links: 90 | .Bl -bullet -width 1m 91 | .It 92 | abbr:description for ... 93 | .It 94 | class:name for ... 95 | .It 96 | id:name for ... 97 | .It 98 | raw:text for verbatim unprocessed text inclusion 99 | .El 100 | .It 101 | class blocks: blockquotes beginning with %class% will be rendered 102 | as a div of the given class(es) 103 | .El 104 | .Pp 105 | .Va nat_html 106 | and 107 | .Va nat_xhtml 108 | implement on top of Discount extensions and Natasha's own extensions. 109 | Here is a list of these extensions: 110 | .Bl -bullet -width 1m 111 | .It 112 | id attribute for headers, using the syntax id#Header text 113 | .It 114 | class attribute for paragraphs, by putting class name(s) 115 | between parenthesis at the very beginning of the paragraph 116 | .It 117 | and spans, using respectively ++ and -- as delimiters (with 118 | emphasis-like restrictions, i.e. an opening delimiter cannot be followed by a 119 | whitespace, and a closing delimiter cannot be preceded by a whitespace) 120 | .It 121 | plain without attribute, using emphasis-like delimiter | 122 | .El 123 | .Sh RETURN VALUES 124 | The 125 | .Fn lus_attr_escape 126 | and 127 | .Fn lus_body_escape 128 | functions do not return a value. 129 | .Sh SEE ALSO 130 | .Xr soldout 3 , 131 | .Xr soldout_buffer 3 , 132 | .Xr soldout_markdown 3 133 | .Sh AUTHORS 134 | .An -nosplit 135 | The 136 | .Nm soldout 137 | library 138 | was written by 139 | .An Natasha Qo Kerensikova Qc Porte Aq Mt natacha@instinctive.eu . 140 | Manual page was originally written by 141 | .An Massimo Manghi Aq Mt mxmanghi@apache.org , 142 | and rewritten to mdoc format by 143 | .An Svyatoslav Mishyn Aq Mt juef@openmailbox.org . 144 | -------------------------------------------------------------------------------- /ColouredBuffer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "console-colour.h" 5 | 6 | #ifdef CONMD_WINDOWS 7 | #include 8 | #endif 9 | 10 | namespace cmdmd 11 | { 12 | template , class A = std::allocator> 13 | class ColouredBuffer : public std::basic_stringbuf 14 | { 15 | public: 16 | static void StandardInstall(); 17 | 18 | private: 19 | typedef 20 | std::basic_stringbuf 21 | stringbuf_type; 22 | 23 | typedef 24 | std::basic_ostream 25 | ostream_type; 26 | 27 | typedef 28 | std::basic_streambuf 29 | streambuf_type; 30 | 31 | typedef 32 | typename T::int_type 33 | int_type; 34 | 35 | typedef 36 | typename T::pos_type 37 | pos_type; 38 | 39 | typedef 40 | typename T::off_type 41 | off_type; 42 | 43 | typedef 44 | C 45 | char_type; 46 | 47 | typedef 48 | T 49 | traits_type; 50 | 51 | typedef 52 | std::streamsize 53 | size_type; 54 | 55 | streambuf_type & 56 | buffer_; 57 | 58 | ColouredBuffer() = delete; 59 | 60 | #ifdef CONMD_WINDOWS 61 | bool const 62 | coloured_; 63 | 64 | ostream_type* 65 | src_; 66 | 67 | HANDLE 68 | handle_; 69 | 70 | struct console_colour_stream_s 71 | stream_; 72 | 73 | struct console_colour_call_s 74 | call_; 75 | 76 | template 77 | size_type WriteColouredX(C2 const * data, size_type len); 78 | 79 | template <> 80 | size_type WriteColouredX(char const * data, size_type len) 81 | { 82 | return ::WriteColouredA(data, (int)len, &stream_); 83 | } 84 | 85 | template <> 86 | size_type WriteColouredX(wchar_t const * data, size_type len) 87 | { 88 | return ::WriteColouredW(data, (int)len, &stream_); 89 | } 90 | 91 | static int OutputC(void* data, wchar_t c, struct console_colour_stream_s* const stream); 92 | static int OutputA(void* data, char const* c, int len, struct console_colour_stream_s* const stream); 93 | static int OutputW(void* data, wchar_t const* c, int len, struct console_colour_stream_s* const stream); 94 | static void OutputColour(void* data, unsigned short colour, struct console_colour_stream_s* const stream); 95 | 96 | #endif 97 | 98 | public: 99 | 100 | #ifdef CONMD_WINDOWS 101 | // cons 102 | ColouredBuffer(ostream_type & src, bool coloured, bool err = false) 103 | : 104 | buffer_(*src.rdbuf()), 105 | coloured_(coloured), 106 | src_(&src), 107 | handle_(GetStdHandle(err ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE)), 108 | call_{ 109 | &OutputC, 110 | &OutputA, 111 | &OutputW, 112 | &OutputColour, 113 | (void *)this 114 | }, 115 | stream_{ 116 | &call_, 117 | &gConsoleStreamState, 118 | } 119 | { 120 | src_->rdbuf(this); 121 | CONSOLE_SCREEN_BUFFER_INFO 122 | info; 123 | HANDLE 124 | handle = GetStdHandle(STD_OUTPUT_HANDLE); 125 | 126 | GetConsoleScreenBufferInfo(handle, &info); 127 | stream_.State->CurrentStyle = stream_.State->DefaultStyle = info.wAttributes; 128 | } 129 | 130 | // dest 131 | ~ColouredBuffer() 132 | { 133 | if (src_) 134 | { 135 | src_->rdbuf(&buffer_); 136 | } 137 | HANDLE 138 | handle = GetStdHandle(STD_OUTPUT_HANDLE); 139 | SetConsoleTextAttribute(handle, stream_.State->DefaultStyle); 140 | } 141 | #else 142 | // cons 143 | ColouredBuffer(ostream_type & src, bool coloured, bool err) 144 | : 145 | buffer_(*src.rdbuf()) 146 | { 147 | } 148 | 149 | // dest 150 | ~ColouredBuffer() 151 | { 152 | } 153 | #endif 154 | 155 | int_type overflow(int_type c) override 156 | { 157 | #ifdef CONMD_WINDOWS 158 | char_type 159 | s = (char_type)c; 160 | return (int_type)WriteColouredX(&s, 1); 161 | #else 162 | return buffer_.sputc(c); 163 | #endif 164 | } 165 | 166 | protected: 167 | size_type xsputn(char_type const * s, size_type n) override 168 | { 169 | #ifdef CONMD_WINDOWS 170 | return (size_type)WriteColouredX(s, n); 171 | #else 172 | return buffer_.sputn(s, n); 173 | #endif 174 | } 175 | 176 | int sync() override 177 | { 178 | #ifdef CONMD_WINDOWS 179 | // If they flush mid sequence there's not a lot we can do about it! We can't keep buffering 180 | // characters under the hood, even via a state machine, if they explicitly requested a flush. 181 | ::Backout(&stream_); 182 | return 0; 183 | #else 184 | return buffer_.pubsync(); 185 | #endif 186 | } 187 | }; 188 | 189 | #ifdef CONMD_WINDOWS 190 | #pragma warning(push) 191 | #pragma warning(disable: 4661) 192 | #endif 193 | 194 | template class ColouredBuffer; 195 | template class ColouredBuffer; 196 | 197 | #ifdef CONMD_WINDOWS 198 | #pragma warning(pop) 199 | #endif 200 | } 201 | 202 | 203 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /dllmain.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 2 | // Windows Header Files 3 | #include 4 | 5 | #include "console-colour.h" 6 | #include "subhook/subhook.h" 7 | 8 | #define CONSOLECOLOUR_API __declspec(dllexport) 9 | 10 | static int 11 | OutputC(void* data, wchar_t c, struct console_colour_stream_s* const stream) 12 | { 13 | return WriteConsoleW(data, &c, 1, NULL, NULL) ? 1 : 0; 14 | } 15 | 16 | static int 17 | OutputA(void* data, char const* c, int len, struct console_colour_stream_s* const stream) 18 | { 19 | DWORD 20 | ret = 0; 21 | WriteConsoleA(data, c, len, &ret, NULL); 22 | return ret; 23 | } 24 | 25 | static int 26 | OutputW(void* data, wchar_t const* c, int len, struct console_colour_stream_s* const stream) 27 | { 28 | DWORD 29 | ret = 0; 30 | WriteConsoleW(data, c, len, &ret, NULL); 31 | return ret; 32 | } 33 | 34 | static void 35 | OutputColour(void* data, unsigned short colour, struct console_colour_stream_s* const stream) 36 | { 37 | SetConsoleTextAttribute(data, colour); 38 | } 39 | 40 | static struct console_colour_call_s 41 | gConsoleStreamCall = { 42 | &OutputC, 43 | &OutputA, 44 | &OutputW, 45 | &OutputColour, 46 | }; 47 | 48 | static struct console_colour_stream_s 49 | gStream = { 50 | &gConsoleStreamCall, 51 | &gConsoleStreamState, 52 | }; 53 | 54 | extern "C" 55 | { 56 | BOOL WINAPI 57 | Hook_WriteConsoleA( 58 | _In_ HANDLE hConsoleOutput, 59 | _In_reads_(nNumberOfCharsToWrite) CONST VOID* lpBuffer, 60 | _In_ DWORD nNumberOfCharsToWrite, 61 | _Out_opt_ LPDWORD lpNumberOfCharsWritten, 62 | _Reserved_ LPVOID lpReserved 63 | ) 64 | { 65 | if (nNumberOfCharsToWrite == 0) 66 | { 67 | if (lpNumberOfCharsWritten) 68 | { 69 | *lpNumberOfCharsWritten = 0; 70 | } 71 | return TRUE; 72 | } 73 | gStream.Call->Data = hConsoleOutput; 74 | int 75 | num = WriteColouredA((char const*)lpBuffer, nNumberOfCharsToWrite, &gStream); 76 | if (lpNumberOfCharsWritten) 77 | { 78 | *lpNumberOfCharsWritten = nNumberOfCharsToWrite; 79 | } 80 | return TRUE; 81 | } 82 | 83 | BOOL WINAPI 84 | Hook_WriteConsoleW( 85 | _In_ HANDLE hConsoleOutput, 86 | _In_reads_(nNumberOfCharsToWrite) CONST VOID* lpBuffer, 87 | _In_ DWORD nNumberOfCharsToWrite, 88 | _Out_opt_ LPDWORD lpNumberOfCharsWritten, 89 | _Reserved_ LPVOID lpReserved 90 | ) 91 | { 92 | if (nNumberOfCharsToWrite == 0) 93 | { 94 | if (lpNumberOfCharsWritten) 95 | { 96 | *lpNumberOfCharsWritten = 0; 97 | } 98 | return TRUE; 99 | } 100 | gStream.Call->Data = hConsoleOutput; 101 | int 102 | num = WriteColouredW((wchar_t const*)lpBuffer, nNumberOfCharsToWrite, &gStream); 103 | if (lpNumberOfCharsWritten) 104 | { 105 | *lpNumberOfCharsWritten = nNumberOfCharsToWrite; 106 | } 107 | return TRUE; 108 | } 109 | 110 | subhook_t 111 | WriteConsoleA_ = 0, 112 | WriteConsoleW_ = 0; 113 | 114 | void 115 | RemoveStreamHooks() 116 | { 117 | subhook_remove(WriteConsoleA_); 118 | subhook_remove(WriteConsoleW_); 119 | } 120 | 121 | void 122 | ReaddStreamHooks() 123 | { 124 | subhook_install(WriteConsoleW_); 125 | subhook_install(WriteConsoleA_); 126 | } 127 | 128 | CONSOLECOLOUR_API void 129 | InstallConsoleColour(void) 130 | { 131 | CONSOLE_SCREEN_BUFFER_INFO 132 | info; 133 | HANDLE 134 | handle = GetStdHandle(STD_OUTPUT_HANDLE); 135 | 136 | GetConsoleScreenBufferInfo(handle, &info); 137 | gStream.State->CurrentStyle = gStream.State->DefaultStyle = info.wAttributes; 138 | 139 | if (!WriteConsoleA_) 140 | { 141 | WriteConsoleA_ = subhook_new((void*)&WriteConsoleA, (void*)&Hook_WriteConsoleA, SUBHOOK_64BIT_OFFSET); 142 | } 143 | if (WriteConsoleA_) 144 | { 145 | subhook_install(WriteConsoleA_); 146 | } 147 | if (!WriteConsoleW_) 148 | { 149 | WriteConsoleW_ = subhook_new((void*)&WriteConsoleW, (void*)&Hook_WriteConsoleW, SUBHOOK_64BIT_OFFSET); 150 | } 151 | if (WriteConsoleW_) 152 | { 153 | subhook_install(WriteConsoleW_); 154 | } 155 | } 156 | 157 | CONSOLECOLOUR_API void 158 | UninstallConsoleColour(void) 159 | { 160 | HANDLE 161 | handle = GetStdHandle(STD_OUTPUT_HANDLE); 162 | SetConsoleTextAttribute(handle, gStream.State->DefaultStyle); 163 | if (WriteConsoleA_) 164 | { 165 | subhook_remove(WriteConsoleA_); 166 | subhook_free(WriteConsoleA_); 167 | WriteConsoleA_ = 0; 168 | } 169 | if (WriteConsoleW_) 170 | { 171 | subhook_remove(WriteConsoleW_); 172 | subhook_free(WriteConsoleW_); 173 | WriteConsoleW_ = 0; 174 | } 175 | } 176 | } 177 | 178 | BOOL APIENTRY 179 | DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) 180 | { 181 | return TRUE; 182 | } 183 | 184 | -------------------------------------------------------------------------------- /libsoldout/array.h: -------------------------------------------------------------------------------- 1 | /* array.h - automatic dynamic array for pointers */ 2 | 3 | /* 4 | * Copyright (c) 2008, Natacha Porté 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef LITHIUM_ARRAY_H 20 | #define LITHIUM_ARRAY_H 21 | 22 | #include 23 | 24 | 25 | /******************** 26 | * TYPE DEFINITIONS * 27 | ********************/ 28 | 29 | /* struct array • generic linear array */ 30 | struct array { 31 | void* base; 32 | int size; 33 | int asize; 34 | size_t unit; }; 35 | 36 | 37 | /* struct parray • array of pointers */ 38 | struct parray { 39 | void ** item; 40 | int size; 41 | int asize; }; 42 | 43 | 44 | /* array_cmp_fn • comparison functions for sorted arrays */ 45 | typedef int (*array_cmp_fn)(void *key, void *array_entry); 46 | 47 | 48 | 49 | /*************************** 50 | * GENERIC ARRAY FUNCTIONS * 51 | ***************************/ 52 | 53 | /* arr_adjust • shrink the allocated memory to fit exactly the needs */ 54 | int 55 | arr_adjust(struct array *); 56 | 57 | /* arr_free • frees the structure contents (buf NOT the struct itself) */ 58 | void 59 | arr_free(struct array *); 60 | 61 | /* arr_grow • increases the array size to fit the given number of elements */ 62 | int 63 | arr_grow(struct array *, int); 64 | 65 | /* arr_init • initialization of the contents of the struct */ 66 | void 67 | arr_init(struct array *, size_t); 68 | 69 | /* arr_insert • inserting elements nb before the nth one */ 70 | int 71 | arr_insert(struct array *, int nb, int n); 72 | 73 | /* arr_item • returns a pointer to the n-th element */ 74 | void * 75 | arr_item(struct array *, int); 76 | 77 | /* arr_newitem • returns the index of a new element appended to the array */ 78 | int 79 | arr_newitem(struct array *); 80 | 81 | /* arr_remove • removes the n-th elements of the array */ 82 | void 83 | arr_remove(struct array *, int); 84 | 85 | /* arr_sorted_find • O(log n) search in a sorted array, returning entry */ 86 | /* equivalent to bsearch(key, arr->base, arr->size, arr->unit, cmp) */ 87 | void * 88 | arr_sorted_find(struct array *, void *key, array_cmp_fn cmp); 89 | 90 | /* arr_sorted_find_i • O(log n) search in a sorted array, 91 | * returning index of the smallest element larger than the key */ 92 | int 93 | arr_sorted_find_i(struct array *, void *key, array_cmp_fn cmp); 94 | 95 | 96 | /*************************** 97 | * POINTER ARRAY FUNCTIONS * 98 | ***************************/ 99 | 100 | /* parr_adjust • shrinks the allocated memory to fit exactly the needs */ 101 | int 102 | parr_adjust(struct parray *); 103 | 104 | /* parr_free • frees the structure contents (buf NOT the struct itself) */ 105 | void 106 | parr_free(struct parray *); 107 | 108 | /* parr_grow • increases the array size to fit the given number of elements */ 109 | int 110 | parr_grow(struct parray *, int); 111 | 112 | /* parr_init • initialization of the struct (which is equivalent to zero) */ 113 | void 114 | parr_init(struct parray *); 115 | 116 | /* parr_insert • inserting nb elements before the nth one */ 117 | int 118 | parr_insert(struct parray *, int nb, int n); 119 | 120 | /* parr_pop • pops the last item of the array and returns it */ 121 | void * 122 | parr_pop(struct parray *); 123 | 124 | /* parr_push • pushes a pointer at the end of the array (= append) */ 125 | int 126 | parr_push(struct parray *, void *); 127 | 128 | /* parr_remove • removes the n-th element of the array and returns it */ 129 | void * 130 | parr_remove(struct parray *, int); 131 | 132 | /* parr_sorted_find • O(log n) search in a sorted array, returning entry */ 133 | void * 134 | parr_sorted_find(struct parray *, void *key, array_cmp_fn cmp); 135 | 136 | /* parr_sorted_find_i • O(log n) search in a sorted array, 137 | * returning index of the smallest element larger than the key */ 138 | int 139 | parr_sorted_find_i(struct parray *, void *key, array_cmp_fn cmp); 140 | 141 | /* parr_top • returns the top the stack (i.e. the last element of the array) */ 142 | void * 143 | parr_top(struct parray *); 144 | 145 | 146 | #endif /* ndef LITHIUM_ARRAY_H */ 147 | 148 | /* vim: set filetype=c: */ 149 | -------------------------------------------------------------------------------- /libsoldout/markdown.h: -------------------------------------------------------------------------------- 1 | /* markdown.h - generic markdown parser */ 2 | 3 | /* 4 | * Copyright (c) 2009, Natacha Porté 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef LITHIUM_MARKDOWN_H 20 | #define LITHIUM_MARKDOWN_H 21 | 22 | #include "buffer.h" 23 | 24 | 25 | /******************** 26 | * TYPE DEFINITIONS * 27 | ********************/ 28 | 29 | /* mkd_autolink • type of autolink */ 30 | enum mkd_autolink { 31 | MKDA_NOT_AUTOLINK, /* used internally when it is not an autolink*/ 32 | MKDA_NORMAL, /* normal http/https/ftp link */ 33 | MKDA_EXPLICIT_EMAIL, /* e-mail link with explicit mailto: */ 34 | MKDA_IMPLICIT_EMAIL /* e-mail link without mailto: */ 35 | }; 36 | 37 | /* mkd_renderer • functions for rendering parsed data */ 38 | struct mkd_renderer { 39 | /* document level callbacks */ 40 | void (*prolog)(struct buf *ob, void *opaque); 41 | void (*epilog)(struct buf *ob, void *opaque); 42 | 43 | /* block level callbacks - NULL skips the block */ 44 | void (*blockcode)(struct buf *ob, struct buf *text, void *opaque); 45 | void (*fencedcode)(struct buf *ob, struct buf *text, char *name, size_t namelen, void *opaque); 46 | void (*blockquote)(struct buf *ob, struct buf *text, void *opaque); 47 | void (*blockhtml)(struct buf *ob, struct buf *text, void *opaque); 48 | void (*header)(struct buf *ob, struct buf *text, 49 | int level, void *opaque); 50 | void (*hrule)(struct buf *ob, void *opaque); 51 | void (*list)(struct buf *ob, struct buf *text, int flags, void *opaque); 52 | void (*listitem)(struct buf *ob, struct buf *text, 53 | int flags, void *opaque); 54 | void (*paragraph)(struct buf *ob, struct buf *text, void *opaque); 55 | void (*table)(struct buf *ob, struct buf *head_row, struct buf *rows, 56 | void *opaque); 57 | void (*table_cell)(struct buf *ob, struct buf *text, int flags, 58 | void *opaque); 59 | void (*table_row)(struct buf *ob, struct buf *cells, int flags, 60 | void *opaque); 61 | 62 | /* span level callbacks - NULL or return 0 prints the span verbatim */ 63 | int (*autolink)(struct buf *ob, struct buf *link, 64 | enum mkd_autolink type, void *opaque); 65 | int (*codespan)(struct buf *ob, struct buf *text, void *opaque); 66 | int (*double_emphasis)(struct buf *ob, struct buf *text, 67 | char c, void *opaque); 68 | int (*emphasis)(struct buf *ob, struct buf *text, char c,void*opaque); 69 | int (*image)(struct buf *ob, struct buf *link, struct buf *title, 70 | struct buf *alt, void *opaque); 71 | int (*linebreak)(struct buf *ob, void *opaque); 72 | int (*link)(struct buf *ob, struct buf *link, struct buf *title, 73 | struct buf *content, void *opaque); 74 | int (*raw_html_tag)(struct buf *ob, struct buf *tag, void *opaque); 75 | int (*triple_emphasis)(struct buf *ob, struct buf *text, 76 | char c, void *opaque); 77 | 78 | /* low level callbacks - NULL copies input directly into the output */ 79 | void (*entity)(struct buf *ob, struct buf *entity, void *opaque); 80 | void (*normal_text)(struct buf *ob, struct buf *text, void *opaque); 81 | 82 | /* renderer data */ 83 | int max_work_stack; /* prevent arbitrary deep recursion, cf README */ 84 | const char *emph_chars; /* chars that trigger emphasis rendering */ 85 | void *opaque; /* opaque data send to every rendering callback */ 86 | }; 87 | 88 | 89 | 90 | /********* 91 | * FLAGS * 92 | *********/ 93 | 94 | /* list/listitem flags */ 95 | #define MKD_LIST_ORDERED 1 96 | #define MKD_LI_BLOCK 2 /*
  • containing block data */ 97 | 98 | /* table cell flags */ 99 | #define MKD_CELL_ALIGN_DEFAULT 0 100 | #define MKD_CELL_ALIGN_LEFT 1 101 | #define MKD_CELL_ALIGN_RIGHT 2 102 | #define MKD_CELL_ALIGN_CENTER 3 /* LEFT | RIGHT */ 103 | #define MKD_CELL_ALIGN_MASK 3 104 | #define MKD_CELL_HEAD 4 105 | 106 | 107 | 108 | /********************** 109 | * EXPORTED FUNCTIONS * 110 | **********************/ 111 | 112 | /* markdown • parses the input buffer and renders it into the output buffer */ 113 | void 114 | markdown(struct buf *ob, struct buf *ib, const struct mkd_renderer *rndr); 115 | 116 | 117 | #endif /* ndef LITHIUM_MARKDOWN_H */ 118 | 119 | /* vim: set filetype=c: */ 120 | -------------------------------------------------------------------------------- /libsoldout/mkd2html.c: -------------------------------------------------------------------------------- 1 | /* main.c - main function for markdown module testing */ 2 | 3 | /* 4 | * Copyright (c) 2009, Natacha Porté 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "markdown.h" 20 | #include "renderers.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #define READ_UNIT 1024 29 | #define OUTPUT_UNIT 64 30 | 31 | 32 | /* usage • print the option list */ 33 | static void 34 | usage(FILE *out, const char *name) { 35 | fprintf(out, "Usage: %s [-h | -x] [-d | -m | -n] [input-file]\n\n", 36 | name); 37 | fprintf(out, "\t-d, --discount\n" 38 | "\t\tEnable some Discount extensions (image size specification,\n" 39 | "\t\tclass blocks and 'abbr:', 'class:', 'id:' and 'raw:'\n" 40 | "\t\tpseudo-protocols)\n" 41 | "\t-H, --html\n" 42 | "\t\tOutput HTML-style self-closing tags (e.g.
    )\n" 43 | "\t-h, --help\n" 44 | "\t\tDisplay this help text and exit without further processing\n" 45 | "\t-m, --markdown\n" 46 | "\t\tDisable all extensions and use strict markdown syntax\n" 47 | "\t-n, --natext\n" 48 | "\t\tEnable support Discount extensions and Natasha's own\n" 49 | "\t\textensions (id header attribute, class paragraph attribute,\n" 50 | "\t\t'ins' and 'del' elements, and plain span elements)\n" 51 | "\t-x, --xhtml\n" 52 | "\t\tOutput XHTML-style self-closing tags (e.g.
    )\n"); } 53 | 54 | 55 | 56 | /* main • main function, interfacing STDIO with the parser */ 57 | int 58 | main(int argc, char **argv) { 59 | struct buf *ib, *ob; 60 | size_t ret; 61 | FILE *in = stdin; 62 | const struct mkd_renderer *hrndr, *xrndr; 63 | const struct mkd_renderer **prndr; 64 | int ch, argerr, help; 65 | struct option longopts[] = { 66 | { "discount", no_argument, 0, 'd' }, 67 | { "html", no_argument, 0, 'H' }, 68 | { "help", no_argument, 0, 'h' }, 69 | { "markdown", no_argument, 0, 'm' }, 70 | { "natext", no_argument, 0, 'n' }, 71 | { "xhtml", no_argument, 0, 'x' }, 72 | { 0, 0, 0, 0 } }; 73 | 74 | /* default options: strict markdown input, HTML output */ 75 | hrndr = &mkd_html; 76 | xrndr = &mkd_xhtml; 77 | prndr = &hrndr; 78 | 79 | /* argument parsing */ 80 | argerr = help = 0; 81 | while (!argerr && 82 | (ch = getopt_long(argc, argv, "dHhmnx", longopts, 0)) != -1) 83 | switch (ch) { 84 | case 'd': /* discount extension */ 85 | hrndr = &discount_html; 86 | xrndr = &discount_xhtml; 87 | break; 88 | case 'H': /* HTML output */ 89 | prndr = &hrndr; 90 | break; 91 | case 'h': /* display help */ 92 | argerr = help = 1; 93 | break; 94 | case 'm': /* strict markdown */ 95 | hrndr = &mkd_html; 96 | xrndr = &mkd_xhtml; 97 | break; 98 | case 'n': /* Discount + Natasha's extensions */ 99 | hrndr = &nat_html; 100 | xrndr = &nat_xhtml; 101 | break; 102 | case 'x': /* XHTML output */ 103 | prndr = &xrndr; 104 | break; 105 | default: 106 | argerr = 1; } 107 | if (argerr) { 108 | usage(help ? stdout : stderr, argv[0]); 109 | return help ? EXIT_SUCCESS : EXIT_FAILURE; } 110 | argc -= optind; 111 | argv += optind; 112 | 113 | /* opening the file if given from the command line */ 114 | if (argc > 0) { 115 | in = fopen(argv[0], "r"); 116 | if (!in) { 117 | fprintf(stderr,"Unable to open input file \"%s\": %s\n", 118 | argv[0], strerror(errno)); 119 | return 1; } } 120 | 121 | /* reading everything */ 122 | ib = bufnew(READ_UNIT); 123 | bufgrow(ib, READ_UNIT); 124 | while ((ret = fread(ib->data + ib->size, 1, 125 | ib->asize - ib->size, in)) > 0) { 126 | ib->size += ret; 127 | bufgrow(ib, ib->size + READ_UNIT); } 128 | if (in != stdin) fclose(in); 129 | 130 | /* performing markdown parsing */ 131 | ob = bufnew(OUTPUT_UNIT); 132 | markdown(ob, ib, *prndr); 133 | 134 | /* writing the result to stdout */ 135 | ret = fwrite(ob->data, 1, ob->size, stdout); 136 | if (ret < ob->size) 137 | fprintf(stderr, "Warning: only %zu output byte written, " 138 | "out of %zu\n", 139 | ret, 140 | ob->size); 141 | 142 | /* cleanup */ 143 | bufrelease(ib); 144 | bufrelease(ob); 145 | 146 | #ifdef BUFFER_STATS 147 | /* memory checks */ 148 | if (buffer_stat_nb) 149 | fprintf(stderr, "Warning: %ld buffers still active\n", 150 | buffer_stat_nb); 151 | if (buffer_stat_alloc_bytes) 152 | fprintf(stderr, "Warning: %zu bytes still allocated\n", 153 | buffer_stat_alloc_bytes); 154 | #endif 155 | return 0; } 156 | 157 | /* vim: set filetype=c: */ 158 | -------------------------------------------------------------------------------- /libsoldout/buffer.h: -------------------------------------------------------------------------------- 1 | /* buffer.h - automatic buffer structure */ 2 | 3 | /* 4 | * Copyright (c) 2008, Natacha Porté 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef LITHIUM_BUFFER_H 20 | #define LITHIUM_BUFFER_H 21 | 22 | #include 23 | #include 24 | 25 | 26 | /******************** 27 | * TYPE DEFINITIONS * 28 | ********************/ 29 | 30 | /* struct buf • character array buffer */ 31 | struct buf { 32 | char * data; /* actual character data */ 33 | size_t size; /* size of the string */ 34 | size_t asize; /* allocated size (0 = volatile buffer) */ 35 | size_t unit; /* reallocation unit size (0 = read-only buffer) */ 36 | int ref; }; /* reference count */ 37 | 38 | 39 | 40 | /********** 41 | * MACROS * 42 | **********/ 43 | 44 | /* CONST_BUF • global buffer from a string literal */ 45 | #define CONST_BUF(name, string) \ 46 | static struct buf name = { string, sizeof string -1, sizeof string } 47 | 48 | 49 | /* VOLATILE_BUF • macro for creating a volatile buffer on the stack */ 50 | #define VOLATILE_BUF(name, strname) \ 51 | struct buf name = { strname, strlen(strname) } 52 | 53 | 54 | /* BUFPUTSL • optimized bufputs of a string literal */ 55 | #define BUFPUTSL(output, literal) \ 56 | bufput(output, literal, sizeof literal - 1) 57 | 58 | 59 | /*********************** 60 | * FUNCTION ATTRIBUTES * 61 | ***********************/ 62 | 63 | /* BUF_ALLOCATOR • the function returns a completely new pointer */ 64 | #ifdef __GNUC__ 65 | #define BUF_ALLOCATOR \ 66 | __attribute__ ((malloc)) 67 | #else 68 | #define BUF_ALLOCATOR 69 | #endif 70 | 71 | 72 | /* BUF_PRINTF_LIKE • marks the function as behaving like printf */ 73 | #ifdef __GNUC__ 74 | #define BUF_PRINTF_LIKE(format_index, first_variadic_index) \ 75 | __attribute__ ((format (printf, format_index, first_variadic_index))) 76 | #else 77 | #define BUF_PRINTF_LIKE(format_index, first_variadic_index) 78 | #endif 79 | 80 | 81 | /******************** 82 | * BUFFER FUNCTIONS * 83 | ********************/ 84 | 85 | /* bufcasecmp • case-insensitive buffer comparison */ 86 | int 87 | bufcasecmp(const struct buf *, const struct buf *); 88 | 89 | /* bufcmp • case-sensitive buffer comparison */ 90 | int 91 | bufcmp(const struct buf *, const struct buf *); 92 | 93 | /* bufcmps • case-sensitive comparison of a string to a buffer */ 94 | int 95 | bufcmps(const struct buf *, const char *); 96 | 97 | /* bufdup • buffer duplication */ 98 | struct buf * 99 | bufdup(const struct buf *, size_t) 100 | BUF_ALLOCATOR; 101 | 102 | /* bufgrow • increasing the allocated size to the given value */ 103 | int 104 | bufgrow(struct buf *, size_t); 105 | 106 | /* bufnew • allocation of a new buffer */ 107 | struct buf * 108 | bufnew(size_t) 109 | BUF_ALLOCATOR; 110 | 111 | /* bufnullterm • NUL-termination of the string array (making a C-string) */ 112 | void 113 | bufnullterm(struct buf *); 114 | 115 | /* bufprintf • formatted printing to a buffer */ 116 | void 117 | bufprintf(struct buf *, const char *, ...) 118 | BUF_PRINTF_LIKE(2, 3); 119 | 120 | /* bufput • appends raw data to a buffer */ 121 | void 122 | bufput(struct buf *, const void*, size_t); 123 | 124 | /* bufputs • appends a NUL-terminated string to a buffer */ 125 | void 126 | bufputs(struct buf *, const char*); 127 | 128 | /* bufputc • appends a single char to a buffer */ 129 | void 130 | bufputc(struct buf *, char); 131 | 132 | /* bufputcn • appends a character repeatedly to a buffer */ 133 | void 134 | bufputcn(struct buf *, char, size_t); 135 | 136 | /* bufrelease • decrease the reference count and free the buffer if needed */ 137 | void 138 | bufrelease(struct buf *); 139 | 140 | /* bufreset • frees internal data of the buffer */ 141 | void 142 | bufreset(struct buf *); 143 | 144 | /* bufset • safely assigns a buffer to another */ 145 | void 146 | bufset(struct buf **, struct buf *); 147 | 148 | /* bufslurp • removes a given number of bytes from the head of the array */ 149 | void 150 | bufslurp(struct buf *, size_t); 151 | 152 | /* buftoi • converts the numbers at the beginning of the buf into an int */ 153 | int 154 | buftoi(struct buf *, size_t, size_t *); 155 | 156 | /* vbufprintf • stdarg variant of formatted printing into a buffer */ 157 | void 158 | vbufprintf(struct buf *, const char*, va_list); 159 | 160 | 161 | /******************** 162 | * GLOBAL VARIABLES * 163 | ********************/ 164 | 165 | #ifdef BUFFER_STATS 166 | 167 | extern long buffer_stat_nb; 168 | extern size_t buffer_stat_alloc_bytes; 169 | 170 | #endif /* def BUFFER_STATS */ 171 | 172 | 173 | #endif /* ndef LITHIUM_BUFFER_H */ 174 | 175 | /* vim: set filetype=c: */ 176 | -------------------------------------------------------------------------------- /libsoldout/soldout_buffer.3: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (c) 2009 - 2016 Natacha Porté 3 | .\" 4 | .\" Permission to use, copy, modify, and distribute this software for any 5 | .\" purpose with or without fee is hereby granted, provided that the above 6 | .\" copyright notice and this permission notice appear in all copies. 7 | .\" 8 | .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | .\" 16 | .Dd April 13, 2016 17 | .Dt SOLDOUT_BUFFER 3 18 | .Os 19 | .Sh NAME 20 | .Nm soldout_buffer , 21 | .Nm bufcasecmp , 22 | .Nm bufcmp , 23 | .Nm bufcmps , 24 | .Nm bufdup , 25 | .Nm bufgrow , 26 | .Nm bufnew , 27 | .Nm bufnullterm , 28 | .Nm bufprintf , 29 | .Nm bufput , 30 | .Nm bufputs , 31 | .Nm bufputc , 32 | .Nm bufrelease , 33 | .Nm bufreset , 34 | .Nm bufset , 35 | .Nm bufslurp , 36 | .Nm buftoi , 37 | .Nm vbufprintf 38 | .Nd buffer handling functions for soldout 39 | .Sh SYNOPSIS 40 | .In buffer.h 41 | .Pp 42 | .Fd #define CONST_BUF(name, string) 43 | .Fd #define VOLATILE_BUF(name, strname) 44 | .Fd #define BUFPUTSL(output, literal) 45 | .Ft int 46 | .Fo bufcasecmp 47 | .Fa "const struct buf *a" 48 | .Fa "const struct buf *b" 49 | .Fc 50 | .Ft int 51 | .Fo bufcmp 52 | .Fa "const struct buf *a" 53 | .Fa "const struct buf *b" 54 | .Fc 55 | .Ft int 56 | .Fo bufcmps 57 | .Fa "const struct buf *a" 58 | .Fa "const char *b" 59 | .Fc 60 | .Ft "struct buf *" 61 | .Fo bufdup 62 | .Fa "const struct buf *src" 63 | .Fa "size_t dupunit" 64 | .Fc 65 | .Ft int 66 | .Fo bufgrow 67 | .Fa "struct buf *buf" 68 | .Fa "size_t sz" 69 | .Fc 70 | .Ft "struct buf *" 71 | .Fo bufnew 72 | .Fa "size_t unit" 73 | .Fc 74 | .Ft void 75 | .Fo bufnullterm 76 | .Fa "struct buf *buf" 77 | .Fc 78 | .Ft void 79 | .Fo bufprintf 80 | .Fa "struct buf *buf" 81 | .Fa "const char *fmt" 82 | .Fa ... 83 | .Fc 84 | .Ft void 85 | .Fo bufput 86 | .Fa "struct buf *buf" 87 | .Fa "const void *data" 88 | .Fa "size_t len" 89 | .Fc 90 | .Ft void 91 | .Fo bufputs 92 | .Fa "struct buf *buf" 93 | .Fa "const char *str" 94 | .Fc 95 | .Ft void 96 | .Fo bufputc 97 | .Fa "struct buf *buf" 98 | .Fa "char c" 99 | .Fc 100 | .Ft void 101 | .Fo bufrelease 102 | .Fa "struct buf *buf" 103 | .Fc 104 | .Ft void 105 | .Fo bufreset 106 | .Fa "struct buf *buf" 107 | .Fc 108 | .Ft void 109 | .Fo bufset 110 | .Fa "struct buf **dest" 111 | .Fa "struct buf *src" 112 | .Fc 113 | .Ft void 114 | .Fo bufslurp 115 | .Fa "struct buf *buf" 116 | .Fa "size_t len" 117 | .Fc 118 | .Ft int 119 | .Fo buftoi 120 | .Fa "struct buf *buf" 121 | .Fa "size_t offset_i" 122 | .Fa "size_t *offset_o" 123 | .Fc 124 | .Ft void 125 | .Fo vbufprintf 126 | .Fa "struct buf *buf" 127 | .Fa "const char *fmt" 128 | .Fa "va_list ap" 129 | .Fc 130 | .Vt extern long buffer_stat_nb; 131 | .Vt extern size_t buffer_stat_alloc_bytes; 132 | .Sh DESCRIPTION 133 | .Ss Variables 134 | Compile time options. 135 | Statistics are kept about memory usage. 136 | .Bl -ohang 137 | .It Va buffer_stat_nb 138 | show how many buffers were created. 139 | .It Va buffer_stat_alloc_bytes 140 | show how many bytes were allocated. 141 | .El 142 | .Ss Types 143 | .Bl -ohang 144 | .It Vt "struct buf" 145 | character array buffer. 146 | Consists of the following fields: 147 | .Bl -tag -width Ds 148 | .It Vt "char *" Va data 149 | actual character data. 150 | .It Vt size_t Va size 151 | size of the string. 152 | .It Vt size_t Va asize 153 | allocated size 154 | .Pq 0 = volatile buffer . 155 | .It Vt size_t Va unit 156 | reallocation unit size 157 | .Pq 0 = read-only buffer . 158 | .It Vt int Va ref 159 | reference count. 160 | .El 161 | .El 162 | .Ss Macros 163 | .Bl -ohang 164 | .It Dv CONST_BUF 165 | create a global buffer 166 | .Va name 167 | from a string literal 168 | .Va string . 169 | .It Dv VOLATILE_BUF 170 | create a volatile buffer 171 | .Va name 172 | on the stack from a string 173 | .Va strname . 174 | .It Dv BUFPUTSL 175 | optimized 176 | .Fn bufputs 177 | of a string literal. 178 | .El 179 | .Ss Functions 180 | .Bl -ohang 181 | .It Fn bufcasecmp 182 | compare two buffers ignoring case. 183 | .It Fn bufcmp 184 | compare two buffers. 185 | .It Fn bufcmps 186 | compare a buffer to a string. 187 | .It Fn bufdup 188 | duplicate a buffer 189 | .Va src . 190 | .It Fn bufgrow 191 | increase the allocated size to the given value. 192 | .It Fn bufnew 193 | create a new buffer. 194 | .It Fn bufnullterm 195 | terminate the string array by NUL 196 | .Pq making a C-string . 197 | .It Fn bufprintf 198 | print formatted output to a buffer 199 | .Va buf . 200 | .It Fn bufput 201 | append raw data to a buffer 202 | .Va buf . 203 | .It Fn bufputs 204 | append a NUL-terminated string 205 | .Va str 206 | to a buffer 207 | .Va buf . 208 | .It Fn bufputc 209 | append a single char 210 | .Va c 211 | to a buffer 212 | .Va buf . 213 | .It Fn bufrelease 214 | decrease the reference count and free the buffer 215 | .Va buf 216 | if needed. 217 | .It Fn bufreset 218 | free internal data of the buffer 219 | .Va buf . 220 | .It Fn bufset 221 | safely assign a buffer to another. 222 | .It Fn bufslurp 223 | remove a given number of bytes from the head of the array. 224 | .It Fn buftoi 225 | convert the numbers at the beginning of the buffer 226 | .Va buf 227 | into an 228 | .Vt int . 229 | .It Fn vbufprintf 230 | .Xr stdarg 3 231 | variant of formatted printing into a buffer 232 | .Va buf . 233 | .El 234 | .Sh RETURN VALUES 235 | The 236 | .Fn bufcasecmp , 237 | .Fn bufcmp 238 | and 239 | .Fn bufcmps 240 | functions return an integer less than, equal to, or greater than zero if 241 | .Va a 242 | is found, respectively, to be less than, to match, or be greater than 243 | .Va b . 244 | .Pp 245 | The 246 | .Fn bufdup 247 | and 248 | .Fn bufnew 249 | functions return a 250 | .Vt "struct buf *" 251 | on success; on error they return 252 | .Dv NULL . 253 | .Pp 254 | The 255 | .Fn bufgrow 256 | function returns on success 1; on error - 0. 257 | .Pp 258 | The 259 | .Fn bufnullterm , 260 | .Fn bufprintf , 261 | .Fn bufput , 262 | .Fn bufputs , 263 | .Fn bufputc , 264 | .Fn bufrelease , 265 | .Fn bufreset , 266 | .Fn bufset , 267 | .Fn bufslurp 268 | and 269 | .Fn vbufprintf 270 | functions do not return a value. 271 | .Pp 272 | The 273 | .Fn buftoi 274 | function return the converted value. 275 | .Sh SEE ALSO 276 | .Xr soldout 3 , 277 | .Xr stdarg 3 278 | .Sh AUTHORS 279 | .An -nosplit 280 | The 281 | .Nm soldout 282 | library 283 | was written by 284 | .An Natasha Qo Kerensikova Qc Porte Aq Mt natacha@instinctive.eu . 285 | Manual page was originally written by 286 | .An Massimo Manghi Aq Mt mxmanghi@apache.org , 287 | and rewritten to mdoc format by 288 | .An Svyatoslav Mishyn Aq Mt juef@openmailbox.org . 289 | -------------------------------------------------------------------------------- /libsoldout/soldout_array.3: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (c) 2009 - 2016 Natacha Porté 3 | .\" 4 | .\" Permission to use, copy, modify, and distribute this software for any 5 | .\" purpose with or without fee is hereby granted, provided that the above 6 | .\" copyright notice and this permission notice appear in all copies. 7 | .\" 8 | .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | .\" 16 | .Dd April 13, 2016 17 | .Dt SOLDOUT_ARRAY 3 18 | .Os 19 | .Sh NAME 20 | .Nm soldout_array , 21 | .Nm arr_adjust , 22 | .Nm arr_free , 23 | .Nm arr_grow , 24 | .Nm arr_init , 25 | .Nm arr_insert , 26 | .Nm arr_item , 27 | .Nm arr_newitem , 28 | .Nm arr_remove , 29 | .Nm arr_sorted_find , 30 | .Nm arr_sorted_find_i , 31 | .Nm parr_adjust , 32 | .Nm parr_free , 33 | .Nm parr_grow , 34 | .Nm parr_init , 35 | .Nm parr_insert , 36 | .Nm parr_pop , 37 | .Nm parr_push , 38 | .Nm parr_remove , 39 | .Nm parr_sorted_find , 40 | .Nm parr_sorted_find_i , 41 | .Nm parr_top 42 | .Nd array handling functions for soldout 43 | .Sh SYNOPSIS 44 | .In array.h 45 | .Ft int 46 | .Fo (*array_cmp_fn) 47 | .Fa "void *key" 48 | .Fa "void *array_entry" 49 | .Fc 50 | .Ft int 51 | .Fo arr_adjust 52 | .Fa "struct array *arr" 53 | .Fc 54 | .Ft void 55 | .Fo arr_free 56 | .Fa "struct array *arr" 57 | .Fc 58 | .Ft int 59 | .Fo arr_grow 60 | .Fa "struct array *arr" 61 | .Fa "int need" 62 | .Fc 63 | .Ft void 64 | .Fo arr_init 65 | .Fa "struct array *arr" 66 | .Fa "size_t unit" 67 | .Fc 68 | .Ft int 69 | .Fo arr_insert 70 | .Fa "struct array *arr" 71 | .Fa "int nb" 72 | .Fa "int n" 73 | .Fc 74 | .Ft "void *" 75 | .Fo arr_item 76 | .Fa "struct array *arr" 77 | .Fa "int no" 78 | .Fc 79 | .Ft int 80 | .Fo arr_newitem 81 | .Fa "struct array *arr" 82 | .Fc 83 | .Ft void 84 | .Fo arr_remove 85 | .Fa "struct array *arr" 86 | .Fa "int idx" 87 | .Fc 88 | .Ft "void *" 89 | .Fo arr_sorted_find 90 | .Fa "struct array *arr" 91 | .Fa "void *key" 92 | .Fa "array_cmp_fn cmp" 93 | .Fc 94 | .Ft int 95 | .Fo arr_sorted_find_i 96 | .Fa "struct array *arr" 97 | .Fa "void *key" 98 | .Fa "array_cmp_fn cmp" 99 | .Fc 100 | .Ft int 101 | .Fo parr_adjust 102 | .Fa "struct parray *arr" 103 | .Fc 104 | .Ft void 105 | .Fo parr_free 106 | .Fa "struct parray *arr" 107 | .Fc 108 | .Ft int 109 | .Fo parr_grow 110 | .Fa "struct parray *arr" 111 | .Fa "int need" 112 | .Fc 113 | .Ft void 114 | .Fo parr_init 115 | .Fa "struct parray *arr" 116 | .Fc 117 | .Ft int 118 | .Fo parr_insert 119 | .Fa "struct parray *parr" 120 | .Fa "int nb" 121 | .Fa "int n" 122 | .Fc 123 | .Ft "void *" 124 | .Fo parr_pop 125 | .Fa "struct parray *arr" 126 | .Fc 127 | .Ft int 128 | .Fo parr_push 129 | .Fa "struct parray *arr" 130 | .Fa "void *i" 131 | .Fc 132 | .Ft "void *" 133 | .Fo parr_remove 134 | .Fa "struct parray *arr" 135 | .Fa "int idx" 136 | .Fc 137 | .Ft "void *" 138 | .Fo parr_sorted_find 139 | .Fa "struct parray *arr" 140 | .Fa "void *key" 141 | .Fa "array_cmp_fn cmp" 142 | .Fc 143 | .Ft int 144 | .Fo parr_sorted_find_i 145 | .Fa "struct parray *arr" 146 | .Fa "void *key" 147 | .Fa "array_cmp_fn cmp" 148 | .Fc 149 | .Ft "void *" 150 | .Fo parr_top 151 | .Fa "struct parray *arr" 152 | .Fc 153 | .Sh DESCRIPTION 154 | .Ss Types 155 | .Bl -ohang 156 | .It Vt "struct array" 157 | generic linear array. 158 | Consists of the following fields: 159 | .Bl -tag -width Ds 160 | .It Vt "void *" Va base 161 | actual array data. 162 | .It Vt int Va size 163 | size of the array. 164 | .It Vt int Va asize 165 | allocated size. 166 | .It Vt size_t Va unit 167 | reallocation unit size. 168 | .El 169 | .It Vt "struct parray" 170 | array of pointers. 171 | Consists of the following fields: 172 | .Bl -tag -width Ds 173 | .It Vt "void **" Va item 174 | actual parray data. 175 | .It Vt int Va size 176 | size of the parray. 177 | .It Vt int Va asize 178 | allocated size. 179 | .El 180 | .It Vt array_cmp_fn 181 | comparison function for sorted arrays. 182 | .El 183 | .Ss Functions 184 | .Bl -ohang 185 | .It Fn arr_adjust 186 | shrink the allocated memory to fit exactly the needs. 187 | .It Fn arr_free 188 | free the structure contents 189 | .Pq but NOT the struct itself . 190 | .It Fn arr_grow 191 | increase the array size to fit the given number of elements. 192 | .It Fn arr_init 193 | initialize the contents of the struct. 194 | .It Fn arr_insert 195 | insert 196 | .Fa nb 197 | elements before the 198 | .Fa n 199 | one. 200 | .It Fn arr_item 201 | return a pointer to the 202 | .Fa n 203 | element. 204 | .It Fn arr_newitem 205 | return the index of a new element appended to the array 206 | .Fa arr . 207 | .It Fn arr_remove 208 | remove the n-th elements of the array. 209 | .It Fn arr_sorted_find 210 | O(log n) search in a sorted array, returning entry. 211 | .It Fn arr_sorted_find_i 212 | O(log n) search in a sorted array, 213 | returning index of the smallest element larger than the key. 214 | .It Fn parr_adjust 215 | shrink the allocated memory to fit exactly the needs. 216 | .It Fn parr_free 217 | free the structure contents 218 | .Pq but NOT the struct itself . 219 | .It Fn parr_grow 220 | increase the array size to fit the given number of elements. 221 | .It Fn parr_init 222 | initialize the contents of the struct. 223 | .It Fn parr_insert 224 | insert 225 | .Fa nb 226 | elements before the 227 | .Fa n 228 | one. 229 | .It Fn parr_pop 230 | pop the last item of the array and return it. 231 | .It Fn parr_push 232 | push a pointer at the end of the array 233 | .Pq = append . 234 | .It Fn parr_remove 235 | remove the 236 | .Fa idx 237 | element of the array and return it. 238 | .It Fn parr_sorted_find 239 | O(log n) search in a sorted array, returning entry. 240 | .It Fn parr_sorted_find_i 241 | O(log n) search in a sorted array, 242 | returning index of the smallest element larger than the key. 243 | .It Fn parr_top 244 | return the top the stack 245 | .Pq i.e. the last element of the array . 246 | .El 247 | .Sh RETURN VALUES 248 | The 249 | .Fn arr_adjust , 250 | .Fn arr_grow , 251 | .Fn arr_insert , 252 | .Fn parr_adjust , 253 | .Fn parr_grow , 254 | .Fn parr_insert 255 | and 256 | .Fn parr_push 257 | functions return on success 1; on error - 0. 258 | .Pp 259 | The 260 | .Fn arr_free , 261 | .Fn arr_init , 262 | .Fn arr_remove , 263 | .Fn parr_free 264 | and 265 | .Fn parr_init 266 | functions do not return a value. 267 | .Pp 268 | The 269 | .Fn arr_item , 270 | .Fn arr_sorted_find , 271 | .Fn parr_pop , 272 | .Fn parr_remove , 273 | .Fn parr_sorted_find 274 | and 275 | .Fn parr_top 276 | functions return a pointer to the element on success; on error - 277 | .Dv NULL . 278 | .Pp 279 | The 280 | .Fn arr_newitem 281 | function returns the index on success; on error -1. 282 | .Pp 283 | The 284 | .Fn arr_sorted_find_i 285 | and 286 | .Fn parr_sorted_find_i 287 | functions return an index. 288 | .Sh SEE ALSO 289 | .Xr soldout 3 290 | .Sh AUTHORS 291 | .An -nosplit 292 | The 293 | .Nm soldout 294 | library 295 | was written by 296 | .An Natasha Qo Kerensikova Qc Porte Aq Mt natacha@instinctive.eu . 297 | Manual page was originally written by 298 | .An Massimo Manghi Aq Mt mxmanghi@apache.org , 299 | and rewritten to mdoc format by 300 | .An Svyatoslav Mishyn Aq Mt juef@openmailbox.org . 301 | -------------------------------------------------------------------------------- /console-colour.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | {FE74624B-9FAB-4D3D-B7B8-649885FBFA16} 24 | Win32Proj 25 | consolecolour 26 | 10.0 27 | 28 | 29 | 30 | DynamicLibrary 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | DynamicLibrary 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | DynamicLibrary 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | DynamicLibrary 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | CONSOLECOLOUR 75 | true 76 | 77 | 78 | CONSOLECOLOUR 79 | true 80 | 81 | 82 | CONSOLECOLOUR 83 | false 84 | 85 | 86 | CONSOLECOLOUR 87 | false 88 | 89 | 90 | 91 | NotUsing 92 | Level3 93 | true 94 | SUBHOOK_IMPLEMENTATION;CONMD_HOOKS;WIN32;_DEBUG;CONSOLECOLOUR_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS 95 | true 96 | 97 | 98 | 99 | 100 | Windows 101 | true 102 | false 103 | 104 | 105 | 106 | 107 | NotUsing 108 | Level3 109 | true 110 | SUBHOOK_IMPLEMENTATION;CONMD_HOOKS;_DEBUG;CONSOLECOLOUR_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS 111 | true 112 | 113 | 114 | 115 | 116 | Windows 117 | true 118 | false 119 | 120 | 121 | 122 | 123 | NotUsing 124 | Level3 125 | true 126 | true 127 | true 128 | SUBHOOK_IMPLEMENTATION;CONMD_HOOKS;WIN32;NDEBUG;CONSOLECOLOUR_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS 129 | true 130 | 131 | 132 | 133 | 134 | Windows 135 | true 136 | true 137 | true 138 | false 139 | 140 | 141 | 142 | 143 | NotUsing 144 | Level3 145 | true 146 | true 147 | true 148 | SUBHOOK_IMPLEMENTATION;CONMD_HOOKS;NDEBUG;CONSOLECOLOUR_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS 149 | true 150 | 151 | 152 | 153 | 154 | Windows 155 | true 156 | true 157 | true 158 | false 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /libsoldout/array.c: -------------------------------------------------------------------------------- 1 | /* array.c - automatic dynamic array for pointers */ 2 | 3 | /* 4 | * Copyright (c) 2008, Natacha Porté 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "array.h" 20 | 21 | #include 22 | 23 | 24 | /*************************** 25 | * STATIC HELPER FUNCTIONS * 26 | ***************************/ 27 | 28 | /* arr_realloc • realloc memory of a struct array */ 29 | static int 30 | arr_realloc(struct array* arr, int neosz) { 31 | void* neo; 32 | neo = realloc(arr->base, neosz * arr->unit); 33 | if (neo == 0) return 0; 34 | arr->base = neo; 35 | arr->asize = neosz; 36 | if (arr->size > neosz) arr->size = neosz; 37 | return 1; } 38 | 39 | 40 | /* parr_realloc • realloc memory of a struct parray */ 41 | static int 42 | parr_realloc(struct parray* arr, int neosz) { 43 | void* neo; 44 | neo = realloc(arr->item, neosz * sizeof (void*)); 45 | if (neo == 0) return 0; 46 | arr->item = neo; 47 | arr->asize = neosz; 48 | if (arr->size > neosz) arr->size = neosz; 49 | return 1; } 50 | 51 | 52 | 53 | /*************************** 54 | * GENERIC ARRAY FUNCTIONS * 55 | ***************************/ 56 | 57 | /* arr_adjust • shrink the allocated memory to fit exactly the needs */ 58 | int 59 | arr_adjust(struct array *arr) { 60 | return arr_realloc(arr, arr->size); } 61 | 62 | 63 | /* arr_free • frees the structure contents (buf NOT the struct itself) */ 64 | void 65 | arr_free(struct array *arr) { 66 | if (!arr) return; 67 | free(arr->base); 68 | arr->base = 0; 69 | arr->size = arr->asize = 0; } 70 | 71 | 72 | /* arr_grow • increases the array size to fit the given number of elements */ 73 | int 74 | arr_grow(struct array *arr, int need) { 75 | if (arr->asize >= need) return 1; 76 | else return arr_realloc(arr, need); } 77 | 78 | 79 | /* arr_init • initialization of the contents of the struct */ 80 | void 81 | arr_init(struct array *arr, size_t unit) { 82 | arr->base = 0; 83 | arr->size = arr->asize = 0; 84 | arr->unit = unit; } 85 | 86 | 87 | /* arr_insert • inserting nb elements before the nth one */ 88 | int 89 | arr_insert(struct array *arr, int nb, int n) { 90 | char *src, *dst; 91 | size_t len; 92 | if (!arr || nb <= 0 || n < 0 93 | || !arr_grow(arr, arr->size + nb)) 94 | return 0; 95 | if (n < arr->size) { 96 | src = arr->base; 97 | src += n * arr->unit; 98 | dst = src + nb * arr->unit; 99 | len = (arr->size - n) * arr->unit; 100 | memmove(dst, src, len); } 101 | arr->size += nb; 102 | return 1; } 103 | 104 | 105 | /* arr_item • returns a pointer to the n-th element */ 106 | void * 107 | arr_item(struct array *arr, int no) { 108 | char *ptr; 109 | if (!arr || no < 0 || no >= arr->size) return 0; 110 | ptr = arr->base; 111 | ptr += no * arr->unit; 112 | return ptr; } 113 | 114 | 115 | /* arr_newitem • returns the index of a new element appended to the array */ 116 | int 117 | arr_newitem(struct array *arr) { 118 | if (!arr_grow(arr, arr->size + 1)) return -1; 119 | arr->size += 1; 120 | return arr->size - 1; } 121 | 122 | 123 | /* arr_remove • removes the n-th elements of the array */ 124 | void 125 | arr_remove(struct array *arr, int idx) { 126 | if (!arr || idx < 0 || idx >= arr->size) return; 127 | arr->size -= 1; 128 | if (idx < arr->size) { 129 | char *dst = arr->base; 130 | char *src; 131 | dst += idx * arr->unit; 132 | src = dst + arr->unit; 133 | memmove(dst, src, (arr->size - idx) * arr->unit); } } 134 | 135 | 136 | /* arr_sorted_find • O(log n) search in a sorted array, returning entry */ 137 | void * 138 | arr_sorted_find(struct array *arr, void *key, array_cmp_fn cmp) { 139 | int mi, ma, cu, ret; 140 | char *ptr = arr->base; 141 | mi = -1; 142 | ma = arr->size; 143 | while (mi < ma - 1) { 144 | cu = mi + (ma - mi) / 2; 145 | ret = cmp(key, ptr + cu * arr->unit); 146 | if (ret == 0) return ptr + cu * arr->unit; 147 | else if (ret < 0) ma = cu; 148 | else /* if (ret > 0) */ mi = cu; } 149 | return 0; } 150 | 151 | 152 | /* arr_sorted_find_i • O(log n) search in a sorted array, 153 | * returning index of the smallest element larger than the key */ 154 | int 155 | arr_sorted_find_i(struct array *arr, void *key, array_cmp_fn cmp) { 156 | int mi, ma, cu, ret; 157 | char *ptr = arr->base; 158 | mi = -1; 159 | ma = arr->size; 160 | while (mi < ma - 1) { 161 | cu = mi + (ma - mi) / 2; 162 | ret = cmp(key, ptr + cu * arr->unit); 163 | if (ret == 0) { 164 | while (cu < arr->size && ret == 0) { 165 | cu += 1; 166 | ret = cmp(key, ptr + cu * arr->unit); } 167 | return cu; } 168 | else if (ret < 0) ma = cu; 169 | else /* if (ret > 0) */ mi = cu; } 170 | return ma; } 171 | 172 | 173 | 174 | /*************************** 175 | * POINTER ARRAY FUNCTIONS * 176 | ***************************/ 177 | 178 | /* parr_adjust • shrinks the allocated memory to fit exactly the needs */ 179 | int 180 | parr_adjust(struct parray* arr) { 181 | return parr_realloc (arr, arr->size); } 182 | 183 | 184 | /* parr_free • frees the structure contents (buf NOT the struct itself) */ 185 | void 186 | parr_free(struct parray *arr) { 187 | if (!arr) return; 188 | free (arr->item); 189 | arr->item = 0; 190 | arr->size = 0; 191 | arr->asize = 0; } 192 | 193 | 194 | /* parr_grow • increases the array size to fit the given number of elements */ 195 | int 196 | parr_grow(struct parray *arr, int need) { 197 | if (arr->asize >= need) return 1; 198 | else return parr_realloc (arr, need); } 199 | 200 | 201 | /* parr_init • initialization of the struct (which is equivalent to zero) */ 202 | void 203 | parr_init(struct parray *arr) { 204 | arr->item = 0; 205 | arr->size = 0; 206 | arr->asize = 0; } 207 | 208 | 209 | /* parr_insert • inserting nb elements before the nth one */ 210 | int 211 | parr_insert(struct parray *parr, int nb, int n) { 212 | char *src, *dst; 213 | size_t len, i; 214 | if (!parr || nb <= 0 || n < 0 215 | || !parr_grow(parr, parr->size + nb)) 216 | return 0; 217 | if (n < parr->size) { 218 | src = (void *)parr->item; 219 | src += n * sizeof (void *); 220 | dst = src + nb * sizeof (void *); 221 | len = (parr->size - n) * sizeof (void *); 222 | memmove(dst, src, len); 223 | for (i = 0; i < nb; ++i) 224 | parr->item[n + i] = 0; } 225 | parr->size += nb; 226 | return 1; } 227 | 228 | 229 | /* parr_pop • pops the last item of the array and returns it */ 230 | void * 231 | parr_pop(struct parray *arr) { 232 | if (arr->size <= 0) return 0; 233 | arr->size -= 1; 234 | return arr->item[arr->size]; } 235 | 236 | 237 | /* parr_push • pushes a pointer at the end of the array (= append) */ 238 | int 239 | parr_push(struct parray *arr, void *i) { 240 | if (!parr_grow(arr, arr->size + 1)) return 0; 241 | arr->item[arr->size] = i; 242 | arr->size += 1; 243 | return 1; } 244 | 245 | 246 | /* parr_remove • removes the n-th element of the array and returns it */ 247 | void * 248 | parr_remove(struct parray *arr, int idx) { 249 | void* ret; 250 | int i; 251 | if (!arr || idx < 0 || idx >= arr->size) return 0; 252 | ret = arr->item[idx]; 253 | for (i = idx+1; i < arr->size; ++i) 254 | arr->item[i - 1] = arr->item[i]; 255 | arr->size -= 1; 256 | return ret; } 257 | 258 | 259 | /* parr_sorted_find • O(log n) search in a sorted array, returning entry */ 260 | void * 261 | parr_sorted_find(struct parray *arr, void *key, array_cmp_fn cmp) { 262 | int mi, ma, cu, ret; 263 | mi = -1; 264 | ma = arr->size; 265 | while (mi < ma - 1) { 266 | cu = mi + (ma - mi) / 2; 267 | ret = cmp(key, arr->item[cu]); 268 | if (ret == 0) return arr->item[cu]; 269 | else if (ret < 0) ma = cu; 270 | else /* if (ret > 0) */ mi = cu; } 271 | return 0; } 272 | 273 | 274 | /* parr_sorted_find_i • O(log n) search in a sorted array, 275 | * returning index of the smallest element larger than the key */ 276 | int 277 | parr_sorted_find_i(struct parray *arr, void *key, array_cmp_fn cmp) { 278 | int mi, ma, cu, ret; 279 | mi = -1; 280 | ma = arr->size; 281 | while (mi < ma - 1) { 282 | cu = mi + (ma - mi) / 2; 283 | ret = cmp(key, arr->item[cu]); 284 | if (ret == 0) { 285 | while (cu < arr->size && ret == 0) { 286 | cu += 1; 287 | ret = cmp(key, arr->item[cu]); } 288 | return cu; } 289 | else if (ret < 0) ma = cu; 290 | else /* if (ret > 0) */ mi = cu; } 291 | return ma; } 292 | 293 | 294 | /* parr_top • returns the top the stack (i.e. the last element of the array) */ 295 | void * 296 | parr_top(struct parray *arr) { 297 | if (arr == 0 || arr->size <= 0) return 0; 298 | else return arr->item[arr->size - 1]; } 299 | 300 | /* vim: set filetype=c: */ 301 | -------------------------------------------------------------------------------- /libsoldout/buffer.c: -------------------------------------------------------------------------------- 1 | /* buffer.c - automatic buffer structure */ 2 | 3 | /* 4 | * Copyright (c) 2008, Natacha Porté 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "buffer.h" 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | 26 | /******************** 27 | * GLOBAL VARIABLES * 28 | ********************/ 29 | 30 | /* 31 | * COMPILE TIME OPTIONS 32 | * 33 | * BUFFER_STATS • if defined, stats are kept about memory usage 34 | */ 35 | 36 | #ifdef BUFFER_STATS 37 | long buffer_stat_nb = 0; 38 | size_t buffer_stat_alloc_bytes = 0; 39 | #endif 40 | 41 | 42 | /*************************** 43 | * STATIC HELPER FUNCTIONS * 44 | ***************************/ 45 | 46 | /* lower • returns the lower-case variant of the input char */ 47 | static char 48 | lower(char c) { 49 | return (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c; } 50 | 51 | 52 | 53 | /******************** 54 | * BUFFER FUNCTIONS * 55 | ********************/ 56 | 57 | /* bufcasecmp • case-insensitive buffer comparison */ 58 | int 59 | bufcasecmp(const struct buf *a, const struct buf *b) { 60 | size_t i = 0; 61 | size_t cmplen; 62 | if (a == b) return 0; 63 | if (!a) return -1; else if (!b) return 1; 64 | cmplen = (a->size < b->size) ? a->size : b->size; 65 | while (i < cmplen && lower(a->data[i]) == lower(b->data[i])) ++i; 66 | if (i < a->size) { 67 | if (i < b->size) return lower(a->data[i]) - lower(b->data[i]); 68 | else return 1; } 69 | else { if (i < b->size) return -1; 70 | else return 0; } } 71 | 72 | 73 | /* bufcmp • case-sensitive buffer comparison */ 74 | int 75 | bufcmp(const struct buf *a, const struct buf *b) { 76 | size_t i = 0; 77 | size_t cmplen; 78 | if (a == b) return 0; 79 | if (!a) return -1; else if (!b) return 1; 80 | cmplen = (a->size < b->size) ? a->size : b->size; 81 | while (i < cmplen && a->data[i] == b->data[i]) ++i; 82 | if (i < a->size) { 83 | if (i < b->size) return a->data[i] - b->data[i]; 84 | else return 1; } 85 | else { if (i < b->size) return -1; 86 | else return 0; } } 87 | 88 | 89 | /* bufcmps • case-sensitive comparison of a string to a buffer */ 90 | int 91 | bufcmps(const struct buf *a, const char *b) { 92 | const size_t len = strlen(b); 93 | size_t cmplen = len; 94 | int r; 95 | if (!a || !a->size) return b ? 0 : -1; 96 | if (len < a->size) cmplen = a->size; 97 | r = strncmp(a->data, b, cmplen); 98 | if (r) return r; 99 | else if (a->size == len) return 0; 100 | else if (a->size < len) return -1; 101 | else return 1; } 102 | 103 | 104 | /* bufdup • buffer duplication */ 105 | struct buf * 106 | bufdup(const struct buf *src, size_t dupunit) { 107 | size_t blocks; 108 | struct buf *ret; 109 | if (src == 0) return 0; 110 | ret = malloc(sizeof (struct buf)); 111 | if (ret == 0) return 0; 112 | ret->unit = dupunit; 113 | ret->size = src->size; 114 | ret->ref = 1; 115 | if (!src->size) { 116 | ret->asize = 0; 117 | ret->data = 0; 118 | return ret; } 119 | blocks = (src->size + dupunit - 1) / dupunit; 120 | ret->asize = blocks * dupunit; 121 | ret->data = malloc(ret->asize); 122 | if (ret->data == 0) { 123 | free(ret); 124 | return 0; } 125 | memcpy(ret->data, src->data, src->size); 126 | #ifdef BUFFER_STATS 127 | buffer_stat_nb += 1; 128 | buffer_stat_alloc_bytes += ret->asize; 129 | #endif 130 | return ret; } 131 | 132 | 133 | /* bufgrow • increasing the allocated size to the given value */ 134 | int 135 | bufgrow(struct buf *buf, size_t neosz) { 136 | size_t neoasz; 137 | void *neodata; 138 | if (!buf || !buf->unit) return 0; 139 | if (buf->asize >= neosz) return 1; 140 | neoasz = buf->asize + buf->unit; 141 | while (neoasz < neosz) neoasz += buf->unit; 142 | neodata = realloc(buf->data, neoasz); 143 | if (!neodata) return 0; 144 | #ifdef BUFFER_STATS 145 | buffer_stat_alloc_bytes += (neoasz - buf->asize); 146 | #endif 147 | buf->data = neodata; 148 | buf->asize = neoasz; 149 | return 1; } 150 | 151 | 152 | /* bufnew • allocation of a new buffer */ 153 | struct buf * 154 | bufnew(size_t unit) { 155 | struct buf *ret; 156 | ret = malloc(sizeof (struct buf)); 157 | if (ret) { 158 | #ifdef BUFFER_STATS 159 | buffer_stat_nb += 1; 160 | #endif 161 | ret->data = 0; 162 | ret->size = ret->asize = 0; 163 | ret->ref = 1; 164 | ret->unit = unit; } 165 | return ret; } 166 | 167 | 168 | /* bufnullterm • NUL-termination of the string array (making a C-string) */ 169 | void 170 | bufnullterm(struct buf *buf) { 171 | if (!buf || !buf->unit) return; 172 | if (buf->size + 1 <= buf->asize || bufgrow(buf, buf->size + 1)) 173 | buf->data[buf->size] = 0; } 174 | 175 | 176 | /* bufprintf • formatted printing to a buffer */ 177 | void 178 | bufprintf(struct buf *buf, const char *fmt, ...) { 179 | va_list ap; 180 | if (!buf || !buf->unit) return; 181 | va_start(ap, fmt); 182 | vbufprintf(buf, fmt, ap); 183 | va_end(ap); } 184 | 185 | 186 | /* bufput • appends raw data to a buffer */ 187 | void 188 | bufput(struct buf *buf, const void *data, size_t len) { 189 | if (!buf) return; 190 | if (buf->size + len > buf->asize && !bufgrow(buf, buf->size + len)) 191 | return; 192 | memcpy(buf->data + buf->size, data, len); 193 | buf->size += len; } 194 | 195 | 196 | /* bufputs • appends a NUL-terminated string to a buffer */ 197 | void 198 | bufputs(struct buf *buf, const char *str) { 199 | bufput(buf, str, strlen (str)); } 200 | 201 | 202 | /* bufputc • appends a single char to a buffer */ 203 | void 204 | bufputc(struct buf *buf, char c) { 205 | if (!buf) return; 206 | if (buf->size + 1 > buf->asize && !bufgrow(buf, buf->size + 1)) 207 | return; 208 | buf->data[buf->size] = c; 209 | buf->size += 1; } 210 | 211 | 212 | /* bufputcn • appends a character repeatedly to a buffer */ 213 | void 214 | bufputcn(struct buf *buf, char c, size_t n) { 215 | if (!buf) return; 216 | if (buf->size + n > buf->asize && !bufgrow(buf, buf->size + n)) 217 | return; 218 | memset(buf->data + buf->size, c, n); 219 | buf->size += n; } 220 | 221 | 222 | /* bufrelease • decrease the reference count and free the buffer if needed */ 223 | void 224 | bufrelease(struct buf *buf) { 225 | if (!buf || !buf->unit) return; 226 | buf->ref -= 1; 227 | if (buf->ref == 0) { 228 | #ifdef BUFFER_STATS 229 | buffer_stat_nb -= 1; 230 | buffer_stat_alloc_bytes -= buf->asize; 231 | #endif 232 | free(buf->data); 233 | free(buf); } } 234 | 235 | 236 | /* bufreset • frees internal data of the buffer */ 237 | void 238 | bufreset(struct buf *buf) { 239 | if (!buf || !buf->unit || !buf->asize) return; 240 | #ifdef BUFFER_STATS 241 | buffer_stat_alloc_bytes -= buf->asize; 242 | #endif 243 | free(buf->data); 244 | buf->data = 0; 245 | buf->size = buf->asize = 0; } 246 | 247 | 248 | /* bufset • safely assigns a buffer to another */ 249 | void 250 | bufset(struct buf **dest, struct buf *src) { 251 | if (src) { 252 | if (!src->asize) src = bufdup(src, 1); 253 | else src->ref += 1; } 254 | bufrelease(*dest); 255 | *dest = src; } 256 | 257 | 258 | /* bufslurp • removes a given number of bytes from the head of the array */ 259 | void 260 | bufslurp(struct buf *buf, size_t len) { 261 | if (!buf || !buf->unit || !len) return; 262 | if (len >= buf->size) { 263 | buf->size = 0; 264 | return; } 265 | buf->size -= len; 266 | memmove(buf->data, buf->data + len, buf->size); } 267 | 268 | 269 | /* buftoi • converts the numbers at the beginning of the buf into an int */ 270 | int 271 | buftoi(struct buf *buf, size_t offset_i, size_t *offset_o) { 272 | int r = 0, neg = 0; 273 | size_t i = offset_i; 274 | if (!buf || !buf->size) return 0; 275 | if (buf->data[i] == '+') i += 1; 276 | else if (buf->data[i] == '-') { 277 | neg = 1; 278 | i += 1; } 279 | while (i < buf->size && buf->data[i] >= '0' && buf->data[i] <= '9') { 280 | r = (r * 10) + buf->data[i] - '0'; 281 | i += 1; } 282 | if (offset_o) *offset_o = i; 283 | return neg ? -r : r; } 284 | 285 | 286 | 287 | /* vbufprintf • stdarg variant of formatted printing into a buffer */ 288 | void 289 | vbufprintf(struct buf *buf, const char *fmt, va_list ap) { 290 | int n; 291 | va_list ap_save; 292 | if (buf == 0 293 | || (buf->size >= buf->asize && !bufgrow (buf, buf->size + 1))) 294 | return; 295 | va_copy(ap_save, ap); 296 | n = vsnprintf(buf->data + buf->size, buf->asize - buf->size, fmt, ap); 297 | if (n >= buf->asize - buf->size) { 298 | if (buf->size + n + 1 > buf->asize 299 | && !bufgrow (buf, buf->size + n + 1)) 300 | return; 301 | n = vsnprintf (buf->data + buf->size, 302 | buf->asize - buf->size, fmt, ap_save); } 303 | va_end(ap_save); 304 | if (n < 0) return; 305 | buf->size += n; } 306 | 307 | /* vim: set filetype=c: */ 308 | -------------------------------------------------------------------------------- /libsoldout/soldout_markdown.3: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (c) 2009 - 2016 Natacha Porté 3 | .\" 4 | .\" Permission to use, copy, modify, and distribute this software for any 5 | .\" purpose with or without fee is hereby granted, provided that the above 6 | .\" copyright notice and this permission notice appear in all copies. 7 | .\" 8 | .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | .\" 16 | .Dd May 30, 2016 17 | .Dt SOLDOUT_MARKDOWN 3 18 | .Os 19 | .Sh NAME 20 | .Nm soldout_markdown , 21 | .Nm markdown 22 | .Nd parse markdown document 23 | .Sh SYNOPSIS 24 | .In markdown.h 25 | .Pp 26 | .Fd #define MKD_CELL_ALIGN_DEFAULT 27 | .Fd #define MKD_CELL_ALIGN_LEFT 28 | .Fd #define MKD_CELL_ALIGN_RIGHT 29 | .Fd #define MKD_CELL_ALIGN_CENTER 30 | .Fd #define MKD_CELL_ALIGN_MASK 31 | .Fd #define MKD_CELL_HEAD 32 | .Fd #define MKD_LIST_ORDERED 33 | .Fd #define MKD_LI_BLOCK 34 | .Ft void 35 | .Fo markdown 36 | .Fa "struct buf *ob" 37 | .Fa "struct buf *ib" 38 | .Fa "const struct mkd_renderer *rndr" 39 | .Fc 40 | .Sh DESCRIPTION 41 | The 42 | .Fn markdown 43 | function parses the input buffer 44 | .Fa ib 45 | and renders it into the output buffer 46 | .Fa ob ; 47 | .Fa rndr 48 | is a pointer to the renderer structure. 49 | .Pp 50 | Function pointers in 51 | .Vt "struct mkd_renderer" 52 | can be 53 | .Dv NULL . 54 | A null block-level callback will make the corresponding block 55 | disappear from the output, as if the callback was an empty function. 56 | A null span-level callback will cause the corresponding element 57 | to be treated as normal characters, copied verbatim to the output. 58 | Moreover, span-level callbacks return an integer, which tells 59 | whether the renderer accepts to render the item (non-zero return value) 60 | or whether it should be copied verbatim (zero return value). 61 | .Pp 62 | The first argument of a renderer function is always the output buffer, 63 | where the function is supposed to write its output. 64 | The last argument of a renderer function is always 65 | a private pointer, which is 66 | .Va opaque 67 | member of 68 | .Vt struct mkd_renderer . 69 | libsoldout itself never does nothing with this data. 70 | .Pp 71 | .Va emph_chars 72 | is a zero-terminated string which contains 73 | the set of characters that trigger emphasis. 74 | In regular markdown, emphasis is only 75 | triggered by 76 | .Ql _ 77 | and 78 | .Ql * , 79 | but in some extensions it might be useful to 80 | add other characters to this list. 81 | The character that triggered the emphasis is then passed to 82 | .Va emphasis , double_emphasis 83 | and 84 | .Va triple_emphasis 85 | function callbacks through the parameter 86 | .Va c . 87 | .Pp 88 | The 89 | .Va normal_text 90 | callback should perform whatever escape is needed 91 | to have the output looking like the input data. 92 | .Pp 93 | libsoldout supports PHP-Markdown-like tables. 94 | It uses the following flags: 95 | .Dv MKD_CELL_ALIGN_DEFAULT , 96 | .Dv MKD_CELL_ALIGN_LEFT , 97 | .Dv MKD_CELL_ALIGN_RIGHT , 98 | .Dv MKD_CELL_ALIGN_CENTER , 99 | .Dv MKD_CELL_ALIGN_MASK , 100 | .Dv MKD_CELL_HEAD 101 | in these function callbacks: 102 | .Va table_cell 103 | and 104 | .Va table_row . 105 | .Pp 106 | .Dv MKD_LIST_ORDERED , 107 | .Dv MKD_LI_BLOCK 108 | are used as flags in these function callbacks: 109 | .Va list 110 | and 111 | .Va listitem . 112 | .Ss Types 113 | .Bl -ohang 114 | .It Vt "enum mkd_autolink" 115 | type of autolink: 116 | .Bl -tag -width Ds 117 | .It MKDA_NOT_AUTOLINK 118 | used internally when it is not an autolink. 119 | .It MKDA_NORMAL 120 | normal http/https/ftp link. 121 | .It MKDA_EXPLICIT_EMAIL 122 | e-mail link with explicit mailto. 123 | .It MKDA_IMPLICIT_EMAIL 124 | e-mail link without mailto. 125 | .El 126 | .It Vt "struct mkd_renderer" 127 | consists of the following fields: 128 | .Bl -tag -width Ds 129 | .It Document level callbacks 130 | .Bl -tag -width Ds 131 | .It Va prolog 132 | a pointer to a 133 | .Ft void 134 | .Fo function 135 | .Fa "struct buf *ob" 136 | .Fa "void *opaque" 137 | .Fc 138 | .It Va epilog 139 | a pointer to a 140 | .Ft void 141 | .Fo function 142 | .Fa "struct buf *ob" 143 | .Fa "void *opaque" 144 | .Fc 145 | .El 146 | .El 147 | .Bl -tag -width Ds 148 | .It Block level callbacks Pq Dv NULL skips the block 149 | .Bl -tag -width Ds 150 | .It Va blockcode 151 | a pointer to a 152 | .Ft void 153 | .Fo function 154 | .Fa "struct buf *ob" 155 | .Fa "struct buf *text" 156 | .Fa "void *opaque" 157 | .Fc 158 | .It Va blockquote 159 | a pointer to a 160 | .Ft void 161 | .Fo function 162 | .Fa "struct buf *ob" 163 | .Fa "struct buf *text" 164 | .Fa "void *opaque" 165 | .Fc 166 | .It Va blockhtml 167 | a pointer to a 168 | .Ft void 169 | .Fo function 170 | .Fa "struct buf *ob" 171 | .Fa "struct buf *text" 172 | .Fa "void *opaque" 173 | .Fc 174 | .It Va header 175 | a pointer to a 176 | .Ft void 177 | .Fo function 178 | .Fa "struct buf *ob" 179 | .Fa "struct buf *text" 180 | .Fa "int level" 181 | .Fa "void *opaque" 182 | .Fc 183 | .It Va hrule 184 | a pointer to a 185 | .Ft void 186 | .Fo function 187 | .Fa "struct buf *ob" 188 | .Fa "void *opaque" 189 | .Fc 190 | .It Va list 191 | a pointer to a 192 | .Ft void 193 | .Fo function 194 | .Fa "struct buf *ob" 195 | .Fa "struct buf *text" 196 | .Fa "int flags" 197 | .Fa "void *opaque" 198 | .Fc 199 | .It Va listitem 200 | a pointer to a 201 | .Ft void 202 | .Fo function 203 | .Fa "struct buf *ob" 204 | .Fa "struct buf *text" 205 | .Fa "int flags" 206 | .Fa "void *opaque" 207 | .Fc 208 | .It Va paragraph 209 | a pointer to a 210 | .Ft void 211 | .Fo function 212 | .Fa "struct buf *ob" 213 | .Fa "struct buf *text" 214 | .Fa "void *opaque" 215 | .Fc 216 | .It Va table 217 | a pointer to a 218 | .Ft void 219 | .Fo function 220 | .Fa "struct buf *ob" 221 | .Fa "struct buf *head_row" 222 | .Fa "struct buf *rows" 223 | .Fa "void *opaque" 224 | .Fc 225 | .It Va table_cell 226 | a pointer to a 227 | .Ft void 228 | .Fo function 229 | .Fa "struct buf *ob" 230 | .Fa "struct buf *text" 231 | .Fa "int flags" 232 | .Fa "void *opaque" 233 | .Fc 234 | .It Va table_row 235 | a pointer to a 236 | .Ft void 237 | .Fo function 238 | .Fa "struct buf *ob" 239 | .Fa "struct buf *cells" 240 | .Fa "int flags" 241 | .Fa "void *opaque" 242 | .Fc 243 | .El 244 | .It Span level callbacks Pq Dv NULL or return 0 prints the span verbatim 245 | .Bl -tag -width Ds 246 | .It Va autolink 247 | a pointer to a 248 | .Ft int 249 | .Fo function 250 | .Fa "struct buf *ob" 251 | .Fa "struct buf *link" 252 | .Fa "enum mkd_autolink type" 253 | .Fa "void *opaque" 254 | .Fc 255 | .It Va codespan 256 | a pointer to a 257 | .Ft int 258 | .Fo function 259 | .Fa "struct buf *ob" 260 | .Fa "struct buf *text" 261 | .Fa "void *opaque" 262 | .Fc 263 | .It Va emphasis 264 | a pointer to a 265 | .Ft int 266 | .Fo function 267 | .Fa "struct buf *ob" 268 | .Fa "struct buf *text" 269 | .Fa "char c" 270 | .Fa "void *opaque" 271 | .Fc 272 | .It Va double_emphasis 273 | a pointer to a 274 | .Ft int 275 | .Fo function 276 | .Fa "struct buf *ob" 277 | .Fa "struct buf *text" 278 | .Fa "char c" 279 | .Fa "void *opaque" 280 | .Fc 281 | .It Va triple_emphasis 282 | a pointer to a 283 | .Ft int 284 | .Fo function 285 | .Fa "struct buf *ob" 286 | .Fa "struct buf *text" 287 | .Fa "char c" 288 | .Fa "void *opaque" 289 | .Fc 290 | .It Va image 291 | a pointer to a 292 | .Ft int 293 | .Fo function 294 | .Fa "struct buf *ob" 295 | .Fa "struct buf *link" 296 | .Fa "struct buf *title" 297 | .Fa "struct buf *alt" 298 | .Fa "void *opaque" 299 | .Fc 300 | .It Va linebreak 301 | a pointer to a 302 | .Ft int 303 | .Fo function 304 | .Fa "struct buf *ob" 305 | .Fa "void *opaque" 306 | .Fc 307 | .It Va link 308 | a pointer to a 309 | .Ft int 310 | .Fo function 311 | .Fa "struct buf *ob" 312 | .Fa "struct buf *link" 313 | .Fa "struct buf *title" 314 | .Fa "struct buf *content" 315 | .Fa "void *opaque" 316 | .Fc 317 | .It Va raw_html_tag 318 | a pointer to a 319 | .Ft int 320 | .Fo function 321 | .Fa "struct buf *ob" 322 | .Fa "struct buf *tag" 323 | .Fa "void *opaque" 324 | .Fc 325 | .El 326 | .It Low level callbacks Pq Dv NULL copies input directly into the output 327 | .Bl -tag -width Ds 328 | .It Va entity 329 | a pointer to a 330 | .Ft void 331 | .Fo function 332 | .Fa "struct buf *ob" 333 | .Fa "struct buf *entity" 334 | .Fa "void *opaque" 335 | .Fc 336 | .It Va normal_text 337 | a pointer to a 338 | .Ft void 339 | .Fo function 340 | .Fa "struct buf *ob" 341 | .Fa "struct buf *text" 342 | .Fa "void *opaque" 343 | .Fc 344 | .El 345 | .It Renderer data 346 | .Bl -tag -width Ds 347 | .It Vt int Va max_work_stack 348 | prevent arbitrary deep recursion. 349 | .It Vt "const char *" Va emph_chars 350 | chars that trigger emphasis rendering. 351 | .It Vt "void *" Va opaque 352 | opaque data send to every rendering callback. 353 | .El 354 | .El 355 | .El 356 | .Sh RETURN VALUES 357 | The 358 | .Fn markdown 359 | function does not return a value. 360 | .Sh SEE ALSO 361 | .Xr soldout 3 , 362 | .Xr soldout_buffer 3 , 363 | .Xr soldout_renderers 3 364 | .Sh AUTHORS 365 | .An -nosplit 366 | The 367 | .Nm soldout 368 | library was written by 369 | .An Natasha Qo Kerensikova Qc Porte Aq Mt natacha@instinctive.eu . 370 | Manual page was originally written by 371 | .An Massimo Manghi Aq Mt mxmanghi@apache.org , 372 | and rewritten to mdoc format by 373 | .An Svyatoslav Mishyn Aq Mt juef@openmailbox.org . 374 | -------------------------------------------------------------------------------- /libsoldout/mkd2man.c: -------------------------------------------------------------------------------- 1 | /* mkd2man.c - man-page-formatted output from markdown text */ 2 | 3 | /* 4 | * Copyright (c) 2009-2015, Baptiste Daroussin and Natacha Porté 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "markdown.h" 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #define READ_UNIT 1024 32 | #define OUTPUT_UNIT 64 33 | 34 | 35 | /**************************** 36 | * MARKDOWN TO MAN RENDERER * 37 | ****************************/ 38 | 39 | /* usage • print the option list */ 40 | 41 | static void 42 | usage(FILE *out, const char *name) { 43 | fprintf(out, "Usage: %s [-h] [-d ] [-s
    ] " 44 | "[ -t ] [input-file]\n\n", name); 45 | fprintf(out, "\t-d, --date\n" 46 | "\t\tSet the date of the manpage (default: now),\n" 47 | "\t-h, --help\n" 48 | "\t\tDisplay this help text and exit without further processing\n" 49 | "\t-s, --section\n" 50 | "\t\tSet the section of the manpage (default: 1)\n" 51 | "\t-t, --title\n" 52 | "\t\tSet the title of the manpage (default: filename)\n"); } 53 | 54 | struct metadata { 55 | char *title; 56 | char *date; 57 | int section; 58 | }; 59 | 60 | static void 61 | man_text_escape(struct buf *ob, char *src, size_t size) { 62 | size_t i = 0, org; 63 | while (i < size) { 64 | /* copying directly unescaped characters */ 65 | org = i; 66 | while (i < size && src[i] != '-') 67 | i += 1; 68 | if (i > org) bufput(ob, src + org, i - org); 69 | 70 | /* escaping */ 71 | if (i >= size) break; 72 | else if (src[i] == '-') BUFPUTSL(ob, "\\-"); 73 | i += 1; } } 74 | 75 | static void 76 | man_prolog(struct buf *ob, void *opaque) { 77 | struct metadata *m = (struct metadata *)opaque; 78 | bufprintf(ob, 79 | ".\\\" Generated by mkd2man\n" 80 | ".Dd %s\n" 81 | ".Dt %s %d\n" 82 | ".Os", 83 | m->date, 84 | m->title, 85 | m->section 86 | ); } 87 | 88 | static void 89 | man_epilog(struct buf *ob, void *opaque) { 90 | BUFPUTSL(ob, "\n"); } 91 | 92 | static void 93 | man_blockcode(struct buf *ob, struct buf *text, void *opaque) { 94 | if (ob->size) bufputc(ob, '\n'); 95 | BUFPUTSL(ob, ".Bd -literal\n"); 96 | if (text) man_text_escape(ob, text->data, text->size); 97 | BUFPUTSL(ob, ".Ed"); } 98 | 99 | static void 100 | man_blockquote(struct buf *ob, struct buf *text, void *opaque) { 101 | if (ob->size) bufputc(ob, '\n'); 102 | BUFPUTSL(ob, ".Eo\n"); 103 | if (text) man_text_escape(ob, text->data, text->size); 104 | BUFPUTSL(ob, "\n.Ec"); } 105 | 106 | static int 107 | man_codespan(struct buf *ob, struct buf *text, void *opaque) { 108 | if (ob->size) bufputc(ob, '\n'); 109 | BUFPUTSL(ob, ".Bd -literal\n"); 110 | if (text) man_text_escape(ob, text->data, text->size); 111 | BUFPUTSL(ob, ".Ed"); 112 | return 1; } 113 | 114 | static void 115 | man_header(struct buf *ob, struct buf *text, int level, void *opaque) { 116 | if (ob->size) bufputc(ob, '\n'); 117 | switch(level) { 118 | case 1: 119 | BUFPUTSL(ob,".Sh "); 120 | break; 121 | case 2: 122 | BUFPUTSL(ob, ".Ss "); 123 | break; 124 | case 3: 125 | BUFPUTSL(ob, ".Pp\n.Em "); 126 | break; 127 | } 128 | if (text) bufput(ob, text->data, text->size); 129 | } 130 | 131 | static int 132 | man_double_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) { 133 | if (!text || !text->size) return 0; 134 | BUFPUTSL(ob, "\\fB"); 135 | bufput(ob, text->data, text->size); 136 | BUFPUTSL(ob, "\\fP"); 137 | return 1; } 138 | 139 | static int 140 | man_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) { 141 | if (!text || !text->size) return 0; 142 | BUFPUTSL(ob, "\\fI"); 143 | if (text) bufput(ob, text->data, text->size); 144 | BUFPUTSL(ob, "\\fP"); 145 | return 1; } 146 | 147 | static int 148 | man_linebreak(struct buf *ob, void *opaque) { 149 | BUFPUTSL(ob, ".br"); 150 | return 1; } 151 | 152 | static void 153 | man_paragraph(struct buf *ob, struct buf *text, void *opaque) { 154 | if (ob->size) bufputc(ob, '\n'); 155 | BUFPUTSL(ob, ".Pp\n"); 156 | if (text) bufput(ob, text->data, text->size); } 157 | 158 | static void 159 | man_list(struct buf *ob, struct buf *text, int flags, void *opaque) { 160 | if (ob->size) bufputc(ob, '\n'); 161 | if (flags & MKD_LIST_ORDERED) 162 | BUFPUTSL(ob,".Bl -enum\n"); 163 | else 164 | BUFPUTSL(ob,".Bl -bullet\n"); 165 | if (text) bufput(ob, text->data, text->size); 166 | BUFPUTSL(ob, ".El"); } 167 | 168 | static void 169 | man_listitem(struct buf *ob, struct buf *text, int flags, void *opaque) { 170 | BUFPUTSL(ob, ".It\n"); 171 | if (text) { 172 | while (text->size && text->data[text->size - 1] == '\n') 173 | text->size -= 1; 174 | bufput(ob, text->data, text->size); } 175 | BUFPUTSL(ob, "\n"); } 176 | 177 | static void 178 | man_normal_text(struct buf *ob, struct buf *text, void *opaque) { 179 | if (text) man_text_escape(ob, text->data, text->size); } 180 | 181 | 182 | /* renderer structure */ 183 | static struct mkd_renderer to_man = { 184 | /* document-level callbacks */ 185 | man_prolog, 186 | man_epilog, 187 | 188 | /* block-level callbacks */ 189 | man_blockcode, 190 | NULL, 191 | man_blockquote, 192 | NULL, 193 | man_header, 194 | NULL, 195 | man_list, 196 | man_listitem, 197 | man_paragraph, 198 | NULL, 199 | NULL, 200 | NULL, 201 | 202 | /* span-level callbacks */ 203 | NULL, 204 | man_codespan, 205 | man_double_emphasis, 206 | man_emphasis, 207 | NULL, 208 | man_linebreak, 209 | NULL, 210 | NULL, 211 | NULL, 212 | 213 | /* low-level callbacks */ 214 | NULL, 215 | man_normal_text, 216 | 217 | /* renderer data */ 218 | 64, 219 | "*_", 220 | NULL }; 221 | 222 | 223 | 224 | /***************** 225 | * MAIN FUNCTION * 226 | *****************/ 227 | 228 | /* main • main function, interfacing STDIO with the parser */ 229 | int 230 | main(int argc, char **argv) { 231 | struct buf *ib, *ob; 232 | size_t ret; 233 | size_t i; 234 | FILE *in = stdin; 235 | int ch, argerr, help; 236 | char *tmp; 237 | char datebuf[64]; 238 | time_t ttm; 239 | struct tm *tm; 240 | struct stat st; 241 | struct metadata man_metadata; 242 | 243 | struct option longopts[] = { 244 | { "date", required_argument, 0, 'd' }, 245 | { "help", no_argument, 0, 'h' }, 246 | { "section", required_argument, 0, 's' }, 247 | { "title", required_argument, 0, 't' }, 248 | { 0, 0, 0, 0} 249 | }; 250 | 251 | man_metadata.section = 1; 252 | man_metadata.title = NULL; 253 | man_metadata.date = NULL; 254 | /* opening the file if given from the command line */ 255 | argerr = help = 0; 256 | while (!argerr && 257 | (ch = getopt_long(argc, argv, "d:hs:t:", longopts, 0)) != -1) 258 | switch (ch) { 259 | case 'd': 260 | man_metadata.date = optarg; 261 | break; 262 | case 'h': 263 | argerr = help = 1; 264 | break; 265 | case 's': 266 | if (strlen(optarg) != 1 && 267 | strspn(optarg, "123456789") != 1) { 268 | argerr = 1; 269 | break; } 270 | man_metadata.section = (int)strtol(optarg, 271 | (char **)NULL, 10); 272 | break; 273 | case 't': 274 | man_metadata.title = optarg; 275 | break; 276 | default: 277 | argerr = 1; } 278 | if (argerr) { 279 | usage(help ? stdout : stderr, argv[0]); 280 | return help ? EXIT_SUCCESS : EXIT_FAILURE; 281 | } 282 | 283 | argc -= optind; 284 | argv += optind; 285 | 286 | if (argc > 0) { 287 | in = fopen(argv[0], "r"); 288 | if (!in) { 289 | fprintf(stderr,"Unable to open input file \"%s\": %s\n", 290 | argv[0], strerror(errno)); 291 | return EXIT_FAILURE; } } 292 | 293 | if (!man_metadata.date) { 294 | if (in == stdin || stat(argv[0], &st) == -1) { 295 | ttm = time(NULL); 296 | tm = localtime(&ttm); } 297 | else 298 | tm = localtime(&st.st_mtime); 299 | strftime(datebuf, sizeof(datebuf), "%B %d, %Y", tm); 300 | man_metadata.date = datebuf; 301 | } 302 | 303 | if (in == stdin && !man_metadata.title) { 304 | fprintf(stderr, "When reading from stdin the title should be " 305 | "specified is expected\n"); 306 | return EXIT_FAILURE; } 307 | 308 | if (!man_metadata.title) { 309 | tmp = strrchr(argv[0], '/'); 310 | man_metadata.title = strrchr(argv[0], '/'); 311 | if (!tmp) 312 | tmp = argv[0]; 313 | else 314 | tmp++; 315 | man_metadata.title = tmp; 316 | tmp = strrchr(man_metadata.title, '.'); 317 | if (tmp) 318 | *tmp = '\0'; } 319 | 320 | /* Ensure the title is uppercase */ 321 | for (i = 0; i < strlen(man_metadata.title); i++) 322 | man_metadata.title[i] = toupper(man_metadata.title[i]); 323 | 324 | /* reading everything */ 325 | ib = bufnew(READ_UNIT); 326 | bufgrow(ib, READ_UNIT); 327 | while ((ret = fread(ib->data + ib->size, 1, 328 | ib->asize - ib->size, in)) > 0) { 329 | ib->size += ret; 330 | bufgrow(ib, ib->size + READ_UNIT); } 331 | if (in != stdin) fclose(in); 332 | 333 | to_man.opaque = &man_metadata; 334 | /* performing markdown to man */ 335 | ob = bufnew(OUTPUT_UNIT); 336 | markdown(ob, ib, &to_man); 337 | 338 | /* writing the result to stdout */ 339 | ret = fwrite(ob->data, 1, ob->size, stdout); 340 | if (ret < ob->size) 341 | fprintf(stderr, "Warning: only %zu output byte written, " 342 | "out of %zu\n", 343 | ret, 344 | ob->size); 345 | 346 | /* cleanup */ 347 | bufrelease(ib); 348 | bufrelease(ob); 349 | return EXIT_SUCCESS; } 350 | 351 | /* vim: set filetype=c: */ 352 | -------------------------------------------------------------------------------- /ConsoleRenderer.cpp: -------------------------------------------------------------------------------- 1 | extern "C" 2 | { 3 | #include "libsoldout/markdown.h" 4 | } 5 | 6 | #include <stack> 7 | #include <iostream> 8 | #include <string> 9 | #include <cstring> 10 | 11 | #include "ColouredBuffer.hpp" 12 | #include "include/console-markdown/CMDMD.hpp" 13 | 14 | #include "include/console-markdown/CPP.hpp" 15 | #include "include/console-markdown/Pawn.hpp" 16 | 17 | #ifndef CONMD_WINDOWS 18 | #include <sys/ioctl.h> 19 | #include <unistd.h> 20 | #define sprintf_s sprintf 21 | #endif 22 | 23 | static int RenderMarkdown(struct buf * ob, char const * style, struct buf const * text) 24 | { 25 | if (text && text->size) 26 | { 27 | size_t 28 | len = strlen(style); 29 | bufput(ob, style, len); 30 | bufput(ob, text->data, text->size); 31 | BUFPUTSL(ob, "\x1B[0m"); 32 | return 1; 33 | } 34 | return 0; 35 | } 36 | 37 | struct console_data_s 38 | { 39 | size_t WindowWidth; 40 | size_t BufferWidth; 41 | int BulletPoint; 42 | }; 43 | 44 | /*static void 45 | rndr_blockquote(struct buf *ob, struct buf *text, void *opaque) 46 | { 47 | if (ob->size) 48 | { 49 | bufputc(ob, '\n'); 50 | } 51 | BUFPUTSL(ob, "<blockquote>\n"); 52 | if (text) 53 | { 54 | bufput(ob, text->data, text->size); 55 | } 56 | BUFPUTSL(ob, "</blockquote>\n"); 57 | }*/ 58 | 59 | static int 60 | rndr_codespan(struct buf *ob, struct buf *text, void *opaque) 61 | { 62 | if (text) 63 | { 64 | BUFPUTSL(ob, "\x1B[47;30m"); 65 | bufput(ob, text->data, text->size); 66 | BUFPUTSL(ob, "\x1B[0m"); 67 | } 68 | return 1; 69 | } 70 | 71 | static int 72 | rndr_double_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) 73 | { 74 | // Double `*` or `_` is `__bold__`. 75 | #ifndef CONMD_WINDOWS 76 | return RenderMarkdown(ob, "\x1B[5;47;30m", text); 77 | #else 78 | return RenderMarkdown(ob, "\x1B[1m", text); 79 | #endif 80 | } 81 | 82 | static int 83 | rndr_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) 84 | { 85 | // Single `*` or `_` is `*italic*`. 86 | #ifdef CONMD_WINDOWS 87 | return RenderMarkdown(ob, "\x1B[37;1m", text); 88 | #else 89 | return RenderMarkdown(ob, "\x1B[4m", text); 90 | #endif 91 | } 92 | 93 | void rndr_blockcode(struct buf *ob, struct buf *text, void *opaque) 94 | { 95 | // Just indent it all 4 spaces. 96 | if (text && text->size) 97 | { 98 | std::string 99 | code(text->data, text->size); 100 | 101 | std::string::size_type 102 | lastPos = 0, 103 | findPos; 104 | 105 | size_t 106 | windowWidth = reinterpret_cast<console_data_s *>(opaque)->WindowWidth; 107 | bool 108 | nl = windowWidth < reinterpret_cast<console_data_s *>(opaque)->BufferWidth; 109 | if (ob->size) 110 | { 111 | bufputc(ob, '\n'); 112 | } 113 | while (std::string::npos != (findPos = code.find("\n", lastPos))) 114 | { 115 | BUFPUTSL(ob, "\x1B[0m \x1B[47;30m"); 116 | bufput(ob, code.c_str() + lastPos, findPos - lastPos); 117 | if (findPos - lastPos < windowWidth - 4) 118 | { 119 | bufputcn(ob, ' ', windowWidth - 4 - findPos + lastPos); 120 | if (nl) 121 | { 122 | bufputc(ob, '\n'); 123 | } 124 | } 125 | else 126 | { 127 | bufputc(ob, '\n'); 128 | } 129 | lastPos = findPos + 1; 130 | } 131 | bufput(ob, code.c_str() + lastPos, code.length() - lastPos); 132 | BUFPUTSL(ob, "\x1B[0m\n"); 133 | } 134 | } 135 | 136 | void rndr_fencedcode(struct buf *ob, struct buf *text, char *name, size_t namelen, void *opaque) 137 | { 138 | if (!text || !text->size) 139 | { 140 | return; 141 | } 142 | if (namelen) 143 | { 144 | if (!strncmp(name, "cpp", 3)) 145 | { 146 | if (ob->size) 147 | { 148 | bufputc(ob, '\n'); 149 | } 150 | std::string 151 | out = cmdmd::CPP(std::string(text->data, text->size)); 152 | bufput(ob, out.c_str(), out.length()); 153 | bufputc(ob, '\n'); 154 | return; 155 | } 156 | else if (!strncmp(name, "pawn", 3)) 157 | { 158 | if (ob->size) 159 | { 160 | bufputc(ob, '\n'); 161 | } 162 | std::string 163 | out = cmdmd::Pawn(std::string(text->data, text->size)); 164 | bufput(ob, out.c_str(), out.length()); 165 | bufputc(ob, '\n'); 166 | return; 167 | } 168 | } 169 | rndr_blockcode(ob, text, opaque); 170 | } 171 | 172 | static void 173 | rndr_header(struct buf *ob, struct buf *text, int level, void *opaque) 174 | { 175 | if (text && text->size) 176 | { 177 | if (ob->size) 178 | { 179 | bufputc(ob, '\n'); 180 | } 181 | size_t 182 | width = reinterpret_cast<console_data_s *>(opaque)->WindowWidth, 183 | len = text->size; 184 | if (len > width - 2) 185 | { 186 | len = width - 2; 187 | } 188 | switch (level) 189 | { 190 | case 1: 191 | BUFPUTSL(ob, "\x1B[42;5;30m "); 192 | bufput(ob, text->data, text->size); 193 | BUFPUTSL(ob, " \x1B[0;49m\n\x1B[42;5;30m"); 194 | bufputcn(ob, '=', len + 2); 195 | BUFPUTSL(ob, "\x1B[0m\n\x1B[0m"); 196 | break; 197 | case 2: 198 | BUFPUTSL(ob, "\x1B[32;1m "); 199 | bufput(ob, text->data, text->size); 200 | BUFPUTSL(ob, "\n "); 201 | bufputcn(ob, '-', len); 202 | BUFPUTSL(ob, "\x1B[0m\n"); 203 | break; 204 | case 3: 205 | BUFPUTSL(ob, "\x1B[33;1m "); 206 | bufput(ob, text->data, text->size); 207 | BUFPUTSL(ob, "\x1B[0m\n"); 208 | break; 209 | case 4: 210 | BUFPUTSL(ob, "\x1B[31;1m "); 211 | bufput(ob, text->data, text->size); 212 | BUFPUTSL(ob, "\x1B[0m\n"); 213 | break; 214 | } 215 | } 216 | } 217 | 218 | /*static int 219 | rndr_link(struct buf *ob, struct buf *link, struct buf *title, struct buf *content, void *opaque) 220 | { 221 | BUFPUTSL(ob, "<a href=\""); 222 | if (link && link->size) 223 | { 224 | lus_attr_escape(ob, link->data, link->size); 225 | } 226 | if (title && title->size) 227 | { 228 | BUFPUTSL(ob, "\" title=\""); 229 | lus_attr_escape(ob, title->data, title->size); 230 | } 231 | BUFPUTSL(ob, "\">"); 232 | if (content && content->size) 233 | { 234 | bufput(ob, content->data, content->size); 235 | } 236 | BUFPUTSL(ob, "</a>"); 237 | return 1; 238 | }*/ 239 | 240 | static void 241 | rndr_list(struct buf *ob, struct buf *text, int flags, void *opaque) 242 | { 243 | if (text) 244 | { 245 | if (ob->size) 246 | { 247 | bufputc(ob, '\n'); 248 | } 249 | bufput(ob, text->data, text->size); 250 | reinterpret_cast<console_data_s *>(opaque)->BulletPoint = 0; 251 | } 252 | } 253 | 254 | static void 255 | rndr_listitem(struct buf *ob, struct buf *text, int flags, void *opaque) 256 | { 257 | char num[4]; 258 | if (text) 259 | { 260 | while (text->size && text->data[text->size - 1] == '\n') 261 | { 262 | text->size -= 1; 263 | } 264 | if (text->size) 265 | { 266 | if (flags & MKD_LIST_ORDERED) 267 | { 268 | BUFPUTSL(ob, " \x1B[35m"); 269 | sprintf_s(num, "%d", ++reinterpret_cast<console_data_s *>(opaque)->BulletPoint); 270 | bufput(ob, num, strlen(num)); 271 | BUFPUTSL(ob, ")\x1B[0m "); 272 | bufput(ob, text->data, text->size); 273 | bufputc(ob, '\n'); 274 | } 275 | else 276 | { 277 | BUFPUTSL(ob, " \x1B[35m*\x1B[0m "); 278 | bufput(ob, text->data, text->size); 279 | bufputc(ob, '\n'); 280 | } 281 | } 282 | } 283 | } 284 | 285 | static void 286 | rndr_normal_text(struct buf *ob, struct buf *text, void *opaque) 287 | { 288 | bufput(ob, text->data, text->size); 289 | } 290 | 291 | static void 292 | null_block(struct buf *ob, struct buf *text, void *opaque) 293 | { 294 | bufput(ob, text->data, text->size); 295 | } 296 | 297 | static int 298 | null_span(struct buf *ob, struct buf *text, void *opaque) 299 | { 300 | bufput(ob, text->data, text->size); 301 | return 1; 302 | } 303 | 304 | static void 305 | rndr_paragraph(struct buf *ob, struct buf *text, void *opaque) 306 | { 307 | if (ob->size) 308 | { 309 | bufputc(ob, '\n'); 310 | } 311 | if (text) 312 | { 313 | size_t 314 | width = reinterpret_cast<console_data_s *>(opaque)->WindowWidth, 315 | rem = width, 316 | len = text->size; 317 | bool 318 | nl = reinterpret_cast<console_data_s *>(opaque)->BufferWidth > width; 319 | char 320 | * start = text->data, 321 | * end; 322 | while ((end = (char *)memchr(start, '\n', len))) 323 | { 324 | size_t 325 | diff = end - start; 326 | len -= diff + 1; 327 | while (diff >= rem) 328 | { 329 | bufput(ob, start, rem); 330 | diff -= rem; 331 | start += rem; 332 | rem = width; 333 | if (nl) 334 | { 335 | bufputc(ob, '\n'); 336 | } 337 | } 338 | if (diff) 339 | { 340 | bufput(ob, start, diff); 341 | rem = width - diff; 342 | } 343 | start = end + 1; 344 | } 345 | if (len) 346 | { 347 | while (len >= rem) 348 | { 349 | bufput(ob, start, rem); 350 | len -= rem; 351 | start += rem; 352 | rem = width; 353 | if (nl) 354 | { 355 | bufputc(ob, '\n'); 356 | } 357 | } 358 | if (len) 359 | { 360 | bufput(ob, start, len); 361 | } 362 | } 363 | } 364 | bufputc(ob, '\n'); 365 | } 366 | 367 | /*static void 368 | rndr_raw_block(struct buf *ob, struct buf *text, void *opaque) 369 | { 370 | size_t org, sz; 371 | if (!text) 372 | { 373 | return; 374 | } 375 | sz = text->size; 376 | while (sz > 0 && text->data[sz - 1] == '\n') 377 | { 378 | sz -= 1; 379 | } 380 | org = 0; 381 | while (org < sz && text->data[org] == '\n') 382 | { 383 | org += 1; 384 | } 385 | if (org >= sz) 386 | { 387 | return; 388 | } 389 | if (ob->size) 390 | { 391 | bufputc(ob, '\n'); 392 | } 393 | bufput(ob, text->data + org, sz - org); 394 | bufputc(ob, '\n'); 395 | } 396 | 397 | static int 398 | rndr_raw_inline(struct buf *ob, struct buf *text, void *opaque) 399 | { 400 | bufput(ob, text->data, text->size); 401 | return 1; 402 | }*/ 403 | 404 | static int 405 | rndr_linebreak(struct buf *ob, void *opaque) 406 | { 407 | bufputc(ob, '\n'); 408 | return 1; 409 | } 410 | 411 | void rndr_hrule(struct buf *ob, void *opaque) 412 | { 413 | if (ob->size) 414 | { 415 | bufputc(ob, '\n'); 416 | } 417 | size_t 418 | len = reinterpret_cast<console_data_s *>(opaque)->WindowWidth; 419 | BUFPUTSL(ob, "\x1B[44;34m"); 420 | bufputcn(ob, ' ', len); 421 | BUFPUTSL(ob, "\x1B[0m\n"); 422 | } 423 | 424 | /* exported renderer structures */ 425 | std::string cmdmd::Render(std::string const & input) 426 | { 427 | return cmdmd::Render(input.c_str(), input.length()); 428 | } 429 | 430 | std::string cmdmd::Render(char const * input) 431 | { 432 | return cmdmd::Render(input, strlen(input)); 433 | } 434 | 435 | std::string cmdmd::Render(char const * input, size_t len) 436 | { 437 | static struct mkd_renderer 438 | consoleRenderer = 439 | { 440 | NULL, 441 | NULL, 442 | 443 | rndr_blockcode,//NULL, 444 | rndr_fencedcode, 445 | null_block,//NULL, //discount_blockquote, 446 | null_block,//NULL, //rndr_raw_block, 447 | rndr_header, 448 | rndr_hrule, 449 | rndr_list, 450 | rndr_listitem, 451 | rndr_paragraph, 452 | NULL, //discount_table, 453 | NULL, //discount_table_cell, 454 | NULL, //discount_table_row, 455 | 456 | NULL, 457 | rndr_codespan, 458 | rndr_double_emphasis, 459 | rndr_emphasis, 460 | NULL, //html_discount_image, 461 | rndr_linebreak, 462 | NULL, //discount_link, 463 | null_span,//NULL, //rndr_raw_inline, 464 | NULL, //rndr_triple_emphasis, 465 | 466 | NULL, 467 | NULL, 468 | 469 | 64, 470 | "*_", 471 | NULL 472 | }; 473 | buf 474 | * ob = bufnew(64), 475 | ib = { 476 | const_cast<char *>(input), 477 | len, 478 | 0, 479 | 0, 480 | 0xFFFF 481 | }; 482 | #ifdef CONMD_WINDOWS 483 | CONSOLE_SCREEN_BUFFER_INFO 484 | console; 485 | size_t 486 | windowWidth = 80, 487 | bufferWidth = 80; 488 | if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &console)) 489 | { 490 | // Use the console window size, not the line buffer size. 491 | windowWidth = console.srWindow.Right - console.srWindow.Left + 1; 492 | bufferWidth = console.dwSize.X; 493 | } 494 | console_data_s 495 | data = { windowWidth, bufferWidth, 0 }; 496 | #else 497 | struct winsize w; 498 | ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); 499 | 500 | console_data_s 501 | data = { w.ws_col, w.ws_col, 0 }; 502 | #endif 503 | consoleRenderer.opaque = reinterpret_cast<void *>(&data); 504 | markdown(ob, &ib, &consoleRenderer); 505 | 506 | std::string 507 | ret(ob->data, ob->size); 508 | 509 | bufrelease(ob); 510 | 511 | return ret; 512 | } 513 | 514 | void cmdmd::Init() 515 | { 516 | ColouredBuffer<char>::StandardInstall(); 517 | ColouredBuffer<wchar_t>::StandardInstall(); 518 | } 519 | 520 | std::string 521 | cmdmd:: 522 | Literals:: 523 | operator "" _cmdmd(char const * s, size_t len) 524 | { 525 | return ::cmdmd::Render(s, len); 526 | } 527 | 528 | std::string 529 | cmdmd:: 530 | Literals:: 531 | operator "" _md(char const * s, size_t len) 532 | { 533 | return ::cmdmd::Render(s, len); 534 | } 535 | 536 | -------------------------------------------------------------------------------- /libsoldout/mkd2latex.c: -------------------------------------------------------------------------------- 1 | /* mkd2latex.c - LaTeX-formatted output from markdown text */ 2 | 3 | /* 4 | * Copyright (c) 2009, Baptiste Daroussin, Natacha Porté, and Michael Huang 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* 20 | * Links require the hyperref package, and images require the graphicx 21 | * package. 22 | */ 23 | 24 | #include "markdown.h" 25 | 26 | #include <stdio.h> 27 | #include <stdlib.h> 28 | #include <errno.h> 29 | #include <string.h> 30 | 31 | #define READ_UNIT 1024 32 | #define OUTPUT_UNIT 64 33 | 34 | /********************* 35 | * ENTITY CONVERSION * 36 | *********************/ 37 | 38 | struct str_pair { 39 | const char *entity; 40 | const char *latex; }; 41 | 42 | static struct str_pair entity_latex[] = { 43 | { "Æ", "\\AE{}" }, 44 | { "Á", "\\'A" }, 45 | { "Â", "\\^A" }, 46 | { "À", "\\`A" }, 47 | { "Å", "\\AA{}" }, 48 | { "Ã", "\\~A" }, 49 | { "Ä", "\\\"A" }, 50 | { "Ç", "\\c{C}" }, 51 | // { "Ð", "\\DH{}" }, // not available in OT1 52 | { "É", "\\'E" }, 53 | { "Ê", "\\^E" }, 54 | { "È", "\\`E" }, 55 | { "Ë", "\\\"E" }, 56 | { "Í", "\\'I" }, 57 | { "Î", "\\^I" }, 58 | { "Ì", "\\`I" }, 59 | { "Ï", "\\\"I" }, 60 | { "Ñ", "\\~N" }, 61 | { "Ó", "\\'O" }, 62 | { "Ô", "\\^O" }, 63 | { "Ò", "\\`O" }, 64 | { "Ø", "\\O{}" }, 65 | { "Õ", "\\~O" }, 66 | { "Ö", "\\\"O" }, 67 | // { "Þ", "\\TH{}" }, // not available in OT1 68 | { "Ú", "\\'U" }, 69 | { "Û", "\\^U" }, 70 | { "Ù", "\\`U" }, 71 | { "Ü", "\\\"U" }, 72 | { "&Ygrave;", "\\`Y" }, 73 | { "á", "\\'a" }, 74 | { "â", "\\^a" }, 75 | // { "´", "\\textasciiacute{}" }, // requires textcomp 76 | { "æ", "\\ae{}" }, 77 | { "à", "\\`a" }, 78 | { "&", "\\&" }, 79 | { "'", "'" }, 80 | { "å", "\\aa{}" }, 81 | { "ã", "\\~a" }, 82 | { "ä", "\\\"a" }, 83 | // { "¦", "\\textbrokenbar{}" }, // requires textcomp 84 | { "ç", "\\c{c}" }, 85 | { "¸", "\\c{}" }, 86 | // { "¢", "\\textcent{}" }, // requires textcomp 87 | { "©", "\\copyright{}" }, 88 | // { "¤", "\\textcurrency{}" }, // requires textcomp 89 | // { "°", "\\textdegree{}" }, // requires textcomp 90 | // { "÷", "\\textdiv{}" }, // requires textcomp 91 | { "é", "\\'e" }, 92 | { "ê", "\\^e" }, 93 | { "è", "\\`e" }, 94 | // { "ð", "\\dh{}" }. // not available in OT1 95 | { "ë", "\\\"e" }, 96 | // { "½", "\\textonehalf{}" }, // requires textcomp 97 | // { "¼", "\\textonequarter{}" }, // requires textcomp 98 | // { "¾", "\\textthreequarter{}" }, // requires textcomp 99 | { ">", "$>$" }, 100 | { "í", "\\'\\i{}" }, 101 | { "î", "\\^\\i{}" }, 102 | { "¡", "\\textexclamdown{}" }, 103 | { "ì", "\\`\\i{}" }, 104 | { "¿", "\\" }, 105 | { "ï", "\\\"\\i{}" }, 106 | // { "«", "\\guillemotleft{}" }, // not available in OT1 107 | { "<", "$<$" }, 108 | // { "¯", "\\textasciimacaron{}" }, // requires textcomp 109 | // { "µ", "\\textmu{}"}, // requires textcomp 110 | { "·", "\\textperiodcentered{}" }, 111 | { " ", "~" }, 112 | // { "¬", "\\textlnot{}" }, // requires textcomp 113 | { "ñ", "\\~n" }, 114 | { "ó", "\\'o" }, 115 | { "ô", "\\^o" }, 116 | { "ò", "\\`o" }, 117 | { "ª", "\\textordfeminine{}" }, 118 | { "º", "\\textordmasculine{}" }, 119 | { "ø", "\\o{}" }, 120 | { "õ", "\\~o" }, 121 | { "ö", "\\\"o" }, 122 | { "¶", "\\P{}" }, 123 | // { "±", "\\textpm{}" }, // requires textcomp 124 | { "£", "\\textsterling{}" }, 125 | { """, "\"" }, 126 | // { "»", "\\guillemotright{}" }, // not available in OT1 127 | { "®", "\\textregistered{}" }, 128 | { "§", "\\S{}" }, 129 | { "­", "\\-" }, 130 | // { "¹", "\\textonesuperior{}" }, // requires textcomp 131 | // { "²", "\\texttwosuperior{}" }, // requires textcomp 132 | // { "³", "\\textthreesuperior{}" }, // requires textcomp 133 | { "ß", "\\ss{}" }, 134 | // { "þ", "\\th{}" }, // not available in OT1 135 | // { "×", "\\texttimes{}" }, // requires textcomp 136 | { "ú", "\\'u" }, 137 | { "û", "\\^u" }, 138 | { "ù", "\\`u" }, 139 | // { "¨", "\\textasciidieresis{}" }, // requires textcomp 140 | { "ü", "\\\"u" }, 141 | { "ý", "\\'y" }, 142 | // { "¥", "\\textyen{}" }, // requires textcomp 143 | { "ÿ", "\\\"y" }, 144 | }; 145 | 146 | static int cmp_entity(const void *key, const void *element) { 147 | const struct str_pair *pair = element; 148 | const struct buf *entity = key; 149 | return bufcmps(entity, pair->entity); } 150 | 151 | static const char *entity2latex(const struct buf *entity) { 152 | const struct str_pair *pair; 153 | pair = bsearch(entity, entity_latex, 154 | sizeof entity_latex / sizeof *entity_latex, 155 | sizeof *entity_latex, 156 | &cmp_entity); 157 | return pair ? pair->latex : 0; } 158 | 159 | 160 | 161 | /****************************** 162 | * MARKDOWN TO LATEX RENDERER * 163 | ******************************/ 164 | 165 | static void 166 | latex_text_escape(struct buf *ob, char *src, size_t size) { 167 | size_t i = 0, org; 168 | while (i < size) { 169 | /* copying directly unescaped characters */ 170 | org = i; 171 | while (i < size && src[i] != '&' && src[i] != '%' 172 | && src[i] != '$' && src[i] != '#' && src[i] != '_' 173 | && src[i] != '{' && src[i] != '}' && src[i] != '~' 174 | && src[i] != '^' && src[i] != '\\' && src[i] != '<' 175 | && src[i] != '>') 176 | i += 1; 177 | if (i > org) bufput(ob, src + org, i - org); 178 | 179 | /* escaping */ 180 | if (i >= size) break; 181 | else if (src[i] == '&') BUFPUTSL(ob, "\\&"); 182 | else if (src[i] == '%') BUFPUTSL(ob, "\\%"); 183 | else if (src[i] == '$') BUFPUTSL(ob, "\\$"); 184 | else if (src[i] == '#') BUFPUTSL(ob, "\\#"); 185 | else if (src[i] == '_') BUFPUTSL(ob, "\\_"); 186 | else if (src[i] == '{') BUFPUTSL(ob, "\\{"); 187 | else if (src[i] == '}') BUFPUTSL(ob, "\\}"); 188 | else if (src[i] == '<') BUFPUTSL(ob, "$<$"); 189 | else if (src[i] == '>') BUFPUTSL(ob, "$<$"); 190 | else if (src[i] == '~') BUFPUTSL(ob, "\\textasciitilde{}"); 191 | else if (src[i] == '^') BUFPUTSL(ob, "\\textasciicircum{}"); 192 | else if (src[i] == '\\') BUFPUTSL(ob, "\\textbackslash{}"); 193 | i += 1; } } 194 | 195 | static void 196 | latex_prolog(struct buf *ob, void *opaque) { 197 | BUFPUTSL(ob, 198 | "\\documentclass{article}\n" 199 | "\\usepackage{hyperref}\n" 200 | "\\usepackage{graphicx}\n" 201 | "\\begin{document}\n"); } 202 | 203 | static void 204 | latex_epilog(struct buf *ob, void *opaque) { 205 | BUFPUTSL(ob, "\n\\end{document}\n"); } 206 | 207 | static int 208 | latex_autolink(struct buf *ob, struct buf *link, enum mkd_autolink type, 209 | void *opaque) { 210 | if (!link || !link->size) return 0; 211 | BUFPUTSL(ob, "\\href{"); 212 | if (type == MKDA_IMPLICIT_EMAIL) BUFPUTSL(ob, "mailto:"); 213 | bufput(ob, link->data, link->size); 214 | BUFPUTSL(ob, "}{"); 215 | if (type == MKDA_EXPLICIT_EMAIL && link->size > 7) 216 | latex_text_escape(ob, link->data + 7, link->size - 7); 217 | else latex_text_escape(ob, link->data, link->size); 218 | BUFPUTSL(ob, "}"); 219 | return 1; } 220 | 221 | static int 222 | latex_link(struct buf *ob, struct buf *link, struct buf *title, 223 | struct buf *content, void *opaque) { 224 | BUFPUTSL(ob, "\\href{"); 225 | if (link && link->size) bufput(ob, link->data, link->size); 226 | BUFPUTSL(ob, "}{"); 227 | if (content && content->size) 228 | bufput(ob, content->data, content->size); 229 | BUFPUTSL(ob, "}"); 230 | return 1; } 231 | 232 | static int 233 | latex_image(struct buf *ob, struct buf *link, struct buf *title, 234 | struct buf *alt, void *opaque) { 235 | if (!link || !link->size) return 0; 236 | BUFPUTSL(ob, "\\includegraphics{"); 237 | bufput(ob, link->data, link->size); 238 | BUFPUTSL(ob, "}"); 239 | return 1; } 240 | 241 | static void 242 | latex_blockcode(struct buf *ob, struct buf *text, void *opaque) { 243 | if (ob->size) bufputc(ob, '\n'); 244 | BUFPUTSL(ob, "\\begin{verbatim}\n"); 245 | if (text) bufput(ob, text->data, text->size); 246 | BUFPUTSL(ob, "\\end{verbatim}\n"); } 247 | 248 | static void 249 | latex_blockquote(struct buf *ob, struct buf *text, void *opaque) { 250 | if (ob->size) bufputc(ob, '\n'); 251 | BUFPUTSL(ob, "\\begin{quote}\n"); 252 | if (text) bufput(ob, text->data, text->size); 253 | BUFPUTSL(ob, "\\end{quote}\n"); } 254 | 255 | static int 256 | latex_codespan(struct buf *ob, struct buf *text, void *opaque) { 257 | BUFPUTSL(ob, "\\texttt{"); 258 | if (text) latex_text_escape(ob, text->data, text->size); 259 | BUFPUTSL(ob, "}"); 260 | return 1; } 261 | 262 | static void 263 | latex_header(struct buf *ob, struct buf *text, int level, void *opaque) { 264 | if (ob->size) bufputc(ob, '\n'); 265 | switch(level) { 266 | case 1: 267 | BUFPUTSL(ob,"\\section{"); 268 | break; 269 | case 2: 270 | BUFPUTSL(ob, "\\subsection{"); 271 | break; 272 | case 3: 273 | BUFPUTSL(ob, "\\subsubsection{"); 274 | break; 275 | default: 276 | fprintf(stderr, "Warning: ignoring header level %d\n", 277 | level); 278 | } 279 | if (text) bufput(ob, text->data, text->size); 280 | if (level >= 1 && level <= 3) BUFPUTSL(ob, "}\n"); 281 | } 282 | 283 | static int 284 | latex_double_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) { 285 | if (!text || !text->size) return 0; 286 | BUFPUTSL(ob, "\\textbf{"); 287 | bufput(ob, text->data, text->size); 288 | BUFPUTSL(ob, "}"); 289 | return 1; } 290 | 291 | static int 292 | latex_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) { 293 | if (!text || !text->size) return 0; 294 | BUFPUTSL(ob, "\\emph{"); 295 | if (text) bufput(ob, text->data, text->size); 296 | BUFPUTSL(ob, "}"); 297 | return 1; } 298 | 299 | static int 300 | latex_linebreak(struct buf *ob, void *opaque) { 301 | BUFPUTSL(ob, "\\\\"); 302 | return 1; } 303 | 304 | static void 305 | latex_paragraph(struct buf *ob, struct buf *text, void *opaque) { 306 | if (ob->size) bufputc(ob, '\n'); 307 | if (text) bufput(ob, text->data, text->size); 308 | BUFPUTSL(ob, "\n"); } 309 | 310 | static void 311 | latex_list(struct buf *ob, struct buf *text, int flags, void *opaque) { 312 | if (ob->size) bufputc(ob, '\n'); 313 | if (flags & MKD_LIST_ORDERED) 314 | BUFPUTSL(ob, "\\begin{enumerate}\n"); 315 | else 316 | BUFPUTSL(ob, "\\begin{itemize}\n"); 317 | if (text) bufput(ob, text->data, text->size); 318 | if (flags & MKD_LIST_ORDERED) 319 | BUFPUTSL(ob, "\\end{enumerate}\n"); 320 | else 321 | BUFPUTSL(ob, "\\end{itemize}\n"); } 322 | 323 | static void 324 | latex_listitem(struct buf *ob, struct buf *text, int flags, void *opaque) { 325 | BUFPUTSL(ob, "\\item "); 326 | if (text) { 327 | while (text->size && text->data[text->size - 1] == '\n') 328 | text->size -= 1; 329 | bufput(ob, text->data, text->size); } 330 | BUFPUTSL(ob, "\n"); } 331 | 332 | static void 333 | latex_hrule(struct buf *ob, void *opaque) { 334 | if (ob->size) bufputc(ob, '\n'); 335 | BUFPUTSL(ob, "\\hrule"); } 336 | 337 | static int 338 | latex_triple_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) { 339 | if (!text || !text->size) return 0; 340 | BUFPUTSL(ob, "\\textbf{\\emph{"); 341 | bufput(ob, text->data, text->size); 342 | BUFPUTSL(ob, "}}"); 343 | return 1; } 344 | 345 | static void 346 | latex_entity(struct buf *ob, struct buf *entity, void *opaque) { 347 | const char *rendered = entity2latex(entity); 348 | if (rendered) 349 | bufputs(ob, rendered); 350 | else { 351 | BUFPUTSL(ob, "\\texttt{"); 352 | bufput(ob, entity->data, entity->size); 353 | BUFPUTSL(ob, "}"); } } 354 | 355 | static void 356 | latex_normal_text(struct buf *ob, struct buf *text, void *opaque) { 357 | if (text) latex_text_escape(ob, text->data, text->size); } 358 | 359 | 360 | /* renderer structure */ 361 | static struct mkd_renderer to_latex = { 362 | /* document-level callbacks */ 363 | latex_prolog, 364 | latex_epilog, 365 | 366 | /* block-level callbacks */ 367 | latex_blockcode, 368 | NULL, 369 | latex_blockquote, 370 | latex_blockcode, 371 | latex_header, 372 | latex_hrule, 373 | latex_list, 374 | latex_listitem, 375 | latex_paragraph, 376 | NULL, 377 | NULL, 378 | NULL, 379 | 380 | /* span-level callbacks */ 381 | latex_autolink, 382 | latex_codespan, 383 | latex_double_emphasis, 384 | latex_emphasis, 385 | latex_image, 386 | latex_linebreak, 387 | latex_link, 388 | latex_codespan, 389 | latex_triple_emphasis, 390 | 391 | /* low-level callbacks */ 392 | latex_entity, 393 | latex_normal_text, 394 | 395 | /* renderer data */ 396 | 64, 397 | "*_", 398 | NULL }; 399 | 400 | 401 | 402 | /***************** 403 | * MAIN FUNCTION * 404 | *****************/ 405 | 406 | /* main • main function, interfacing STDIO with the parser */ 407 | int 408 | main(int argc, char **argv) { 409 | struct buf *ib, *ob; 410 | size_t ret; 411 | FILE *in = stdin; 412 | 413 | /* opening the file if given from the command line */ 414 | if (argc > 1) { 415 | in = fopen(argv[1], "r"); 416 | if (!in) { 417 | fprintf(stderr,"Unable to open input file \"%s\": %s\n", 418 | argv[1], strerror(errno)); 419 | return 1; } } 420 | 421 | /* reading everything */ 422 | ib = bufnew(READ_UNIT); 423 | bufgrow(ib, READ_UNIT); 424 | while ((ret = fread(ib->data + ib->size, 1, 425 | ib->asize - ib->size, in)) > 0) { 426 | ib->size += ret; 427 | bufgrow(ib, ib->size + READ_UNIT); } 428 | if (in != stdin) fclose(in); 429 | 430 | /* performing markdown to LaTeX */ 431 | ob = bufnew(OUTPUT_UNIT); 432 | markdown(ob, ib, &to_latex); 433 | 434 | /* writing the result to stdout */ 435 | ret = fwrite(ob->data, 1, ob->size, stdout); 436 | if (ret < ob->size) 437 | fprintf(stderr, "Warning: only %zu output byte written, " 438 | "out of %zu\n", 439 | ret, 440 | ob->size); 441 | 442 | /* cleanup */ 443 | bufrelease(ib); 444 | bufrelease(ob); 445 | return 0; } 446 | 447 | /* vim: set filetype=c: */ 448 | -------------------------------------------------------------------------------- /libsoldout/renderers.c: -------------------------------------------------------------------------------- 1 | /* renderers.h - example markdown renderers */ 2 | 3 | /* 4 | * Copyright (c) 2009, Natacha Porté 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "renderers.h" 20 | 21 | #include <strings.h> 22 | 23 | 24 | /***************************** 25 | * EXPORTED HELPER FUNCTIONS * 26 | *****************************/ 27 | 28 | /* lus_attr_escape • copy the buffer entity-escaping '<', '>', '&' and '"' */ 29 | void 30 | lus_attr_escape(struct buf *ob, const char *src, size_t size) { 31 | size_t i = 0, org; 32 | while (i < size) { 33 | /* copying directly unescaped characters */ 34 | org = i; 35 | while (i < size && src[i] != '<' && src[i] != '>' 36 | && src[i] != '&' && src[i] != '"') 37 | i += 1; 38 | if (i > org) bufput(ob, src + org, i - org); 39 | 40 | /* escaping */ 41 | if (i >= size) break; 42 | else if (src[i] == '<') BUFPUTSL(ob, "<"); 43 | else if (src[i] == '>') BUFPUTSL(ob, ">"); 44 | else if (src[i] == '&') BUFPUTSL(ob, "&"); 45 | else if (src[i] == '"') BUFPUTSL(ob, """); 46 | i += 1; } } 47 | 48 | 49 | /* lus_body_escape • copy the buffer entity-escaping '<', '>' and '&' */ 50 | void 51 | lus_body_escape(struct buf *ob, const char *src, size_t size) { 52 | size_t i = 0, org; 53 | while (i < size) { 54 | /* copying directly unescaped characters */ 55 | org = i; 56 | while (i < size && src[i] != '<' && src[i] != '>' 57 | && src[i] != '&') 58 | i += 1; 59 | if (i > org) bufput(ob, src + org, i - org); 60 | 61 | /* escaping */ 62 | if (i >= size) break; 63 | else if (src[i] == '<') BUFPUTSL(ob, "<"); 64 | else if (src[i] == '>') BUFPUTSL(ob, ">"); 65 | else if (src[i] == '&') BUFPUTSL(ob, "&"); 66 | i += 1; } } 67 | 68 | 69 | 70 | /******************** 71 | * GENERIC RENDERER * 72 | ********************/ 73 | 74 | static int 75 | rndr_autolink(struct buf *ob, struct buf *link, enum mkd_autolink type, 76 | void *opaque) { 77 | if (!link || !link->size) return 0; 78 | BUFPUTSL(ob, "<a href=\""); 79 | if (type == MKDA_IMPLICIT_EMAIL) BUFPUTSL(ob, "mailto:"); 80 | lus_attr_escape(ob, link->data, link->size); 81 | BUFPUTSL(ob, "\">"); 82 | if (type == MKDA_EXPLICIT_EMAIL && link->size > 7) 83 | lus_body_escape(ob, link->data + 7, link->size - 7); 84 | else lus_body_escape(ob, link->data, link->size); 85 | BUFPUTSL(ob, "</a>"); 86 | return 1; } 87 | 88 | static void 89 | rndr_blockcode(struct buf *ob, struct buf *text, void *opaque) { 90 | if (ob->size) bufputc(ob, '\n'); 91 | BUFPUTSL(ob, "<pre><code>"); 92 | if (text) lus_body_escape(ob, text->data, text->size); 93 | BUFPUTSL(ob, "</code></pre>\n"); } 94 | 95 | static void 96 | rndr_blockquote(struct buf *ob, struct buf *text, void *opaque) { 97 | if (ob->size) bufputc(ob, '\n'); 98 | BUFPUTSL(ob, "<blockquote>\n"); 99 | if (text) bufput(ob, text->data, text->size); 100 | BUFPUTSL(ob, "</blockquote>\n"); } 101 | 102 | static int 103 | rndr_codespan(struct buf *ob, struct buf *text, void *opaque) { 104 | BUFPUTSL(ob, "<code>"); 105 | if (text) lus_body_escape(ob, text->data, text->size); 106 | BUFPUTSL(ob, "</code>"); 107 | return 1; } 108 | 109 | static int 110 | rndr_double_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) { 111 | if (!text || !text->size) return 0; 112 | BUFPUTSL(ob, "<strong>"); 113 | bufput(ob, text->data, text->size); 114 | BUFPUTSL(ob, "</strong>"); 115 | return 1; } 116 | 117 | static int 118 | rndr_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) { 119 | if (!text || !text->size) return 0; 120 | BUFPUTSL(ob, "<em>"); 121 | if (text) bufput(ob, text->data, text->size); 122 | BUFPUTSL(ob, "</em>"); 123 | return 1; } 124 | 125 | static void 126 | rndr_header(struct buf *ob, struct buf *text, int level, void *opaque) { 127 | if (ob->size) bufputc(ob, '\n'); 128 | bufprintf(ob, "<h%d>", level); 129 | if (text) bufput(ob, text->data, text->size); 130 | bufprintf(ob, "</h%d>\n", level); } 131 | 132 | static int 133 | rndr_link(struct buf *ob, struct buf *link, struct buf *title, 134 | struct buf *content, void *opaque) { 135 | BUFPUTSL(ob, "<a href=\""); 136 | if (link && link->size) lus_attr_escape(ob, link->data, link->size); 137 | if (title && title->size) { 138 | BUFPUTSL(ob, "\" title=\""); 139 | lus_attr_escape(ob, title->data, title->size); } 140 | BUFPUTSL(ob, "\">"); 141 | if (content && content->size) bufput(ob, content->data, content->size); 142 | BUFPUTSL(ob, "</a>"); 143 | return 1; } 144 | 145 | static void 146 | rndr_list(struct buf *ob, struct buf *text, int flags, void *opaque) { 147 | if (ob->size) bufputc(ob, '\n'); 148 | bufput(ob, (flags & MKD_LIST_ORDERED) ? "<ol>\n" : "<ul>\n", 5); 149 | if (text) bufput(ob, text->data, text->size); 150 | bufput(ob, (flags & MKD_LIST_ORDERED) ? "</ol>\n" : "</ul>\n", 6); } 151 | 152 | static void 153 | rndr_listitem(struct buf *ob, struct buf *text, int flags, void *opaque) { 154 | BUFPUTSL(ob, "<li>"); 155 | if (text) { 156 | while (text->size && text->data[text->size - 1] == '\n') 157 | text->size -= 1; 158 | bufput(ob, text->data, text->size); } 159 | BUFPUTSL(ob, "</li>\n"); } 160 | 161 | static void 162 | rndr_normal_text(struct buf *ob, struct buf *text, void *opaque) { 163 | if (text) lus_body_escape(ob, text->data, text->size); } 164 | 165 | static void 166 | rndr_paragraph(struct buf *ob, struct buf *text, void *opaque) { 167 | if (ob->size) bufputc(ob, '\n'); 168 | BUFPUTSL(ob, "<p>"); 169 | if (text) bufput(ob, text->data, text->size); 170 | BUFPUTSL(ob, "</p>\n"); } 171 | 172 | static void 173 | rndr_raw_block(struct buf *ob, struct buf *text, void *opaque) { 174 | size_t org, sz; 175 | if (!text) return; 176 | sz = text->size; 177 | while (sz > 0 && text->data[sz - 1] == '\n') sz -= 1; 178 | org = 0; 179 | while (org < sz && text->data[org] == '\n') org += 1; 180 | if (org >= sz) return; 181 | if (ob->size) bufputc(ob, '\n'); 182 | bufput(ob, text->data + org, sz - org); 183 | bufputc(ob, '\n'); } 184 | 185 | static int 186 | rndr_raw_inline(struct buf *ob, struct buf *text, void *opaque) { 187 | bufput(ob, text->data, text->size); 188 | return 1; } 189 | 190 | static int 191 | rndr_triple_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) { 192 | if (!text || !text->size) return 0; 193 | BUFPUTSL(ob, "<strong><em>"); 194 | bufput(ob, text->data, text->size); 195 | BUFPUTSL(ob, "</em></strong>"); 196 | return 1; } 197 | 198 | 199 | 200 | /******************* 201 | * HTML 4 RENDERER * 202 | *******************/ 203 | 204 | static void 205 | html_hrule(struct buf *ob, void *opaque) { 206 | if (ob->size) bufputc(ob, '\n'); 207 | BUFPUTSL(ob, "<hr>\n"); } 208 | 209 | static int 210 | html_image(struct buf *ob, struct buf *link, struct buf *title, 211 | struct buf *alt, void *opaque) { 212 | if (!link || !link->size) return 0; 213 | BUFPUTSL(ob, "<img src=\""); 214 | lus_attr_escape(ob, link->data, link->size); 215 | BUFPUTSL(ob, "\" alt=\""); 216 | if (alt && alt->size) 217 | lus_attr_escape(ob, alt->data, alt->size); 218 | if (title && title->size) { 219 | BUFPUTSL(ob, "\" title=\""); 220 | lus_attr_escape(ob, title->data, title->size); } 221 | BUFPUTSL(ob, "\">"); 222 | return 1; } 223 | 224 | static int 225 | html_linebreak(struct buf *ob, void *opaque) { 226 | BUFPUTSL(ob, "<br>\n"); 227 | return 1; } 228 | 229 | 230 | /* exported renderer structure */ 231 | const struct mkd_renderer mkd_html = { 232 | NULL, 233 | NULL, 234 | 235 | rndr_blockcode, 236 | NULL, 237 | rndr_blockquote, 238 | rndr_raw_block, 239 | rndr_header, 240 | html_hrule, 241 | rndr_list, 242 | rndr_listitem, 243 | rndr_paragraph, 244 | NULL, 245 | NULL, 246 | NULL, 247 | 248 | rndr_autolink, 249 | rndr_codespan, 250 | rndr_double_emphasis, 251 | rndr_emphasis, 252 | html_image, 253 | html_linebreak, 254 | rndr_link, 255 | rndr_raw_inline, 256 | rndr_triple_emphasis, 257 | 258 | NULL, 259 | rndr_normal_text, 260 | 261 | 64, 262 | "*_", 263 | NULL }; 264 | 265 | 266 | 267 | /********************** 268 | * XHTML 1.0 RENDERER * 269 | **********************/ 270 | 271 | static void 272 | xhtml_hrule(struct buf *ob, void *opaque) { 273 | if (ob->size) bufputc(ob, '\n'); 274 | BUFPUTSL(ob, "<hr />\n"); } 275 | 276 | static int 277 | xhtml_image(struct buf *ob, struct buf *link, struct buf *title, 278 | struct buf *alt, void *opaque) { 279 | if (!link || !link->size) return 0; 280 | BUFPUTSL(ob, "<img src=\""); 281 | lus_attr_escape(ob, link->data, link->size); 282 | BUFPUTSL(ob, "\" alt=\""); 283 | if (alt && alt->size) 284 | lus_attr_escape(ob, alt->data, alt->size); 285 | if (title && title->size) { 286 | BUFPUTSL(ob, "\" title=\""); 287 | lus_attr_escape(ob, title->data, title->size); } 288 | BUFPUTSL(ob, "\" />"); 289 | return 1; } 290 | 291 | static int 292 | xhtml_linebreak(struct buf *ob, void *opaque) { 293 | BUFPUTSL(ob, "<br />\n"); 294 | return 1; } 295 | 296 | 297 | /* exported renderer structure */ 298 | const struct mkd_renderer mkd_xhtml = { 299 | NULL, 300 | NULL, 301 | 302 | rndr_blockcode, 303 | NULL, 304 | rndr_blockquote, 305 | rndr_raw_block, 306 | rndr_header, 307 | xhtml_hrule, 308 | rndr_list, 309 | rndr_listitem, 310 | rndr_paragraph, 311 | NULL, 312 | NULL, 313 | NULL, 314 | 315 | rndr_autolink, 316 | rndr_codespan, 317 | rndr_double_emphasis, 318 | rndr_emphasis, 319 | xhtml_image, 320 | xhtml_linebreak, 321 | rndr_link, 322 | rndr_raw_inline, 323 | rndr_triple_emphasis, 324 | 325 | NULL, 326 | rndr_normal_text, 327 | 328 | 64, 329 | "*_", 330 | NULL }; 331 | 332 | 333 | 334 | /********************** 335 | * DISCOUNT RENDERERS * 336 | **********************/ 337 | 338 | static int 339 | print_link_wxh(struct buf *ob, struct buf *link) { 340 | size_t eq, ex, end; 341 | if (link->size < 1) return 0; 342 | eq = link->size - 1; 343 | while (eq > 0 && (link->data[eq - 1] != ' ' || link->data[eq] != '=')) 344 | eq -= 1; 345 | if (!eq) return 0; 346 | ex = eq + 1; 347 | while (ex < link->size 348 | && link->data[ex] >= '0' && link->data[ex] <= '9') 349 | ex += 1; 350 | if (ex >= link->size || ex == eq + 1 || link->data[ex] != 'x') return 0; 351 | end = ex + 1; 352 | while (end < link->size 353 | && link->data[end] >= '0' && link->data[end] <= '9') 354 | end += 1; 355 | if (end == ex + 1) return 0; 356 | /* everything is fine, proceeding to actual printing */ 357 | lus_attr_escape(ob, link->data, eq - 1); 358 | BUFPUTSL(ob, "\" width="); 359 | bufput(ob, link->data + eq + 1, ex - eq - 1); 360 | BUFPUTSL(ob, " height="); 361 | bufput(ob, link->data + ex + 1, end - ex - 1); 362 | return 1; } 363 | 364 | static int 365 | discount_image(struct buf *ob, struct buf *link, struct buf *title, 366 | struct buf *alt, int xhtml) { 367 | if (!link || !link->size) return 0; 368 | BUFPUTSL(ob, "<img src=\""); 369 | if (!print_link_wxh(ob, link)) { 370 | lus_attr_escape(ob, link->data, link->size); 371 | bufputc(ob, '"'); } 372 | BUFPUTSL(ob, " alt=\""); 373 | if (alt && alt->size) 374 | lus_attr_escape(ob, alt->data, alt->size); 375 | if (title && title->size) { 376 | BUFPUTSL(ob, "\" title=\""); 377 | lus_attr_escape(ob, title->data, title->size); } 378 | bufputs(ob, xhtml ? "\" />" : "\">"); 379 | return 1; } 380 | 381 | static int 382 | html_discount_image(struct buf *ob, struct buf *link, struct buf *title, 383 | struct buf *alt, void *opaque) { 384 | return discount_image(ob, link, title, alt, 0); } 385 | 386 | static int 387 | xhtml_discount_image(struct buf *ob, struct buf *link, struct buf *title, 388 | struct buf *alt, void *opaque) { 389 | return discount_image(ob, link, title, alt, 1); } 390 | 391 | static int 392 | discount_link(struct buf *ob, struct buf *link, struct buf *title, 393 | struct buf *content, void *opaque) { 394 | if (!link) return rndr_link(ob, link, title, content, opaque); 395 | else if (link->size > 5 && !strncasecmp(link->data, "abbr:", 5)) { 396 | BUFPUTSL(ob, "<abbr title=\""); 397 | lus_attr_escape(ob, link->data + 5, link->size - 5); 398 | BUFPUTSL(ob, "\">"); 399 | bufput(ob, content->data, content->size); 400 | BUFPUTSL(ob, "</abbr>"); 401 | return 1; } 402 | else if (link->size > 6 && !strncasecmp(link->data, "class:", 6)) { 403 | BUFPUTSL(ob, "<span class=\""); 404 | lus_attr_escape(ob, link->data + 6, link->size - 6); 405 | BUFPUTSL(ob, "\">"); 406 | bufput(ob, content->data, content->size); 407 | BUFPUTSL(ob, "</span>"); 408 | return 1; } 409 | else if (link->size > 3 && !strncasecmp(link->data, "id:", 3)) { 410 | BUFPUTSL(ob, "<span id=\""); 411 | lus_attr_escape(ob, link->data + 3, link->size - 3); 412 | BUFPUTSL(ob, "\">"); 413 | bufput(ob, content->data, content->size); 414 | BUFPUTSL(ob, "</span>"); 415 | return 1; } 416 | else if (link->size > 4 && !strncasecmp(link->data, "raw:", 4)) { 417 | bufput(ob, link->data + 4, link->size - 4); 418 | return 1; } 419 | return rndr_link(ob, link, title, content, opaque); } 420 | 421 | static void 422 | discount_blockquote(struct buf *ob, struct buf *text, void *opaque) { 423 | size_t i = 5, size = text->size; 424 | char *data = text->data; 425 | if (text->size < 5 || strncasecmp(text->data, "<p>%", 4)) { 426 | rndr_blockquote(ob, text, opaque); 427 | return; } 428 | while (i < size && data[i] != '\n' && data[i] != '%') 429 | i += 1; 430 | if (i >= size || data[i] != '%') { 431 | rndr_blockquote(ob, text, opaque); 432 | return; } 433 | BUFPUTSL(ob, "<div class=\""); 434 | bufput(ob, text->data + 4, i - 4); 435 | BUFPUTSL(ob, "\"><p>"); 436 | i += 1; 437 | if (i + 4 >= text->size && !strncasecmp(text->data + i, "</p>", 4)) { 438 | size_t old_i = i; 439 | i += 4; 440 | while (i + 3 < text->size 441 | && (data[i] != '<' || data[i + 1] != 'p' || data[i + 2] != '>')) 442 | i += 1; 443 | if (i + 3 >= text->size) i = old_i; } 444 | bufput(ob, text->data + i, text->size - i); 445 | BUFPUTSL(ob, "</div>\n"); } 446 | 447 | static void 448 | discount_table(struct buf *ob, struct buf *head_row, struct buf *rows, 449 | void *opaque) { 450 | if (ob->size) bufputc(ob, '\n'); 451 | BUFPUTSL(ob, "<table>\n"); 452 | if (head_row) { 453 | BUFPUTSL(ob, "<thead>\n"); 454 | bufput(ob, head_row->data, head_row->size); 455 | BUFPUTSL(ob, "</thead>\n<tbody>\n"); } 456 | if (rows) 457 | bufput(ob, rows->data, rows->size); 458 | if (head_row) 459 | BUFPUTSL(ob, "</tbody>\n"); 460 | BUFPUTSL(ob, "</table>\n"); } 461 | 462 | static void 463 | discount_table_row(struct buf *ob, struct buf *cells, int flags, void *opaque){ 464 | (void)flags; 465 | BUFPUTSL(ob, " <tr>\n"); 466 | if (cells) bufput(ob, cells->data, cells->size); 467 | BUFPUTSL(ob, " </tr>\n"); } 468 | 469 | static void 470 | discount_table_cell(struct buf *ob, struct buf *text, int flags, void *opaque){ 471 | if (flags & MKD_CELL_HEAD) 472 | BUFPUTSL(ob, " <th"); 473 | else 474 | BUFPUTSL(ob, " <td"); 475 | switch (flags & MKD_CELL_ALIGN_MASK) { 476 | case MKD_CELL_ALIGN_LEFT: 477 | BUFPUTSL(ob, " align=\"left\""); 478 | break; 479 | case MKD_CELL_ALIGN_RIGHT: 480 | BUFPUTSL(ob, " align=\"right\""); 481 | break; 482 | case MKD_CELL_ALIGN_CENTER: 483 | BUFPUTSL(ob, " align=\"center\""); 484 | break; } 485 | bufputc(ob, '>'); 486 | if (text) bufput(ob, text->data, text->size); 487 | if (flags & MKD_CELL_HEAD) 488 | BUFPUTSL(ob, "</th>\n"); 489 | else 490 | BUFPUTSL(ob, "</td>\n"); } 491 | 492 | /* exported renderer structures */ 493 | const struct mkd_renderer discount_html = { 494 | NULL, 495 | NULL, 496 | 497 | rndr_blockcode, 498 | NULL, 499 | discount_blockquote, 500 | rndr_raw_block, 501 | rndr_header, 502 | html_hrule, 503 | rndr_list, 504 | rndr_listitem, 505 | rndr_paragraph, 506 | discount_table, 507 | discount_table_cell, 508 | discount_table_row, 509 | 510 | rndr_autolink, 511 | rndr_codespan, 512 | rndr_double_emphasis, 513 | rndr_emphasis, 514 | html_discount_image, 515 | html_linebreak, 516 | discount_link, 517 | rndr_raw_inline, 518 | rndr_triple_emphasis, 519 | 520 | NULL, 521 | rndr_normal_text, 522 | 523 | 64, 524 | "*_", 525 | NULL }; 526 | const struct mkd_renderer discount_xhtml = { 527 | NULL, 528 | NULL, 529 | 530 | rndr_blockcode, 531 | NULL, 532 | discount_blockquote, 533 | rndr_raw_block, 534 | rndr_header, 535 | xhtml_hrule, 536 | rndr_list, 537 | rndr_listitem, 538 | rndr_paragraph, 539 | discount_table, 540 | discount_table_cell, 541 | discount_table_row, 542 | 543 | rndr_autolink, 544 | rndr_codespan, 545 | rndr_double_emphasis, 546 | rndr_emphasis, 547 | xhtml_discount_image, 548 | xhtml_linebreak, 549 | discount_link, 550 | rndr_raw_inline, 551 | rndr_triple_emphasis, 552 | 553 | NULL, 554 | rndr_normal_text, 555 | 556 | 64, 557 | "*_", 558 | NULL }; 559 | 560 | 561 | /**************************** 562 | * NATACHA'S OWN EXTENSIONS * 563 | ****************************/ 564 | 565 | static void 566 | nat_span(struct buf *ob, struct buf *text, char *tag) { 567 | bufprintf(ob, "<%s>", tag); 568 | bufput(ob, text->data, text->size); 569 | bufprintf(ob, "</%s>", tag); } 570 | 571 | static int 572 | nat_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) { 573 | if (!text || !text->size || c == '+' || c == '-') return 0; 574 | if (c == '|') nat_span(ob, text, "span"); 575 | else nat_span(ob, text, "em"); 576 | return 1; } 577 | 578 | static int 579 | nat_double_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) { 580 | if (!text || !text->size || c == '|') return 0; 581 | if (c == '+') nat_span(ob, text, "ins"); 582 | else if (c == '-') nat_span(ob, text, "del"); 583 | else nat_span(ob, text, "strong"); 584 | return 1; } 585 | 586 | static int 587 | nat_triple_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) { 588 | if (!text || !text->size || c == '+' || c == '-' || c == '|') return 0; 589 | BUFPUTSL(ob, "<strong><em>"); 590 | bufput(ob, text->data, text->size); 591 | BUFPUTSL(ob, "</em></strong>"); 592 | return 1; } 593 | 594 | static void 595 | nat_header(struct buf *ob, struct buf *text, int level, void *opaque) { 596 | size_t i = 0; 597 | if (ob->size) bufputc(ob, '\n'); 598 | while (i < text->size && (text->data[i] == '-' || text->data[i] == '_' 599 | || text->data[i] == '.' || text->data[i] == ':' 600 | || (text->data[i] >= 'a' && text->data[i] <= 'z') 601 | || (text->data[i] >= 'A' && text->data[i] <= 'Z') 602 | || (text->data[i] >= '0' && text->data[i] <= '0'))) 603 | i += 1; 604 | bufprintf(ob, "<h%d", level); 605 | if (i < text->size && text->data[i] == '#') { 606 | bufprintf(ob, " id=\"%.*s\">", (int)i, text->data); 607 | i += 1; } 608 | else { 609 | bufputc(ob, '>'); 610 | i = 0; } 611 | bufput(ob, text->data + i, text->size - i); 612 | bufprintf(ob, "</h%d>\n", level); } 613 | 614 | static void 615 | nat_paragraph(struct buf *ob, struct buf *text, void *opaque) { 616 | size_t i = 0; 617 | if (ob->size) bufputc(ob, '\n'); 618 | BUFPUTSL(ob, "<p"); 619 | if (text && text->size && text->data[0] == '(') { 620 | i = 1; 621 | while (i < text->size && (text->data[i] == ' ' 622 | /* this seems to be a bit more restrictive than */ 623 | /* what is allowed for class names */ 624 | || (text->data[i] >= 'a' && text->data[i] <= 'z') 625 | || (text->data[i] >= 'A' && text->data[i] <= 'Z') 626 | || (text->data[i] >= '0' && text->data[i] <= '0'))) 627 | i += 1; 628 | if (i < text->size && text->data[i] == ')') { 629 | bufprintf(ob, " class=\"%.*s\"", 630 | (int)(i - 1), text->data + 1); 631 | i += 1; } 632 | else i = 0; } 633 | bufputc(ob, '>'); 634 | if (text) bufput(ob, text->data + i, text->size - i); 635 | BUFPUTSL(ob, "</p>\n"); } 636 | 637 | 638 | /* exported renderer structures */ 639 | const struct mkd_renderer nat_html = { 640 | NULL, 641 | NULL, 642 | 643 | rndr_blockcode, 644 | NULL, 645 | discount_blockquote, 646 | rndr_raw_block, 647 | nat_header, 648 | html_hrule, 649 | rndr_list, 650 | rndr_listitem, 651 | nat_paragraph, 652 | NULL, 653 | NULL, 654 | NULL, 655 | 656 | rndr_autolink, 657 | rndr_codespan, 658 | nat_double_emphasis, 659 | nat_emphasis, 660 | html_discount_image, 661 | html_linebreak, 662 | discount_link, 663 | rndr_raw_inline, 664 | nat_triple_emphasis, 665 | 666 | NULL, 667 | rndr_normal_text, 668 | 669 | 64, 670 | "*_-+|", 671 | NULL }; 672 | const struct mkd_renderer nat_xhtml = { 673 | NULL, 674 | NULL, 675 | 676 | rndr_blockcode, 677 | NULL, 678 | discount_blockquote, 679 | rndr_raw_block, 680 | nat_header, 681 | xhtml_hrule, 682 | rndr_list, 683 | rndr_listitem, 684 | nat_paragraph, 685 | NULL, 686 | NULL, 687 | NULL, 688 | 689 | rndr_autolink, 690 | rndr_codespan, 691 | nat_double_emphasis, 692 | nat_emphasis, 693 | xhtml_discount_image, 694 | xhtml_linebreak, 695 | discount_link, 696 | rndr_raw_inline, 697 | nat_triple_emphasis, 698 | 699 | NULL, 700 | rndr_normal_text, 701 | 702 | 64, 703 | "*_-+|", 704 | NULL }; 705 | -------------------------------------------------------------------------------- /console-colour.c: -------------------------------------------------------------------------------- 1 | #include "console-colour.h" 2 | 3 | struct console_colour_state_s 4 | gConsoleStreamState = { 5 | 0x80, 6 | STATE_NONE 7 | }; 8 | 9 | #ifdef CONMD_WINDOWS 10 | 11 | #include <stdio.h> 12 | #include <limits.h> 13 | 14 | static int OutputC(wchar_t c, struct console_colour_stream_s * const stream) 15 | { 16 | return stream->Call->OutputC(stream->Call->Data, c, stream); 17 | } 18 | 19 | static int OutputA(char const * c, int len, struct console_colour_stream_s * const stream) 20 | { 21 | return stream->Call->OutputA(stream->Call->Data, c, len, stream); 22 | } 23 | 24 | static int OutputW(wchar_t const * c, int len, struct console_colour_stream_s * const stream) 25 | { 26 | return stream->Call->OutputW(stream->Call->Data, c, len, stream); 27 | } 28 | 29 | static void Colour(struct console_colour_stream_s * const stream) 30 | { 31 | //if (!stream->State->Coloured) 32 | //{ 33 | // // Colouring is disabled. Just do nothing. 34 | //} 35 | //else 36 | if (stream->State->DefaultStyle & COMMON_LVB_REVERSE_VIDEO) 37 | { 38 | stream->Call->OutputColour(stream->Call->Data, stream->State->CurrentStyle >> 4 & 0x0F | stream->State->CurrentStyle << 4 & 0xF0, stream); 39 | } 40 | else 41 | { 42 | stream->Call->OutputColour(stream->Call->Data, stream->State->CurrentStyle, stream); 43 | } 44 | } 45 | 46 | void Backout(struct console_colour_stream_s * const stream) 47 | { 48 | // Back out of the buffering. 49 | char backout[32] = ""; 50 | // Run the state machine. 51 | switch (stream->State->State) 52 | { 53 | default: 54 | // Skip might be an issue - we are in the middle of a multi-byte character... 55 | return; 56 | case STATE_ESC: 57 | sprintf_s(backout, sizeof (backout), "\x1B"); 58 | break; 59 | case STATE_START: 60 | sprintf_s(backout, sizeof (backout), "\x1B["); 61 | break; 62 | case STATE_A00: 63 | sprintf_s(backout, sizeof (backout), "\x1B[%d", stream->State->Attr0); 64 | break; 65 | case STATE_S0: 66 | sprintf_s(backout, sizeof (backout), "\x1B[%d;", stream->State->Attr0); 67 | break; 68 | case STATE_A10: 69 | sprintf_s(backout, sizeof (backout), "\x1B[%d;%d", stream->State->Attr0, stream->State->Attr1); 70 | break; 71 | case STATE_S1: 72 | sprintf_s(backout, sizeof (backout), "\x1B[%d;%d;", stream->State->Attr0, stream->State->Attr1); 73 | break; 74 | case STATE_A20: 75 | sprintf_s(backout, sizeof (backout), "\x1B[%d;%d;%d", stream->State->Attr0, stream->State->Attr1, stream->State->Attr2); 76 | break; 77 | case STATE_S2: 78 | sprintf_s(backout, sizeof (backout), "\x1B[%d;%d;%d;", stream->State->Attr0, stream->State->Attr1, stream->State->Attr2); 79 | break; 80 | case STATE_A30: 81 | sprintf_s(backout, sizeof (backout), "\x1B[%d;%d;%d;%d", stream->State->Attr0, stream->State->Attr1, stream->State->Attr2, stream->State->Attr3); 82 | break; 83 | case STATE_S3: 84 | sprintf_s(backout, sizeof (backout), "\x1B[%d;%d;%d;%d;", stream->State->Attr0, stream->State->Attr1, stream->State->Attr2, stream->State->Attr3); 85 | break; 86 | case STATE_A40: 87 | sprintf_s(backout, sizeof (backout), "\x1B[%d;%d;%d;%d;%d", stream->State->Attr0, stream->State->Attr1, stream->State->Attr2, stream->State->Attr3, stream->State->Attr4); 88 | break; 89 | } 90 | if (backout[0]) 91 | { 92 | Colour(stream); 93 | OutputA(backout, (int)strlen(backout), stream); 94 | } 95 | stream->State->State = STATE_NONE; 96 | stream->State->Attr0 = stream->State->Attr1 = stream->State->Attr2 = stream->State->Attr3 = stream->State->Attr4 = 0; 97 | } 98 | 99 | static const WORD 100 | FOREGROUND = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, 101 | BACKGROUND = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY; 102 | 103 | static WORD NToColour(unsigned char attr) 104 | { 105 | switch (attr) 106 | { 107 | // Original colours. 108 | case 0: 109 | // Black. 110 | return 0; 111 | case 1: 112 | // Red. 113 | return FOREGROUND_RED; 114 | case 2: 115 | // Green. 116 | return FOREGROUND_GREEN; 117 | case 3: 118 | // Yellow. 119 | return FOREGROUND_RED | FOREGROUND_GREEN; 120 | case 4: 121 | // Blue. 122 | return FOREGROUND_BLUE; 123 | case 5: 124 | // Magenta. 125 | return FOREGROUND_RED | FOREGROUND_BLUE; 126 | case 6: 127 | // Cyan. 128 | return FOREGROUND_GREEN | FOREGROUND_BLUE; 129 | case 7: 130 | // White. 131 | return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; 132 | case 8 + 0: 133 | // Black. 134 | return FOREGROUND_INTENSITY; 135 | case 8 + 1: 136 | // Red. 137 | return FOREGROUND_RED | FOREGROUND_INTENSITY; 138 | case 8 + 2: 139 | // Green. 140 | return FOREGROUND_GREEN | FOREGROUND_INTENSITY; 141 | case 8 + 3: 142 | // Yellow. 143 | return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; 144 | case 8 + 4: 145 | // Blue. 146 | return FOREGROUND_BLUE | FOREGROUND_INTENSITY; 147 | case 8 + 5: 148 | // Magenta. 149 | return FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY; 150 | case 8 + 6: 151 | // Cyan. 152 | return FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; 153 | case 8 + 7: 154 | // White. 155 | return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; 156 | } 157 | return 0; 158 | } 159 | 160 | static unsigned char Shuffle6Colour(unsigned char colour) 161 | { 162 | // The search results are better using a different search order. 163 | const unsigned char 164 | ORDER[16] = { 0, 15, 1, 2, 4, 3, 6, 5, 9, 10, 12, 11, 14, 13, 8, 7 }; 165 | return ORDER[colour]; 166 | } 167 | 168 | static WORD Make6Colour(int r, int g, int b, struct console_colour_stream_s* const stream) 169 | { 170 | // There's no easy way to map 16 colours to 3 components. So we need full colour space mapping. 171 | // I didn't want to have to write that... I KNOW there are better mapping functions based on 172 | // what colours are eyes are better at perceiving, but I'm only mapping to 16 so it hardly 173 | // matters. 174 | // 175 | // I did a LOT of experimenting with orders to get this as the best order I could find. 176 | const int 177 | CONSOLE_COLOURS[16][3] = { 178 | {0, 0, 0}, 179 | {5, 5, 5}, 180 | {2, 0, 0}, 181 | {0, 2, 0}, 182 | {0, 0, 2}, 183 | {2, 2, 0}, 184 | {0, 2, 2}, 185 | {2, 0, 2}, 186 | {4, 0, 0}, 187 | {0, 4, 0}, 188 | {0, 0, 4}, 189 | {4, 4, 0}, 190 | {0, 4, 4}, 191 | {4, 0, 4}, 192 | {2, 2, 2}, 193 | {4, 4, 4}, 194 | }; 195 | 196 | int 197 | dist = INT_MAX; 198 | unsigned char 199 | found = 0; 200 | for (unsigned char i = 0; i != 16; ++i) 201 | { 202 | int 203 | tr = r - CONSOLE_COLOURS[i][0], 204 | tg = g - CONSOLE_COLOURS[i][1], 205 | tb = b - CONSOLE_COLOURS[i][2], 206 | cur = (tr * tr) + (tg * tg) + (tb * tb); 207 | if (cur < dist) 208 | { 209 | dist = cur; 210 | found = i; 211 | } 212 | } 213 | 214 | return NToColour(Shuffle6Colour(found)); 215 | } 216 | 217 | static WORD Make24Colour(int r, int g, int b, struct console_colour_stream_s* const stream) 218 | { 219 | // Use the reduced 6-value colour space mapping, since we've tweaked that a lot already. 220 | r = r + 25 / 51; // Rounding division, to get the component from 0-5 (inclusive). 221 | g = g + 25 / 51; // Rounding division, to get the component from 0-5 (inclusive). 222 | b = b + 25 / 51; // Rounding division, to get the component from 0-5 (inclusive). 223 | return Make6Colour(r, g, b, stream); 224 | } 225 | 226 | static WORD Make256Colour(unsigned char attr, struct console_colour_stream_s * const stream) 227 | { 228 | // Greyscales. Find the nearest. 229 | if (attr >= 250) 230 | return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; 231 | else if (attr >= 244) 232 | return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; 233 | else if (attr >= 238) 234 | return FOREGROUND_INTENSITY; 235 | else if (attr >= 232) 236 | return 0; 237 | else if (attr >= 16) 238 | { 239 | // 216 colours. Extract the RGB from the colour. Then scale them to 8-bit. 240 | attr -= 16; 241 | unsigned char 242 | r = attr / 36, 243 | g = attr % 36 / 6, 244 | b = attr % 6; 245 | return Make6Colour(r, g, b, stream); 246 | } 247 | 248 | return NToColour(attr); 249 | } 250 | 251 | static WORD GetColour(unsigned char attr, struct console_colour_stream_s* const stream) 252 | { 253 | WORD 254 | current = stream->State->CurrentStyle; 255 | switch (attr) 256 | { 257 | case 1: 258 | return current | FOREGROUND_INTENSITY; 259 | case 2: 260 | return current & ~FOREGROUND_INTENSITY; 261 | case 3: 262 | case 7: 263 | // Inverse. Store the fact that it is inverted somewhere, and only apply it at the very 264 | // last possible moment. 265 | stream->State->DefaultStyle |= COMMON_LVB_REVERSE_VIDEO; 266 | break; 267 | case 23: 268 | case 27: 269 | stream->State->DefaultStyle &= ~COMMON_LVB_REVERSE_VIDEO; 270 | break; 271 | case 4: 272 | case 21: 273 | return current | COMMON_LVB_UNDERSCORE; 274 | case 22: 275 | return current & ~FOREGROUND_INTENSITY; 276 | case 24: 277 | return current & ~COMMON_LVB_UNDERSCORE; 278 | case 39: 279 | return current & ~FOREGROUND | stream->State->DefaultStyle & FOREGROUND; 280 | case 49: 281 | return current & ~BACKGROUND | stream->State->DefaultStyle & BACKGROUND; 282 | case 30: 283 | // Black. 284 | return current & ~FOREGROUND; 285 | case 31: 286 | // Red. 287 | return current & ~FOREGROUND | FOREGROUND_RED; 288 | case 32: 289 | // Green. 290 | return current & ~FOREGROUND | FOREGROUND_GREEN; 291 | case 33: 292 | // Yellow. 293 | return current & ~FOREGROUND | FOREGROUND_RED | FOREGROUND_GREEN; 294 | case 34: 295 | // Blue. 296 | return current & ~FOREGROUND | FOREGROUND_BLUE; 297 | case 35: 298 | // Magenta. 299 | return current & ~FOREGROUND | FOREGROUND_RED | FOREGROUND_BLUE; 300 | case 36: 301 | // Cyan. 302 | return current & ~FOREGROUND | FOREGROUND_GREEN | FOREGROUND_BLUE; 303 | case 37: 304 | // White. 305 | return current & ~FOREGROUND | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; 306 | case 40: 307 | // Black. 308 | return current & ~BACKGROUND; 309 | case 41: 310 | // Red. 311 | return current & ~BACKGROUND | BACKGROUND_RED; 312 | case 42: 313 | // Green. 314 | return current & ~BACKGROUND | BACKGROUND_GREEN; 315 | case 43: 316 | // Yellow. 317 | return current & ~BACKGROUND | BACKGROUND_RED | BACKGROUND_GREEN; 318 | case 44: 319 | // Blue. 320 | return current & ~BACKGROUND | BACKGROUND_BLUE; 321 | case 45: 322 | // Magenta. 323 | return current & ~BACKGROUND | BACKGROUND_RED | BACKGROUND_BLUE; 324 | case 46: 325 | // Cyan. 326 | return current & ~BACKGROUND | BACKGROUND_GREEN | BACKGROUND_BLUE; 327 | case 47: 328 | // White. 329 | return current & ~BACKGROUND | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE; 330 | case 60 + 30: 331 | // Black. 332 | return current & ~FOREGROUND | FOREGROUND_INTENSITY; 333 | case 60 + 31: 334 | // Red. 335 | return current & ~FOREGROUND | FOREGROUND_RED | FOREGROUND_INTENSITY; 336 | case 60 + 32: 337 | // Green. 338 | return current & ~FOREGROUND | FOREGROUND_GREEN | FOREGROUND_INTENSITY; 339 | case 60 + 33: 340 | // Yellow. 341 | return current & ~FOREGROUND | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; 342 | case 60 + 34: 343 | // Blue. 344 | return current & ~FOREGROUND | FOREGROUND_BLUE | FOREGROUND_INTENSITY; 345 | case 60 + 35: 346 | // Magenta. 347 | return current & ~FOREGROUND | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY; 348 | case 60 + 36: 349 | // Cyan. 350 | return current & ~FOREGROUND | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; 351 | case 60 + 37: 352 | // White. 353 | return current & ~FOREGROUND | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; 354 | case 60 + 40: 355 | // Black. 356 | return current & ~BACKGROUND | BACKGROUND_INTENSITY; 357 | case 60 + 41: 358 | // Red. 359 | return current & ~BACKGROUND | BACKGROUND_RED | BACKGROUND_INTENSITY; 360 | case 60 + 42: 361 | // Green. 362 | return current & ~BACKGROUND | BACKGROUND_GREEN | BACKGROUND_INTENSITY; 363 | case 60 + 43: 364 | // Yellow. 365 | return current & ~BACKGROUND | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_INTENSITY; 366 | case 60 + 44: 367 | // Blue. 368 | return current & ~BACKGROUND | BACKGROUND_BLUE | BACKGROUND_INTENSITY; 369 | case 60 + 45: 370 | // Magenta. 371 | return current & ~BACKGROUND | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY; 372 | case 60 + 46: 373 | // Cyan. 374 | return current & ~BACKGROUND | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY; 375 | case 60 + 47: 376 | // White. 377 | return current & ~BACKGROUND | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY; 378 | } 379 | return current; 380 | } 381 | 382 | static void GetColours(struct console_colour_stream_s * const stream) 383 | { 384 | switch (stream->State->Attr0) 385 | { 386 | case 0: 387 | // Reset. 388 | stream->State->DefaultStyle &= ~COMMON_LVB_REVERSE_VIDEO; 389 | stream->State->CurrentStyle = stream->State->DefaultStyle; 390 | break; 391 | case 38: 392 | // Special foreground colour. 393 | // TODO: Find the closest working colour from the 16 available. 394 | switch (stream->State->Attr1) 395 | { 396 | case 5: 397 | // 256 colours. 398 | stream->State->CurrentStyle = stream->State->CurrentStyle & ~FOREGROUND | Make256Colour(stream->State->Attr2, stream); 399 | break; 400 | case 2: 401 | // 24-bit colours. 402 | stream->State->CurrentStyle = stream->State->CurrentStyle & ~FOREGROUND | Make24Colour(stream->State->Attr2, stream->State->Attr3, stream->State->Attr4, stream); 403 | break; 404 | } 405 | break; 406 | case 48: 407 | // Special background colour. 408 | switch (stream->State->Attr1) 409 | { 410 | case 5: 411 | // 256 colours. 412 | stream->State->CurrentStyle = stream->State->CurrentStyle & ~BACKGROUND | Make256Colour(stream->State->Attr2, stream) << 4; 413 | break; 414 | case 2: 415 | // 24-bit colours. 416 | stream->State->CurrentStyle = stream->State->CurrentStyle & ~BACKGROUND | Make24Colour(stream->State->Attr2, stream->State->Attr3, stream->State->Attr4, stream) << 4; 417 | break; 418 | } 419 | break; 420 | default: 421 | // Normal code. 422 | stream->State->CurrentStyle = GetColour(stream->State->Attr0, stream); 423 | stream->State->CurrentStyle = GetColour(stream->State->Attr1, stream); 424 | stream->State->CurrentStyle = GetColour(stream->State->Attr2, stream); 425 | stream->State->CurrentStyle = GetColour(stream->State->Attr3, stream); 426 | stream->State->CurrentStyle = GetColour(stream->State->Attr4, stream); 427 | break; 428 | } 429 | } 430 | 431 | static int RunStateMachine(wchar_t c, struct console_colour_stream_s * const stream) 432 | { 433 | // Run the stream machine. 434 | switch (stream->State->State) 435 | { 436 | case STATE_NONE: 437 | //if (stream->State->Coloured) 438 | { 439 | switch (c) 440 | { 441 | case 29: // '\x1D', Group separator. 442 | return /*stream->State->*/OutputC(10, stream); // `\n` 443 | case 31: // '\x1F', Unit separator. 444 | return /*stream->State->*/OutputC(10, stream); // `\n` 445 | case 30: // '\x1E', Record separator. 446 | return 0; 447 | } 448 | } 449 | if (c == '\x1B') 450 | { 451 | stream->State->State = STATE_ESC; 452 | return 0; 453 | } 454 | else if ((c & stream->State->UnicodeMask)) 455 | { 456 | // Start of a multi-byte character. Skip all checks on the next one. 457 | stream->State->State = STATE_SKIP; 458 | } 459 | break; 460 | case STATE_SKIP: 461 | if (!(c & stream->State->UnicodeMask)) 462 | { 463 | stream->State->State = STATE_NONE; 464 | } 465 | break; 466 | case STATE_ESC: 467 | if (c == '[') 468 | { 469 | stream->State->State = STATE_START; 470 | } 471 | else 472 | { 473 | Backout(stream); 474 | break; 475 | } 476 | return 0; 477 | case STATE_START: 478 | if ('0' <= c && c <= '9') 479 | { 480 | stream->State->Attr0 = (unsigned char)(c - '0'); 481 | stream->State->State = STATE_A00; 482 | } 483 | else 484 | { 485 | Backout(stream); 486 | break; 487 | } 488 | return 0; 489 | case STATE_A00: 490 | if ('0' <= c && c <= '9') 491 | { 492 | stream->State->Attr0 = stream->State->Attr0 * 10 + (unsigned char)(c - '0'); 493 | } 494 | else if (c == ';') 495 | stream->State->State = STATE_S0; 496 | //else if (c == ':') 497 | // stream->State->State = STATE_T416; 498 | else if (c == 'm') 499 | { 500 | stream->State->State = STATE_DONE; 501 | break; 502 | } 503 | else 504 | { 505 | Backout(stream); 506 | break; 507 | } 508 | return 0; 509 | case STATE_S0: 510 | if ('0' <= c && c <= '9') 511 | { 512 | stream->State->Attr1 = (unsigned char)(c - '0'); 513 | stream->State->State = STATE_A10; 514 | } 515 | else if (c == 'm') 516 | { 517 | stream->State->State = STATE_DONE; 518 | break; 519 | } 520 | else 521 | { 522 | Backout(stream); 523 | break; 524 | } 525 | return 0; 526 | case STATE_A10: 527 | if ('0' <= c && c <= '9') 528 | { 529 | stream->State->Attr1 = stream->State->Attr1 * 10 + (unsigned char)(c - '0'); 530 | } 531 | else if (c == ';') 532 | stream->State->State = STATE_S1; 533 | else if (c == 'm') 534 | { 535 | stream->State->State = STATE_DONE; 536 | break; 537 | } 538 | else 539 | { 540 | Backout(stream); 541 | break; 542 | } 543 | return 0; 544 | case STATE_S1: 545 | if ('0' <= c && c <= '9') 546 | { 547 | stream->State->Attr2 = (unsigned char)(c - '0'); 548 | stream->State->State = STATE_A20; 549 | } 550 | else if (c == 'm') 551 | { 552 | stream->State->State = STATE_DONE; 553 | break; 554 | } 555 | else 556 | { 557 | Backout(stream); 558 | break; 559 | } 560 | return 0; 561 | case STATE_A20: 562 | if ('0' <= c && c <= '9') 563 | { 564 | stream->State->Attr2 = stream->State->Attr2 * 10 + (unsigned char)(c - '0'); 565 | } 566 | else if (c == ';') 567 | stream->State->State = STATE_S2; 568 | else if (c == 'm') 569 | { 570 | stream->State->State = STATE_DONE; 571 | break; 572 | } 573 | else 574 | { 575 | Backout(stream); 576 | break; 577 | } 578 | return 0; 579 | case STATE_S2: 580 | if ('0' <= c && c <= '9') 581 | { 582 | stream->State->Attr3 = (unsigned char)(c - '0'); 583 | stream->State->State = STATE_A30; 584 | } 585 | else if (c == 'm') 586 | { 587 | stream->State->State = STATE_DONE; 588 | break; 589 | } 590 | else 591 | { 592 | Backout(stream); 593 | break; 594 | } 595 | return 0; 596 | case STATE_A30: 597 | if ('0' <= c && c <= '9') 598 | { 599 | stream->State->Attr3 = stream->State->Attr3 * 10 + (unsigned char)(c - '0'); 600 | } 601 | else if (c == ';') 602 | stream->State->State = STATE_S3; 603 | else if (c == 'm') 604 | { 605 | stream->State->State = STATE_DONE; 606 | break; 607 | } 608 | else 609 | { 610 | Backout(stream); 611 | break; 612 | } 613 | return 0; 614 | case STATE_S3: 615 | if ('0' <= c && c <= '9') 616 | { 617 | stream->State->Attr4 = (unsigned char)(c - '0'); 618 | stream->State->State = STATE_A40; 619 | } 620 | else if (c == 'm') 621 | { 622 | stream->State->State = STATE_DONE; 623 | break; 624 | } 625 | else 626 | { 627 | Backout(stream); 628 | break; 629 | } 630 | return 0; 631 | case STATE_A40: 632 | if ('0' <= c && c <= '9') 633 | { 634 | stream->State->Attr4 = stream->State->Attr4 * 10 + (unsigned char)(c - '0'); 635 | } 636 | else if (c == 'm') 637 | { 638 | stream->State->State = STATE_DONE; 639 | break; 640 | } 641 | else 642 | { 643 | Backout(stream); 644 | break; 645 | } 646 | return 0; 647 | } 648 | if (stream->State->State == STATE_DONE) 649 | { 650 | GetColours(stream); 651 | stream->State->State = STATE_NONE; 652 | stream->State->Attr0 = stream->State->Attr1 = stream->State->Attr2 = stream->State->Attr3 = stream->State->Attr4 = 0; 653 | return 0; 654 | } 655 | Colour(stream); 656 | return /*stream->State->*/OutputC(c, stream); 657 | } 658 | 659 | #if defined CONMD_HOOKS 660 | #define MAYBE_REMOVE_HOOKS() RemoveStreamHooks() 661 | #define MAYBE_READD_HOOKS() ReaddStreamHooks() 662 | #else 663 | #define MAYBE_REMOVE_HOOKS() ((void)0) 664 | #define MAYBE_READD_HOOKS() ((void)0) 665 | #endif 666 | 667 | int WriteColouredA(char const * s, int n, struct console_colour_stream_s* const stream) 668 | { 669 | MAYBE_REMOVE_HOOKS(); 670 | char const 671 | * cur, 672 | * start = s, 673 | * end = s + n; 674 | // Just in case. 675 | if (n == 0 || s == NULL) 676 | { 677 | MAYBE_READD_HOOKS(); 678 | return 0; 679 | } 680 | int 681 | ret = 0; 682 | for (; ; ) 683 | { 684 | while (*s && !(stream->State->State == STATE_NONE || stream->State->State == STATE_SKIP)) 685 | { 686 | RunStateMachine(*s++, stream); 687 | } 688 | char const * 689 | esc = s; 690 | //if (stream->State->Coloured) 691 | { 692 | for (cur = s; cur != end; ++cur) 693 | { 694 | switch (*cur) 695 | { 696 | case '\x1B': // ESC. 697 | stream->State->State = STATE_ESC; 698 | esc = cur; 699 | goto WriteColoured_loop_done; 700 | case '\x1D': // Group separator. 701 | stream->State->State = STATE_EXTRA_NL; 702 | esc = cur; 703 | goto WriteColoured_loop_done; 704 | case '\x1F': // Unit separator. 705 | stream->State->State = STATE_EXTRA_2NL; 706 | esc = cur; 707 | goto WriteColoured_loop_done; 708 | case '\x1E': // Record separator. 709 | stream->State->State = STATE_NONE; 710 | esc = cur; 711 | goto WriteColoured_loop_done; 712 | } 713 | } 714 | WriteColoured_loop_done: 715 | if (cur == end) 716 | { 717 | break; 718 | } 719 | } 720 | //else 721 | //{ 722 | // esc = strchr(s, '\x1B'); 723 | // if (esc == NULL) 724 | // break; 725 | // stream->State->State = STATE_ESC; 726 | //} 727 | // Write the bit before the escape sequence. 728 | if (esc != s) 729 | { 730 | // Restore the correct colours. 731 | Colour(stream); 732 | int 733 | wrote = OutputA(s, (int)(esc - s), stream); 734 | ret += wrote; 735 | if (wrote != esc - s) 736 | { 737 | MAYBE_READD_HOOKS(); 738 | return (int)(s - start + wrote); 739 | } 740 | } 741 | s = esc + 1; 742 | switch (stream->State->State) 743 | { 744 | case STATE_EXTRA_2NL: 745 | OutputC(10, stream); 746 | case STATE_EXTRA_NL: 747 | OutputC(10, stream); 748 | break; 749 | /*case STATE_EXTRA_SPACE: 750 | OutputA(' '); 751 | break;*/ 752 | default: 753 | continue; 754 | } 755 | stream->State->State = STATE_NONE; 756 | } 757 | // Restore the correct colours. 758 | Colour(stream); 759 | int 760 | wrote = OutputA(s, (int)(end - s), stream); 761 | MAYBE_READD_HOOKS(); 762 | return (int)(s - start + wrote); 763 | } 764 | 765 | int WriteColouredW(wchar_t const * s, int n, struct console_colour_stream_s* const stream) 766 | { 767 | MAYBE_REMOVE_HOOKS(); 768 | wchar_t const 769 | * cur, 770 | * start = s, 771 | * end = s + n; 772 | // Just in case. 773 | if (n == 0 || s == NULL) 774 | { 775 | MAYBE_READD_HOOKS(); 776 | return 0; 777 | } 778 | int 779 | ret = 0; 780 | for (; ; ) 781 | { 782 | while (*s && !(stream->State->State == STATE_NONE || stream->State->State == STATE_SKIP)) 783 | { 784 | RunStateMachine(*s++, stream); 785 | } 786 | wchar_t const * 787 | esc = s; 788 | //if (stream->State->Coloured) 789 | { 790 | for (cur = s; cur != end; ++cur) 791 | { 792 | switch (*cur) 793 | { 794 | case '\x1B': // ESC. 795 | stream->State->State = STATE_ESC; 796 | esc = cur; 797 | goto WriteColoured_loop_done; 798 | case '\x1D': // Group separator. 799 | stream->State->State = STATE_EXTRA_NL; 800 | esc = cur; 801 | goto WriteColoured_loop_done; 802 | case '\x1F': // Unit separator. 803 | stream->State->State = STATE_EXTRA_2NL; 804 | esc = cur; 805 | goto WriteColoured_loop_done; 806 | case '\x1E': // Record separator. 807 | stream->State->State = STATE_NONE; 808 | esc = cur; 809 | goto WriteColoured_loop_done; 810 | } 811 | } 812 | WriteColoured_loop_done: 813 | if (cur == end) 814 | { 815 | break; 816 | } 817 | } 818 | //else 819 | //{ 820 | // esc = wcschr(s, '\x1B'); 821 | // if (esc == NULL) 822 | // break; 823 | // stream->State->State = STATE_ESC; 824 | //} 825 | // Write the bit before the escape sequence. 826 | if (esc != s) 827 | { 828 | // Restore the correct colours. 829 | Colour(stream); 830 | int 831 | wrote = OutputW(s, (int)(esc - s), stream); 832 | ret += wrote; 833 | if (wrote != esc - s) 834 | { 835 | MAYBE_READD_HOOKS(); 836 | return (int)(s - start + wrote); 837 | } 838 | } 839 | s = esc + 1; 840 | switch (stream->State->State) 841 | { 842 | case STATE_EXTRA_2NL: 843 | OutputC(10, stream); 844 | case STATE_EXTRA_NL: 845 | OutputC(10, stream); 846 | break; 847 | /*case STATE_EXTRA_SPACE: 848 | OutputW(' '); 849 | break;*/ 850 | default: 851 | continue; 852 | } 853 | stream->State->State = STATE_NONE; 854 | } 855 | // Restore the correct colours. 856 | Colour(stream); 857 | int 858 | wrote = OutputW(s, (int)(end - s), stream); 859 | MAYBE_READD_HOOKS(); 860 | return (int)(s - start + wrote); 861 | } 862 | 863 | #endif 864 | 865 | --------------------------------------------------------------------------------