/* * tpllib * * C library of functions for text template processing. * Copyright (C) 2003-2007 Niels Wojciech Tadeusz Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef WIN32 #include /* write() */ #else #include /* write() */ #endif #include /* exit() */ #include /* fopen(), sprintf() ... */ #include /* va_start(), va_end() */ #include /* memset(), memcmp() ... */ /* Define ZTS when building a PHP extension */ /* Define WIN32 when compiling under MS Windows */ #ifdef WIN32 # define snprintf _snprintf # define vsnprintf _vsnprintf #endif #include "lib_tpl.h" /* Length defines for markup elements */ #define DELIM_LEN_LEFT (sizeof(DELIMITER_LEFT) - 1) #define DELIM_LEN_RIGHT (sizeof(DELIMITER_RIGHT) - 1) #define SEC_HEAD_LEN (sizeof(SECTIONTAG_HEAD) - 1) #define SEC_TAIL_LEN (sizeof(SECTIONTAG_TAIL) - 1) #define M_MIN(a, b) (((a) < (b)) ? (a) : (b)) #define M_MAX(a, b) (((a) > (b)) ? (a) : (b)) #define M_NODE_LEN(n) (((n)->val) ? (n)->len : (n)->fval->len) #define M_NODE_TEXT(n) (((n)->val) ? (n)->val : (n)->fval->val) #define M_INT_TO_STRING(buffer, buffer_len, val, signed_v) \ do \ { \ char rev[5 * sizeof(int) / 2]; \ int i = 0, digits = 0; \ if ((signed_v) && (val) < 0) \ { \ buffer[i] = '-'; \ i++; \ } \ do \ { \ rev[digits++] = '0' + abs((val) % 10); \ (val) /= 10; \ } \ while ((val) != 0); \ for (; digits > 0 && i < (buffer_len); i++) \ buffer[i] = rev[--digits]; \ buffer[i] = 0; \ (buffer_len) = i; \ } while (0) /* Binary safe string search */ static const char *tpl_strstr(register const char *haystack, ssize_t haystack_len, const char *needle, int needle_len) { ssize_t i; const char *p_toofar = haystack + haystack_len - needle_len + 1; if (p_toofar <= haystack) return NULL; do { if (*haystack == *needle) { for (i = 1; i != needle_len && haystack[i] == needle[i]; i++); if (i == needle_len) return haystack; } haystack++; } while (haystack != p_toofar); return NULL; } static tpl_node_t* create_node(int len) { tpl_node_t* node = (tpl_node_t*)acl_mymalloc(len+1+sizeof(tpl_node_t)); node->len = len; node->val = (char*)(node + 1); node->next = NULL; return node; } static void destroy_node(tpl_node_t* node) { acl_myfree(node); } static tpl_fcell_t* create_fcell(const char *key, int key_len, int data_len) { tpl_fcell_t* field = (tpl_fcell_t*)acl_mymalloc(key_len + 1 + sizeof(tpl_fcell_t)); field->key = (char *)(field + 1); (void)memcpy(field->key, key, key_len); field->key[key_len] = 0; field->val = (char *)acl_mymalloc(M_MAX(data_len, INITIAL_FIELD_LEN) + 1); field->len = 0; field->val[0] = 0; field->next = NULL; return field; } static void destroy_fcell(tpl_fcell_t* field) { acl_myfree(field->val); acl_myfree(field); } static tpl_tcell_t* create_tcell(const char *key, int key_len) { tpl_tcell_t* section = (tpl_tcell_t*)acl_mymalloc(key_len + 1 + sizeof(tpl_tcell_t) + sizeof(tpl_t)); section->tpl = (tpl_t *)(section + 1); tpl_init(section->tpl); section->key = (char *)(section->tpl + 1); (void)memcpy(section->key, key, key_len); section->key[key_len] = 0; section->next = NULL; section->_next = NULL; return section; } static void destroy_tcell(tpl_tcell_t* section) { tpl_release(section->tpl); acl_myfree(section); } /* Hash function using multipy by 31 */ static unsigned int tpl_hash(const char *key, int key_len) { const char *p_key_end = key + key_len; register unsigned int h = *key++; while (key != p_key_end && *key != 0) { unsigned int g; h = (h << 5) + *key++; if ((g = h & 0xf0000000) != 0) h = (h ^ (g >> 24) ^ g); } return h % HASH_TABLE_SIZE; } static tpl_fcell_t* tpl_produce_field(tpl_t *tpl, const char* key, int key_len, int data_len, int may_create) { tpl_fcell_t **pfield = &tpl->fields[tpl_hash(key, key_len)]; while (*pfield != NULL) { if (memcmp((*pfield)->key, key, key_len) == 0) { if (data_len > INITIAL_FIELD_LEN && data_len > (*pfield)->len) (*pfield)->val = (char*) acl_myrealloc((*pfield)->val, data_len); return *pfield; } pfield = &(*pfield)->next; } if (*pfield == NULL && may_create != 0) { *pfield = create_fcell(key, key_len, data_len); return *pfield; } return NULL; } #define tpl_get_field(tpl, key) \ tpl_produce_field((tpl), (key), strlen((key)), 0, 0) #define tpl_new_field(tpl, key, key_len) \ tpl_produce_field((tpl), (key), (key_len), 0, 1) #define tpl_cpy_field(field, tpl, key, key_len, data, data_len) \ do \ { \ (field) = tpl_produce_field((tpl), (key), (key_len), (data_len), 1); \ if ((field)->len == 0 && (data_len) != 0) \ { \ (void)memcpy((field)->val, (data), (data_len));\ (field)->val[(data_len)] = 0; \ (field)->len = (data_len); \ } \ } while (0) static tpl_tcell_t* tpl_produce_section(tpl_t *tpl, const char* key, int key_len, int must_create) { tpl_tcell_t **psection = &tpl->sections[tpl_hash(key, key_len)]; while (*psection != NULL) { if (memcmp((*psection)->key, key, key_len) == 0) return (must_create) ? NULL : *psection; psection = &(*psection)->_next; } if (*psection == NULL && must_create) { *psection = create_tcell(key, key_len); (*psection)->tpl->parent = tpl; return *psection; } return NULL; } #define tpl_get_section(tpl, key) \ tpl_produce_section(tpl, key, strlen(key), 0) #define tpl_make_section(tpl, key, key_len) \ tpl_produce_section(tpl, key, key_len, 1) /* Big, ugly, horrible, quite possibly buggy... live with it or correct it and let me know */ static int tpl_construct(tpl_t *tpl, const char *p_last, const char *p_end) { const char *p_begin = p_last, *p_curr, *p_next; tpl_node_t **tail = &tpl->head; tpl_tcell_t **last_section = &tpl->first; /* While a field delimiter can be found in what is left */ while ((p_curr = tpl_strstr(p_last, p_end - p_last, DELIMITER_LEFT, DELIM_LEN_LEFT)) != NULL) { /* Advance to beginning of field/section name */ p_curr += DELIM_LEN_LEFT; /* Find end delimiter of identifier or fail with syntax error */ p_next = tpl_strstr(p_curr, p_end - p_curr, DELIMITER_RIGHT, DELIM_LEN_RIGHT); if (p_next == NULL) { *last_section = NULL; *tail = NULL; return TPL_SYNTAX_ERROR; } /* Section */ if (p_curr + 1 >= p_begin + SEC_HEAD_LEN && p_end - p_next >= (int) SEC_TAIL_LEN && memcmp(SECTIONTAG_HEAD, p_curr - SEC_HEAD_LEN, SEC_HEAD_LEN) == 0 && memcmp(SECTIONTAG_TAIL, p_next, SEC_TAIL_LEN) == 0) { tpl_tcell_t *section; if ((p_curr - SEC_HEAD_LEN - p_last) != 0) { int val_len = p_curr - SEC_HEAD_LEN - p_last; *tail = create_node(val_len); (*tail)->val[val_len] = 0; (void)memcpy((*tail)->val, p_last, val_len); tail = &(*tail)->next; } /* Create and chain in entry for section */ section = tpl_make_section(tpl, p_curr, p_next - p_curr); if (section != NULL) { int code; const char *beginning, *ending; *last_section = section; last_section = §ion->next; section->preceding = tail; /* Advance past the section tag */ p_last = p_next + SEC_TAIL_LEN; beginning = p_next + SEC_TAIL_LEN; /* Find next occurrence of this section tag */ ending = tpl_strstr(beginning, p_end - beginning, p_curr - SEC_HEAD_LEN, beginning - p_curr + SEC_HEAD_LEN); p_last = ending + (SEC_HEAD_LEN + p_last - p_curr); /* If found and found before p_end */ if (ending != NULL && p_last <= p_end) { /* Construct recursively */ code = tpl_construct(section->tpl, beginning, ending); } else code = TPL_SYNTAX_ERROR; if (code != TPL_OK) { tpl_release(section->tpl); tpl_init(section->tpl); *last_section = NULL; *tail = NULL; return TPL_SYNTAX_ERROR; } } else { *last_section = NULL; *tail = NULL; return TPL_SYNTAX_ERROR; } } /* Field */ else { if ((p_curr - DELIM_LEN_LEFT - p_last) != 0) { int val_len = p_curr - DELIM_LEN_LEFT - p_last; *tail = create_node(val_len); (*tail)->val[val_len] = 0; (void)memcpy((*tail)->val, p_last, val_len); tail = &(*tail)->next; } p_last = p_next + DELIM_LEN_RIGHT; /* Create node and set fval to new field cell */ *tail = create_node(0); (*tail)->val = NULL; (*tail)->fval = tpl_new_field(tpl, p_curr, p_next - p_curr); tail = &(*tail)->next; } } /* Store rest of the text */ if (p_last < p_end) { int val_len = p_end - p_last; *tail = create_node(val_len); (void)memcpy((*tail)->val, p_last, val_len); (*tail)->val[val_len] = 0; tail = &(*tail)->next; } *tail = NULL; *last_section = NULL; return TPL_OK; } void tpl_init(tpl_t *tpl) { /* Set everything to NULL */ (void)memset(tpl, 0, sizeof(tpl_t)); tpl->tpl = tpl; tpl->added_tail = &tpl->added_head; } void tpl_release(tpl_t *tpl) { register int i; /* Free nodes */ while (tpl->head != NULL) { tpl_node_t *deleted = tpl->head; tpl->head = tpl->head->next; destroy_node(deleted); } /* Free any added content */ while (tpl->added_head != NULL) { tpl_node_t *deleted = tpl->added_head; tpl->added_head = tpl->added_head->next; destroy_node(deleted); } /* Free field cells */ for (i = 0; i < HASH_TABLE_SIZE; i++) { tpl_fcell_t *fc = tpl->fields[i]; while (fc != NULL) { tpl_fcell_t *deleted = fc; fc = fc->next; destroy_fcell(deleted); } } /* Free sections including added content */ while (tpl->first != NULL) { tpl_tcell_t *deleted = tpl->first; tpl->first = tpl->first->next; /* This will call tpl_release() recursively */ destroy_tcell(deleted); } } tpl_t* tpl_alloc(void) { tpl_t *tpl = (tpl_t*)acl_mymalloc(sizeof(tpl_t)); tpl_init(tpl); return tpl; } void tpl_free(tpl_t *tpl) { tpl_release(tpl); acl_myfree(tpl); } /* Load template file */ int tpl_load(tpl_t *tpl, const char *filename) { char *buffer; int len; FILE *fp = fopen(filename, "rb"); if (fp == NULL) return TPL_OPEN_ERROR; /* Find length of file content */ (void)fseek(fp, 0, SEEK_END); len = ftell(fp); (void) rewind(fp); /* Allocate buffer for data + 0 byte */ buffer = (char*)acl_mymalloc(len+1); if (fread(buffer, 1, len, fp) < (unsigned)len) { (void)fclose(fp); acl_myfree(buffer); return TPL_READ_ERROR; } (void)fclose(fp); buffer[len] = 0; /* Use data in buffer to construct template */ len = tpl_construct(tpl, buffer, buffer + len); if (len != TPL_OK) { tpl_release(tpl); tpl_init(tpl); } acl_myfree(buffer); return len; } /* Load from a string */ int tpl_from_string(tpl_t *tpl, const char *buffer, int len) { return tpl_construct(tpl, buffer, buffer + len); } /* Recursively clear added content */ void tpl_reset(tpl_t* tpl) { int i = HASH_TABLE_SIZE; tpl_tcell_t* section; /* Clear fields */ while (i != 0) { tpl_fcell_t* field = tpl->fields[--i]; while (field != NULL) { field->len = 0; *field->val = 0; field = field->next; } } /* Clear sections */ for (section = tpl->first; section != NULL; section = section->next) { /* Clear added content */ while (section->tpl->added_head != NULL) { tpl_node_t* n = section->tpl->added_head; section->tpl->added_head = section->tpl->added_head->next; destroy_node(n); } section->tpl->added_tail = §ion->tpl->added_head; /* Clear this section */ tpl_reset(section->tpl); } } /* Set up the selected section pointer to correspond to that of the copied template */ static void tpl_adjust_selection(tpl_t* tpl, const tpl_t* srctpl, tpl_tcell_t* sec, const tpl_tcell_t* srcsec) { const tpl_tcell_t *sec1 = srcsec; tpl_tcell_t *sec2 = sec; if (srctpl->tpl != srctpl) { for (; tpl->tpl == tpl && sec1 != NULL; sec1 = sec1->next, sec2 = sec2->next) { if (srctpl->tpl == sec1->tpl) tpl->tpl = sec2->tpl; else { tpl_adjust_selection(tpl, srctpl, sec2->tpl->first, sec1->tpl->first); } } } } /* Recursive copy */ void tpl_copy(tpl_t* tpl, const tpl_t* srctpl) { tpl_node_t **tail = &tpl->head; tpl_node_t *curr_node = srctpl->head; tpl_tcell_t **last_section = &tpl->first; tpl_tcell_t *curr_section = srctpl->first; for (; curr_section != NULL; curr_section = curr_section->next) { /* Copy text and fields until the current section's position */ while (curr_node != *curr_section->preceding) { /* Text */ if (curr_node->val != NULL) { *tail = create_node(curr_node->len); (void)memcpy((*tail)->val, curr_node->val, curr_node->len); (*tail)->val[curr_node->len] = 0; } /* Field */ else { /* Create node and set fval to field cell */ *tail = create_node(0); (*tail)->val = NULL; tpl_cpy_field((*tail)->fval, tpl, curr_node->fval->key, strlen(curr_node->fval->key), curr_node->fval->val, curr_node->fval->len); } tail = &(*tail)->next; curr_node = curr_node->next; } /* Create emtpy section entry for current section */ *last_section = tpl_make_section(tpl, curr_section->key, strlen(curr_section->key)); (*last_section)->preceding = tail; /* Consolidate added content into a single node in newly created section entry so that only one memory allocation is needed */ if (curr_section->tpl->added_head != NULL) { tpl_node_t *some_node = curr_section->tpl->added_head; char *buffer; int len = 0; /* Get length of added content */ for (; some_node!=NULL; some_node=some_node->next) len += some_node->len; /* Create node for added content */ (*last_section)->tpl->added_head = create_node(len); (*last_section)->tpl->added_head->next = NULL; (*last_section)->tpl->added_tail = &(*last_section)->tpl->added_head->next; /* Copy added content into new node */ buffer = (*last_section)->tpl->added_head->val; for (some_node = curr_section->tpl->added_head; some_node != NULL; some_node = some_node->next) { (void)memcpy(buffer, some_node->val, some_node->len); buffer += some_node->len; } *buffer = 0; } /* Recursively copy section */ tpl_copy((*last_section)->tpl, curr_section->tpl); last_section = &(*last_section)->next; } /* Copy the rest */ while (curr_node != NULL) { /* Text */ if (curr_node->val != NULL) { *tail = create_node(curr_node->len); (void)memcpy((*tail)->val, curr_node->val, curr_node->len + 1); } /* Field */ else { *tail = create_node(0); (*tail)->val = NULL; tpl_cpy_field((*tail)->fval, tpl, curr_node->fval->key, strlen(curr_node->fval->key), curr_node->fval->val, curr_node->fval->len); } tail = &(*tail)->next; curr_node = curr_node->next; } *tail = NULL; *last_section = NULL; tpl->tpl = tpl; tpl_adjust_selection(tpl, srctpl, tpl->first, srctpl->first); } void tpl_set_field(tpl_t* tpl, const char* key, const char* val, int len) { tpl_fcell_t *field = tpl_get_field(tpl->tpl, key); if (field != NULL) { if (len > INITIAL_FIELD_LEN && len > field->len) field->val = (char*) acl_myrealloc(field->val, len + 1); field->len = len; (void)memcpy(field->val, val, len); field->val[len] = 0; } } void tpl_set_field_fmt(tpl_t* tpl, const char* key, const char* fmt, ...) { tpl_fcell_t *field = tpl_get_field(tpl->tpl, key); if (field != NULL) { int n; if (field->len < INITIAL_FIELD_LEN) field->len = INITIAL_FIELD_LEN; while (1) { va_list ap; va_start(ap, fmt); n = vsnprintf(field->val, field->len + 1, fmt, ap); va_end(ap); if (n > -1 && n <= field->len) break; field->len *= 2; field->val = (char*) acl_myrealloc(field->val, field->len + 1); } field->len = n; } } void tpl_set_field_int(tpl_t* tpl, const char* key, int val) { tpl_fcell_t *field = tpl_get_field(tpl->tpl, key); if (field != NULL) { if (field->len < INITIAL_FIELD_LEN) field->len = INITIAL_FIELD_LEN; M_INT_TO_STRING(field->val, field->len, val, 1); } } void tpl_set_field_uint(tpl_t* tpl, const char* key, unsigned int val) { tpl_fcell_t *field = tpl_get_field(tpl->tpl, key); if (field != NULL) { int v = (int) val; if (field->len < INITIAL_FIELD_LEN) field->len = INITIAL_FIELD_LEN; M_INT_TO_STRING(field->val, field->len, v, 0); } } void tpl_set_field_double(tpl_t* tpl, const char* key, double val) { tpl_fcell_t *field = tpl_get_field(tpl->tpl, key); if (field != NULL) { if (field->len < INITIAL_FIELD_LEN) field->len = INITIAL_FIELD_LEN; field->len = snprintf(field->val, field->len, "%.2f", val); } } int tpl_set_field_from_file(tpl_t* tpl, const char* key, const char* filename) { tpl_fcell_t *field = tpl_get_field(tpl->tpl, key); if (field != NULL) { int len; FILE *fp = fopen(filename, "rb"); if (fp == NULL) return TPL_OPEN_ERROR; /* Find length of file content */ (void)fseek(fp, 0, SEEK_END); len = ftell(fp); (void)rewind(fp); if (len > INITIAL_FIELD_LEN && len > field->len) field->val = (char*) acl_myrealloc(field->val, len + 1); if (fread(field->val, 1, len, fp) < (unsigned)len) { (void)fclose(fp); return TPL_READ_ERROR; } (void)fclose(fp); field->len = len; field->val[len] = 0; } return TPL_OK; } void tpl_set_field_global(tpl_t* tpl, const char* key, const char* val, int len) { tpl_tcell_t *section = tpl->first; tpl_set_field(tpl, key, val, len); while (section != NULL) { /* Recursively set in all sections */ tpl_set_field_global(section->tpl, key, val, len); section = section->next; } } void tpl_set_field_fmt_global(tpl_t* tpl, const char* key, const char* fmt, ...) { int n; int len = 256; char *buf = (char*)acl_mymalloc(len); while (1) { va_list ap; va_start(ap, fmt); n = vsnprintf(buf, len, fmt, ap); va_end(ap); if (n > -1 && n < len) break; len *= 2; buf = (char*) acl_myrealloc(buf, len); } tpl_set_field_global(tpl, key, buf, n); acl_myfree(buf); } void tpl_set_field_int_global(tpl_t* tpl, const char* key, int val) { tpl_tcell_t *section = tpl->first; tpl_set_field_int(tpl, key, val); while (section != NULL) { /* Recursively set in all sections */ tpl_set_field_int_global(section->tpl, key, val); section = section->next; } } void tpl_set_field_uint_global(tpl_t* tpl, const char* key, unsigned int val) { tpl_tcell_t *section = tpl->first; tpl_set_field_uint(tpl, key, val); while (section != NULL) { /* Recursively set in all sections */ tpl_set_field_uint_global(section->tpl, key, val); section = section->next; } } void tpl_set_field_double_global(tpl_t* tpl, const char* key, double val) { tpl_tcell_t *section = tpl->first; tpl_set_field_double(tpl, key, val); while (section != NULL) { /* Recursively set in all sections */ tpl_set_field_double_global(section->tpl, key, val); section = section->next; } } int tpl_select_section(tpl_t *tpl, const char* key) { tpl_tcell_t *section = tpl_get_section(tpl->tpl, key); if (section != NULL) { tpl->tpl->tpl = section->tpl; tpl->tpl = tpl->tpl->tpl; return TPL_OK; } return TPL_SECTION_NOT_FOUND_ERROR; } int tpl_deselect_section(tpl_t *tpl) { if (tpl->tpl != tpl) { tpl->tpl = tpl->tpl->parent; tpl->tpl->tpl = tpl->tpl; return TPL_OK; } return TPL_NO_SECTION_SELECTED_ERROR; } /* Set or replace added content in section with supplied value */ static int tpl_set_section_ex(tpl_t* tpl, const char* key, const char* val, int len, tpl_node_t *existing_node) { tpl_tcell_t *section = tpl_get_section(tpl->tpl, key); if (section != NULL) { if (section->tpl->first != NULL || section->tpl->head == NULL || section->tpl->head->next != NULL || section->tpl->head->len != len) { /* Destroy the contents of this section */ tpl_release(section->tpl); tpl_init(section->tpl); section->tpl->parent = tpl->tpl; section->tpl->head = create_node(len); } (void)memcpy(section->tpl->head->val, val, len); section->tpl->head->val[len] = 0; if (existing_node != NULL) { existing_node->next = section->tpl->added_head; section->tpl->added_head = existing_node; } else { if (section->tpl->added_head != NULL && section->tpl->added_head->len != len) { tpl_node_t *node = create_node(len); node->next = section->tpl->added_head; section->tpl->added_head = node; } else if (section->tpl->added_head == NULL) section->tpl->added_head = create_node(len); (void)memcpy(section->tpl->added_head->val, val, len); section->tpl->added_head->val[len] = 0; } while (section->tpl->added_head->next != NULL) { tpl_node_t *deleted = section->tpl->added_head->next; section->tpl->added_head->next = deleted->next; destroy_node(deleted); } section->tpl->added_tail = §ion->tpl->added_head->next; return TPL_OK; } return TPL_SECTION_NOT_FOUND_ERROR; } int tpl_set_section(tpl_t* tpl, const char* key, const char* val, int len) { return tpl_set_section_ex(tpl, key, val, len, NULL); } int tpl_set_section_from_file(tpl_t* tpl, const char* key, const char* filename) { int len, status; tpl_node_t *node; FILE *fp = fopen(filename, "rb"); if (fp == NULL) return TPL_OPEN_ERROR; /* Find length of file content */ (void)fseek(fp, 0, SEEK_END); len = ftell(fp); (void)rewind(fp); node = create_node(len); if (fread(node->val, 1, len, fp) < (unsigned)len) status = TPL_READ_ERROR; else status = tpl_set_section_ex(tpl, key, node->val, len, node); (void)fclose(fp); if (status != TPL_OK) destroy_node(node); return status; } int tpl_append_section(tpl_t* tpl) { if (tpl->tpl != tpl) { tpl_tcell_t *curr_sect = tpl->tpl->first; tpl_node_t *curr_node = tpl->tpl->head; tpl_node_t *some_node; int len; char *buffer; for (; curr_sect != NULL; curr_sect = curr_sect->next) { if (curr_sect->tpl->added_head != NULL) { for (len = 0, some_node = curr_node; some_node != *curr_sect->preceding; some_node = some_node->next) { len += M_NODE_LEN(some_node); } *tpl->tpl->added_tail = create_node(len); buffer = (*tpl->tpl->added_tail)->val; tpl->tpl->added_tail = &(*tpl->tpl->added_tail)->next; for (; curr_node != *curr_sect->preceding; curr_node = curr_node->next) { if (curr_node->val) { (void)memcpy(buffer, curr_node->val, curr_node->len); buffer += curr_node->len; } else { (void)memcpy(buffer, curr_node->fval->val, curr_node->fval->len); buffer += curr_node->fval->len; } } *buffer = 0; /* Cut 'n paste added content chain from section */ *tpl->tpl->added_tail = curr_sect->tpl->added_head; tpl->tpl->added_tail = curr_sect->tpl->added_tail; curr_sect->tpl->added_tail = &curr_sect->tpl->added_head; curr_sect->tpl->added_head = NULL; } } for (len = 0, some_node = curr_node; some_node != NULL; some_node = some_node->next) { len += M_NODE_LEN(some_node); } *tpl->tpl->added_tail = create_node(len); buffer = (*tpl->tpl->added_tail)->val; tpl->tpl->added_tail = &(*tpl->tpl->added_tail)->next; for (; curr_node != NULL; curr_node = curr_node->next) { if (curr_node->val) { (void)memcpy(buffer, curr_node->val, curr_node->len); buffer += curr_node->len; } else { (void)memcpy(buffer, curr_node->fval->val, curr_node->fval->len); buffer += curr_node->fval->len; } } *buffer = 0; *tpl->tpl->added_tail = NULL; return TPL_OK; } return TPL_NO_SECTION_SELECTED_ERROR; } int tpl_length(const tpl_t* tpl) { tpl_node_t *n = tpl->head; tpl_tcell_t *s = tpl->first; int len; for (len = 0; n != NULL; n = n->next) len += (n->val) ? n->len : n->fval->len; for (; s != NULL; s = s->next) { for (n = s->tpl->added_head; n != NULL; n = n->next) len += n->len; } return len; } int tpl_section_length(const tpl_t* tpl) { return tpl_length(tpl->tpl); } void tpl_get_content(const tpl_t* tpl, char* buffer) { tpl_node_t *n = tpl->head; tpl_tcell_t *s = tpl->first; for (; s != NULL; s = s->next) { tpl_node_t *an = s->tpl->added_head; /* Copy part until beginning of section */ for (; n != *s->preceding; n = n->next) { if (n->val) { (void)memcpy(buffer, n->val, n->len); buffer += n->len; } else { (void)memcpy(buffer, n->fval->val, n->fval->len); buffer += n->fval->len; } } /* Copy content appended in section */ for (; an != NULL; an = an->next) { (void)memcpy(buffer, an->val, an->len); buffer += an->len; } } /* Copy the rest */ for (; n != NULL; n = n->next) { if (n->val) { (void)memcpy(buffer, n->val, n->len); buffer += n->len; } else { (void)memcpy(buffer, n->fval->val, n->fval->len); buffer += n->fval->len; } } *buffer = 0; } void tpl_get_section_content(const tpl_t* tpl, char* buffer) { tpl_get_content(tpl->tpl, buffer); } int tpl_save_as(const tpl_t* tpl, const char* filename) { FILE *fp = fopen(filename, "wb"); if (fp != NULL) { int len = tpl_length(tpl); int n; char *content = (char *)acl_mymalloc(len + 1); tpl_get_content(tpl, content); n = fwrite(content, len, 1, fp); if (n != 1) { (void)fclose(fp); acl_myfree(content); return TPL_WRITE_ERROR; } (void)fclose(fp); acl_myfree(content); return TPL_OK; } return TPL_OPEN_ERROR; } int tpl_write(const tpl_t* tpl, int fd) { int len = tpl_length(tpl); char *content = (char *)acl_mymalloc(len + 1); tpl_get_content(tpl, content); len = write(fd, content, len); acl_myfree(content); if (len < 0) return TPL_WRITE_ERROR; return TPL_OK; } int tpl_http_write(const tpl_t* tpl, int fd) { /* Assumption: the content length can be max a 10 digit number */ int length_digits = 10; int header_len = sizeof(TPL_NPH_HTTP_HEADER_START) + sizeof(TPL_NPH_HTTP_HEADER_END) + length_digits; int content_len = tpl_length(tpl); int tmp_len = content_len; /* Long enough for both the HTTP header and content */ char *response = (char*)acl_mymalloc(content_len + header_len + 1); char *str_p = response; (void)memcpy(str_p, TPL_NPH_HTTP_HEADER_START, sizeof(TPL_NPH_HTTP_HEADER_START) - 1); str_p += sizeof(TPL_NPH_HTTP_HEADER_START) - 1; M_INT_TO_STRING(str_p, length_digits, tmp_len, 0); str_p += length_digits; (void)memcpy(str_p, TPL_NPH_HTTP_HEADER_END, sizeof(TPL_NPH_HTTP_HEADER_END) - 1); str_p += sizeof(TPL_NPH_HTTP_HEADER_END) - 1; tpl_get_content(tpl, str_p); content_len = write(fd, response, content_len + (str_p - response)); acl_myfree(response); if (content_len < 0) return TPL_WRITE_ERROR; return TPL_OK; } void tpl_out(tpl_t *tpl, ACL_VSTREAM *out) { char *buf; int n; if (tpl == NULL) return; if (out == NULL) out = ACL_VSTREAM_OUT; n = tpl_length(tpl); if (n <= 0) return; buf = (char*) acl_mymalloc(n + 1); tpl_get_content(tpl, buf); acl_vstream_writen(out, buf, n); acl_myfree(buf); }