Main Page | Modules | Data Structures | Directories | File List | Data Fields | Globals

trans_c.c

00001 /*
00002  * Copyright (c) 2005, 2006, 2006 by KoanLogic s.r.l. <http://www.koanlogic.com>
00003  * All rights reserved.
00004  *
00005  * This file is part of KLone, and as such it is subject to the license stated
00006  * in the LICENSE file which you have received as part of this distribution.
00007  *
00008  * $Id: trans_c.c,v 1.35 2007/09/15 16:36:12 tat Exp $
00009  */
00010 
00011 #include "klone_conf.h"
00012 #include <stdio.h>
00013 #include <stdlib.h>
00014 #include <limits.h>
00015 #include <string.h>
00016 #include <u/libu.h>
00017 #include <klone/os.h>
00018 #include <klone/translat.h>
00019 #include <klone/parser.h>
00020 #include <klone/utils.h>
00021 #include <klone/codecs.h>
00022 
00023 struct code_block_s;
00024 
00025 TAILQ_HEAD(code_block_list_s, code_block_s);
00026 struct code_block_s
00027 {
00028     TAILQ_ENTRY(code_block_s) np; /* next & prev pointers                   */
00029     char *buf;
00030     size_t sz;
00031     size_t code_line;
00032     const char *file_in;
00033 };
00034 
00035 typedef struct code_block_s code_block_t;
00036 typedef struct code_block_list_s code_block_list_t;
00037 
00038 struct lang_c_ctx_s
00039 {
00040     code_block_list_t code_blocks;
00041     trans_info_t *ti;
00042     size_t html_block_cnt;
00043 };
00044 
00045 typedef struct lang_c_ctx_s lang_c_ctx_t;
00046 
00047 static const char copyright_hdr[] =
00048     "/*\n"
00049     " * Copyright (c) 2005, 2006, 2006 by KoanLogic s.r.l. <http://www.koanlogic.com>\n"
00050     " * All rights reserved.\n"
00051     " *\n"
00052     " * This file is part of KLone, and as such it is subject to the license\n"
00053     " * stated in the LICENSE file which you have received as part of this\n"
00054     " * distribution\n"
00055     " *\n"
00056     " */\n";
00057 
00058 static void free_code_block(code_block_t *node)
00059 {
00060     if(node)
00061     {
00062         U_FREE(node->buf);
00063         U_FREE(node);
00064     }
00065 }
00066 
00067 static void free_code_blocks(lang_c_ctx_t *ctx)
00068 {
00069     code_block_t *node;
00070     code_block_list_t *head;
00071 
00072     dbg_ifb (ctx == NULL) return;
00073 
00074     head = &ctx->code_blocks;
00075     
00076     while((node = head->tqh_first) != NULL)
00077     {
00078         TAILQ_REMOVE(head, node, np);
00079         free_code_block(node);
00080     }
00081 }
00082 
00083 static int push_code_block(lang_c_ctx_t *ctx, parser_t *p, 
00084     const char *buf, size_t sz)
00085 {
00086     code_block_t *node;
00087     
00088     dbg_return_if (p == NULL, ~0);
00089     dbg_return_if (ctx == NULL, ~0);
00090  
00091     node = (code_block_t*)u_zalloc(sizeof(code_block_t));
00092     dbg_err_if(node == NULL);
00093 
00094     node->sz = sz;
00095     node->buf = (char*)u_malloc(sz);
00096     dbg_err_if(node->buf == NULL);
00097 
00098     node->code_line = p->code_line;
00099     node->file_in = ctx->ti->file_in;
00100 
00101     memcpy(node->buf, buf, sz);
00102 
00103     TAILQ_INSERT_TAIL(&ctx->code_blocks, node, np);
00104 
00105     return 0;
00106 err:
00107     if(node)
00108         free_code_block(node);
00109     return ~0;
00110 }
00111 
00112 static void print_header(parser_t *p, lang_c_ctx_t *ctx)
00113 {
00114     const char *file;
00115 
00116     dbg_ifb (p == NULL) return;
00117     dbg_ifb (ctx == NULL) return;
00118 
00119     (void)ctx;
00120 
00121     io_printf(p->out, "%s", copyright_hdr);
00122     io_printf(p->out, "#include <klone/emb.h>\n");
00123 
00124     file = ctx->ti->uri + strlen(ctx->ti->uri) - 1;
00125 
00126     for(; *file != '/' && file >= ctx->ti->uri; --file)
00127             ;
00128     io_printf(p->out, "static const char *SCRIPT_NAME = \"%s\";\n", 
00129                 ++file);
00130 
00131     io_printf(p->out, 
00132         "static request_t *request = NULL;\n"
00133         "static response_t *response = NULL;\n"
00134         "static session_t *session = NULL;\n"
00135         "static io_t *in = NULL;\n"
00136         "static io_t *out = NULL;\n");
00137  
00138 }
00139 
00140 static int print_var_definition(parser_t *p, int comp, const char *varname, 
00141         const char *buf, size_t bufsz)
00142 {
00143     codec_t *zip = NULL;
00144     io_t *ios = NULL;
00145     int rc, i;
00146     unsigned char c;
00147 
00148     dbg_err_if(p == NULL);
00149     dbg_err_if(varname == NULL);
00150     dbg_err_if(buf == NULL);
00151 
00152     /* create an io_t around the HTML block */
00153     dbg_err_if(io_mem_create((char*)buf, bufsz, 0, &ios));
00154 
00155 #ifdef HAVE_LIBZ
00156     /* if compression is enabled zip the data block */
00157     if(comp)
00158     {
00159         /* apply a gzip codec */
00160         dbg_err_if(codec_gzip_create(GZIP_COMPRESS, &zip));
00161         dbg_err_if(io_codec_add_tail(ios, zip));
00162         zip = NULL; /* io_free() will free the codec */
00163     }
00164 #endif
00165 
00166     io_printf(p->out, "static const char %s[] = {\n", varname);
00167 
00168     for(i = 1; (rc = io_getc(ios, (char*)&c)) > 0; ++i)
00169     {
00170         io_printf(p->out, "0x%02X, ", c);
00171         if(i % 12 == 0)
00172             io_printf(p->out, "\n");
00173     }
00174     dbg_err_if(rc < 0); /* input stream error */
00175 
00176     io_printf(p->out, "};\n");
00177 
00178     io_free(ios);
00179 
00180     return 0;
00181 err:
00182     if(zip)
00183         codec_free(zip);
00184     if(ios)
00185         io_free(ios);
00186     return ~0;
00187 }
00188 
00189 static void print_code_blocks(parser_t *p, lang_c_ctx_t *ctx)
00190 {
00191     code_block_t *node;
00192     code_block_list_t *head;
00193 
00194     dbg_ifb (p == NULL) return;
00195     dbg_ifb (ctx == NULL) return;
00196 
00197     io_printf(p->out, 
00198         "\n\n"
00199         "static void exec_page(request_t *rq, response_t *rs, session_t *ses)\n"
00200         "{\n"
00201         "   request = rq;                       \n"
00202         "   response = rs;                      \n"
00203         "   session = ses;                      \n"
00204         "   in = request_io(request);           \n"
00205         "   out = response_io(response);        \n"
00206         "   u_unused_args(SCRIPT_NAME, request, response, session, in, out); \n"
00207         );
00208 
00209     head = &ctx->code_blocks;
00210     for(node = head->tqh_first; node != NULL; node = node->np.tqe_next)
00211         io_write(p->out, node->buf, node->sz);
00212 
00213     io_printf(p->out, 
00214             "goto klone_script_exit;\n" /* just to avoid a warning */
00215             "klone_script_exit:     \n"
00216             "   return;             \n"
00217             "}                      \n"
00218             );
00219 }
00220 
00221 static void print_static_page_block(io_t *out, lang_c_ctx_t *ctx)
00222 {
00223     dbg_ifb (out == NULL) return;
00224     dbg_ifb (ctx == NULL) return;
00225     dbg_ifb (ctx->ti == NULL) return;
00226  
00227     io_printf(out, 
00228         "static embfile_t e;                \n"
00229         "static void res_ctor(void)         \n"
00230         "{                                  \n"
00231         "   e.res.type = ET_FILE;           \n"
00232         "   e.res.filename = \"%s\";        \n"
00233         "   e.data = (unsigned char*)data;  \n"
00234         "   e.size = sizeof(data);          \n"
00235         "   e.file_size = %u;               \n"
00236         "   e.mime_type = \"%s\";           \n"
00237         "   e.mtime = %lu;                  \n"
00238         "   e.comp = %d;                    \n"
00239         "   e.encrypted = %d;               \n"
00240         "}                                  \n",
00241         ctx->ti->uri, 
00242          /* file_size will be == to size if the file is not compressed */
00243         ctx->ti->file_size, 
00244         u_guess_mime_type(ctx->ti->uri), 
00245         ctx->ti->mtime, 
00246         ctx->ti->comp,
00247         ctx->ti->encrypt);
00248 }
00249 
00250 static void print_dynamic_page_block(io_t *out, lang_c_ctx_t *ctx)
00251 {
00252     dbg_ifb (out == NULL) return;
00253     dbg_ifb (ctx == NULL) return;
00254     dbg_ifb (ctx->ti == NULL) return;
00255 
00256     io_printf(out, 
00257         "static embpage_t e;                \n"
00258         "static void res_ctor(void)         \n"
00259         "{                                  \n"
00260         "   e.res.type = ET_PAGE;           \n"
00261         "   e.res.filename = \"%s\";        \n"
00262         "   e.run = exec_page;              \n"
00263         "}                                  \n",
00264         ctx->ti->uri);
00265 }
00266 
00267 static void print_register_block(io_t *out, lang_c_ctx_t *ctx)
00268 {
00269     char md5[MD5_DIGEST_BUFSZ];
00270 
00271     dbg_ifb (out == NULL) return;
00272     dbg_ifb (ctx == NULL) return;
00273     dbg_ifb (ctx->ti == NULL) return;
00274 
00275     u_md5(ctx->ti->uri, strlen(ctx->ti->uri), md5);
00276 
00277     io_printf(out, 
00278         "#ifdef __cplusplus                 \n"
00279         "extern \"C\" {                     \n"
00280         "#endif                             \n"
00281         "void module_init_%s(void);         \n" /* avoids a warning */
00282         "void module_init_%s(void)          \n"
00283         "{                                  \n"
00284         "    res_ctor();                    \n"
00285         "    emb_register((embres_t*)&e);   \n"
00286         "}                                  \n"
00287         "void module_term_%s(void);         \n" /* avoids a warning */
00288         "void module_term_%s(void)          \n"
00289         "{                                  \n"
00290         "    emb_unregister((embres_t*)&e); \n"
00291         "}                                  \n"
00292         "#ifdef __cplusplus                 \n"
00293         "}                                  \n"
00294         "#endif                             \n",
00295         md5, md5, md5, md5);
00296 }
00297 
00298 static int print_c_line(parser_t *p, lang_c_ctx_t *ctx)
00299 {
00300     dbg_err_if (p == NULL);
00301     dbg_err_if (ctx == NULL);
00302     dbg_err_if (ctx->ti == NULL);
00303 
00304     dbg_err_if(io_printf(p->out, "#line %d \"%s\"\n", p->code_line, 
00305         ctx->ti->file_in) < 0);
00306 
00307     return 0;
00308 err:
00309     return ~0;
00310 }
00311 
00312 static int process_declaration(parser_t *p, void *arg, const char *buf, 
00313         size_t sz)
00314 {
00315     u_unused_args(arg);
00316 
00317     dbg_err_if (p == NULL);
00318 
00319     dbg_err_if(io_write(p->out, buf, sz) < 0);
00320 
00321     /* a newline is required after #includes or #defines */
00322     dbg_err_if(io_printf(p->out, "\n") < 0);
00323 
00324     return 0;
00325 err:
00326     return ~0;
00327 }
00328 
00329 static int process_expression(parser_t *p, void *arg, const char *buf, 
00330         size_t sz)
00331 {
00332     lang_c_ctx_t *ctx;
00333     const char before[] = "io_printf(out, \"%s\",";
00334     const char after[] = ");";
00335 
00336     dbg_err_if (p == NULL);
00337     dbg_err_if (arg == NULL);
00338 
00339     ctx = (lang_c_ctx_t*)arg;
00340     
00341     dbg_err_if(push_code_block(ctx, p, before, strlen(before)));
00342     dbg_err_if(push_code_block(ctx, p, buf, sz));
00343     dbg_err_if(push_code_block(ctx, p, after, strlen(after)));
00344 
00345     return 0;
00346 err:
00347     return ~0;
00348 }
00349 
00350 static int process_code(parser_t *p, void *arg, const char *buf, size_t sz)
00351 {
00352     lang_c_ctx_t *ctx;
00353 
00354     dbg_err_if (p == NULL);
00355     dbg_err_if (arg == NULL);
00356 
00357     ctx = (lang_c_ctx_t*)arg;
00358  
00359     dbg_err_if(push_code_block(ctx, p, buf, sz));
00360 
00361     return 0;
00362 err:
00363     return ~0;
00364 }
00365 
00366 static int translate_set_error(trans_info_t *ti, parser_t *p, const char *msg)
00367 {
00368     char file[U_FILENAME_MAX];
00369 
00370     dbg_err_if (ti == NULL);
00371     dbg_err_if (p == NULL || p->in == NULL);
00372  
00373     dbg_err_if(io_name_get(p->in, file, U_FILENAME_MAX));
00374 
00375     dbg_err_if(u_snprintf(ti->emsg, EMSG_BUFSZ, "[%s:%d] %s", 
00376         file, p->line, msg));
00377 
00378     return 0;
00379 err:
00380     return ~0;
00381 }
00382 
00383 static int cb_html_block(parser_t *p, void *arg, const char *buf, size_t sz)
00384 {
00385     enum { CODESZ = 128, VARNSZ = 32 };
00386     lang_c_ctx_t *ctx;
00387     char code[CODESZ];
00388     char varname[VARNSZ];
00389 
00390     dbg_err_if (p == NULL);
00391     dbg_err_if (arg == NULL);
00392 
00393     ctx = (lang_c_ctx_t*)arg;
00394 
00395     if(ctx->ti->comp)
00396     {   /* zip embedded HTML blocks */
00397         dbg_err_if(u_snprintf(varname, VARNSZ, "klone_html_zblock_%lu", 
00398             ctx->html_block_cnt));
00399 
00400         dbg_err_if(print_var_definition(p, 1 /* zip it */, varname, buf, sz));
00401 
00402         dbg_err_if(u_snprintf(code, CODESZ, 
00403             "\ndbg_if(u_io_unzip_copy(out, klone_html_zblock_%lu, "
00404             "   sizeof(klone_html_zblock_%lu)));\n", 
00405             ctx->html_block_cnt, ctx->html_block_cnt));
00406 
00407     } else {
00408         /* embedded HTML blocks will not be zipped */
00409         dbg_err_if(u_snprintf(varname, VARNSZ, "klone_html_%lu", 
00410             ctx->html_block_cnt));
00411 
00412         dbg_err_if(print_var_definition(p, 0, varname, buf, sz));
00413 
00414         dbg_err_if(u_snprintf(code, CODESZ, 
00415             "\ndbg_if(io_write(out, klone_html_%lu, "
00416             "   sizeof(klone_html_%lu)) < 0);\n", 
00417             ctx->html_block_cnt, ctx->html_block_cnt));
00418     }
00419 
00420     dbg_err_if(push_code_block(ctx, p, code, strlen(code)));
00421 
00422     ctx->html_block_cnt++;
00423 
00424     return 0;
00425 err:
00426     return ~0;
00427 }
00428 
00429 static int cb_code_block(parser_t *p, int cmd, void *arg, const char *buf, 
00430         size_t sz)
00431 {
00432     lang_c_ctx_t *ctx;
00433 
00434     dbg_err_if (p == NULL);
00435     dbg_err_if (arg == NULL);
00436 
00437     ctx = (lang_c_ctx_t *)arg;
00438 
00439     switch(cmd)
00440     {
00441     case 0: /* plain code block <% ... %> */
00442         process_code(p, arg, buf, sz);
00443         break;
00444     case '@': /* <%@ ... %> */
00445         dbg_err_if("the file should have already been preprocessed");
00446         break;
00447     case '!': /* <%! ... %> */
00448         process_declaration(p, arg, buf, sz);
00449         break;
00450     case '=': /* <%= ... %> */
00451         process_expression(p, arg, buf, sz);
00452         break;
00453     default:
00454         translate_set_error(ctx->ti, p, "bad command char after <%");
00455         warn_err("unknown code type");
00456     }
00457     return 0;
00458 err:
00459     return ~0;
00460 }
00461 
00462 /* translate a opaque file to a const char array */
00463 int translate_opaque_to_c(io_t *in, io_t *out, trans_info_t *ti)
00464 {
00465     lang_c_ctx_t ctx;
00466     int i = 0;
00467     ssize_t rc;
00468     unsigned char c;
00469 
00470     dbg_err_if (in == NULL);
00471     dbg_err_if (out == NULL);
00472     dbg_err_if (ti == NULL);
00473     
00474     memset(&ctx, 0, sizeof(lang_c_ctx_t));
00475     TAILQ_INIT(&ctx.code_blocks);
00476     ctx.ti = ti;
00477 
00478     io_printf(out, "%s", copyright_hdr);
00479     io_printf(out, "#include <klone/emb.h>\n");
00480 
00481     io_printf(out, "static const char data[] = {\n");
00482 
00483     for(i = 1; (rc = io_getc(in, (char*)&c)) > 0; ++i)
00484     {
00485         io_printf(out, "0x%02X, ", c);
00486         if(i % 12 == 0)
00487             io_printf(out, "\n");
00488     }
00489     dbg_err_if(rc < 0); /* input stream error */
00490 
00491     io_printf(out, "};\n");
00492 
00493     print_static_page_block(out, &ctx);
00494     print_register_block(out, &ctx);
00495 
00496     return 0;
00497 err:
00498     return ~0;
00499 }
00500 
00501 int translate_script_to_c(io_t *in, io_t *out, trans_info_t *ti)
00502 {
00503     parser_t *p = NULL;
00504     lang_c_ctx_t ctx;
00505 
00506     dbg_return_if (in == NULL, ~0);
00507     dbg_return_if (out == NULL, ~0);
00508     dbg_return_if (ti == NULL, ~0);
00509     
00510     /* init the context obj */
00511     memset(&ctx, 0, sizeof(lang_c_ctx_t));
00512     TAILQ_INIT(&ctx.code_blocks);
00513     ctx.ti = ti;
00514 
00515     /* create a parse that reads from in and writes to out */
00516     dbg_err_if(parser_create(&p));
00517 
00518     parser_set_io(p, in, out);
00519 
00520     parser_set_cb_arg(p, &ctx);
00521     parser_set_cb_code(p, cb_code_block);
00522     parser_set_cb_html(p, cb_html_block);
00523 
00524     print_header(p, &ctx);
00525 
00526     dbg_err_if(parser_run(p));
00527 
00528     print_code_blocks(p, &ctx);
00529 
00530     print_dynamic_page_block(p->out, &ctx);
00531 
00532     print_register_block(p->out, &ctx);
00533 
00534     free_code_blocks(&ctx);
00535 
00536     parser_free(p);
00537 
00538     return 0;
00539 err:
00540     free_code_blocks(&ctx);
00541     if(p)
00542         parser_free(p);
00543     return ~0;
00544 }