From c92d0c963522212aa521af8e931fd720e1bc39ff Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Thu, 15 May 2025 17:07:07 +1000 Subject: [PATCH] Implement basic stdout emitter --- htmlcc/__main__.py | 3 +- htmlcc/emitter/stdout.py | 86 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 htmlcc/emitter/stdout.py diff --git a/htmlcc/__main__.py b/htmlcc/__main__.py index 05b6179..faa62bb 100644 --- a/htmlcc/__main__.py +++ b/htmlcc/__main__.py @@ -16,13 +16,14 @@ from .emitter import Emitter, known_emitters from .emitter.cgit import CgitEmitter +from .emitter.stdout import StdoutEmitter from .parser import Parser import argparse # Initialise ArgumentParser arg_parser = argparse.ArgumentParser(prog='htmlcc', description='Statically compiled HTML templates for C') -arg_parser.add_argument('--emitter', default='cgit', choices=list(known_emitters.keys())) +arg_parser.add_argument('--emitter', default='stdout', choices=list(known_emitters.keys())) arg_parser.add_argument('filename', nargs='+') args = arg_parser.parse_args() diff --git a/htmlcc/emitter/stdout.py b/htmlcc/emitter/stdout.py new file mode 100644 index 0000000..e566896 --- /dev/null +++ b/htmlcc/emitter/stdout.py @@ -0,0 +1,86 @@ +# htmlcc - Statically compiled HTML templates for C +# Copyright (C) 2025 Lee Yingtong Li +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from . import Emitter, register_emitter + +class StdoutEmitter(Emitter): + """Emitter which prints templates to stdout (useful for testing/demonstration)""" + + def emit_preamble(self) -> None: + self.emit(r'''#include +#include +#include + +void output_variable_encoded(char *variable, bool encode_quotes) { + // First compute size of buffer required + size_t extra_chars = 0; + char *ptr = variable; + for (; *ptr != '\0'; ptr++) { + if (*ptr == '&') { extra_chars += 4; } + if (*ptr == '<') { extra_chars += 3; } + if (*ptr == '"' && encode_quotes) { extra_chars += 5; } + if (*ptr == '\'' && encode_quotes) { extra_chars += 4; } + } + + size_t len = ptr - variable; + char *buf = malloc(len + extra_chars + 1); + + // Replace characters + char *c_inp = variable; + char *c_out = buf; + for (; *c_inp != '\0'; c_inp++) { + if (*c_inp == '&') { + *c_out++ = '&'; *c_out++ = 'a'; *c_out++ = 'm'; *c_out++ = 'p'; *c_out++ = ';'; + } else if (*c_inp == '<') { + *c_out++ = '&'; *c_out++ = 'l'; *c_out++ = 't'; *c_out++ = ';'; + } else if (*c_inp == '"' && encode_quotes) { + *c_out++ = '&'; *c_out++ = 'q'; *c_out++ = 'u'; *c_out++ = 'o'; *c_out++ = 't'; *c_out++ = ';'; + } else if (*c_inp == '\'' && encode_quotes) { + *c_out++ = '&'; *c_out++ = '#'; *c_out++ = '3'; *c_out++ = '9'; *c_out++ = ';'; + } else { + *c_out++ = *c_inp; + } + } + *c_out = '\0'; + + printf("%s", buf); + free(buf); +} + +void output_variable_as_attr(char *variable) { + output_variable_encoded(variable, true); +} + +void output_variable_as_text(char *variable) { + output_variable_encoded(variable, false); +}''') + + def output_literal_string(self, literal_string: str) -> None: + self.emit(f'printf("%s", {self.escape_cstr(literal_string)});') + + def output_variable_as_attr(self, variable: str) -> None: + self.emit(f'output_variable_as_attr({variable});') + + def output_variable_as_text(self, variable: str) -> None: + self.emit(f'output_variable_as_text({variable});') + + def output_variable_formatted(self, format_string: str, variable: str) -> None: + self.emit(f'printf({self.escape_cstr(format_string)}, {variable});') + + def output_variable_urlencoded(self, variable: str) -> None: + raise NotImplementedError('urlencode filter is not implemented for the stdout emitter') + +register_emitter('stdout', StdoutEmitter)