%% options copyright owner = Dirk Krause copyright year = 2011-2014 license = bsd %% module #include "dk3all.h" #include "dkt.h" $!trace-include /** Job structure. */ typedef struct { dk3_app_t *app; /**< Application. */ dk3_option_set_t *opt; /**< Option set. */ dkChar const * const *msg; /**< Localized messages. */ dkChar const * const *kwnl; /**< Keywords, not localized. */ dk3_uc2lat_t *uc2l; /**< UC to LaTeX converter. */ int exval; /**< Exit status code. */ int enc_s; /**< Encoding for stdin. */ int enc_f; /**< Encoding for files. */ int f_em; /**< Flag: Echo mode. */ int f_nl; /**< Flag: Last was newline. */ int f_hm; /**< Flag: HTML echo mode. */ int f_mm; /**< Flag: In math mode. */ int f_nac; /**< Flag: Non-ASCII char reported. */ int f_pkg; /**< Flag: Show used packages. */ } DKT_LAT_J; /** Data for the option set. */ static dk3_option_t const dkt_lat_options[] = { { dkT('R'), dkT("reset"), 0 }, { dkT('i'), dkT("input-encoding"), 1 }, { dkT('p'), dkT("plain"), 0 }, { dkT('e'), dkT("echo"), 0 }, { dkT('u'), dkT("used-packages"), 0 }, { dkT('x'), dkT("hex"), 0 } }; /** Number of options in the dkt_lat_options array. */ static size_t const dkt_lat_sz_options = sizeof(dkt_lat_options)/sizeof(dk3_option_t); /** Configuration file keys. */ static dkChar const * const dkt_lat_long_options[] = { dkT("stdin-encoding"), dkT("file-encoding"), dkT("reset"), NULL }; /** Start and end of math mode. */ static char const * const dkt_lat_mm[] = { "\\(", "\\)", " ", "\n", "% \\usepackage{", "}\n" }; /** Initialize job structure. @param j Job structure. */ static void dkt_lat_job_init(DKT_LAT_J *j) { j->opt = NULL; j->enc_s = dk3app_get_input_stdin_encoding(j->app); j->enc_f = dk3app_get_input_file_encoding(j->app); j->f_em = 0; j->f_hm = 0; j->f_nl = 0; j->uc2l = NULL; j->f_mm = 0; j->f_nac = 0; j->f_pkg = 0; } /** Reset job structure. @param j Job structure. */ static void dkt_lat_job_reset(DKT_LAT_J *j) { j->enc_s = dk3app_get_default_stdin_encoding(j->app); j->enc_f = dk3app_get_default_file_encoding(j->app); j->f_em = 0; j->f_hm = 0; } /** Cleanup job structure. @param j Job structure. */ static void dkt_lat_job_cleanup(DKT_LAT_J *j) { if(j->opt) { dk3opt_close(j->opt); } if(j->uc2l) { dk3uc2lat_close(j->uc2l); } } /** Process one configuration file line. @param vj Job structure. @param key Key. @param val Value (may be NULL). @return 1 on success, 0 on error. */ static int dkt_lat_conf_line(void *vj, dkChar const *key, dkChar const *val) { int back = 0; DKT_LAT_J *j; j = (DKT_LAT_J *)vj; switch(dk3str_array_index(dkt_lat_long_options, key, 0)) { case 0: { if(val) { back = dkt_tool_set_encoding( j->app, &(j->enc_s), val, dk3app_get_input_stdin_encoding(j->app) ); } } break; case 1: { if(val) { back = dkt_tool_set_encoding( j->app, &(j->enc_f), val, dk3app_get_input_file_encoding(j->app) ); } } break; case 2: { dkt_lat_job_reset(j); back = 1; } break; } return back; } /** Process the command line arguments. @param j Job structure. @return 1 on success, 0 on error. */ static int dkt_lat_process_arguments(DKT_LAT_J *j) { int back = 1; int xargc = 0; /* Number of cmd line args. */ int res = 0; /* Result of set-encoding operation. */ dkChar const * const *xargv = NULL; /* Cmd line args array. */ dkChar const *x = NULL; /* Option argument (encoding name). */ xargc = dk3app_get_argc(j->app); xargv = dk3app_get_argv(j->app); xargv++; xargv++; xargc--; xargc--; j->opt = dk3opt_open_app( dkt_lat_options, dkt_lat_sz_options, dkT('\0'), NULL, xargc, xargv, j->app ); if(j->opt) { if(dk3opt_get_error_code(j->opt) == 0) { if(dk3opt_is_set(j->opt, dkT('R'))) { dkt_lat_job_reset(j); } if(dk3opt_is_set(j->opt, dkT('p'))) { j->enc_s = j->enc_f = DK3_FILE_ENCODING_ASCII; if(dk3opt_is_set(j->opt, dkT('i'))) { back = 0; /* ERROR: -i and -p are exclusive. */ dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 0); } } else { if(dk3opt_is_set(j->opt, dkT('i'))) { x = dk3opt_get_short_arg(j->opt, dkT('i')); if(x) { res = dkt_tool_set_encoding( j->app, &(j->enc_s), x, dk3app_get_input_stdin_encoding(j->app) ); j->enc_f = j->enc_s; if(!(res)) { back = 0; j->exval = DKT_RESULT_ERR_OPTION; } } else { back = 0; j->exval = DKT_RESULT_ERR_OPTION; } } } if(dk3opt_is_set(j->opt, dkT('u'))) { j->f_pkg = 1; } if(dk3opt_is_set(j->opt, dkT('e'))) { j->f_em = 1; j->f_hm = 0; if(dk3opt_is_set(j->opt, dkT('x'))) { back = 0; dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 32); } } else { if(dk3opt_is_set(j->opt, dkT('x'))) { j->f_em = 1; j->f_hm = 1; } } } else { j->exval = DKT_RESULT_ERR_OPTION; } } else { j->exval = DKT_RESULT_ERR_MEMORY; } return back; } /** Handler to process one character. @param vj Job structure. @param c32 Character to process. @return 1 on success, 0 on error (can continue), -1 on error (abort processing). */ static int dkt_lat_char_handler(void *vj, dk3_c32_t c32) { int back = 1; DKT_LAT_J *j = NULL; /* Job structure. */ char const *string = NULL; /* Conversion result. */ size_t used = 0; /* Bytes used in UTF-8 encoding. */ size_t i = 0; /* Current byte index to process. */ unsigned char u8b[16]; /* UTF-8 encoding buffer. */ u8b[0] = '\0'; j = (DKT_LAT_J *)vj; if((unsigned long)c32 == 0x0000000DUL) { if(j->f_mm) { fputs(dkt_lat_mm[1], stdout); j->f_mm = 0; } } else { if((unsigned long)c32 == 0x0000000AUL) { if(j->f_mm) { fputs(dkt_lat_mm[1], stdout); j->f_mm = 0; } fputc('\n', stdout); j->f_nl = 1; } else { string = dk3uc2lat_get(j->uc2l, c32, 0); if(string) { if(j->f_mm) { j->f_mm = 0; fputs(dkt_lat_mm[1], stdout); } fputs(string, stdout); } else { string = dk3uc2lat_get(j->uc2l, c32, 1); if(string) { if(!(j->f_mm)) { j->f_mm = 1; fputs(dkt_lat_mm[0], stdout); } fputs(string, stdout); } else { if(dk3app_get_output_encoding(j->app) == DK3_FILE_ENCODING_UTF8) { used = dk3enc_uc2utf8(c32, u8b, sizeof(u8b)); for(i = 0; i < used; i++) { fputc((char)(u8b[i]), stdout); } } else { fputc((char)c32, stdout); if((unsigned long)c32 > 0x000000FFUL) { back = 0; j->exval = DKT_RESULT_ERR_UNSPECIFIC; if(!(j->f_nac)) { /* Non-ASCII character(s) in input! */ dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 1); j->f_nac = 1; j->exval = DKT_RESULT_ERR_INPUT; } } } } } j->f_nl = 0; } } return back; } /** Finish output (end math mode and write newline if necessary). @param j Job structure. */ static void dkt_lat_finish_file(DKT_LAT_J *j) { if(j->f_mm) { j->f_mm = 0; j->f_nl = 0; fputs(dkt_lat_mm[1], stdout); } if(!(j->f_nl)) { fputc('\n', stdout); } } /** Process one file name. @param j Job structure. @param fn File name to process. */ static void dkt_lat_process_one_name(DKT_LAT_J *j, dkChar const *fn) { dkChar bu[DK3_MAX_PATH]; /* Private copy for modification. */ dk3_dir_t *fne = NULL; /* File name expander. */ dkChar const *en = NULL; /* Exapended name. */ if(fn) { if(dk3str_len(fn) < DK3_SIZEOF(bu,dkChar)) { dk3str_cpy_not_overlapped(bu, fn); dk3str_correct_filename(bu); if(dk3sf_must_expand(bu)) { fne = dk3dir_fne_open_app(bu, j->app); if(fne) { if(dk3dir_get_number_of_files(fne)) { while(dk3dir_get_next_file(fne)) { en = dk3dir_get_fullname(fne); if(en) { j->f_nl = 0; j->f_mm = 0; dk3stream_process_filename_chars_app( (void *)j, dkt_lat_char_handler, en, j->enc_f, j->app ); dkt_lat_finish_file(j); } } } else { /* ERROR: No such file! */ dk3app_log_i3(j->app, DK3_LL_ERROR, 215, 216, fn); j->exval = DKT_RESULT_ERR_FILENAME; } dk3dir_close(fne); } else { j->exval = DKT_RESULT_ERR_MEMORY; } } else { j->f_nl = 0; j->f_mm = 0; dk3stream_process_filename_chars_app( (void *)j, dkt_lat_char_handler, bu, j->enc_f, j->app ); dkt_lat_finish_file(j); } } else { j->exval = DKT_RESULT_ERR_FILENAME; /* ERROR: File name too long! */ dk3app_log_i3(j->app, DK3_LL_ERROR, 65, 66, fn); } } } /** Process arguments in hex echo mode. @param j Job structure. @param nfn Number of arguments. */ static void dkt_lat_process_hex_echo(DKT_LAT_J *j, int nfn) { long l = 0L; /* Hexadecimal value. */ dk3_c32_t c32 = 0UL; /* 32-bit character. */ dkChar const *arg = NULL; /* Current argument to process. */ char const *output = NULL; /* Output string. */ int i = 0; /* Index of current argument. */ $? "+ dkt_lat_process_hex_echo" for(i = 0; i < nfn; i++) { arg = dk3opt_get_arg(j->opt, i); if(arg) { $? ". processing \"%s\"", TR_STR(arg) if(dk3sf_sscanf3(arg, dkT("%lx"), &l) == 1) { $? ". sscanf ok" c32 = (dk3_c32_t)l; output = dk3uc2lat_get(j->uc2l, c32, 0); if(output) { $? ". output=\"%s\"", TR_STR(output) fputs(output, stdout); } else { $? "! no output" output = dk3uc2lat_get(j->uc2l, c32, 1); if(output) { $? ". output=\"%s\"", TR_STR(output) fputs(dkt_lat_mm[0], stdout); fputs(output, stdout); fputs(dkt_lat_mm[1], stdout); } else { $? "! no output" if(dk3uc2lat_direct(c32)) { $? ". direct character ok" fputc((char)c32, stdout); } else { $? "! no direct character" /* ERROR: No LaTeX encoding found for... */ dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 33, 34, arg); } } } } else { $? "! sscanf failed" /* ERROR: Not a hexadecimal number. */ dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 35, 36, arg); } } else { $? "! no arg" j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } if(nfn > 0) { fputc('\n', stdout); } $? "- dkt_lat_process_hex_echo" } /** Process arguments in echo mode. @param j Job structure. @param nfn Number of arguments. */ static void dkt_lat_process_echo(DKT_LAT_J *j, int nfn) { dk3_stream_t *os = NULL; /* Output stream. */ int i = 0; /* Index of current command line argument. */ dkChar const *arg = NULL; /* Current command line argument. */ os = dk3stream_open_file_app(stdout, DK3_STREAM_FLAG_WRITE, j->app); if(os) { for(i = 0; i < nfn; i++) { arg = dk3opt_get_arg(j->opt, i); if(arg) { if(i > 0) { dk3stream_c8_fputs(os, dkt_lat_mm[2]); } dk3uc2lat_stputs(j->uc2l, os, arg, dk3app_get_encoding(j->app)); } } dk3stream_c8_fputs(os, dkt_lat_mm[3]); dk3stream_close(os); } else { j->exval = DKT_RESULT_ERR_MEMORY; } } /** Run conversion. @param j Job structure. */ static void dkt_lat_run(DKT_LAT_J *j) { int nfn = 0; /* Number of file names. */ int i = 0; /* Index of current file name. */ dk3_uc2lat_pkg_t *cpkg = NULL; /* Current package. */ int f_utf8 = 0; /* Flag: UTF-8. */ f_utf8 = 0; if(DK3_ENCODING_UTF8 == dk3app_get_encoding(j->app)) { f_utf8 = 1; } j->uc2l = dk3uc2lat_open_app(NULL, 0, f_utf8, j->app); if(j->uc2l) { nfn = dk3opt_get_num_args(j->opt); if(nfn > 0) { if(j->f_em) { if(j->f_hm) { dkt_lat_process_hex_echo(j, nfn); } else { dkt_lat_process_echo(j, nfn); } } else { for(i = 0; i < nfn; i++) { dkt_lat_process_one_name(j, dk3opt_get_arg(j->opt, i)); } } } else { j->f_nl = 0; j->f_mm = 0; dk3app_process_stdin_chars( j->app, (void *)j, dkt_lat_char_handler, j->enc_s ); dkt_lat_finish_file(j); } if(j->f_pkg) { dk3uc2lat_package_reset(j->uc2l); while((cpkg = dk3uc2lat_package_next(j->uc2l)) != NULL) { if(cpkg->used) { if(cpkg->name) { fputs(dkt_lat_mm[4], stdout); fputs(cpkg->name, stdout); fputs(dkt_lat_mm[5], stdout); } } } } dk3uc2lat_font_encoding_report(j->uc2l); dk3uc2lat_close(j->uc2l); j->uc2l = NULL; } else { j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } int dkt_lat( dk3_app_t *app, dkChar const *sn, dkChar const * const *msg, dkChar const * const *kwnl ) { int back = DKT_RESULT_ERR_UNSPECIFIC; DKT_LAT_J j; $? "+ dkt_lat" j.app = app; j.msg = msg; j.kwnl = kwnl; dkt_lat_job_init(&j); dkt_tool_read_conf(app, sn, (void *)(&j), dkt_lat_conf_line); $? ". file encoding = %d, stdin encoding = %d", j.enc_f, j.enc_s if(dkt_lat_process_arguments(&j)) { j.exval = DKT_RESULT_OK; dkt_lat_run(&j); } back = j.exval; dkt_lat_job_cleanup(&j); $? "- dkt_lat %d", back return back; } /* vim: set ai sw=2 : */