libs/web: introduce recursive expression support for datatypes, introduce "or" and...
[project/luci.git] / libs / web / src / template_parser.c
index 58de5bb..a0a400b 100644 (file)
 
 
 /* leading and trailing code for different types */
 
 
 /* leading and trailing code for different types */
-const char * gen_code[6][2] = {
-       { "write(\"",                   "\")"   },
-       { NULL,                                 NULL    },
-       { "write(tostring(",    "))"    },
-       { "include(\"",                 "\")"   },
-       { "write(translate(\"", "\"))"  },
-       { NULL,                                 " "             }
+const char * gen_code[7][2] = {
+       { "write(\"",                   "\")"                   },
+       { NULL,                                 NULL                    },
+       { "write(tostring(",    " or \"\"))"    },
+       { "include(\"",                 "\")"                   },
+       { "write(pcdata(translate(\"",  "\")))" },
+       { "write(translate(\"", "\"))"                  },
+       { NULL,                                 " "                             }
 };
 
 /* Simple strstr() like function that takes len arguments for both haystack and needle. */
 };
 
 /* Simple strstr() like function that takes len arguments for both haystack and needle. */
@@ -58,12 +59,12 @@ static char *strfind(char *haystack, int hslen, const char *needle, int ndlen)
        return NULL;
 }
 
        return NULL;
 }
 
-/* 
- * Inspect current read buffer and find the number of "vague" characters at the end 
+/*
+ * Inspect current read buffer and find the number of "vague" characters at the end
  * which could indicate an opening token. Returns the number of "vague" chars.
  * The last continuous sequence of whitespace, optionally followed by a "<" is
  * treated as "vague" because whitespace may be discarded if the upcoming opening
  * which could indicate an opening token. Returns the number of "vague" chars.
  * The last continuous sequence of whitespace, optionally followed by a "<" is
  * treated as "vague" because whitespace may be discarded if the upcoming opening
- * token indicates pre-whitespace-removal ("<%-"). A single remaining "<" char 
+ * token indicates pre-whitespace-removal ("<%-"). A single remaining "<" char
  * can't be differentiated from an opening token ("<%"), so it's kept to be processed
  * in the next cycle.
  */
  * can't be differentiated from an opening token ("<%"), so it's kept to be processed
  * in the next cycle.
  */
