Main Page | Modules | File List | Globals

translat.c

00001 /*
00002  * Copyright (c) 2005, 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: translat.c,v 1.27 2007/12/23 10:28:45 tat Exp $
00009  */
00010 
00011 #include "klone_conf.h"
00012 #include <stdio.h>
00013 #include <stdlib.h>
00014 #include <ctype.h>
00015 #include <fcntl.h>
00016 #include <unistd.h>
00017 #include <limits.h>
00018 #ifdef HAVE_LIBOPENSSL
00019 #include <openssl/evp.h>
00020 #endif /* HAVE_LIBOPENSSL */
00021 #include <u/libu.h>
00022 #include <klone/os.h>
00023 #include <klone/translat.h>
00024 #include <klone/parser.h>
00025 #include <klone/utils.h>
00026 #include <klone/os.h>
00027 #include <klone/io.h>
00028 #include <klone/codec.h>
00029 #include <klone/codecs.h>
00030 
00031 #define tr_err(...)             \
00032     do  { con_p_ctx(p); con_err(__VA_ARGS__); } while(0)
00033 #define tr_err_if(expr)          \
00034     do { if( (expr) ) { con_p_ctx(p); con("%s", #expr); goto err; } } while(0)
00035 #define tr_err_ifm(expr, ...)    \
00036     do { if( (expr) ) { con_p_ctx(p); con(__VA_ARGS__); goto err; } } while(0)
00037 
00038 static int preprocess(io_t *in, io_t *out);
00039 
00040 /* print parser context to the console */
00041 static void con_p_ctx(parser_t *p)
00042 {
00043     char fn[U_FILENAME_MAX];
00044 
00045     dbg_err_if(io_name_get(p->in, fn, U_FILENAME_MAX));
00046 
00047     /* con_ macro should be used here; we'd need a con_no_newline(...) */
00048     fprintf(stderr, "[%s:%d]: error:  ", fn, p->code_line);
00049 err:
00050     return;
00051 }
00052 
00053 static int is_a_script(const char *filename)
00054 {
00055     static const char *script_ext[] = { 
00056         ".klone", ".kl1", ".klc", 
00057         ".klx",  /* C++ page */
00058         NULL 
00059     };
00060     const char **ext;
00061 
00062     dbg_return_if(filename == NULL, 0);
00063 
00064     /* try to find an index page between default index uris */
00065     for(ext = script_ext; *ext; ++ext)
00066     {
00067         /* case insensitive matching */
00068         if(u_match_ext(filename, *ext))
00069             return 1;
00070     }
00071     return 0;
00072 }
00073 
00074 static int process_directive_include(parser_t *p, char *inc_file)
00075 {
00076     enum { BUFSZ = 4096 };
00077     char buf[U_FILENAME_MAX], *pc;
00078     char file[U_FILENAME_MAX];
00079     io_t *io = NULL;
00080 
00081     dbg_return_if (p == NULL, ~0);
00082     dbg_return_if (inc_file == NULL, ~0);
00083 
00084     dbg_err_if(io_name_get(p->in, file, U_FILENAME_MAX));
00085     dbg_err_if(io_name_get(p->in, buf, U_FILENAME_MAX));
00086 
00087     /* remove file name, just path is needed */
00088     dbg_err_if((pc = strrchr(buf, '/')) == NULL);
00089     ++pc; *pc = 0;
00090 
00091     dbg_err_if(strlen(buf) + strlen(inc_file) >= BUFSZ);
00092 
00093     strcat(buf, inc_file);
00094 
00095     /* copy include file to p->out */
00096     tr_err_ifm(u_file_open(buf, O_RDONLY, &io), 
00097         "unable to open included file %s", buf);
00098 
00099     dbg_err_if(io_printf(p->out, "<%% #line 1 \"%s\" \n %%>", buf));
00100 
00101     dbg_err_if(preprocess(io, p->out));
00102 
00103     dbg_err_if(io_printf(p->out, "<%% #line %d \"%s\" \n %%>", 
00104         p->code_line, file));
00105 
00106     io_free(io);
00107 
00108     return 0;
00109 err:
00110     if(io)
00111         io_free(io);
00112     return ~0;
00113 }
00114 
00115 static int process_directive(parser_t *p, char *buf)
00116 {
00117     char *tok, *pp;
00118 
00119     dbg_return_if (p == NULL, ~0);
00120     dbg_return_if (buf == NULL, ~0);
00121 
00122     /* get preprocessor command */
00123     tr_err_ifm((tok = strtok_r(buf, " \t", &pp)) == NULL,
00124         "bad or missing preprocessor command");
00125 
00126     if(strcasecmp(tok, "include") == 0)
00127     {
00128         /* get include file name */
00129         tr_err_ifm((tok = strtok_r(NULL, " \t\"", &pp)) == NULL,
00130             "bad or missing include filename");
00131 
00132         dbg_err_if(process_directive_include(p, tok));
00133     } else {
00134         tr_err("unknown preprocessor directive: %s", tok);
00135     }
00136 
00137     return 0;
00138 err:
00139     return ~0;
00140 }
00141 
00142 static int parse_directive(parser_t *p, void *arg, const char *buf, size_t sz)
00143 {
00144     enum { LINE_BUFSZ = 1024 };
00145     char line[LINE_BUFSZ];
00146     io_t *io = NULL;
00147 
00148     u_unused_args(arg);
00149 
00150     dbg_return_if (p == NULL, ~0);
00151     dbg_return_if (buf == NULL, ~0);
00152     
00153     dbg_err_if(io_mem_create((char*)buf, sz, 0, &io));
00154 
00155     while(io_gets(io, line, LINE_BUFSZ) > 0)
00156         dbg_err_if(process_directive(p, line));
00157 
00158     io_free(io);
00159 
00160     return 0;
00161 err:
00162     if(io)
00163         io_free(io);
00164     return ~0;
00165 }
00166 
00167 static int cb_pre_html_block(parser_t *p, void *arg, const char *buf, size_t sz)
00168 {
00169     u_unused_args(arg);
00170 
00171     dbg_err_if (p == NULL);
00172 
00173     dbg_err_if(io_write(p->out, buf, sz) < 0);
00174 
00175     return 0;
00176 err:
00177     return ~0;
00178 }
00179 
00180 static int cb_pre_code_block(parser_t *p, int cmd, void *arg, const char *buf, 
00181         size_t sz)
00182 {
00183     char file[U_FILENAME_MAX];
00184 
00185     dbg_err_if (p == NULL);
00186 
00187     if(cmd == '@')
00188     {   /* do preprocess */
00189         dbg_err_if(parse_directive(p, arg, buf, sz));
00190     } else {
00191         dbg_err_if(io_name_get(p->in, file, U_FILENAME_MAX));
00192 
00193         if(cmd != '=')
00194             dbg_err_if(io_printf(p->out, "<%%%c #line %d \"%s\" \n%%>", 
00195                 (cmd == 0 ? ' ' : cmd), p->code_line, file)); 
00196         else
00197             dbg_err_if(io_printf(p->out, "<%% #line %d \"%s\" \n%%>", 
00198                 p->code_line, file)); 
00199 
00200         dbg_err_if(io_printf(p->out, "<%%%c ", (cmd == 0 ? ' ' : cmd)) < 0);
00201         dbg_err_if(io_write(p->out, buf, sz) < 0);
00202         dbg_err_if(io_printf(p->out, "%%>") < 0);
00203 
00204         /* placeholder to be subst'd in the last translation phase */
00205         dbg_err_if(io_printf(p->out, "<%%%c #line 0 __PG_FILE_C__ \n%%>", 
00206                     (cmd == '!' ? cmd : ' ')));
00207     }
00208 
00209     return 0;
00210 err:
00211     return ~0;
00212 }
00213 
00214 static int preprocess(io_t *in, io_t *out)
00215 {
00216     parser_t *p = NULL;
00217 
00218     /* create a parse that reads from in and writes to out */
00219     dbg_err_if(parser_create(&p));
00220 
00221     parser_set_io(p, in, out);
00222 
00223     parser_set_cb_code(p, cb_pre_code_block);
00224     parser_set_cb_html(p, cb_pre_html_block);
00225 
00226     dbg_err_if(parser_run(p));
00227 
00228     parser_free(p);
00229 
00230     return 0;
00231 err:
00232     if(p)
00233         parser_free(p);
00234     return ~0;
00235 }
00236 
00237 static int fix_line_decl(trans_info_t *pti)
00238 {
00239     io_t *in = NULL, *tmp = NULL;
00240     char tname[U_FILENAME_MAX], buf[1024];
00241     int ln = 0;
00242 
00243     /* open the input file */
00244     con_err_ifm(u_file_open(pti->file_out, O_RDONLY, &in),
00245         "unable to open %s", pti->file_out);
00246 
00247     /* get a temporary io_t */
00248     con_err_if(u_tmpfile_open(&tmp));
00249 
00250     while(io_gets(in, buf, sizeof(buf)) > 0)
00251     {
00252         if(strstr(buf, "#line 0 __PG_FILE_C__") == NULL)
00253         {
00254             io_printf(tmp, "%s", buf);
00255             ln++; /* line number */
00256         } else
00257             io_printf(tmp, "#line %d \"%s\"", ln + 2, pti->file_out);
00258     }
00259 
00260     /* get the filename of the temporary io_t */
00261     dbg_err_if(io_name_get(tmp, tname, U_FILENAME_MAX));
00262 
00263     io_free(in), in = NULL;
00264     io_free(tmp), tmp = NULL;
00265 
00266     /* move tmp to file_out */
00267     u_remove(pti->file_out);
00268 
00269     u_move(tname, pti->file_out);
00270 
00271     return 0;
00272 err:
00273     if(in)
00274         io_free(in);
00275     if(tmp)
00276         io_free(tmp);
00277     return ~0;
00278 }
00279 
00280 int translate(trans_info_t *pti)
00281 {
00282     io_t *in = NULL, *out = NULL, *tmp = NULL;
00283     codec_t *gzip = NULL, *aes = NULL;
00284     char tname[U_FILENAME_MAX];
00285 
00286     dbg_return_if (pti == NULL, ~0);
00287     
00288     /* open the input file */
00289     con_err_ifm(u_file_open(pti->file_in, O_RDONLY, &in),
00290         "unable to open %s", pti->file_in);
00291 
00292     /* open the output file */
00293     con_err_ifm(u_file_open(pti->file_out, O_CREAT | O_TRUNC | O_WRONLY, &out),
00294         "unable to open %s", pti->file_out);
00295 
00296     /* should choose the right translator based on file extensions or config */
00297     if(is_a_script(pti->file_in))
00298     {
00299         /* get a temporary io_t */
00300         con_err_if(u_tmpfile_open(&tmp));
00301 
00302         /* save the preprocessed in file to tmp */
00303         dbg_err_if(preprocess(in, tmp));
00304 
00305         /* reset the tmp io */
00306         io_seek(tmp, 0);
00307 
00308         /* translate it */
00309         dbg_err_if(translate_script_to_c(tmp, out, pti));
00310 
00311         /* get the filename of the temporary io_t */
00312         dbg_err_if(io_name_get(tmp, tname, U_FILENAME_MAX));
00313 
00314         /* free the tmp io */
00315         io_free(tmp);
00316 
00317         /* remove the tmp file */
00318         u_remove(tname);
00319     } else {
00320         /* check if compression is requested */
00321 #ifdef HAVE_LIBZ
00322         if(pti->comp)
00323         {
00324             /* set a compression filter to the input stream */
00325             dbg_err_if(codec_gzip_create(GZIP_COMPRESS, &gzip));
00326             dbg_err_if(io_codec_add_tail(in, gzip));
00327             gzip = NULL;
00328         }
00329 #endif
00330 #ifdef HAVE_LIBOPENSSL
00331         /* check if encryption is requested */
00332         if(pti->encrypt)
00333         {
00334             /* set a cipher filter */
00335             dbg_err_if(codec_cipher_create(CIPHER_ENCRYPT, EVP_aes_256_cbc(),
00336                 pti->key, NULL, &aes));
00337             dbg_err_if(io_codec_add_tail(in, aes));
00338             aes = NULL;
00339         }
00340 #endif
00341         dbg_err_if(translate_opaque_to_c(in, out, pti));
00342     }
00343 
00344     io_free(in), in = NULL;
00345     io_free(out), out = NULL;
00346 
00347     /* replace '#line 0 __PG_FILE_C__' lines with '#line N "real_filename.c"' */
00348     if(is_a_script(pti->file_in))
00349         dbg_err_if(fix_line_decl(pti));
00350 
00351     return 0;
00352 err:
00353     if(pti && strlen(pti->emsg))
00354         con("%s", pti->emsg);
00355     if(gzip)
00356         codec_free(gzip);
00357     if(tmp)
00358         io_free(tmp);
00359     if(in)
00360         io_free(in);
00361     if(out)
00362         io_free(out);
00363     return ~0;
00364 }