/** * @short Parses a block variable and writes the code necesary. * * It can go deep inside a dict or list, and apply filters. */ void variable_write(parser_status *st, onion_block *b){ function_add_code(st, " {\n" " const char *tmp;\n"); variable_solve(st, onion_block_data(b), "tmp", STRING); function_add_code(st, " if (tmp)\n" " onion_response_write_html_safe(res, tmp);\n" " }\n"); }
/// Ends a for void tag_endfor(parser_status *st, list *l){ // First the preorder function function_data *d=function_pop(st); function_add_code(st, " %s(dr->dict, dr->res);\n", d->id); // Now the normal code d=function_pop(st); function_add_code(st, "%s, &dr);\n" " }\n" " onion_dict_free(tmpcontext);\n" " }\n", d->id); }
/** * @short One block read from in, prepare the output. * * Depending on the mode of the block it calls the appropiate handler: variable, tag or just write text. */ void write_block(parser_status *st, onion_block *b){ int mode=st->last_wmode; //ONION_DEBUG("Write mode %d, code %s", mode, b->data); switch(mode){ case TEXT: { int oldl; if ( (oldl=onion_block_size(b)) ){ if (oldl>0){ // Not Just \0 int use_orig_line_numbers_bak=use_orig_line_numbers; use_orig_line_numbers=0; char *safe=onion_c_quote_new(onion_block_data(b)); function_add_code(st, " onion_response_write(res, "); int pos=0; int size=strlen(safe); char *s=safe; //ONION_DEBUG("------- pos %d, size %d",pos,size); while (pos<size){ //ONION_DEBUG("pos %d, size %d",pos,size); char *e=strstr(s, "\n"); if (!e) break; *e='\0'; if (pos==0) function_add_code(st, "%s\n", s); else function_add_code(st, " %s\n", s); pos+=(e-s)+1; s=e+1; } if (pos==0) function_add_code(st, "%s, %d);\n", s, oldl); else function_add_code(st, " %s, %d);\n", s, oldl); use_orig_line_numbers=use_orig_line_numbers_bak; free(safe); } } } break; case VARIABLE: variable_write(st, b); break; case TAG: tag_write(st, b); break; default: ONION_ERROR("Unknown final mode %d", mode); } onion_block_clear(st->rawblock); }
/** * @short One block read from in, prepare the output. * * Depending on the mode of the block it calls the appropiate handler: variable, tag or just write text. */ void write_block(parser_status *st, onion_block *b){ int mode=st->last_wmode; //ONION_DEBUG("Write mode %d, code %s", mode, b->data); switch(mode){ case TEXT: { int oldl; if ( (oldl=onion_block_size(b)) ){ char *safe=onion_c_quote_new(onion_block_data(b)); function_add_code(st, " onion_response_write(res, %s, %d);\n", safe, oldl); free(safe); } } break; case VARIABLE: variable_write(st, b); break; case TAG: tag_write(st, b); break; default: ONION_ERROR("Unknown final mode %d", mode); } onion_block_clear(st->rawblock); }
/// Do the first for part. void tag_for(parser_status *st, list *l){ function_add_code(st, " {\n" " onion_dict *loopdict=onion_dict_get_dict(context, \"%s\");\n", tag_value_arg (l,3)); function_add_code(st, " onion_dict *tmpcontext=onion_dict_hard_dup(context);\n" " if (loopdict){\n" " dict_res dr={ .dict = tmpcontext, .res=res };\n" " onion_dict_preorder(loopdict, "); function_data *d=function_new(st, NULL); d->signature="dict_res *dr, const char *key, const void *value, int flags"; function_add_code(st, " onion_dict_add(dr->dict, \"%s\", value, OD_DUP_VALUE|OD_REPLACE|(flags&OD_TYPE_MASK));\n", tag_value_arg(l,1)); function_new(st, NULL); }
/// Include an external html. This is only the call, the programmer must compile such html too. void tag_include(parser_status* st, list* l){ function_data *d=function_new(st, "%s", tag_value_arg(l, 1)); function_pop(st); onion_block_free(d->code); // This means no impl d->code=NULL; function_add_code(st, " %s(context, res);\n", d->id); }
/// Starts an if void tag_if(parser_status *st, list *l){ int lc=list_count(l); if (lc==2){ function_add_code(st, " {\n" " const char *tmp;\n" ); variable_solve(st, tag_value_arg(l, 1), "tmp", tag_type_arg(l,1)); function_add_code(st, " if (tmp && strcmp(tmp, \"false\")!=0)\n", tag_value_arg(l,1)); } else if (lc==4){ const char *op=tag_value_arg(l, 2); const char *opcmp=NULL; if (strcmp(op,"==")==0) opcmp="==0"; else if (strcmp(op,"<=")==0) opcmp="<=0"; else if (strcmp(op,"<")==0) opcmp="<0"; else if (strcmp(op,">=")==0) opcmp=">=0"; else if (strcmp(op,">")==0) opcmp=">0"; else if (strcmp(op,"!=")==0) opcmp="!=0"; if (opcmp){ function_add_code(st, " {\n" " const char *op1, *op2;\n"); variable_solve(st, tag_value_arg(l, 1), "op1", tag_type_arg(l,1)); variable_solve(st, tag_value_arg(l, 3), "op2", tag_type_arg(l,3)); function_add_code(st, " if (op1==op2 || (op1 && op2 && strcmp(op1, op2)%s))\n",opcmp); } else{ ONION_ERROR("%s:%d Unkonwn operator for if: %s", st->infilename, st->line, op); st->status=1; } } else{ ONION_ERROR("%s:%d If only allows 1 or 3 arguments. TODO. Has %d.", st->infilename, st->line, lc-1); st->status=1; } function_new(st, NULL); }
void tag_extends(parser_status *st, list *l){ function_data *d=function_new(st, "%s", tag_value_arg(l, 1)); function_pop(st); onion_block_free(d->code); d->code=NULL; function_add_code(st, " %s(context, res);", d->id); d=(function_data*)st->function_stack->tail->data; d->flags|=F_NO_MORE_WRITE; }
void tag_block(parser_status *st, list *l){ const char *block_name=tag_value_arg(l, 1); function_add_code(st, " {\n" " void (*f)(onion_dict *context, onion_response *res);\n" " f=(void*)onion_dict_get(context, \"__block_%s__\");\n" " if (f)\n" " f(context, res);\n" " }\n", block_name); char tmp[256]; strncpy(tmp, st->infilename, sizeof(tmp)); function_data *d=function_new(st, "%s__block_%s", basename(tmp), block_name); function_add_code_f(st->blocks_init, " if (!onion_dict_get(context, \"__block_%s__\"))\n" " onion_dict_add(context, \"__block_%s__\", %s, 0);\n", block_name, block_name, d->id); }
/// endif void tag_endif(parser_status *st, list *l){ function_data *d=function_pop(st); function_add_code(st, " %s(context, res);\n }\n", d->id); }
/// Else part void tag_else(parser_status *st, list *l){ function_data *d=function_pop(st); function_add_code(st, " %s(context, res);\n else\n", d->id); function_new(st, NULL); }
/** * @short Compiles the infilename to outfilename. */ int work(const char *infilename, const char *outfilename, onion_assets_file *assets){ tag_init(); parser_status status; memset(&status, 0, sizeof(status)); status.mode=TEXT; status.functions=list_new((void*)function_free); status.function_stack=list_new(NULL); status.status=0; status.line=1; status.rawblock=onion_block_new(); status.infilename=infilename; char tmp2[256]; strncpy(tmp2, infilename, sizeof(tmp2)-1); const char *tname=basename(tmp2); ONION_DEBUG("Create init function on top, tname %s",tname); status.blocks_init=function_new(&status, "%s_blocks_init", tname); status.blocks_init->signature="onion_dict *context"; if (strcmp(infilename, "-")==0) status.in=stdin; else status.in=fopen(infilename,"rt"); if (!status.in){ ONION_ERROR("Could not open in file %s", infilename); goto work_end; } ONION_DEBUG("Create main function on top, tname %s",tname); function_new(&status, tname); function_add_code(&status, " int has_context=(context!=NULL);\n" " if (!has_context)\n" " context=onion_dict_new();\n" " \n" " %s(context);\n", status.blocks_init->id); parse_template(&status); ((function_data*)status.function_stack->tail->data)->flags=0; function_add_code(&status, " if (!has_context)\n" " onion_dict_free(context);\n" ); if (status.status){ ONION_ERROR("Parsing error"); goto work_end; } if (strcmp(outfilename, "-")==0) status.out=stdout; else status.out=fopen(outfilename,"wt"); if (!status.out){ ONION_ERROR("Could not open out file %s", infilename); goto work_end; } fprintf(status.out, "/** Autogenerated by otemplate v. 0.2.0 */\n" "\n" "#include <libintl.h>\n" "#include <string.h>\n\n" "#include <onion/onion.h>\n" "#include <onion/dict.h>\n" "\n" "typedef struct dict_res_t{\n" " onion_dict *dict;\n" " onion_response *res;\n" "}dict_res;\n" "\n" "\n"); functions_write_declarations_assets(&status, assets); functions_write_declarations(&status); functions_write_main_code(&status); if (use_orig_line_numbers) fprintf(status.out, "#line 1 \"%s\"\n", infilename); functions_write_code(&status); work_end: if (status.in) fclose(status.in); if (status.out) fclose(status.out); list_free(status.functions); list_free(status.function_stack); //list_free(status.blocks); onion_block_free(status.rawblock); tag_free(); return status.status; }
/** * @short Solves a variable into code. * * It uses the type to check it its a literal string, a dcit string or a dict. */ void variable_solve(parser_status *st, const char *data, const char *tmpname, vartype_e type){ if (type==LITERAL){ char *s=onion_c_quote_new(data); function_add_code(st, " %s=%s;\n", tmpname, s); free(s); return; } if (! (type==STRING || type==DICT) ){ ONION_ERROR("Invalid type for variable solve"); exit(1); } list *parts=list_new(onion_block_free); onion_block *lastblock; list_add(parts, lastblock=onion_block_new()); int i=0; int l=strlen(data); const char *d=data; for (i=0;i<l;i++){ if (d[i]=='.') list_add(parts, lastblock=onion_block_new()); else if (d[i]==' ') continue; else onion_block_add_char(lastblock, d[i]); } if (list_count(parts)==1){ char *s=onion_c_quote_new(onion_block_data(lastblock)); if (type==STRING) function_add_code(st, " %s=onion_dict_get(context, %s);\n", tmpname, s); else if (type==DICT) function_add_code(st, " %s=onion_dict_get_dict(context, %s);\n", tmpname, s); free(s); } else{ if (type==STRING) function_add_code(st," %s=onion_dict_rget(context", tmpname); else if (type==DICT) function_add_code(st," %s=onion_dict_rget_dict(context", tmpname); else{ ONION_ERROR("Invalid type for variable solve"); exit(1); } list_item *it=parts->head; while (it){ lastblock=it->data; char *s=onion_c_quote_new(onion_block_data(lastblock)); function_add_code(st,", %s", s); free(s); it=it->next; } function_add_code(st,", NULL);\n"); } list_free(parts); }