Main Page | Modules | File List | 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.40 2008/04/10 16:30:58 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     static const char dfun_prefix[] = "page_";
00115     const char *file;
00116     char *dfun;
00117     int i;
00118 
00119     dbg_ifb (p == NULL) return;
00120     dbg_ifb (ctx == NULL) return;
00121 
00122     (void)ctx;
00123 
00124     io_printf(p->out, "%s", copyright_hdr);
00125     io_printf(p->out, "#include <klone/emb.h>\n");
00126 
00127     file = ctx->ti->uri + strlen(ctx->ti->uri) - 1;
00128 
00129     for(; *file != '/' && file >= ctx->ti->uri; --file)
00130             ;
00131 
00132     io_printf(p->out, "static const char *SCRIPT_NAME = \"%s\";\n", 
00133                 ++file);
00134 
00135     dfun = ctx->ti->dfun; /* shortcut */
00136 
00137     /* we need a prefix to avoid errors with pages whose filename starts with
00138        a number (404-handler.kl1) or any other char not allowed by C as the
00139        first char of function names */
00140     strlcpy(dfun, dfun_prefix, strlen(dfun_prefix));
00141 
00142     if(strlcat(dfun, file, URI_BUFSZ) >= URI_BUFSZ)
00143         dfun[URI_BUFSZ - 1] = 0;
00144 
00145     for(i = 0; i < strlen(dfun); ++i)
00146         if(!isalnum(dfun[i]))
00147             dfun[i] = '_'; /* just a-zA-Z0-9 allowed */
00148 
00149     /* add a dummy function that the user can use to set a breakpoint when
00150        entering the page code. the "static volatile" variable is used to avoid
00151        the compiler to optimize-out (inlining) the function call */
00152     io_printf(p->out, 
00153             "static int %s (void) { "
00154             "static volatile int dummy; return dummy; }\n", dfun);
00155 
00156     io_printf(p->out, 
00157         "static request_t *request = NULL;\n"
00158         "static response_t *response = NULL;\n"
00159         "static session_t *session = NULL;\n"
00160         "static io_t *in = NULL;\n"
00161         "static io_t *out = NULL;\n");
00162  
00163 }
00164 
00165 static int print_var_definition(parser_t *p, int comp, const char *varname, 
00166         const char *buf, size_t bufsz)
00167 {
00168     codec_t *zip = NULL;
00169     io_t *ios = NULL;
00170     int rc, i;
00171     unsigned char c;
00172 
00173     dbg_err_if(p == NULL);
00174     dbg_err_if(varname == NULL);
00175     dbg_err_if(buf == NULL);
00176 
00177     /* create an io_t around the HTML block */
00178     dbg_err_if(io_mem_create((char*)buf, bufsz, 0, &ios));
00179 
00180 #ifdef HAVE_LIBZ
00181     /* if compression is enabled zip the data block */
00182     if(comp)
00183     {
00184         /* apply a gzip codec */
00185         dbg_err_if(codec_gzip_create(GZIP_COMPRESS, &zip));
00186         dbg_err_if(io_codec_add_tail(ios, zip));
00187         zip = NULL; /* io_free() will free the codec */
00188     }
00189 #endif
00190 
00191     io_printf(p->out, "static const char %s[] = {\n", varname);
00192 
00193     for(i = 1; (rc = io_getc(ios, (char*)&c)) > 0; ++i)
00194     {
00195         io_printf(p->out, "0x%02X, ", c);
00196         if(i % 12 == 0)
00197             io_printf(p->out, "\n");
00198     }
00199     dbg_err_if(rc < 0); /* input stream error */
00200 
00201     io_printf(p->out, "};\n");
00202 
00203     io_free(ios);
00204 
00205     return 0;
00206 err:
00207     if(zip)
00208         codec_free(zip);
00209     if(ios)
00210         io_free(ios);
00211     return ~0;
00212 }
00213 
00214 static void print_code_blocks(parser_t *p, lang_c_ctx_t *ctx)
00215 {
00216     code_block_t *node;
00217     code_block_list_t *head;
00218 
00219     dbg_ifb (p == NULL) return;
00220     dbg_ifb (ctx == NULL) return;
00221 
00222     io_printf(p->out, 
00223         "\n\n"
00224         "static void exec_page(request_t *rq, response_t *rs, session_t *ses)\n"
00225         "{\n"
00226         "   request = rq;                       \n"
00227         "   response = rs;                      \n"
00228         "   session = ses;                      \n"
00229         "   in = request_io(request);           \n"
00230         "   out = response_io(response);        \n"
00231         "   u_unused_args(SCRIPT_NAME, request, response, session, in, out); \n"
00232         "   %s () ; \n ", ctx->ti->dfun
00233         );
00234 
00235     head = &ctx->code_blocks;
00236     for(node = head->tqh_first; node != NULL; node = node->np.tqe_next)
00237     {
00238         io_printf(p->out, "\n");
00239         io_write(p->out, node->buf, node->sz);
00240     }
00241 
00242     io_printf(p->out, 
00243             "goto klone_script_exit;\n" /* just to avoid a warning */
00244             "klone_script_exit:     \n"
00245             "   return;             \n"
00246             "}                      \n"
00247             );
00248 }
00249 
00250 static void print_static_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 embfile_t e;                \n"
00258         "static void res_ctor(void)         \n"
00259         "{                                  \n"
00260         "   e.res.type = ET_FILE;           \n"
00261         "   e.res.filename = \"%s\";        \n"
00262         "   e.data = (unsigned char*)data;  \n"
00263         "   e.size = sizeof(data);          \n"
00264         "   e.file_size = %u;               \n"
00265         "   e.mime_type = \"%s\";           \n"
00266         "   e.mtime = %lu;                  \n"
00267         "   e.comp = %d;                    \n"
00268         "   e.encrypted = %d;               \n"
00269         "}                                  \n",
00270         ctx->ti->uri, 
00271          /* file_size will be == to size if the file is not compressed */
00272         ctx->ti->file_size, 
00273         u_guess_mime_type(ctx->ti->uri), 
00274         ctx->ti->mtime, 
00275         ctx->ti->comp,
00276         ctx->ti->encrypt);
00277 }
00278 
00279 static void print_dynamic_page_block(io_t *out, lang_c_ctx_t *ctx)
00280 {
00281     dbg_ifb (out == NULL) return;
00282     dbg_ifb (ctx == NULL) return;
00283     dbg_ifb (ctx->ti == NULL) return;
00284 
00285     io_printf(out, 
00286         "static embpage_t e;                \n"
00287         "static void res_ctor(void)         \n"
00288         "{                                  \n"
00289         "   e.res.type = ET_PAGE;           \n"
00290         "   e.res.filename = \"%s\";        \n"
00291         "   e.run = exec_page;              \n"
00292         "}                                  \n",
00293         ctx->ti->uri);
00294 }
00295 
00296 static void print_register_block(io_t *out, lang_c_ctx_t *ctx)
00297 {
00298     char md5[MD5_DIGEST_BUFSZ];
00299 
00300     dbg_ifb (out == NULL) return;
00301     dbg_ifb (ctx == NULL) return;
00302     dbg_ifb (ctx->ti == NULL) return;
00303 
00304     u_md5(ctx->ti->uri, strlen(ctx->ti->uri), md5);
00305 
00306     io_printf(out, 
00307         "#ifdef __cplusplus                 \n"
00308         "extern \"C\" {                     \n"
00309         "#endif                             \n"
00310         "void module_init_%s(void);         \n" /* avoids a warning */
00311         "void module_init_%s(void)          \n"
00312         "{                                  \n"
00313         "    res_ctor();                    \n"
00314         "    emb_register((embres_t*)&e);   \n"
00315         "}                                  \n"
00316         "void module_term_%s(void);         \n" /* avoids a warning */
00317         "void module_term_%s(void)          \n"
00318         "{                                  \n"
00319         "    emb_unregister((embres_t*)&e); \n"
00320         "}                                  \n"
00321         "#ifdef __cplusplus                 \n"
00322         "}                                  \n"
00323         "#endif                             \n",
00324         md5, md5, md5, md5);
00325 }
00326 
00327 static int process_declaration(parser_t *p, void *arg, const char *buf, 
00328         size_t sz)
00329 {
00330     u_unused_args(arg);
00331 
00332     dbg_err_if (p == NULL);
00333 
00334     dbg_err_if(io_write(p->out, buf, sz) < 0);
00335 
00336     /* a newline is required after #includes or #defines */
00337     dbg_err_if(io_printf(p->out, "\n") < 0);
00338 
00339     return 0;
00340 err:
00341     return ~0;
00342 }
00343 
00344 static int process_expression(parser_t *p, void *arg, const char *buf, 
00345         size_t sz)
00346 {
00347     lang_c_ctx_t *ctx;
00348     const char before[] = "io_printf(out, \"%s\",";
00349     const char after[] = ");\n";
00350 
00351     dbg_err_if (p == NULL);
00352     dbg_err_if (arg == NULL);
00353 
00354     ctx = (lang_c_ctx_t*)arg;
00355     
00356     dbg_err_if(push_code_block(ctx, p, before, strlen(before)));
00357     dbg_err_if(push_code_block(ctx, p, buf, sz));
00358     dbg_err_if(push_code_block(ctx, p, after, strlen(after)));
00359 
00360     return 0;
00361 err:
00362     return ~0;
00363 }
00364 
00365 static int process_code(parser_t *p, void *arg, const char *buf, size_t sz)
00366 {
00367     lang_c_ctx_t *ctx;
00368 
00369     dbg_err_if (p == NULL);
00370     dbg_err_if (arg == NULL);
00371 
00372     ctx = (lang_c_ctx_t*)arg;
00373  
00374     dbg_err_if(push_code_block(ctx, p, buf, sz));
00375 
00376     return 0;
00377 err:
00378     return ~0;
00379 }
00380 
00381 static int translate_set_error(trans_info_t *ti, parser_t *p, const char *msg)
00382 {
00383     char file[U_FILENAME_MAX];
00384 
00385     dbg_err_if (ti == NULL);
00386     dbg_err_if (p == NULL || p->in == NULL);
00387  
00388     dbg_err_if(io_name_get(p->in, file, U_FILENAME_MAX));
00389 
00390     dbg_err_if(u_snprintf(ti->emsg, EMSG_BUFSZ, "[%s:%d] %s", 
00391         file, p->line, msg));
00392 
00393     return 0;
00394 err:
00395     return ~0;
00396 }
00397 
00398 static int cb_html_block(parser_t *p, void *arg, const char *buf, size_t sz)
00399 {
00400     enum { CODESZ = 128, VARNSZ = 32 };
00401     lang_c_ctx_t *ctx;
00402     char code[CODESZ];
00403     char varname[VARNSZ];
00404 
00405     dbg_err_if (p == NULL);
00406     dbg_err_if (arg == NULL);
00407 
00408     ctx = (lang_c_ctx_t*)arg;
00409 
00410     if(ctx->ti->comp)
00411     {   /* zip embedded HTML blocks */
00412         dbg_err_if(u_snprintf(varname, VARNSZ, "klone_html_zblock_%lu", 
00413             ctx->html_block_cnt));
00414 
00415         dbg_err_if(print_var_definition(p, 1 /* zip it */, varname, buf, sz));
00416 
00417         dbg_err_if(u_snprintf(code, CODESZ, 
00418             "\ndbg_if(u_io_unzip_copy(out, klone_html_zblock_%lu, "
00419             "   sizeof(klone_html_zblock_%lu)));\n", 
00420             ctx->html_block_cnt, ctx->html_block_cnt));
00421 
00422     } else {
00423         /* embedded HTML blocks will not be zipped */
00424         dbg_err_if(u_snprintf(varname, VARNSZ, "klone_html_%lu", 
00425             ctx->html_block_cnt));
00426 
00427         dbg_err_if(print_var_definition(p, 0, varname, buf, sz));
00428 
00429         dbg_err_if(u_snprintf(code, CODESZ, 
00430             "\ndbg_if(io_write(out, klone_html_%lu, "
00431             "   sizeof(klone_html_%lu)) < 0);\n", 
00432             ctx->html_block_cnt, ctx->html_block_cnt));
00433     }
00434 
00435     dbg_err_if(push_code_block(ctx, p, code, strlen(code)));
00436 
00437     ctx->html_block_cnt++;
00438 
00439     return 0;
00440 err:
00441     return ~0;
00442 }
00443 
00444 static int cb_code_block(parser_t *p, int cmd, void *arg, const char *buf, 
00445         size_t sz)
00446 {
00447     lang_c_ctx_t *ctx;
00448 
00449     dbg_err_if (p == NULL);
00450     dbg_err_if (arg == NULL);
00451 
00452     ctx = (lang_c_ctx_t *)arg;
00453 
00454     switch(cmd)
00455     {
00456     case 0: /* plain code block <% ... %> */
00457         process_code(p, arg, buf, sz);
00458         break;
00459     case '@': /* <%@ ... %> */
00460         dbg_err_if("the file should have already been preprocessed");
00461         break;
00462     case '!': /* <%! ... %> */
00463         process_declaration(p, arg, buf, sz);
00464         break;
00465     case '=': /* <%= ... %> */
00466         process_expression(p, arg, buf, sz);
00467         break;
00468     default:
00469         translate_set_error(ctx->ti, p, "bad command char after <%");
00470         warn_err("unknown code type");
00471     }
00472     return 0;
00473 err:
00474     return ~0;
00475 }
00476 
00477 /* translate a opaque file to a const char array */
00478 int translate_opaque_to_c(io_t *in, io_t *out, trans_info_t *ti)
00479 {
00480     lang_c_ctx_t ctx;
00481     int i = 0;
00482     ssize_t rc;
00483     unsigned char c;
00484 
00485     dbg_err_if (in == NULL);
00486     dbg_err_if (out == NULL);
00487     dbg_err_if (ti == NULL);
00488     
00489     memset(&ctx, 0, sizeof(lang_c_ctx_t));
00490     TAILQ_INIT(&ctx.code_blocks);
00491     ctx.ti = ti;
00492 
00493     io_printf(out, "%s", copyright_hdr);
00494     io_printf(out, "#include <klone/emb.h>\n");
00495 
00496     io_printf(out, "static const char data[] = {\n");
00497 
00498     for(i = 1; (rc = io_getc(in, (char*)&c)) > 0; ++i)
00499     {
00500         io_printf(out, "0x%02X, ", c);
00501         if(i % 12 == 0)
00502             io_printf(out, "\n");
00503     }
00504     dbg_err_if(rc < 0); /* input stream error */
00505 
00506     io_printf(out, "};\n");
00507 
00508     print_static_page_block(out, &ctx);
00509     print_register_block(out, &ctx);
00510 
00511     return 0;
00512 err:
00513     return ~0;
00514 }
00515 
00516 int translate_script_to_c(io_t *in, io_t *out, trans_info_t *ti)
00517 {
00518     parser_t *p = NULL;
00519     lang_c_ctx_t ctx;
00520 
00521     dbg_return_if (in == NULL, ~0);
00522     dbg_return_if (out == NULL, ~0);
00523     dbg_return_if (ti == NULL, ~0);
00524     
00525     /* init the context obj */
00526     memset(&ctx, 0, sizeof(lang_c_ctx_t));
00527     TAILQ_INIT(&ctx.code_blocks);
00528     ctx.ti = ti;
00529 
00530     /* create a parse that reads from in and writes to out */
00531     dbg_err_if(parser_create(&p));
00532 
00533     parser_set_io(p, in, out);
00534 
00535     parser_set_cb_arg(p, &ctx);
00536     parser_set_cb_code(p, cb_code_block);
00537     parser_set_cb_html(p, cb_html_block);
00538 
00539     print_header(p, &ctx);
00540 
00541     dbg_err_if(parser_run(p));
00542 
00543     print_code_blocks(p, &ctx);
00544 
00545     print_dynamic_page_block(p->out, &ctx);
00546 
00547     print_register_block(p->out, &ctx);
00548 
00549     free_code_blocks(&ctx);
00550 
00551     parser_free(p);
00552 
00553     return 0;
00554 err:
00555     free_code_blocks(&ctx);
00556     if(p)
00557         parser_free(p);
00558     return ~0;
00559 }