Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added ngt_set_delimiters() and ngt_variable_equals(). Start and end d…

…elimiters can now be set from code as well as the template itself
  • Loading branch information...
commit 692864679af89624df8e37a2af2f4a81becf4db9 1 parent 698c122
breckinloggins authored
View
2  README
@@ -1,4 +1,4 @@
-ngtemplate - A template engine written in C designed to be syntax-compatible with Google CTemplate
+ngtemplate - A template engine written in C designed to be syntax-compatible with Google CTemplate, released under the MIT license
Version 0.1-ish, so there's plenty more work to do.
View
3  doc/TODO
@@ -27,11 +27,12 @@ Version 0.1
[X] Break up into multiple files
[] Support modifier args
[X] Support builtin modifiers :none and :cstring_escape
-[] Add show/hide section
[X] Add global dictionary
[X] Add BI_SPACE and BI_NEWLINE to global dictionary
[X] Decouple dictionaries from templates
[X] Make sure all API names are as close as possible to the CTemplate counterparts
+[X] Provide way to set the delimiters from code
+[] Add show/hide section
[] Create tool for turning template files into strings in C headers
[] Make proper QUICKSTART documentation
View
1  src/CMakeLists.txt
@@ -46,3 +46,4 @@ ADD_TEMPLATE_TEST_1(6 ../tests/template_6.subtemplate)
ADD_TEMPLATE_TEST(7)
ADD_TEMPLATE_TEST(8)
ADD_TEMPLATE_TEST(9)
+ADD_TEMPLATE_TEST(10)
View
29 src/include/ngtemplate.h
@@ -41,6 +41,17 @@ typedef struct ngt_dictionary_tag {
struct ngt_dictionary_tag* parent;
} ngt_dictionary;
+// Represents a start or stop marker delimiter
+#define MAX_DELIMITER_LENGTH 8 /* I really don't know why someone would want a
+ delimiter this long, but just in case */
+typedef struct delimiter_tag {
+ int length;
+ char literal[MAX_DELIMITER_LENGTH]; /* Keep the literal as the last record in the
+ struct so people can do the "overallocated
+ struct" trick if they need more than
+ MAX_DELIMITER_LENGTH */
+} delimiter;
+
typedef struct ngt_template_tag {
ngt_dictionary* dictionary;
@@ -48,6 +59,9 @@ typedef struct ngt_template_tag {
char* template;
+ delimiter start_delimiter; /* Characters that signify start of a marker */
+ delimiter end_delimiter; /* Characters that signify end of a marker */
+
modifier_fn modifier_missing;
get_variable_fn variable_missing;
} ngt_template;
@@ -95,6 +109,16 @@ int ngt_load_from_file(ngt_template* tpl, FILE* fp);
int ngt_load_from_filename(ngt_template* tpl, const char* filename);
/**
+ * Sets the default start and end delimiters for the given template
+ *
+ * NOTES:
+ * - Each delimiter must have at least one character
+ * - Delimiters with more than 8 characters will be truncated
+ * - Delimiters can still be overridden inside the template
+ */
+void ngt_set_delimiters(ngt_template* tpl, const char* start_delimiter, const char* end_delimiter);
+
+/**
* Sets a modifier function that can be called when the given modifier name is encountered
* in the template. The modifier will have the opportunity to adjust the output of the
* marker, and will be passed in any arguments. If another modifier is already present with
@@ -119,6 +143,11 @@ void ngt_set_modifier_missing_cb(ngt_template* tpl, modifier_fn mod_fn);
void ngt_set_variable_missing_cb(ngt_template* tpl, get_variable_fn get_fn);
/**
+ * Returns nonzero if the variable with the given marker name equals str, zero otherwise
+ */
+int ngt_variable_equals(ngt_dictionary* dict, const char* marker, const char* str);
+
+/**
* Sets a string value in the template dictionary. Any instance of "marker" in the template
* will be replaced by "value"
*
View
32 src/internal.c
@@ -333,7 +333,7 @@ struct _include_params_tag* _get_include_params_ref(ngt_dictionary* dict, const
* Helper function - returns nonzero if the portion of the input string starting at p matches the given
* marker, 0 otherwise
*/
-int _match_marker(const char* p, _delimiter* delim) {
+int _match_marker(const char* p, delimiter* delim) {
int i;
i = 0;
@@ -347,7 +347,7 @@ int _match_marker(const char* p, _delimiter* delim) {
/**
* Helper function. Copies the data from the source delimiter into dest
*/
-void _copy_delimiter(_delimiter* dest, const _delimiter* src) {
+void _copy_delimiter(delimiter* dest, const delimiter* src) {
if (!dest || !src) {
return;
}
@@ -359,10 +359,10 @@ void _copy_delimiter(_delimiter* dest, const _delimiter* src) {
/**
* Helper function. Returns a freshly allocated copy of the given delimiter
*/
-_delimiter* _duplicate_delimiter(const _delimiter* delim) {
- _delimiter* new_delim;
+delimiter* _duplicate_delimiter(const delimiter* delim) {
+ delimiter* new_delim;
- new_delim = (_delimiter*)malloc(sizeof(_delimiter));
+ new_delim = (delimiter*)malloc(sizeof(delimiter));
_copy_delimiter(new_delim, delim);
return new_delim;
@@ -374,7 +374,7 @@ _delimiter* _duplicate_delimiter(const _delimiter* delim) {
*
* Returns the adjusted character pointer, pointing to one after the last position processed
*/
-char* _extract_delimiter(const char* p, _delimiter* delim) {
+char* _extract_delimiter(const char* p, delimiter* delim) {
int length;
length = 0;
@@ -440,24 +440,24 @@ void _append_line_whitespace(_parse_context* line_ctx, _parse_context* out_ctx)
* Helper function - Processes a set delimiter {{= =}} sequence in the template
*/
void _process_set_delimiter(_parse_context* ctx) {
- ctx->in_ptr = _extract_delimiter(ctx->in_ptr, &(ctx->start_delimiter));
+ ctx->in_ptr = _extract_delimiter(ctx->in_ptr, &(ctx->active_start_delimiter));
EAT_SPACES(ctx->in_ptr);
// OK now we should either be at the new end delimiter or an =
if (*ctx->in_ptr == '=') {
ctx->in_ptr++;
- if (!_match_marker(ctx->in_ptr, &(ctx->end_delimiter))) {
+ if (!_match_marker(ctx->in_ptr, &(ctx->active_end_delimiter))) {
fprintf(stderr, "Unexpected '=' in middle of Set Delimiter\n");
exit(-1);
}
- ctx->in_ptr += ctx->end_delimiter.length;
+ ctx->in_ptr += ctx->active_end_delimiter.length;
// In these cases, the start and end delimiters are the same
- _copy_delimiter(&(ctx->end_delimiter), &(ctx->start_delimiter));
+ _copy_delimiter(&(ctx->active_end_delimiter), &(ctx->active_start_delimiter));
} else {
- _delimiter* old_end_delimiter = _duplicate_delimiter(&(ctx->end_delimiter));
- ctx->in_ptr = _extract_delimiter(ctx->in_ptr, &(ctx->end_delimiter));
+ delimiter* old_end_delimiter = _duplicate_delimiter(&ctx->active_end_delimiter);
+ ctx->in_ptr = _extract_delimiter(ctx->in_ptr, &(ctx->active_end_delimiter));
EAT_SPACES(ctx->in_ptr);
if (*ctx->in_ptr != '=') {
@@ -731,11 +731,11 @@ char* _process(_parse_context *ctx) {
ctx->mode = MODE_NORMAL;
continue;
- } else if (_match_marker(ctx->in_ptr, &(ctx->end_delimiter))) {
+ } else if (_match_marker(ctx->in_ptr, &(ctx->active_end_delimiter))) {
// At the end of the marker
marker[m] = '\0';
modifiers[mod] = '\0';
- ctx->in_ptr += ctx->end_delimiter.length;
+ ctx->in_ptr += ctx->active_end_delimiter.length;
if (ctx->mode & MODE_MARKER_VARIABLE) {
_process_variable(marker, modifiers, ctx);
@@ -814,8 +814,8 @@ char* _process(_parse_context *ctx) {
continue;
}
- if (_match_marker(ctx->in_ptr, &(ctx->start_delimiter))) {
- ctx->in_ptr += ctx->start_delimiter.length; // Skip over those characters
+ if (_match_marker(ctx->in_ptr, &(ctx->active_start_delimiter))) {
+ ctx->in_ptr += ctx->active_start_delimiter.length; // Skip over those characters
ctx->mode = MODE_MARKER;
memset(marker, 0, MAXMARKERLENGTH);
memset(modifiers, 0, MAXMODIFIERLENGTH);
View
24 src/internal.h
@@ -63,17 +63,6 @@ typedef struct _dictionary_item_tag {
} val;
} _dictionary_item;
-// Represents a start or stop marker delimiter
-#define MAX_DELIMITER_LENGTH 8 /* I really don't know why someone would want a
- delimiter this long, but just in case */
-typedef struct _delimiter_tag {
- int length;
- char literal[MAX_DELIMITER_LENGTH]; /* Keep the literal as the last record in the
- struct so people can do the "overallocated
- struct" trick if they need more than
- MAX_DELIMITER_LENGTH */
-} _delimiter;
-
// Represents a marker modifier
typedef struct _modifier_tag {
char* name; // The name that will be used to call the modifier
@@ -92,8 +81,9 @@ typedef struct _parse_context_tag {
// section expansions. Used for separator logic
ngt_template* template; // The current template
ngt_dictionary* active_dictionary; // The curently active dictionary
- _delimiter start_delimiter; // Characters that signify start of a marker
- _delimiter end_delimiter; // Characters that signify end of a marker
+
+ delimiter active_start_delimiter; // The current start delimiter
+ delimiter active_end_delimiter; // The current end delimiter
int template_line; // Line number in the current source template
char* in_ptr; // Where we are in the template
@@ -220,17 +210,17 @@ struct _include_params_tag* _get_include_params_ref(ngt_dictionary* dict, const
* Helper function - returns nonzero if the portion of the input string starting at p matches the given
* marker, 0 otherwise
*/
-int _match_marker(const char* p, _delimiter* delim);
+int _match_marker(const char* p, delimiter* delim);
/**
* Helper function. Copies the data from the source delimiter into dest
*/
-void _copy_delimiter(_delimiter* dest, const _delimiter* src);
+void _copy_delimiter(delimiter* dest, const delimiter* src);
/**
* Helper function. Returns a freshly allocated copy of the given delimiter
*/
-_delimiter* _duplicate_delimiter(const _delimiter* delim);
+delimiter* _duplicate_delimiter(const delimiter* delim);
/**
* Helper function - assuming that p points to the character after '<start delim>=', reads the string up
@@ -238,7 +228,7 @@ _delimiter* _duplicate_delimiter(const _delimiter* delim);
*
* Returns the adjusted character pointer, pointing to one after the last position processed
*/
-char* _extract_delimiter(const char* p, _delimiter* delim);
+char* _extract_delimiter(const char* p, delimiter* delim);
/**
* Helper function - creates a new context and duplicates the contents of the given context
View
52 src/ngtemplate.c
@@ -135,6 +135,37 @@ int ngt_load_from_filename(ngt_template* tpl, const char* filename) {
}
/**
+ * Sets the default start and end delimiters for the given template
+ *
+ * NOTES:
+ * - Each delimiter must have at least one character
+ * - Delimiters with more than 8 characters will be truncated
+ * - Delimiters can still be overridden inside the template
+ */
+void ngt_set_delimiters(ngt_template* tpl, const char* start_delimiter, const char* end_delimiter) {
+ int i;
+
+ if (!start_delimiter || !end_delimiter || strlen(start_delimiter) < 1 || strlen(end_delimiter) < 1) {
+ // Gotta have somethin, man...
+ return;
+ }
+
+ i = 0;
+ for (i = 0; i < MAX_DELIMITER_LENGTH && i < strlen(start_delimiter); i++) {
+ tpl->start_delimiter.literal[i] = start_delimiter[i];
+ }
+ tpl->start_delimiter.literal[i] = '\0';
+ tpl->start_delimiter.length = i;
+
+ i = 0;
+ for (i = 0; i < MAX_DELIMITER_LENGTH && i < strlen(end_delimiter); i++) {
+ tpl->end_delimiter.literal[i] = end_delimiter[i];
+ }
+ tpl->end_delimiter.literal[i] = '\0';
+ tpl->end_delimiter.length = i;
+}
+
+/**
* Sets a modifier function that will be called when a modifier in the template does not
* resolve to any known modifiers. The function will have the opportunity to adjust the output
* of the marker, and will be passed any arguments.
@@ -153,6 +184,18 @@ void ngt_set_variable_missing_cb(ngt_template* tpl, get_variable_fn get_fn) {
}
/**
+ * Returns nonzero if the variable with the given marker name equals str, zero otherwise
+ */
+int ngt_variable_equals(ngt_dictionary* dict, const char* marker, const char* str) {
+ const char* value = _get_string_value_ref(dict, marker);
+ if (!value) {
+ return 0;
+ }
+
+ return !strcmp(value, str);
+}
+
+/**
* Sets a modifier function that can be called when the given modifier name is encountered
* in the template. The modifier will have the opportunity to adjust the output of the
* marker, and will be passed in any arguments. If another modifier is already present with
@@ -359,13 +402,14 @@ int ngt_expand(ngt_template* tpl, char** result) {
memset(&context, 0, sizeof(_parse_context));
context.current_section = "";
- strcpy(context.start_delimiter.literal, "{{");
- context.start_delimiter.length = 2;
- strcpy(context.end_delimiter.literal, "}}");
- context.end_delimiter.length = 2;
+ if (tpl->start_delimiter.length == 0 && tpl->end_delimiter.length == 0) {
+ ngt_set_delimiters(tpl, "{{", "}}");
+ }
context.template = tpl;
context.active_dictionary = tpl->dictionary;
+ _copy_delimiter(&context.active_start_delimiter, &tpl->start_delimiter);
+ _copy_delimiter(&context.active_end_delimiter, &tpl->end_delimiter);
context.in_ptr = (char*)tpl->template;
context.template_line = 1;
View
4 src/testing/template_test.c
@@ -250,6 +250,10 @@ DEFINE_TEST_FUNCTION {
ngt_set_include_filename(dict, "Filename_Template", argv[3]);
}
+ if (ngt_variable_equals(dict, "DelimiterTest", "True")) {
+ ngt_set_delimiters(tpl, "<%", "%>");
+ }
+
if (ngt_expand(tpl, &result) < 0) {
return -1;
}
View
8 tests/template_10.bmk
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+This should say "True": True
+
View
9 tests/template_10.tst
@@ -0,0 +1,9 @@
+<%! Need to switch back to old delimiters temporarily because read_dictionary isn't delimiter-aware %>
+<%! None of this stuff should show up in the output if ngt_set_delimiters() is working %>
+<%={{ }}=%>
+{{!#
+DelimiterTest=True
+#!}}
+{{! OK now back to the originals }}
+{{=<% %>=}}
+This should say "True": <% DelimiterTest %>
Please sign in to comment.
Something went wrong with that request. Please try again.