Main Page | Modules | Data Structures | Directories | File List | Data Fields | 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.24 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 <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         if(cmd != '=')
00193             dbg_err_if(io_printf(p->out, "<%%%c #line %d \"%s\" \n%%>", 
00194                 (cmd == 0 ? ' ' : cmd), p->code_line, file)); 
00195         else
00196             dbg_err_if(io_printf(p->out, "<%% #line %d \"%s\" \n%%>", 
00197                 p->code_line, file)); 
00198 
00199         dbg_err_if(io_printf(p->out, "<%%%c ", (cmd == 0 ? ' ' : cmd)) < 0);
00200         dbg_err_if(io_write(p->out, buf, sz) < 0);
00201         dbg_err_if(io_printf(p->out, "%%>") < 0);
00202     }
00203     return 0;
00204 err:
00205     return ~0;
00206 }
00207 
00208 static int preprocess(io_t *in, io_t *out)
00209 {
00210     parser_t *p = NULL;
00211 
00212     /* create a parse that reads from in and writes to out */
00213     dbg_err_if(parser_create(&p));
00214 
00215     parser_set_io(p, in, out);
00216 
00217     parser_set_cb_code(p, cb_pre_code_block);
00218     parser_set_cb_html(p, cb_pre_html_block);
00219 
00220     dbg_err_if(parser_run(p));
00221 
00222     parser_free(p);
00223 
00224     return 0;
00225 err:
00226     if(p)
00227         parser_free(p);
00228     return ~0;
00229 }
00230 
00231 int translate(trans_info_t *pti)
00232 {
00233     io_t *in = NULL, *out = NULL, *tmp = NULL;
00234     codec_t *gzip = NULL, *aes = NULL;
00235     char tname[U_FILENAME_MAX];
00236 
00237     dbg_return_if (pti == NULL, ~0);
00238     
00239     /* open the input file */
00240     con_err_ifm(u_file_open(pti->file_in, O_RDONLY, &in),
00241         "unable to open %s", pti->file_in);
00242 
00243     /* open the output file */
00244     con_err_ifm(u_file_open(pti->file_out, O_CREAT | O_TRUNC | O_WRONLY, &out),
00245         "unable to open %s", pti->file_out);
00246 
00247     /* should choose the right translator based on file extensions or config */
00248     if(is_a_script(pti->file_in))
00249     {
00250         /* get a temporary io_t */
00251         con_err_if(u_tmpfile_open(&tmp));
00252 
00253         /* save the preprocessed in file to tmp */
00254         dbg_err_if(preprocess(in, tmp));
00255 
00256         /* reset the tmp io */
00257         io_seek(tmp, 0);
00258 
00259         /* translate it */
00260         dbg_err_if(translate_script_to_c(tmp, out, pti));
00261 
00262         /* get the filename of the temporary io_t */
00263         dbg_err_if(io_name_get(tmp, tname, U_FILENAME_MAX));
00264 
00265         /* free the tmp io */
00266         io_free(tmp);
00267 
00268         /* remove the tmp file */
00269         unlink(tname);
00270     } else  {
00271         /* check if compression is requested */
00272 #ifdef HAVE_LIBZ
00273         if(pti->comp)
00274         {
00275             /* set a compression filter to the input stream */
00276             dbg_err_if(codec_gzip_create(GZIP_COMPRESS, &gzip));
00277             dbg_err_if(io_codec_add_tail(in, gzip));
00278             gzip = NULL;
00279         }
00280 #endif
00281 #ifdef HAVE_LIBOPENSSL
00282         /* check if encryption is requested */
00283         if(pti->encrypt)
00284         {
00285             /* set a cipher filter */
00286             dbg_err_if(codec_cipher_create(CIPHER_ENCRYPT, EVP_aes_256_cbc(),
00287                 pti->key, NULL, &aes));
00288             dbg_err_if(io_codec_add_tail(in, aes));
00289             aes = NULL;
00290         }
00291 #endif
00292         dbg_err_if(translate_opaque_to_c(in, out, pti));
00293     }
00294 
00295     io_free(in);
00296     io_free(out);
00297 
00298     return 0;
00299 err:
00300     if(pti && strlen(pti->emsg))
00301         con("%s", pti->emsg);
00302     if(gzip)
00303         codec_free(gzip);
00304     if(tmp)
00305         io_free(tmp);
00306     if(in)
00307         io_free(in);
00308     if(out)
00309         io_free(out);
00310     return ~0;
00311 }