]> git.seodisparate.com - c_simple_http/commitdiff
Impl. extension of template delimeter expressions
authorStephen Seo <seo.disparate@gmail.com>
Mon, 18 Nov 2024 08:07:07 +0000 (17:07 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Mon, 18 Nov 2024 08:07:07 +0000 (17:07 +0900)
Implemented "IF", "ELSEIF", "ELSE", "ENDIF", and "INDEX" for templates.

IF is used like: `{{{!IF Variable==SomeString}}}`.
ELSEIF is used like: `{{{!ELSEIF Variable==AnotherString}}}`.
ELSE is used like: `{{{!ELSE}}}`.
ENDIF is used like: `{{{!ENDIF}}}`.
INDEX is used like: `{{{!INDEX ArrayVar[2]}}}`.

example_config/example.config
src/helpers.c
src/helpers.h
src/http_template.c
src/test.c

index 27540950093168684b8abf177c6cdf2247cd3a3f..454fc39fc9a8845ccced0812f078c27196602e0b 100644 (file)
@@ -28,10 +28,28 @@ HTML='''
   <h1>Test HTML</h1><br>
   <h2>{{{Var}}}</h2>
   <h3><a href="/inner">To inner.</a></h3>
+  <h4>Tests:</h4><br>
+  {{{!IF ThisValue==false}}}
+    ThisValue is <b>false</b>.
+  {{{!ELSEIF ThisValue==true}}}
+    ThisValue is <b>true</b>.
+  {{{!ELSE}}}
+    ThisValue is invalid.
+  {{{!ENDIF}}}
+
+  <h4>Array Value:</h4><br>
+  {{{!INDEX ArrayValue[2]}}}
+  {{{!INDEX ArrayValue[0]}}}
+  {{{!INDEX ArrayValue[1]}}}
   </body>
 '''
 
 Var='''Test var value'''
+ThisValue=derp
+
+ArrayValue=One_IDX_Zero
+ArrayValue=Two_IDX_One
+ArrayValue=Three_IDX_Two
 
 PATH=/inner
 HTML_FILE='''example_config/inner.html'''
index efad799f395ca15592503c698120757784cfdf8a..7abbaee6fb784eafe4e774311d4805dd5785c380 100644 (file)
@@ -17,6 +17,7 @@
 #include "helpers.h"
 
 // Standard library includes.
+#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
@@ -31,7 +32,9 @@ int c_simple_http_internal_get_string_part_full_size(void *data, void *ud) {
   C_SIMPLE_HTTP_String_Part *part = data;
   size_t *count = ud;
 
-  *count += part->size - 1;
+  if (part->size > 0) {
+    *count += part->size - 1;
+  }
 
   return 0;
 }
@@ -86,9 +89,36 @@ void c_simple_http_add_string_part(
   C_SIMPLE_HTTP_String_Part *string_part =
     malloc(sizeof(C_SIMPLE_HTTP_String_Part));
 
-  string_part->size = strlen(c_string) + 1;
-  string_part->buf = malloc(string_part->size);
-  memcpy(string_part->buf, c_string, string_part->size);
+  if (c_string) {
+    string_part->size = strlen(c_string) + 1;
+    string_part->buf = malloc(string_part->size);
+    memcpy(string_part->buf, c_string, string_part->size);
+  } else {
+    string_part->size = 0;
+    string_part->buf = NULL;
+  }
+
+  string_part->extra = extra;
+  simple_archiver_list_add(
+    list, string_part, c_simple_http_cleanup_string_part);
+}
+
+void c_simple_http_add_string_part_sized(
+    SDArchiverLinkedList *list,
+    const char *buffer,
+    size_t size,
+    uintptr_t extra) {
+  C_SIMPLE_HTTP_String_Part *string_part =
+    malloc(sizeof(C_SIMPLE_HTTP_String_Part));
+
+  if (buffer && size > 0) {
+    string_part->size = size;
+    string_part->buf = malloc(string_part->size - 1);
+    memcpy(string_part->buf, buffer, string_part->size - 1);
+  } else {
+    string_part->size = 0;
+    string_part->buf = NULL;
+  }
 
   string_part->extra = extra;
 
@@ -264,4 +294,66 @@ void c_simple_http_cleanup_DIR(DIR **fd) {
   }
 }
 
+char *c_simple_http_FILE_to_c_str(const char *filename, uint64_t *size_out) {
+  FILE *fd = fopen(filename, "rb");
+  if (!fd) {
+    fprintf(stderr, "ERROR Failed to open %s!\n", filename);
+    return NULL;
+  } else if (fseek(fd, 0, SEEK_END) != 0) {
+    fprintf(stderr, "ERROR Failed to seek to end of %s!\n", filename);
+    fclose(fd);
+    return NULL;
+  }
+
+  long size = ftell(fd);
+  if (size < 0) {
+    fprintf(stderr, "ERROR Failed to get seek pos of end of %s!\n", filename);
+    fclose(fd);
+    return NULL;
+  } else if (size == 0) {
+    fprintf(stderr, "ERROR Size of file \"%s\" is zero!\n", filename);
+    fclose(fd);
+    return NULL;
+  }
+  if (size_out) {
+    *size_out = (uint64_t)size;
+  }
+  char *buf = malloc((uint64_t)size + 1);
+  if (fseek(fd, 0, SEEK_SET) != 0) {
+    fprintf(stderr, "ERROR Failed to seek to beginning of %s!\n", filename);
+    free(buf);
+    fclose(fd);
+    return NULL;
+  } else if (fread(buf, 1, (uint64_t)size, fd) != (uint64_t)size) {
+    fprintf(stderr, "ERROR Failed to read from file %s!\n", filename);
+    free(buf);
+    fclose(fd);
+    return NULL;
+  }
+
+  buf[size] = 0;
+
+  fclose(fd);
+  return buf;
+}
+
+size_t c_simple_http_trim_end_whitespace(char *c_str) {
+  size_t trimmed = 0;
+
+  uint64_t idx= strlen(c_str);
+  for (; idx-- > 0;) {
+    if (c_str[idx] == ' '
+        || c_str[idx] == '\n'
+        || c_str[idx] == '\r'
+        || c_str[idx] == '\t') {
+      c_str[idx] = 0;
+      ++trimmed;
+    } else {
+      break;
+    }
+  }
+
+  return trimmed;
+}
+
 // vim: et ts=2 sts=2 sw=2
index 0039f635b70bf6c67ce3e146662729e3409556e0..cbe951f5584d0c1a683425a8903e8a9f2e994d0f 100644 (file)
@@ -56,6 +56,16 @@ void c_simple_http_cleanup_string_part(void *data);
 void c_simple_http_add_string_part(
   SDArchiverLinkedList *list, const char *c_string, uintptr_t extra);
 
+/// Puts a malloced instance of String_Part into the list.
+/// The given c_string will be copied into a newly malloced buffer.
+/// "size" must include NULL if "buffer" is a c_string.
+/// If there is no NULL at the end, "size" must be +1 actual size.
+void c_simple_http_add_string_part_sized(
+  SDArchiverLinkedList *list,
+  const char *buffer,
+  size_t size,
+  uintptr_t extra);
+
 /// Combines all String_Parts in the list and returns it as a single buffer.
 char *c_simple_http_combine_string_parts(const SDArchiverLinkedList *list);
 
@@ -80,6 +90,13 @@ int c_simple_http_helper_mkdir_tree(const char *dirpath);
 
 void c_simple_http_cleanup_DIR(DIR **fd);
 
+/// Must be free'd if non-NULL.
+char *c_simple_http_FILE_to_c_str(const char *filename, uint64_t *size_out);
+
+/// Trims by placing NULL bytes in place of whitespace at the end of c_str.
+/// Returns number of whitespace trimmed.
+size_t c_simple_http_trim_end_whitespace(char *c_str);
+
 #endif
 
 // vim: et ts=2 sts=2 sw=2
index 6d009f8c4acdf053858d5f2c3518e02a879c1f57..bf797aed1f7cf36c5687119674437ef19c1b194f 100644 (file)
@@ -49,83 +49,479 @@ int c_simple_http_internal_ends_with_FILE(const char *c_string) {
   return 2;
 }
 
+int c_simple_http_internal_parse_if_expression(
+    const C_SIMPLE_HTTP_ParsedConfig *wrapped_hash_map,
+    const char *var,
+    const size_t var_offset,
+    const size_t var_size,
+    char **left_side_out,
+    char **right_side_out,
+    uint_fast8_t *is_equality_out,
+    SDArchiverLinkedList **files_list_out) {
+  if (!left_side_out || !right_side_out || !is_equality_out) {
+    fprintf(stderr, "ERROR Internal error! %s\n", var);
+    return 1;
+  }
+  *left_side_out = NULL;
+  *right_side_out = NULL;
+
+  __attribute__((cleanup(simple_archiver_list_free)))
+  SDArchiverLinkedList *var_parts = simple_archiver_list_init();
+  char buf[64];
+  size_t buf_idx = 0;
+  size_t idx = var_offset;
+  ssize_t var_index = -1;
+  for(; idx < var_size; ++idx) {
+    if (idx + 1 < var_size && var[idx] == '!' && var[idx + 1] == '=') {
+      break;
+    } else if (idx + 1 < var_size && var[idx] == '=' && var[idx + 1] == '=') {
+      break;
+    } else if (var[idx] == '[') {
+      if (var_index >= 0) {
+        fprintf(stderr, "ERROR \"[\" Encountered twice! %s\n", var);
+        return 1;
+      }
+      idx += 1;
+      var_index = 0;
+      for(; idx < var_size; ++idx) {
+        if (var[idx] == ']') {
+          break;
+        } else if (var[idx] >= '0' && var[idx] <= '9') {
+          var_index = var_index * 10 + ((ssize_t)(var[idx] - '0'));
+        } else {
+          fprintf(stderr, "ERROR Syntax error in \"[...]\"! %s\n", var);
+          return 1;
+        }
+      }
+      if (idx >= var_size) {
+        fprintf(
+          stderr, "ERROR End of expression during parsing! %s\n", var);
+        return 1;
+      } else if (var[idx] != ']') {
+        fprintf(stderr, "ERROR No closing \"]\"! %s\n", var);
+        return 1;
+      } else if (
+          idx + 1 < var_size && var[idx] == '!' && var[idx + 1] == '=') {
+        break;
+      } else if (
+          idx + 1 < var_size && var[idx] == '=' && var[idx + 1] == '=') {
+        break;
+      } else {
+        fprintf(stderr, "ERROR Invalid expression after \"]\"! %s\n", var);
+        return 1;
+      }
+    }
+    buf[buf_idx++] = var[idx];
+    if (buf_idx >= 63) {
+      buf[63] = 0;
+      c_simple_http_add_string_part(var_parts, buf, 0);
+      buf_idx = 0;
+    }
+  }
+
+  if (buf_idx == 0) {
+    fprintf(stderr, "ERROR Empty VAR in expression! %s\n", var);
+    return 1;
+  } else if (idx >= var_size) {
+    fprintf(stderr, "ERROR Invalid \"IF\" expression! %s\n", var);
+    return 1;
+  }
+
+  __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+  char *var_buf = NULL;
+  if (var_parts->count != 0) {
+    if (buf_idx != 0) {
+      buf[buf_idx] = 0;
+      c_simple_http_add_string_part(var_parts, buf, 0);
+    }
+    var_buf = c_simple_http_combine_string_parts(var_parts);
+  } else {
+    buf[buf_idx] = 0;
+    var_buf = strdup(buf);
+  }
+  buf_idx = 0;
+  simple_archiver_list_free(&var_parts);
+  var_parts = simple_archiver_list_init();
+
+  C_SIMPLE_HTTP_ConfigValue *config_value =
+    simple_archiver_hash_map_get(wrapped_hash_map->hash_map,
+                                 var_buf,
+                                 strlen(var_buf) + 1);
+  if (!config_value || !config_value->value) {
+    fprintf(stderr, "ERROR Invalid VAR after \"IF/ELSEIF\"! %s\n", var);
+    return 1;
+  } else if (var_index >= 0) {
+    for (size_t var_index_idx = 0;
+         var_index_idx < var_index;
+         ++var_index_idx) {
+      config_value = config_value->next;
+      if (!config_value) {
+        fprintf(stderr, "ERROR Array index out of bounds! %s\n", var);
+        return 1;
+      }
+    }
+  }
+
+  uint64_t left_side_size = 0;
+  if (c_simple_http_internal_ends_with_FILE(var_buf) == 0) {
+    *left_side_out =
+      c_simple_http_FILE_to_c_str(config_value->value, &left_side_size);
+    if (!*left_side_out || left_side_size == 0) {
+      fprintf(stderr, "ERROR _FILE variable could not be read! %s\n", var);
+      if (*left_side_out) {
+        free(*left_side_out);
+        *left_side_out = NULL;
+      }
+      return 1;
+    }
+    c_simple_http_trim_end_whitespace(*left_side_out);
+    left_side_size = strlen(*left_side_out);
+    if (files_list_out) {
+      simple_archiver_list_add(*files_list_out,
+                               strdup(config_value->value),
+                               NULL);
+    }
+  } else {
+    *left_side_out = strdup(config_value->value);
+    c_simple_http_trim_end_whitespace(*left_side_out);
+    left_side_size = strlen(*left_side_out);
+  }
+
+  if (idx + 1 < var_size && var[idx] == '=' && var[idx + 1] == '=') {
+    *is_equality_out = 1;
+  } else if (idx + 1 < var_size && var[idx] == '!' && var[idx] == '=') {
+    *is_equality_out = 0;
+  } else {
+    fprintf(stderr, "ERROR Operation not n/eq! %s\n", var);
+    return 1;
+  }
+
+  idx += 2;
+
+  for (; idx < var_size; ++idx) {
+    buf[buf_idx++] = var[idx];
+    if (buf_idx >= 63) {
+      buf[63] = 0;
+      c_simple_http_add_string_part(var_parts, buf, 0);
+      buf_idx = 0;
+    }
+  }
+
+  if (var_parts->count != 0) {
+    if (buf_idx != 0) {
+      buf[buf_idx] = 0;
+      c_simple_http_add_string_part(var_parts, buf, 0);
+    }
+    *right_side_out = c_simple_http_combine_string_parts(var_parts);
+  } else {
+    buf[buf_idx] = 0;
+    *right_side_out = strdup(buf);
+  }
+  c_simple_http_trim_end_whitespace(*right_side_out);
+  uint64_t right_side_size = strlen(*right_side_out);
+  if (right_side_size == 0) {
+    fprintf(stderr, "ERROR Right side is empty! %s\n", var);
+    return 1;
+  }
+  simple_archiver_list_free(&var_parts);
+  var_parts = simple_archiver_list_init();
+  buf_idx = 0;
+  return 0;
+}
+
 /// Returns zero on success.
 int c_simple_http_internal_handle_inside_delimeters(
-    const size_t idx,
+    uint32_t *state,
+    const size_t html_buf_idx,
     const char *var,
     const size_t var_size,
     const C_SIMPLE_HTTP_ParsedConfig *wrapped_hash_map,
     SDArchiverLinkedList *string_part_list,
     SDArchiverLinkedList **files_list_out) {
   C_SIMPLE_HTTP_String_Part string_part;
-  C_SIMPLE_HTTP_ConfigValue *config_value =
-    simple_archiver_hash_map_get(
-      wrapped_hash_map->hash_map,
-      var,
-      (uint32_t)var_size + 1);
-  if (config_value && config_value->value) {
-    if (c_simple_http_internal_ends_with_FILE(var) == 0) {
-      // Load from file specified by "config_value->value".
-      __attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
-      FILE *f = fopen(config_value->value, "r");
-      if (!f) {
-        fprintf(stderr, "ERROR Failed to open file \"%s\"!\n",
-                config_value->value);
+  if (var_size == 0) {
+    fprintf(stderr, "ERROR No characters within delimeters!\n");
+    return 1;
+  } else if (var[0] == '!') {
+    // Is an expression.
+    if (strncmp(var + 1, "IF ", 3) == 0) {
+      if (((*state) & 0xE) != 0) {
+        fprintf(stderr, "ERROR Invalid \"IF\" (bad state)! %s\n", var);
         return 1;
-      } else if (fseek(f, 0, SEEK_END) != 0) {
-        fprintf(stderr, "ERROR Failed to seek to end of file \"%s\"!\n",
-                config_value->value);
+      }
+
+      __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+      char *left_side = NULL;
+      __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+      char *right_side = NULL;
+
+      uint_fast8_t is_equality = 0;
+
+      if (c_simple_http_internal_parse_if_expression(
+          wrapped_hash_map,
+          var,
+          1 + 3,
+          var_size,
+          &left_side,
+          &right_side,
+          &is_equality,
+          files_list_out)) {
+        return 1;
+      } else if (!left_side || !right_side) {
+        fprintf(stderr, "ERROR Internal error! %s\n", var);
+        return 1;
+      }
+
+      if (is_equality) {
+        if (strcmp(left_side, right_side) == 0) {
+          // Is equality is TRUE.
+          (*state) |= 0x12;
+        } else {
+          // Is equality is FALSE.
+          (*state) |= 4;
+        }
+      } else {
+        if (strcmp(left_side, right_side) == 0) {
+          // Is inequality is FALSE.
+          (*state) |= 4;
+        } else {
+          // Is inequality is TRUE.
+          (*state) |= 0x12;
+        }
+      }
+      c_simple_http_add_string_part(string_part_list, NULL, html_buf_idx + 1);
+    } else if (strncmp(var + 1, "ELSEIF ", 7) == 0) {
+      if (((*state) & 0xE) != 2 && ((*state) & 0xE) != 4) {
+        fprintf(stderr, "ERROR Invalid \"ELSEIF\" (bad state)! %s\n", var);
         return 1;
       }
-      long file_size = ftell(f);
-      if (file_size <= 0) {
-        fprintf(stderr, "ERROR Size of file \"%s\" is invalid!\n",
-                config_value->value);
+
+      __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+      char *left_side = NULL;
+      __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+      char *right_side = NULL;
+
+      uint_fast8_t is_equality = 0;
+
+      if (c_simple_http_internal_parse_if_expression(
+          wrapped_hash_map,
+          var,
+          1 + 7,
+          var_size,
+          &left_side,
+          &right_side,
+          &is_equality,
+          files_list_out)) {
         return 1;
-      } else if (fseek(f, 0, SEEK_SET) != 0) {
-        fprintf(stderr, "ERROR Failed to seek to start of file "
-                "\"%s\"!\n",
-                config_value->value);
+      } else if (!left_side || !right_side) {
+        fprintf(stderr, "ERROR Internal error! %s\n", var);
         return 1;
       }
-      string_part.size = (size_t)file_size + 1;
-      string_part.buf = malloc(string_part.size);
-      string_part.extra = idx + 1;
 
-      if (fread(string_part.buf,
-                string_part.size - 1,
-                1,
-                f)
-            != 1) {
-        fprintf(stderr, "ERROR Failed to read from file \"%s\"!\n",
-                config_value->value);
+      if (((*state) & 0x10) == 0) {
+        if (is_equality) {
+          if (strcmp(left_side, right_side) == 0) {
+            // Is equality is TRUE.
+            (*state) &= 0xFFFFFFE1;
+            (*state) |= 0x16;
+          } else {
+            // Is equality is FALSE.
+            (*state) &= 0xFFFFFFE1;
+            (*state) |= 8;
+          }
+        } else {
+          if (strcmp(left_side, right_side) == 0) {
+            // Is inequality is FALSE.
+            (*state) &= 0xFFFFFFE1;
+            (*state) |= 8;
+          } else {
+            // Is inequality is TRUE.
+            (*state) &= 0xFFFFFFE1;
+            (*state) |= 0x16;
+          }
+        }
+      } else {
+        // Previous IF/ELSEIF resolved true.
+        (*state) &= 0xFFFFFFF1;
+        (*state) |= 8;
+      }
+      c_simple_http_add_string_part(string_part_list, NULL, html_buf_idx + 1);
+    } else if (strncmp(var + 1, "ELSE", 4) == 0) {
+      if (((*state) & 0x10) == 0) {
+        // No previous expression resolved to true, enabling ELSE block.
+        (*state) &= 0xFFFFFFE1;
+        (*state) |= 0xA;
+      } else {
+        // No expression resolved to true, disabling ELSE block.
+        (*state) &= 0xFFFFFFE1;
+        (*state) |= 0xC;
+      }
+      c_simple_http_add_string_part(string_part_list, NULL, html_buf_idx + 1);
+    } else if (strncmp(var + 1, "ENDIF", 5) == 0) {
+      if (((*state) & 0xE) == 0) {
+        fprintf(
+          stderr,
+          "ERROR Invalid expression (no preceeding if/else)! %s\n",
+          var);
+        return 1;
+      }
+      (*state) &= 0xFFFFFFE1;
+      c_simple_http_add_string_part(string_part_list, NULL, html_buf_idx + 1);
+    } else if (strncmp(var + 1, "INDEX ", 6) == 0) {
+      // Indexing into variable array.
+
+      __attribute__((cleanup(simple_archiver_list_free)))
+      SDArchiverLinkedList *var_parts = simple_archiver_list_init();
+      char buf[64];
+      size_t buf_idx = 0;
+
+      size_t idx = 7;
+      for (; idx < var_size; ++idx) {
+        if (var[idx] != '[') {
+          buf[buf_idx++] = var[idx];
+          if (buf_idx >= 63) {
+            buf[63] = 0;
+            c_simple_http_add_string_part(var_parts, buf, 0);
+            buf_idx = 0;
+          }
+        } else {
+          break;
+        }
+      }
+      if (var[idx] != '[' || idx >= var_size) {
+        fprintf(stderr, "ERROR Syntax error! %s\n", var);
         return 1;
       }
-      string_part.buf[string_part.size - 1] = 0;
-      if (files_list_out) {
-        char *variable_filename = malloc(strlen(config_value->value) + 1);
-        strcpy(variable_filename, config_value->value);
-        simple_archiver_list_add(
-            *files_list_out, variable_filename, NULL);
+      __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+      char *var_name = NULL;
+      if (var_parts->count != 0) {
+        if (buf_idx != 0) {
+          buf[buf_idx] = 0;
+          c_simple_http_add_string_part(var_parts, buf, 0);
+          buf_idx = 0;
+        }
+        var_name = c_simple_http_combine_string_parts(var_parts);
+      } else if (buf_idx != 0) {
+        buf[buf_idx] = 0;
+        var_name = strdup(buf);
+      } else {
+        fprintf(stderr, "ERROR Empty variable name string! %s\n", var);
+        return 1;
       }
+      C_SIMPLE_HTTP_ConfigValue *config_value =
+        simple_archiver_hash_map_get(wrapped_hash_map->hash_map,
+                                     var_name,
+                                     strlen(var_name) + 1);
+      if (!config_value) {
+        fprintf(stderr, "ERROR Variable not found in config! %s\n", var);
+        return 1;
+      }
+
+      idx += 1;
+      ssize_t array_index = 0;
+      for (; idx < var_size; ++idx) {
+        if (var[idx] >= '0' && var[idx] <= '9') {
+          array_index = array_index * 10 + (ssize_t)(var[idx] - '0');
+        } else if (var[idx] == ']') {
+          break;
+        } else {
+          fprintf(stderr, "ERROR Syntax error getting index! %s\n", var);
+          return 1;
+        }
+      }
+      if (idx >= var_size) {
+        fprintf(stderr, "ERROR Syntax error getting index (reached end without "
+            "reaching \"]\")! %s\n", var);
+        return 1;
+      }
+      for (; array_index-- > 0;) {
+        config_value = config_value->next;
+        if (!config_value) {
+          fprintf(stderr, "ERROR Array index out of bounds! %s\n", var);
+          return 1;
+        }
+      }
+      if (!config_value || !config_value->value) {
+        fprintf(
+          stderr, "ERROR Internal error invalid indexed value! %s\n", var);
+        return 1;
+      }
+
+      __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+      char *value_contents = NULL;
+      uint64_t value_contents_size = 0;
+      if (c_simple_http_internal_ends_with_FILE(var_name) == 0) {
+        value_contents = c_simple_http_FILE_to_c_str(
+          config_value->value, &value_contents_size);
+        if (!value_contents || value_contents_size == 0) {
+          fprintf(stderr, "ERROR Failed to get value from FILE! %s\n", var);
+          return 1;
+        }
+        if (files_list_out) {
+          simple_archiver_list_add(*files_list_out,
+                                   strdup(config_value->value),
+                                   NULL);
+        }
+      } else {
+        value_contents = strdup(config_value->value);
+        value_contents_size = strlen(value_contents);
+      }
+
+      c_simple_http_add_string_part_sized(string_part_list,
+                                          value_contents,
+                                          value_contents_size + 1,
+                                          html_buf_idx + 1);
     } else {
-      // Variable data is "config_value->value".
-      string_part.size = strlen(config_value->value) + 1;
-      string_part.buf = malloc(string_part.size);
-      memcpy(string_part.buf, config_value->value, string_part.size);
-      string_part.buf[string_part.size - 1] = 0;
-      string_part.extra = idx + 1;
+      fprintf(stderr, "ERROR Invalid expression! %s\n", var);
+      return 1;
     }
   } else {
-    string_part.buf = NULL;
-    string_part.size = 0;
-    string_part.extra = idx + 1;
-  }
-  c_simple_http_add_string_part(string_part_list,
-                                string_part.buf,
-                                string_part.extra);
-  if (string_part.buf) {
-    free(string_part.buf);
+    // Refers to a variable by name.
+    C_SIMPLE_HTTP_ConfigValue *config_value =
+      simple_archiver_hash_map_get(
+        wrapped_hash_map->hash_map,
+        var,
+        (uint32_t)var_size + 1);
+    if (config_value && config_value->value) {
+      if (c_simple_http_internal_ends_with_FILE(var) == 0) {
+        // Load from file specified by "config_value->value".
+        uint64_t size = 0;
+        string_part.buf = c_simple_http_FILE_to_c_str(config_value->value, &size);
+        if (!string_part.buf || size == 0) {
+          fprintf(stderr, "ERROR Failed to read from file \"%s\"!\n",
+                  config_value->value);
+          if (string_part.buf) {
+            free(string_part.buf);
+          }
+          return 1;
+        }
+        string_part.size = size + 1;
+        string_part.extra = html_buf_idx + 1;
+
+        string_part.buf[string_part.size - 1] = 0;
+        if (files_list_out) {
+          char *variable_filename = strdup(config_value->value);
+          simple_archiver_list_add(*files_list_out, variable_filename, NULL);
+        }
+      } else {
+        // Variable data is "config_value->value".
+        string_part.size = strlen(config_value->value) + 1;
+        string_part.buf = malloc(string_part.size);
+        memcpy(string_part.buf, config_value->value, string_part.size);
+        string_part.buf[string_part.size - 1] = 0;
+        string_part.extra = html_buf_idx + 1;
+      }
+    } else {
+      string_part.buf = NULL;
+      string_part.size = 0;
+      string_part.extra = html_buf_idx + 1;
+    }
+    c_simple_http_add_string_part(string_part_list,
+                                  string_part.buf,
+                                  string_part.extra);
+    if (string_part.buf) {
+      free(string_part.buf);
+    }
   }
 
   return 0;
@@ -211,6 +607,13 @@ char *c_simple_http_path_to_generated(
 
   // xxxx xxx0 - Initial state, no delimeter reached.
   // xxxx xxx1 - Three left-curly-brace delimeters reached.
+  // xxxx 001x - If expression ALLOW contents.
+  // xxxx 010x - If expression DISALLOW contents.
+  // xxxx 011x - ElseIf expression ALLOW contents.
+  // xxxx 100x - ElseIf expression DISALLOW contents.
+  // xxxx 101x - Else expression ALLOW contents.
+  // xxxx 110x - Else expression DISALLOW contents.
+  // xxx1 xxxx - Previous If/ElseIf had true/ALLOW.
   uint32_t state = 0;
 
   for (; idx < html_buf_size; ++idx) {
@@ -221,23 +624,30 @@ char *c_simple_http_path_to_generated(
         if (delimeter_count >= 3) {
           delimeter_count = 0;
           state |= 1;
-          if (string_part_list->count != 0) {
-            C_SIMPLE_HTTP_String_Part *last_part =
-              string_part_list->tail->prev->data;
-            last_template_idx = last_part->extra;
+          if ((state & 0xE) == 0
+              || (state & 0xE) == 2
+              || (state & 0xE) == 6
+              || (state & 0xE) == 0xA) {
+            if (string_part_list->count != 0) {
+              C_SIMPLE_HTTP_String_Part *last_part =
+                string_part_list->tail->prev->data;
+              last_template_idx = last_part->extra;
+            }
+            string_part.size = idx - last_template_idx - 1;
+            string_part.buf = malloc(string_part.size);
+            memcpy(
+              string_part.buf,
+              html_buf + last_template_idx,
+              string_part.size);
+            string_part.buf[string_part.size - 1] = 0;
+            string_part.extra = idx + 1;
+            c_simple_http_add_string_part(string_part_list,
+                                          string_part.buf,
+                                          string_part.extra);
+            free(string_part.buf);
+          } else {
+            c_simple_http_add_string_part(string_part_list, NULL, idx + 1);
           }
-          string_part.size = idx - last_template_idx - 1;
-          string_part.buf = malloc(string_part.size);
-          memcpy(
-            string_part.buf,
-            html_buf + last_template_idx,
-            string_part.size);
-          string_part.buf[string_part.size - 1] = 0;
-          string_part.extra = idx + 1;
-          c_simple_http_add_string_part(string_part_list,
-                                        string_part.buf,
-                                        string_part.extra);
-          free(string_part.buf);
         }
       } else {
         delimeter_count = 0;
@@ -261,6 +671,7 @@ char *c_simple_http_path_to_generated(
             var_size);
           var[var_size] = 0;
           if (c_simple_http_internal_handle_inside_delimeters(
+                &state,
                 idx,
                 var,
                 var_size,
index 0699574d46bee2385a282180d39483d56d3cc4d1..9034a16c946cf5fc0740f09383c681bd86df29ea 100644 (file)
@@ -620,6 +620,46 @@ int main(int argc, char **argv) {
     CHECK_TRUE(ret == 0);
     CHECK_TRUE(ret2 == 0);
     CHECK_TRUE(ret3 == 0);
+
+    buf = strdup("EndingWithThreeTabs\t\t\t");
+    CHECK_TRUE(c_simple_http_trim_end_whitespace(buf) == 3);
+    CHECK_TRUE(buf[18] == 's');
+    CHECK_TRUE(buf[19] == 0);
+    CHECK_TRUE(buf[20] == 0);
+    CHECK_TRUE(buf[21] == 0);
+    CHECK_TRUE(buf[22] == 0);
+    free(buf);
+    buf = NULL;
+
+    buf = strdup("EndingWithThreeSpaces   ");
+    CHECK_TRUE(c_simple_http_trim_end_whitespace(buf) == 3);
+    CHECK_TRUE(buf[20] == 's');
+    CHECK_TRUE(buf[21] == 0);
+    CHECK_TRUE(buf[22] == 0);
+    CHECK_TRUE(buf[23] == 0);
+    CHECK_TRUE(buf[24] == 0);
+    free(buf);
+    buf = NULL;
+
+    buf = strdup("EndingWithThreeNewlines\n\n\n");
+    CHECK_TRUE(c_simple_http_trim_end_whitespace(buf) == 3);
+    CHECK_TRUE(buf[22] == 's');
+    CHECK_TRUE(buf[23] == 0);
+    CHECK_TRUE(buf[24] == 0);
+    CHECK_TRUE(buf[25] == 0);
+    CHECK_TRUE(buf[26] == 0);
+    free(buf);
+    buf = NULL;
+
+    buf = strdup("EndingWithThreeCarraigeReturns\r\r\r");
+    CHECK_TRUE(c_simple_http_trim_end_whitespace(buf) == 3);
+    CHECK_TRUE(buf[29] == 's');
+    CHECK_TRUE(buf[30] == 0);
+    CHECK_TRUE(buf[31] == 0);
+    CHECK_TRUE(buf[32] == 0);
+    CHECK_TRUE(buf[33] == 0);
+    free(buf);
+    buf = NULL;
   }
 
   // Test html_cache.