/* Copyright (c) 2008-2010, 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 opyright 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 Dirk Krause 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. */ /** @file filtmsql.c The filtmsql program. */ /** Inside the filtmsql module. */ #define FILTMSQL_C 1 #include "filtmsql.h" #include "dktools-version.h" $(trace-include) /** Exit status. */ static int exval = 1; #ifndef GROUPNAME /** Application group name. */ #define GROUPNAME "dktools" #endif #ifndef PROGRAMNAME /** Application name. */ #define PROGRAMNAME "filtmsql" #endif /** System configuratin directory. */ static char scdir[] = { DK_SYSCONFDIR }; /** Application group name. */ static char grname[] = { GROUPNAME }; /** Application name. */ static char programname[] = { PROGRAMNAME }; /** Version number. */ static char versnumb[] = { VERSNUMB }; /** Long options. */ static char *long_keywords[] = { "h$elp", "v$ersion", NULL }; /** License terms. */ static char *license_terms[] = { "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 Dirk Krause nor the names of other 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.", NULL }; /** Help text. */ static char *help_text[] = { "filtmsql - Reformatter for mysqldump output", "===========================================", "", "Usage:", "------", "", "filtmsql", "filtmsql ", "filtmsql ", "\treformats output from mysqldump.", "", "filtmsql -h", "\tshows this help text.", "", "filtmsql -v", "\tshows version information.", "", NULL }; /** Key/value pairs, message title, default message. */ static dk_key_value_t kv[] = { { (char *)"00", (char *)"Option \"" }, { (char *)"01", (char *)"\" is unknown!" }, { (char *)"02", (char *)"Too many file names specified!" }, { (char *)"03", (char *)"Failed to write data to output file!" }, { (char *)"04", (char *)"File pattern \"" }, { (char *)"05", (char *)"\" must match exactly one file name!" }, { (char *)"06", (char *)"Failed to create file name expander!" } }; /** Number of elements in the kv array. */ static size_t szkv = sizeof(kv)/sizeof(dk_key_value_t); /** File open mode "w". */ static char str_w[] = { "w" }; /** File open mode "r". */ static char str_r[] = { "r" }; /** String table name for localized texts. */ static char table_name[] = { "filtmsql" }; $(| genau filtmsql.au) /** Message. @param fj Filtmsql job. @param ll Log level. @param m Index of message in text array. */ static void tool_msg_1 DK_P3(FJ *,fj, int,ll, size_t,m) { dkapp_log_msg(fj->a, ll, &((fj->msg)[m]), 1); } /** Message containing three parts. @param fj Filtmsql job. @param ll Log level. @param m1 Index of first message part in text array. @param m2 Index of third message part in text array. @param t Second message part. */ static void tool_msg_3 DK_P5(FJ *,fj, int,ll, size_t,m1, size_t,m2, char *,t) { char *msgptr[3]; msgptr[0] = (fj->msg)[m1]; msgptr[1] = t; msgptr[2] = (fj->msg)[m2]; dkapp_log_msg(fj->a, ll, msgptr, 3); } /** Check whether the application must run silently or is run as a filter. @param argc Number of command line arguments. @param argv Command line arguments array. @param rs Pointer to result variable (silently). @param rf Pointer to result variable (filter). */ static void silence_check DK_P4(int,argc,char **,argv,int *,rs, int *,rf) { int i, filenames; char *ptr, **lfdptr; filenames = 0; lfdptr = argv; lfdptr++; i = 1; while(i < argc) { ptr = *lfdptr; if(*ptr == '-') { ptr++; if(*ptr == 's') { if(rs) *rs = 1; } } else { filenames++; } lfdptr++; i++; } if(filenames < 2) { if(rf) *rf = 1; } } /** Initialize FJ structure. @param fj Filtmsql job to initialize. */ static void fj_init DK_P1(FJ *,fj) { fj->a = NULL; fj->cmd = FILTMSQL_CMD_NORMAL; fj->exval = 1; fj->ifn1 = fj->ofn1 = fj->ifn2 = fj->ofn2 = NULL; fj->inf = NULL; fj->outf = NULL; } /** Process arguments. Check for -h, -v, --help, --version, -s and for file names. @param fj Filtmsql job. @return 1 on success, 0 on error. */ static int process_args DK_P1(FJ *,fj) { int back = 1, i = 0, xargc = 0; char *p1, *ptr, **xargv, **lfdptr; xargc = dkapp_get_argc(fj->a); xargv = dkapp_get_argv(fj->a); lfdptr = xargv; lfdptr++; i = 1; while(i < xargc) { ptr = *lfdptr; if(*ptr == '-') { p1 = ptr; p1++; switch(*p1) { case '-' : { p1++; switch(dkstr_array_abbr(long_keywords, p1, '$', 0)) { case 0: { fj->cmd |= FILTMSQL_CMD_HELP; } break; case 1: { fj->cmd |= FILTMSQL_CMD_VERSION; } break; default: { back = 0; /* ERROR: Unknown option */ tool_msg_3(fj, DK_LOG_LEVEL_ERROR, 0, 1, ptr); } break; } } break; case 'h' : { fj->cmd |= FILTMSQL_CMD_HELP; } break; case 'v' : { fj->cmd |= FILTMSQL_CMD_VERSION; } break; case 's' : { } break; default: { back = 0; /* ERROR: Unknown option */ tool_msg_3(fj, DK_LOG_LEVEL_ERROR, 0, 1, ptr); } break; } } else { if(fj->ifn1) { if(fj->ofn1) { back = 0; /* ERROR: Too many file name arguments */ tool_msg_1(fj, DK_LOG_LEVEL_ERROR, 2); } else { fj->ofn1 = ptr; } } else { fj->ifn1 = ptr; } } lfdptr++; i++; } return back; } /** Show help text. @param fj Filtmsql job. */ static void print_help DK_P1(FJ *,fj) { dkapp_help(fj->a, "filtmsql.txt", help_text); } /** Show version information. @param fj Filtmsql job. */ static void print_version DK_P1(FJ *,fj) { char **ptr = NULL; printf( "\n%s (part of the dktools collection, version %s)\n", programname, versnumb ); printf("MySQL dump reformatter\n"); printf("Copyright (C) 2008-2010 - Dipl.-Ing. Dirk Krause\n"); printf("http://dktools.sourceforge.net/\n\n"); ptr = license_terms; while(*ptr) { printf("%s\n", *(ptr++)); } } /** Classify input character to produce input for automata functions. @param c Character to classify. @return Classification. */ static int classify_input DK_P1(char,c) { int back = I_ANY; switch(c) { case '`': { back = I_BT; } break; case '\\': { back = I_BS; } break; case '\'': { back = I_SQ; } break; case '"': { back = I_DQ; } break; case '-': { back = I_MINUS; } break; case '/': { back = I_SLASH; } break; case '*': { back = I_ASTERISK; } break; case '(': { back = I_BROPEN; } break; case ')': { back = I_BRCLOSE; } break; case ',': { back = I_COMMA; } break; case '\n': { back = I_NL; } break; } return back; } /** Flush pending output. @param fj Filtmsql job. */ static void flush_output DK_P1(FJ *,fj) { size_t res; if(fj->us_ob) { res = fwrite(fj->ob, sizeof(char), fj->us_ob, fj->outf); if(res != fj->us_ob) { /* Problem while writing */ tool_msg_1(fj, DK_LOG_LEVEL_ERROR, 3); fj->exval = 1; } } fj->us_ob = 0; } /** Add one character to output buffer, flush if necessary. @param fj Filtmsql job. @param c Current character to process. */ static void add_to_output DK_P2(FJ *,fj, char,c) { (fj->ob)[fj->us_ob] = c; fj->us_ob += 1; if(fj->us_ob >= fj->sz_ob) { flush_output(fj); } } /** Do a conversion. The files are opened, buffers were allocated. @param fj Filtmsql job. */ static void run_for_buffers DK_P1(FJ *,fj) { int can_continue; /* flag, can continue */ int it; /* input type */ int s; /* current state */ long br; /* brackets level */ size_t rdbytes; /* number of input bytes read successfully */ size_t i; /* index of current input byte to process */ fj->us_ob = 0; br = 0L; filtmsql_reset(&s); can_continue = 1; while(can_continue) { can_continue = 0; rdbytes = fread(fj->ib, sizeof(char), fj->sz_ib, fj->inf); if(rdbytes > 0) { can_continue = 1; for(i = 0; i < rdbytes; i++) { it = classify_input((fj->ib)[i]); switch(filtmsql_decide(&s, it)) { case O_PRINT_NEWLINE: { if(br == 0L) { add_to_output(fj, '\n'); } add_to_output(fj, (fj->ib)[i]); } break; default: { add_to_output(fj, (fj->ib)[i]); } break; } switch((fj->ib)[i]) { case '(': { switch(s) { case S_START: case S_BRCLOSE: case S_BRCLOSE_COMMA: { br++; } break; } } break; case ')': { switch(s) { case S_START: case S_BRCLOSE: case S_BRCLOSE_COMMA: { br--; if(br < 0L) br = 0L; } break; } } break; } } } } flush_output(fj); } /** Start processing of files. Allocate buffers and invoke the conversion. @param fj Filtmsql job. */ static void run_for_files DK_P1(FJ *,fj) { char inb1def[256], outb1def[256], *ib, *ob; size_t sz; sz = (size_t)16384U; ib = dk_new(char,sz); ob = dk_new(char,sz); if(ib) { fj->ib = ib; fj->sz_ib = sz; } else { fj->ib = inb1def; fj->sz_ib = sizeof(inb1def); } if(ob) { fj->ob = ob; fj->sz_ob = sz; } else { fj->ob = outb1def; fj->sz_ob = sizeof(outb1def); } run_for_buffers(fj); if(ob) { dk_delete(ob); } if(ib) { dk_delete(ib); } } /** Run with a known input file. Open the output file if necessary and start processing. @param fj Filtmsql job. */ static void run_for_input_file DK_P1(FJ *,fj) { FILE *outf = NULL; dk_fne_t *fneo = NULL; if(fj->ofn1) { if(dksf_must_expand_filename(fj->ofn1)) { fneo = dkfne_open(fj->ofn1, 1, 0); if(fneo) { fj->ofn2 = dkfne_get_one(fneo); if(fj->ofn2) { outf = dkapp_fopen(fj->a, fj->ofn2, str_w); if(outf) { fj->outf = outf; fj->exval = 0; run_for_files(fj); fclose(outf); outf = NULL; fj->outf = NULL; } else { dkapp_err_fopenw(fj->a, fj->ofn2); } } else { /* ERROR: Not exactly one name */ tool_msg_3(fj, DK_LOG_LEVEL_ERROR, 4, 5, fj->ofn1); } dkfne_close(fneo); fneo = NULL; } else { /* ERROR: Memory */ tool_msg_1(fj, DK_LOG_LEVEL_ERROR, 6); } } else { outf = dkapp_fopen(fj->a, fj->ofn1, str_w); if(outf) { fj->ofn2 = fj->ofn1; fj->outf = outf; fj->exval = 0; run_for_files(fj); fclose(outf); outf = NULL; fj->outf = NULL; } else { dkapp_err_fopenw(fj->a, fj->ofn1); } } } else { fj->outf = stdout; fj->ofn1 = fj->ofn2 = NULL; fj->exval = 0; run_for_files(fj); } } /** Run with specified file names. Test file names whether or not an expansion is needed. @param fj Filtmsql job. */ static void run_conversion DK_P1(FJ *,fj) { FILE *inf = NULL; dk_fne_t *fneo = NULL; if(fj->ifn1) { if(dksf_must_expand_filename(fj->ifn1)) { fneo = dkfne_open(fj->ifn1, 1, 0); if(fneo) { fj->ifn2 = dkfne_get_one(fneo); if(fj->ifn2) { inf = dkapp_fopen(fj->a, fj->ifn2, str_r); if(inf) { fj->inf = inf; run_for_input_file(fj); fclose(inf); inf = NULL; fj->inf = NULL; } else { dkapp_err_fopenr(fj->a, fj->ifn2); } } else { /* ERROR: No matching file or to many files */ tool_msg_3(fj, DK_LOG_LEVEL_ERROR, 4, 5, fj->ifn1); } dkfne_close(fneo); fneo = NULL; } else { /* ERROR: Memory */ tool_msg_1(fj, DK_LOG_LEVEL_ERROR, 6); } } else { inf = dkapp_fopen(fj->a, fj->ifn1, str_r); if(inf) { fj->ifn2 = fj->ifn1; fj->inf = inf; run_for_input_file(fj); fclose(inf); inf = NULL; fj->inf = NULL; } else { dkapp_err_fopenr(fj->a, fj->ifn1); } } } else { fj->inf = stdin; fj->outf = stdout; fj->exval = 0; fj->ifn1 = fj->ifn2 = fj->ofn1 = fj->ofn2 = NULL; run_for_files(fj); } } /** Process command line arguments. Either run a conversion or print help/version information. @param fj Filtmsql job. */ static void run DK_P1(FJ *,fj) { $? "+ run" if(process_args(fj)) { if(fj->cmd) { if((fj->cmd) & (FILTMSQL_CMD_HELP | FILTMSQL_CMD_VERSION)) { fj->exval = 0; print_version(fj); if((fj->cmd) & FILTMSQL_CMD_HELP) { print_help(fj); } } } else { run_conversion(fj); } } $? "- run" } /** Character pointer. */ typedef char *PCHAR; /** The main() function of the filtmsql program. @param argc Number of command line arguments. @param argv Command line arguments array. @return 0 on success, any other value indicates an error. */ #ifdef DK_HAVE_PROTOTYPES int main(int argc, char *argv[]) #else int main(argc, argv) int argc; char *argv[]; #endif { FJ fj; int rs = 0, rf = 0; char *x; $(trace-init filtmsql.deb) $? "+ main" silence_check(argc, argv, &rs, &rf); fj_init(&fj); fj.a = dkapp_open_ext1(argc,argv, grname, scdir, rs, rf); if(fj.a) { fj.msg = dkapp_find_key_value(fj.a, kv, szkv, table_name); if(fj.msg) { run(&fj); exval = fj.exval; x = (char *)(fj.msg); dk_delete(x); } else { dkapp_err_memory(fj.a, sizeof(PCHAR), szkv); } dkapp_close(fj.a); fj.a = NULL; } else { if(!rs) { /* ERROR: Memory */ fprintf(stderr, "ERROR: Not enough memory (RAM/swap space)!\n"); fflush(stderr); } } $? "- main %d", exval $(trace-end) exit(exval); return exval; }