%% options copyright owner = Dirk Krause copyright year = 2011-2014 license = bsd %% module /** @file dkct.c Dirk Krause's C/C++ tool. */ #if DK3_USE_WX #include "dkwxtrace.h" #else #include "dkct.h" #endif $!trace-include /** Application group name. */ static dkChar const dkct_group_name[] = { dkT("dkt-3") }; /** Version number string. */ static dkChar const dkct_version[] = { dkT("dkct ") DKT_VERSION }; /** License conditions shown for dkct --license. */ static dkChar const * const dkct_license[] = { $!text macro=dkT Copyright (c) 2011-2013, Dirk Krause All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder(s) nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. $!end }; /** Options for the dkct program. */ static dk3_option_t const dkct_options[] = { { dkT('R'), dkT("reset"), 0 }, { dkT('d'), dkT("debug"), 0 }, { dkT('s'), dkT("debug-stdout"), 0 }, { dkT('l'), dkT("line-numbers"), 0 }, { dkT('m'), dkT("make"), 0 }, { dkT('b'), dkT("box-width"), 1 }, { dkT('h'), dkT("help"), 0 }, { dkT('v'), dkT("version"), 0 }, { dkT('L'), dkT("license-terms"), 0 }, { dkT('\0'),dkT("license"), 0 }, { dkT('t'), dkT("timestamp"), 0 }, { dkT('k'), dkT("trace-keyword"), 0 }, { dkT('w'), dkT("windows-wide-char"), 0 }, { dkT('S'), dkT("splint"), 0 }, { dkT('\0'),dkT("splint-comment-char"), 1 } }; /** Number of options in the \a dkct_options array. */ static size_t dkct_sz_options = sizeof(dkct_options)/sizeof(dk3_option_t); /** Keywords without need for localization. */ static dkChar const * dkct_not_localized[] = { /* 0 */ dkT("."), /* Current directory. */ NULL }; /** File name for help file. */ static dkChar const dkct_help_file_name[] = { dkT("dkct.txt") }; /** Default help text, shown if help text file is not found. */ static dkChar const * dkct_help_text[] = { $!text file=dkct.txt,macro=dkT NAME dkct - Dirk Krause's C tool SYNOPSIS dkt [] [] DESCRIPTION The program provides: - A preprocessor for debug/trace instructions, - a code generator for state machines, - a code generator for GUIs using the wxWidgets library. The program converts *.ctr, *.cpt, *.mtr and *.jtr files to *.c, *.cpp, *.m and *.java. The source files can contain debug instructions in a special notation. When producing release versions the debug instructions are ignored. When producing debug versions the debug instructions are converted to output instructtions. OPTIONS -R --reset Ignore all settings from configuration files. -d --debug Create debug output. -s --debug-stdout Debug output goes to standard output. -t --timestamp Debug output contains a timestamp. -k --trace-keyword Debug output contains "trace" string. -w --windows-wide-char Prepare debug output for wchar_t. -l --line-numbers Write preprozessor directives "#line ..." to output. -m --make make mode: rebuild destination files only if necessary. -b --box-width= Set box with for comment boxes. -h --help Show help text. -v --version Show version number. -L --license-terms Show license conditions. RETURN VALUE The program returns 0 on success, any other value indicates an error. SEE ALSO http://dktools.sourceforge.net AUTHOR Dirk Krause LICENSE Use dkct --license-terms to view the license conditions. $!end }; /** Initialize option set. @param o Option set. */ static void dkct_option_set_init(DKCT_OPTION_SET *o) { o->deb = 0; o->lnn = 0; o->mak = 0; o->sty = 0; o->bw = 78; o->tkw = 0; o->ts = 0; o->win = 0; o->tip = 0; o->deben = 1; o->spls = '\0'; } /** Initialize job structure. @param j Structure to initialize. */ static void dkct_job_init(DKCT_J *j) { j->exval = DKT_RESULT_ERR_UNSPECIFIC; j->app = NULL; j->msg = NULL; j->nlc = NULL; j->opt = NULL; j->sCwd = NULL; j->cmd = 0; j->curdi = 0; dkct_option_set_init(&(j->dkcto)); } /** Clean up job structure. @param j Job structure to clean up. */ static void dkct_job_cleanup(DKCT_J *j) { if(j->opt) { dk3opt_close(j->opt); } j->opt = NULL; if(j->app) { dk3app_close(j->app); } j->app = NULL; j->msg = NULL; j->nlc = NULL; } /** Process command line arguments, search for options. @param j Job structure. @return 1 on success, 0 on error. */ static int dkct_process_arguments(DKCT_J *j) { int back = 0; int i; /* Temporary value to read box width. */ dkChar const *arg; /* Argument value. */ $? "+ dkct_process_arguments" j->opt = dk3opt_open_from_app( dkct_options, dkct_sz_options, 0x00, NULL, j->app ); if(j->opt) { if(dk3opt_get_error_code(j->opt) == 0) { back = 1; if(dk3opt_is_set(j->opt, dkT('R'))) { dkct_option_set_init(&(j->dkcto)); } if(dk3opt_is_set(j->opt, dkT('d'))) { (j->dkcto).deb = 1; } if(dk3opt_is_set(j->opt, dkT('s'))) { (j->dkcto).deb = 2; } if((j->dkcto).deb) { if(dk3opt_is_set(j->opt, dkT('t'))) { (j->dkcto).ts = 1; } if(dk3opt_is_set(j->opt, dkT('k'))) { (j->dkcto).tkw = 1; } if(dk3opt_is_set(j->opt, dkT('w'))) { (j->dkcto).win = 1; } } if(dk3opt_is_set(j->opt, dkT('l'))) { (j->dkcto).lnn = 1; } if(dk3opt_is_set(j->opt, dkT('m'))) { (j->dkcto).mak = 1; } if(dk3opt_is_set(j->opt, dkT('h'))) { j->cmd |= DK3_APP_CMD_HELP; } if(dk3opt_is_set(j->opt, dkT('v'))) { j->cmd |= DK3_APP_CMD_VERSION; } if(dk3opt_is_set(j->opt, dkT('L'))) { j->cmd |= DK3_APP_CMD_LICENSE; } if(dk3opt_is_set_long(j->opt, dkT("license"))) { j->cmd |= DK3_APP_CMD_LICENSE; } if(dk3opt_is_set(j->opt, dkT('b'))) { arg = dk3opt_get_short_arg(j->opt, dkT('b')); if(arg) { #if VERSION_BEFORE_20140716 if(dk3sf_sscanf3(arg, dkT("%d"), &i)) #else if(0 != dk3ma_i_from_string(&i, arg, NULL)) #endif { if(i > 20) { (j->dkcto).bw = i; } else { j->exval = DKT_RESULT_ERR_OPTION; back = 0; } } else { j->exval = DKT_RESULT_ERR_OPTION; back = 0; } } else { j->exval = DKT_RESULT_ERR_OPTION; back = 0; } } #if VERSION_BEFORE_20140611 if (dk3opt_is_set(j->opt, dkT('S'))) { $? ". option S" arg = dk3opt_get_short_arg(j->opt, dkT('S')); if (arg) { $? ". have arg" if (dk3str_len(arg) == 1) { $? ". length ok" if (128 > (int)(*arg)) { $? ". char ok" (j->dkcto).spls = (char)(*arg); } else { $? "! not ASCII" j->exval = DKT_RESULT_ERR_OPTION; back = 0; /* ##### ERROR: Not an ASCII character */ } } else { $? "! too long" j->exval = DKT_RESULT_ERR_OPTION; back = 0; /* ##### ERROR: Just one character allowed */ } } else { $? "! missing arg" j->exval = DKT_RESULT_ERR_OPTION; back = 0; } } #else if (dk3opt_is_set_long(j->opt, dkct_options[14].lo)) { $? ". --splint-comment-char set" arg = dk3opt_get_long_arg(j->opt, dkct_options[14].lo); if (arg) { if (1 == dk3str_len(arg)) { if (128 > (int)(*arg)) { (j->dkcto).spls = (char)(*arg); } else { /* ##### ERROR: Not an ASCII character! */ j->exval = DKT_RESULT_ERR_OPTION; back = 0; } } else { /* ##### ERROR: Exactly one character required! */ j->exval = DKT_RESULT_ERR_OPTION; back = 0; } } else { /* ##### ERROR: Argument required for option! */ j->exval = DKT_RESULT_ERR_OPTION; back = 0; } } else { $? ". --splint-comment-char not set" } if (dk3opt_is_set(j->opt, dkT('S'))) { $? ". -S set" if ('\0' == (j->dkcto).spls) { (j->dkcto).spls = '@'; } } else { $? ". -S not set" } #endif } else { j->exval = DKT_RESULT_ERR_OPTION; } } else { j->exval = DKT_RESULT_ERR_OPTION; } $? "- dkct_process_arguments %d exval=%d", back, j->exval return back; } /** Run for file after expanding wildcards. @param j Job structure. @param fn Source file name. @param sufi Source file suffix index. */ static void dkct_really_run_for_file(DKCT_J *j, dkChar const *fn, int sufi) { char shn[DK3_MAX_PATH]; /* Short file name. */ dkChar const *pstart; /* Short file name. */ int ie; /* System input encoding. */ DKCT_SRC *psrc; /* Source structure. */ int success; /* Flag: Success so far. */ $? "+ dkct_really_run_for_file \"%s\"", fn pstart = dk3str_rchr(fn, DK3_CHAR_SEP); if(pstart) { pstart++; } else { pstart = fn; } if(pstart) { ie = dk3app_get_encoding(j->app); if(dk3str_to_c8p_app(shn, sizeof(shn), pstart, ie, j->app)) { $? ". shn ok" psrc = dkct_tr_new(&(j->dkcto), shn, sufi, j->app, fn); if(psrc) { $? ". psrc ok" success = 0; if(j->curdi) { psrc->curdi = 1; } psrc->msg = j->msg; dk3app_log_1(j->app, DK3_LL_PROGRESS, j->msg, 106); if(dkct_tr_read(psrc, fn, sufi)) { $? ". read ok" if(dkct_tr_check(psrc, fn, sufi)) { $? ". check ok" if(dkct_tr_write(psrc, fn, sufi)) { success = 1; } } } dk3app_set_source_line(j->app, 0UL); dk3app_log_1(j->app, DK3_LL_PROGRESS, j->msg, 107); if(!success) { j->exval = DKT_RESULT_ERR_UNSPECIFIC; switch(psrc->ec) { case DK3_ERROR_MEMORY: { j->exval = DKT_RESULT_ERR_MEMORY; } break; case DK3_ERROR_SYNTAX: { j->exval = DKT_RESULT_ERR_INPUT; } break; } } dkct_tr_delete(psrc); psrc = NULL; } else { j->exval = DKT_RESULT_ERR_MEMORY; } } } $? "- dkct_really_run_for_file" } /** Run for a file name. @param j Job structure. @param fn Source file name. */ static void dkct_run_for_file(DKCT_J *j, dkChar const *fn) { int must_run = 1; /* Flag: Must run. */ int ai; /* Source suffix type. */ dkChar const *su; /* Source suffx. */ dkChar const *oldsourcename; /* Old source file name. */ dkChar const *shortSourceName; /* Short source file name. */ dkChar *xsu; /* Suffix position. */ dkChar const * const *lfdsu; /* Destination suffix. */ dkChar b1[DK3_MAX_PATH]; /* Destination file name. */ unsigned long oldsourceline; /* Old source line number. */ $? "+ dkct_run_for_file \"%s\"", fn oldsourcename = dk3app_get_source_file(j->app); oldsourceline = dk3app_get_source_line(j->app); if(j->curdi) { shortSourceName = dk3str_rchr(fn, (dk3app_not_localized(20))[0]); if(shortSourceName) { shortSourceName++; } else { shortSourceName = fn; } } else { shortSourceName = fn; } dk3app_set_source_file(j->app, shortSourceName); dk3app_set_source_line(j->app, 0UL); su = dk3str_get_suffix(fn); if(su) { #if DK3_ON_WINDOWS || DK3_HAVE_FNCASEINS ai = dk3str_array_index(dkct_str_get_source_suffixes(), su, 0); #else ai = dk3str_array_index(dkct_str_get_source_suffixes(), su, 1); #endif if(ai >= 0) { if((j->dkcto).mak) { if(dk3str_len(fn) < DK3_SIZEOF(b1,dkChar)) { dk3str_cpy_not_overlapped(b1, fn); lfdsu = (dkct_str_get_dest_suffixes())[ai]; xsu = dk3str_get_suffix(b1); if(xsu) { must_run = 0; while(*lfdsu) { *xsu = dkT('\0'); if((dk3str_len(b1)+dk3str_len(*lfdsu)) < DK3_SIZEOF(b1,dkChar)) { dk3str_cat(b1, *lfdsu); if(dk3sf_must_rebuild(b1, fn)) { must_run = 1; } } else { must_run = 1; } lfdsu++; } } } } if(must_run) { dkct_really_run_for_file(j, fn, ai); } else { /* Progress: All output files up to date. 105 */ dk3app_log_1(j->app, DK3_LL_PROGRESS, j->msg, 105); } } else { /* Wrong suffix, not an input file! */ dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 0, 1, fn); j->exval = DKT_RESULT_ERR_FILENAME; } } else { /* No suffix, Not an input file! */ dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 0, 1, fn); j->exval = DKT_RESULT_ERR_FILENAME; } dk3app_set_source_file(j->app, oldsourcename); dk3app_set_source_line(j->app, oldsourceline); $? "- dkct_run_for_file" } /** Run for a directory. @param j Job structure. @param dn Directory name. @param fcd Flag: Use chdir() to enter directory (ignored). */ static void dkct_run_for_directory(DKCT_J *j, dkChar const *dn, int fcd) { int ok = 1; /* Flag: Ok so far. */ dk3_dir_t *dir; /* Directory. */ dkChar const *en; /* Current file name. */ dkChar const *su; /* File name suffix. */ dk3_stat_t const *es; /* Stat information for current file. */ int ai; /* Source suffix type. */ if(fcd) { #if 0 /* Change into directory dn. */ if(!dk3sf_chdir_app(dn, j->app)) { ok = 0; } #endif } if(ok) { dir = dk3dir_open_app(dn, j->app); if(dir) { while(dk3dir_get_next_file(dir)) { en = dk3dir_get_fullname(dir); es = dk3dir_get_stat(dir); if((en) && (es)) { switch((es->ft) & (~(DK3_FT_SYMLINK))) { case DK3_FT_REGULAR: { su = dk3str_get_suffix(en); if(su) { #if DK3_ON_WINDOWS || DK3_HAVE_FNCASEINS ai = dk3str_array_index(dkct_str_get_source_suffixes(), su, 0); #else ai = dk3str_array_index(dkct_str_get_source_suffixes(), su, 1); #endif if(ai >= 0) { dkct_run_for_file(j, en); } } } break; } } } dk3dir_close(dir); } else { /* ERROR: Failed to open directory! */ j->exval = DKT_RESULT_ERR_OPENDIR; } } if(fcd) { #if 0 /* Change back into previous current directory. */ if(ok) { dk3sf_chdir_app(j->sCwd, j->app); } #endif } } /** Run for a directory, test for current directory before running. @param j Job structure. @param fn Directory name. */ static void dkct_run_for_directory_test_current(DKCT_J *j, dkChar const *fn) { if(dk3str_cmp(fn, dkct_not_localized[0]) == 0) { j->curdi = 1; dkct_run_for_directory(j, fn, 0); } else { dkct_run_for_directory(j, fn, 1); } } /** Run for the given file names. @param j Job structure. */ static void dkct_run(DKCT_J *j) { int nfn; /* Number of file names. */ dkChar const *arg; /* Current argument. */ dkChar const *en; /* File name. */ dkChar bu[DK3_MAX_PATH]; /* Buffer input file name. */ dk3_stat_t stb; /* Stat inf for input file. */ dk3_stat_t const *es; /* Stat inf for output file. */ dk3_dir_t *fne; /* File name expander. */ int i; /* Current argument index. */ int found = 0; /* Flag: Matching file found. */ nfn = dk3opt_get_num_args(j->opt); if(nfn > 0) { for(i = 0; i < nfn; i++) { arg = dk3opt_get_arg(j->opt, i); if(arg) { if(dk3str_len(arg) < DK3_SIZEOF(bu,dkChar)) { dk3str_cpy_not_overlapped(bu, arg); dk3str_correct_filename(bu); if(dk3sf_must_expand(bu)) { fne = dk3dir_fne_open_app(bu, j->app); if(fne) { found = 0; while(dk3dir_get_next_file(fne)) { en = dk3dir_get_fullname(fne); es = dk3dir_get_stat(fne); if((en) && (es)) { switch((es->ft) & (~(DK3_FT_SYMLINK))) { case DK3_FT_REGULAR: { found = 1; dkct_run_for_file(j, en); } break; default: { /* Illegal file type! */ dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 2, 3, en); j->exval = DKT_RESULT_ERR_INPUT; } break; } } } while(dk3dir_get_next_directory(fne)) { en = dk3dir_get_fullname(fne); es = dk3dir_get_stat(fne); if((en) && (es)) { switch((es->ft) & (~(DK3_FT_SYMLINK))) { case DK3_FT_DIRECTORY: { dkct_run_for_directory_test_current(j, en); } break; default: { /* Illegal file type! */ dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 2, 3, en); j->exval = DKT_RESULT_ERR_INPUT; } break; } } } if(!found) { /* ERROR: No such file or directory! */ dk3app_log_i3(j->app, DK3_LL_ERROR, 215, 216, bu); j->exval = DKT_RESULT_ERR_FILENAME; } dk3dir_close(fne); } else { /* ERROR: No fne */ j->exval = DKT_RESULT_ERR_MEMORY; } } else { if(dk3sf_stat_app(&stb, bu, j->app)) { switch((stb.ft) & (~(DK3_FT_SYMLINK))) { case DK3_FT_REGULAR: { dkct_run_for_file(j, bu); } break; case DK3_FT_DIRECTORY: { dkct_run_for_directory_test_current(j, bu); } break; default: { /* ERROR: Illegal file type! */ dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 2, 3, bu); j->exval = DKT_RESULT_ERR_FILENAME; } break; } } else { /* ERROR: No such file or directory! */ j->exval = DKT_RESULT_ERR_FILENAME; } } } else { /* ERROR: Name too long! */ dk3app_log_i3(j->app, DK3_LL_ERROR, 65, 66, arg); j->exval = DKT_RESULT_ERR_FILENAME; } } else { /* BUG */ j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } } else { j->curdi = 1; dkct_run_for_directory(j, j->sCwd, 0); } } /** Show license conditions. @param j Job structure. */ static void dkct_show_license(DKCT_J *j) { dkChar const * const *sptr; /* Pointer to traverse text. */ sptr = dkct_license; dk3sf_initialize_stdout(); while(*sptr) { dk3sf_fputs(*(sptr++), stdout); dk3sf_fputc(dkT('\n'), stdout); } } /** The main() function. @param argc Number of command line arguments. @param argv Command line arguments array. @return 0 on success, any other value indicates an error. */ DK3_MAIN { int exval = 0; /* Exit status code. */ DKCT_J j; /* Job structure. */ dkChar cd[DK3_MAX_PATH]; /* Buffer for current directory. */ $!trace-init dkct.deb $? "+ main" dkct_job_init(&j); j.app = dk3app_open_command( argc, (dkChar const * const *)argv, dkct_group_name ); if(j.app) { j.msg = dk3app_messages( j.app, dkct_str_get_string_table_file_name(), (dkChar const **)dkct_str_get_message_texts() ); j.nlc = dkct_not_localized; if(dk3sf_getcwd_app(cd, DK3_SIZEOF(cd,dkChar), j.app)) { j.sCwd = cd; if(dkct_process_arguments(&j)) { j.exval = DKT_RESULT_OK; if(j.cmd) { /* Help, version, license */ if((j.cmd) & DK3_APP_CMD_VERSION) { dk3sf_initialize_stdout(); dk3sf_fputs(dkct_version, stdout); dk3sf_fputc(dkT('\n'), stdout); } if((j.cmd) & DK3_APP_CMD_LICENSE) { dkct_show_license(&j); } if((j.cmd) & DK3_APP_CMD_HELP) { dk3app_help(j.app, dkct_help_file_name, dkct_help_text); } } else { dkct_run(&j); } } } } exval = j.exval; dkct_job_cleanup(&j); $? "- main %d", exval $!trace-end fflush(stdout); exit(exval); return exval; } /* vim: set ai sw=2 : */