@@ -127,7 +128,7 @@ static const char * generate_expression(struct template_parser *data, size_t *sz
        int i;
        int size = 0;
        int start = 0;
        int i;
        int size = 0;
        int start = 0;
-       int i18n_hasdef = 0;
+       int whitespace = 0;
 
        memset(tmp, 0, T_OUTBUFSZ);
 
 
        memset(tmp, 0, T_OUTBUFSZ);
 
@@ -142,31 +143,35 @@ static const char * generate_expression(struct template_parser *data, size_t *sz
        for( i = 0; i < data->outsize; i++ )
        {
                /* Skip leading whitespace for non-raw and non-expr chunks */
        for( i = 0; i < data->outsize; i++ )
        {
                /* Skip leading whitespace for non-raw and non-expr chunks */
-               if( !start && isspace(data->out[i]) && (data->type == T_TYPE_I18N || data->type == T_TYPE_INCLUDE) )
+               if( !start && isspace(data->out[i]) && (data->type == T_TYPE_I18N ||
+           data->type == T_TYPE_I18N_RAW || data->type == T_TYPE_INCLUDE) )
                        continue;
                else if( !start )
                        start = 1;
 
                /* Found whitespace after i18n key */
                        continue;
                else if( !start )
                        start = 1;
 
                /* Found whitespace after i18n key */
-               if( (data->type == T_TYPE_I18N) && (i18n_hasdef == 1) )
+               if( data->type == T_TYPE_I18N || data->type == T_TYPE_I18N_RAW )
                {
                {
-                       /* At non-whitespace char, inject seperator token */
-                       if( !isspace(data->out[i]) )
+                       /* Is initial whitespace, insert space */
+                       if( !whitespace && isspace(data->out[i]) )
                        {
                        {
-                               memcpy(&tmp[size], T_TOK_I18NSEP, strlen(T_TOK_I18NSEP));
-                               size += strlen(T_TOK_I18NSEP);
-                               i18n_hasdef = 2;
+                               tmp[size++] = ' ';
+                               whitespace = 1;
                        }
 
                        }
 
-                       /* At further whitespace, skip */
-                       else
+                       /* Suppress subsequent whitespace, escape special chars */
+                       else if( !isspace(data->out[i]) )
                        {
                        {
-                               continue;
+                               if( data->out[i] == '\\' || data->out[i] == '"' )
+                                       tmp[size++] = '\\';
+
+                               tmp[size++] = data->out[i];
+                               whitespace = 0;
                        }
                }
 
                        }
                }
 
-               /* Escape quotes, backslashes and newlines for plain, i18n and include expressions */
-               if( (data->type == T_TYPE_TEXT || data->type == T_TYPE_I18N || data->type == T_TYPE_INCLUDE) &&
+               /* Escape quotes, backslashes and newlines for plain and include expressions */
+               else if( (data->type == T_TYPE_TEXT || data->type == T_TYPE_INCLUDE) &&
                    (data->out[i] == '\\' || data->out[i] == '"' || data->out[i] == '\n' || data->out[i] == '\t') )
                {
                        tmp[size++] = '\\';
                    (data->out[i] == '\\' || data->out[i] == '"' || data->out[i] == '\n' || data->out[i] == '\t') )
                {
                        tmp[size++] = '\\';
@@ -186,12 +191,6 @@ static const char * generate_expression(struct template_parser *data, size_t *sz
                        }
                }
 
                        }
                }
 
-               /* Found whitespace in i18n expression, raise flag */
-               else if( isspace(data->out[i]) && (data->type == T_TYPE_I18N) )
-               {
-                       i18n_hasdef = 1;
-               }
-
                /* Normal char */
                else
                {
                /* Normal char */
                else
                {
@@ -199,16 +198,14 @@ static const char * generate_expression(struct template_parser *data, size_t *sz
                }
        }
 
                }
        }
 
-       /* Processed i18n expression without default text, inject separator */
-       if( (data->type == T_TYPE_I18N) && (i18n_hasdef < 2) )
-       {
-               memcpy(&tmp[size], T_TOK_I18NSEP, strlen(T_TOK_I18NSEP));
-               size += strlen(T_TOK_I18NSEP);
-       }
-
        /* Inject trailing expression code (if any) */
        if( (what & T_GEN_END) && (gen_code[data->type][1] != NULL) )
        {
        /* Inject trailing expression code (if any) */
        if( (what & T_GEN_END) && (gen_code[data->type][1] != NULL) )
        {
+               /* Strip trailing space for i18n expressions */
+               if( data->type == T_TYPE_I18N || data->type == T_TYPE_I18N_RAW )
+                       if( (size > 0) && (tmp[size-1] == ' ') )
+                               size--;
+
                memcpy(&tmp[size], gen_code[data->type][1], strlen(gen_code[data->type][1]));
                size += strlen(gen_code[data->type][1]);
        }
                memcpy(&tmp[size], gen_code[data->type][1], strlen(gen_code[data->type][1]));
                size += strlen(gen_code[data->type][1]);
        }
@@ -403,6 +400,11 @@ const char *template_reader(lua_State *L, void *ud, size_t *sz)
                                                data->type = T_TYPE_I18N;
                                                break;
 
                                                data->type = T_TYPE_I18N;
                                                break;
 
+                                       case '_':
+                                               off++;
+                                               data->type = T_TYPE_I18N_RAW;
+                                               break;
+
                                        default:
                                                data->type = T_TYPE_CODE;
                                                break;
                                        default:
                                                data->type = T_TYPE_CODE;
                                                break;