Implement basic stdout emitter

This commit is contained in:
RunasSudo 2025-05-15 17:07:07 +10:00
parent ffd5ac52e7
commit c92d0c9635
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
2 changed files with 88 additions and 1 deletions

View File

@ -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()

86
htmlcc/emitter/stdout.py Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
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 <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
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)