%% options copyright owner = Dirk Krause copyright year = 2011-2014 license = bsd %% module #include "dk3all.h" #include "dkt.h" $!trace-include /** Job structure for dkt checksum. */ typedef struct { dkChar const * const *msg; /**< Localized message texts. */ dkChar const * const *kwnl; /**< Keywords, not localized. */ dk3_app_t *app; /**< Application structure. */ dk3_option_set_t *opt; /**< Option set. */ int exval; /**< Exit status code. */ int enc_s; /**< Stdin default encoding. */ int enc_f; /**< File default encoding. */ int mdt; /**< Message digest type. */ int mde; /**< Message digest encoding. */ int f_lst; /**< Flag: Arguments are file lists */ int f_chk; /**< Flag: Check. */ int f_sz; /**< Flag: Size (1) or checksum (0). */ int l_vrb; /**< Verbosity level. */ int f_first; /**< Flag: First entry to handle. */ int f_msm; /**< Flag: Mismatch found! */ } DKT_CHKS_J; /** Directory chain cell. */ typedef struct { void *parent; /**< Parent chain element. */ dk3_dir_t *dir; /**< Directory structure. */ dk3_stat_t ds; /**< Stat information for directory. */ } DKT_CHKS_DIR; /** Options used by dkt checksum. */ static dk3_option_t const dkt_chks_options[] = { { dkT('R'), dkT("reset"), 0 }, { dkT('m'), dkT("message-digest"), 1 }, { dkT('l'), dkT("file-list"), 0 }, { dkT('i'), dkT("input-encoding"), 1 }, { dkT('p'), dkT("plain"), 0 }, { dkT('c'), dkT("check"), 0 }, { dkT('v'), dkT("verbose"), 0 }, { dkT('q'), dkT("quiet"), 0 }, }; /** Number of options in the dkt_chks_options array. */ static size_t const dkt_chks_szoptions = sizeof(dkt_chks_options)/sizeof(dk3_option_t); /** Configuration file keywords. */ static dkChar const * const dkt_chks_conf_kw[] = { dkT("reset"), dkT("stdin-encoding"), dkT("file-encoding"), dkT("message-digest"), NULL }; /** Delete directory chain element, release memory. @param dp Direcrory chain element. */ static void dkt_chks_dir_delete(DKT_CHKS_DIR *dp) { if(dp) { if(dp->dir) { dk3dir_close(dp->dir); } dp->dir = NULL; dp->parent = NULL; dk3_delete(dp); } } /** Create directory chain element, allocate memory. @param parent Parent chain element. @param fn File name. @param stb Stat buffer for the file. @param app Application structure for diagnostics. @return Pointer to new element on success, NULL on error. */ static DKT_CHKS_DIR * dkt_chks_dir_new( DKT_CHKS_DIR *parent, dkChar const *fn, dk3_stat_t const *stb, dk3_app_t *app ) { DKT_CHKS_DIR *back = NULL; back = dk3_new_app(DKT_CHKS_DIR,1,app); if(back) { back->parent = NULL; back->dir = NULL; back->parent = parent; dk3mem_cpy(&(back->ds),stb,sizeof(dk3_stat_t)); back->dir = dk3dir_open_app(fn, app); if(!(back->dir)) { dkt_chks_dir_delete(back); back = NULL; } } return back; } /** Initialize job structure. @param j Structure to initialize. */ static void dkt_chks_job_init(DKT_CHKS_J *j) { j->msg = NULL; j->kwnl = NULL; j->app = NULL; j->opt = NULL; j->enc_s = DK3_FILE_ENCODING_ASCII; j->enc_f = DK3_FILE_ENCODING_ASCII; j->exval = DKT_RESULT_ERR_UNSPECIFIC; j->mdt = DK3_MD_TYPE_SHA_1; j->mde = DK3_DATA_ENCODING_ASCII85; j->f_lst = 0; j->f_chk = 0; j->f_sz = 0; j->l_vrb = 1; j->f_first = 1; j->f_msm = 0; } /** Reset options in a job structure (-R or --reset option). @param j Job structure. */ static void dkt_chks_job_reset(DKT_CHKS_J *j) { j->enc_s = dk3app_get_default_stdin_encoding(j->app); j->enc_f = dk3app_get_default_file_encoding(j->app); j->mdt = DK3_MD_TYPE_SHA_1; j->mde = DK3_DATA_ENCODING_ASCII85; } /** Clean up job structure. */ static void dkt_chks_job_cleanup(DKT_CHKS_J *j) { if(j->opt) { dk3opt_close(j->opt); } j->opt = NULL; } /** Process one key/value pair from configuration files. @param jv Pointer to job structure casted to void *. @param k Key. @param v Value. @return 1 to indicate success. */ static int dkt_chks_conf_line(void *jv, dkChar const *k, dkChar const *v) { int back = 0; DKT_CHKS_J *j; $? "+ dkt_ls_conf_line \"%s\"=\"%s\"", TR_STR(k), TR_STR(v) j = (DKT_CHKS_J *)jv; switch(dk3str_array_index(dkt_chks_conf_kw, k, 0)) { case 0: { /* reset */ dkt_chks_job_reset(j); back = 1; } break; case 1: { /* stdin encoding */ if(v) { back = dkt_tool_set_encoding( j->app, &(j->enc_s), v, dk3app_get_input_stdin_encoding(j->app) ); } } break; case 2: { /* file encoding */ if(v) { back = dkt_tool_set_encoding( j->app, &(j->enc_f), v, dk3app_get_input_stdin_encoding(j->app) ); } } break; case 3: { /* message digest */ if(v) { back = dkt_tool_set_md_type( &(j->mdt), &(j->mde), &(j->exval), v, j->app, j->msg, 0 ); } } break; } $? "- dkt_ls_conf_line %d", back return back; } /** Process options and command line arguments. @param j Job structure. @return 1 on success (can continue), 0 on error (exit). */ static int dkt_chks_process_arguments(DKT_CHKS_J *j) { int back = 0; int res = 0; /* Checksum operation result. */ int xargc = 0; /* Number of command line arguments. */ dkChar const *arg = NULL; /* Option argument. */ dkChar const * const *xargv = NULL; /* Command line arguments array. */ $? "+ dkt_chks_process_arguments" xargc = dk3app_get_argc(j->app); xargv = dk3app_get_argv(j->app); xargv++; xargv++; xargc--; xargc--; j->opt = dk3opt_open_app( dkt_chks_options, dkt_chks_szoptions, dkT('\0'), NULL, xargc, xargv, j->app ); if(j->opt) { if(0 == dk3opt_get_error_code(j->opt)) { back = 1; if(dk3opt_is_set(j->opt, dkT('R'))) { dkt_chks_job_reset(j); } if(dk3opt_is_set(j->opt, dkT('m'))) { arg = dk3opt_get_short_arg(j->opt, dkT('m')); if(arg) { res = dkt_tool_set_md_type( &(j->mdt), &(j->mde), &(j->exval), arg, j->app, j->msg, 1 ); if(!res) { back = 0; } } else { back = 0; j->exval = DKT_RESULT_ERR_OPTION; } } if(dk3opt_is_set(j->opt, dkT('c'))) { j->f_chk = 1; } if(dk3opt_is_set(j->opt, dkT('l'))) { j->f_lst = 1; } if(dk3opt_is_set(j->opt, dkT('i'))) { arg = dk3opt_get_short_arg(j->opt, dkT('i')); if(arg) { res = dkt_tool_set_encoding( j->app, &(j->enc_f), arg, dk3app_get_input_stdin_encoding(j->app) ); j->enc_s = j->enc_f; if(!res) { back = 0; } } else { back = 0; j->exval = DKT_RESULT_ERR_OPTION; } if(dk3opt_is_set(j->opt, dkT('p'))) { /* ERROR: -i and -p exclusive */ dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 0); back = 0; j->exval = DKT_RESULT_ERR_OPTION; } } else { 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('v'))) { j->l_vrb = 2; if(dk3opt_is_set(j->opt, dkT('q'))) { /* ERROR: -v and -q exclusive */ dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 19); back = 0; j->exval = DKT_RESULT_ERR_OPTION; } } else { if(dk3opt_is_set(j->opt, dkT('q'))) { j->l_vrb = 0; } } } else { /* ERROR */ j->exval = DKT_RESULT_ERR_OPTION; } } else { /* ERROR */ j->exval = DKT_RESULT_ERR_OPTION; } $? "- dkt_chks_process_arguments %d", back return back; } /** Show a test result. @param j Job structure. @param fn File name. @param msgi Message index (17=success, 18=failure). */ static void dkt_chks_result(DKT_CHKS_J *j, dkChar const *fn, size_t msgi) { if(j->f_first) { dk3sf_initialize_stdout(); } j->f_first = 0; dk3sf_fputs(fn, stdout); dk3sf_fputc(dkT(':'), stdout); dk3sf_fputc(dkT(' '), stdout); dk3sf_fputs((j->msg)[msgi], stdout); dk3sf_fputc(dkT('\n'), stdout); } /** Check whether the line is a control instruction. @param p Line pointer. @return 1 for control instruction, 0 for normal line. */ static int dkt_chks_is_instruction(dkChar const *p) { int back = 0; if(*p == dkT('#')) { if((p[1] == dkT(' ')) || (p[1] == dkT('\t'))) { back = 1; } } return back; } /** Handler for one line from a check list file. @param vj Job structure. @param il Input line to process. @return 1 on success, 0 on error (can continue), -1 on error (abort). */ static int dkt_chks_line_handler_2(void *vj, dkChar *il) { int back = 0; dkChar *p1 = NULL; /* Size or checksum. */ dkChar *p2 = NULL; /* File name. */ int res = 0; /* Checksum result. */ dk3_stat_t stb; /* Stat buffer for file. */ DKT_CHKS_J *j = NULL; /* Job structure. */ dkChar bu[256]; /* Message digest as dkChar text. */ char md[256]; /* Message digest as 8-bit text. */ dk3_um_t um; /* Size found in input line. */ $? "+ dkt_chks_line_handler_2" j = (DKT_CHKS_J *)vj; p1 = dk3str_start(il, NULL); if(p1) { dk3str_delnl(p1); if(dkt_chks_is_instruction(p1)) { p1++; p1 = dk3str_start(p1, NULL); if(p1) { p2 = dk3str_chr(p1, dkT('=')); if(p2) { *(p2++) = dkT('\0'); p2 = dk3str_start(p2, NULL); } dk3str_chomp(p1, NULL); if(p2) { dk3str_chomp(p2, NULL); } dk3str_normalize(p1, NULL, dkT('-')); switch(dk3str_array_index(dkt_chks_conf_kw, p1, 0)) { case 3: { if(p2) { res = dkt_tool_set_md_type( &(j->mdt), &(j->mde), &(j->exval), p2, j->app, j->msg, 1 ); if(res) { back = 1; } } else { /* ERROR: Missing value! */ dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 20); j->exval = DKT_RESULT_ERR_INPUT; } } break; default: { /* ERROR: Unknown control instruction */ dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 21, 22, p1); j->exval = DKT_RESULT_ERR_INPUT; } break; } } else { $? ". empty control instruction" /* ERROR: Not produced by dkt checksum */ j->exval = DKT_RESULT_ERR_INPUT; } } else { p2 = dk3str_next(p1, NULL); if(p2) { res = 0; dk3str_correct_filename(p2); if(dk3sf_stat_app(&stb, p2, j->app)) { switch((stb.ft) & (~(DK3_FT_SYMLINK))) { case DK3_FT_REGULAR: { if(j->f_sz) { $? ". check size" #if VERSION_BEFORE_20140716 if(dk3ma_string_to_um(&um, p1)) #else if (0 != dk3ma_um_from_string(&um, p1, NULL)) #endif { if(um == stb.sz) { res = back = 1; } } else { /* ERROR: p1 is not a number! */ dk3app_log_i3(j->app, DK3_LL_ERROR, 141, 142, p1); j->exval = DKT_RESULT_ERR_INPUT; } } else { $? ". check message digest" res = dk3checksum_build_app( md, sizeof(md), p2, j->mdt, j->mde, j->app ); if(res) { res = 0; if( dk3str_cnv_c8_to_str_app(bu,DK3_SIZEOF(bu,dkChar),md,j->app) ) { $? ". CHECKSUM fn=\"%s\", ori=\"%s\", cs=\"%s\"", p2, p1, md switch(j->mde) { case DK3_DATA_ENCODING_HEX: { if(dk3str_casecmp(p1, bu) == 0) { res = back = 1; } } break; default: { if(dk3str_cmp(p1, bu) == 0) { res = back = 1; } } break; } $? ". res=%d", res } else { /* ERROR: Failed to convert MD to string! */ j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } else { /* ERROR: Failed to build checksum! */ j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } } break; default: { /* ERROR: Not a regular file! */ dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 23, 24, p2); j->exval = DKT_RESULT_ERR_FILENAME; } break; } } else { /* ERROR: No such file or directory! */ j->exval = DKT_RESULT_ERR_FILENAME; } if(res) { if(j->l_vrb > 1) { /* SUCCESS message, */ dkt_chks_result(j, p2, 17); } } else { if(j->l_vrb > 0) { /* ERROR message. */ dkt_chks_result(j, p2, 18); } j->f_msm = 1; } } else { /* ERROR: No file name! */ dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 25); j->exval = DKT_RESULT_ERR_INPUT; } } } else { $? ". empty line" /* Empty line, not produced by dkt checksum. */ j->exval = DKT_RESULT_ERR_INPUT; } $? "- dkt_chks_line_handler_2 %d", back return back; } /** Perform checks for a check list. @param j Job structure. */ static void dkt_chks_check(DKT_CHKS_J *j) { dkChar ib[2 * DK3_MAX_PATH + 256]; /* Input line from check list. */ dkChar bu[DK3_MAX_PATH]; /* File name buffer. */ int nfn = 0; /* Number of cmd line args. */ int i = 0; /* Current arg index. */ dkChar const *arg = NULL; /* Current arg. */ dk3_dir_t *fne = NULL; /* File name expander. */ dkChar const *en = NULL; /* Current entry name. */ dk3_stat_t const *es = NULL; /* Entry stat buffer. */ int found = 0; /* Flag: At least one file found. */ $? "+ dkt_chks_check" if(dk3opt_get_num_args(j->opt) > 0) { $? ". check list from file" nfn = dk3opt_get_num_args(j->opt); 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: { (void)dk3stream_process_filename_lines_app( (void *)j, dkt_chks_line_handler_2, en, ib, DK3_SIZEOF(ib,dkChar), dk3app_get_encoding(j->app), j->enc_f, j->app ); } break; default: { /* ERROR: Not a regular file! */ dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 23, 24, en); j->exval = DKT_RESULT_ERR_FILENAME; } break; } } else { /* BUG */ j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } dk3dir_close(fne); } else { /* ERROR: No file name expander! */ j->exval = DKT_RESULT_ERR_FILENAME; } } else { (void)dk3stream_process_filename_lines_app( (void *)j, dkt_chks_line_handler_2, bu, ib, DK3_SIZEOF(ib,dkChar), dk3app_get_encoding(j->app), j->enc_f, j->app ); } } 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 { $? ". check list from stdin" (void)dk3app_process_stdin_lines( j->app, (void *)j, dkt_chks_line_handler_2, ib, DK3_SIZEOF(ib,dkChar), j->enc_s ); } if(j->f_msm) { /* ERROR: Mismatches found! */ dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 26); j->exval = DKT_RESULT_ERR_MISMATCH; } $? "- dkt_chks_check" } /** Show checksum/size and name for one file. @param j Job structure. @param fn File name. @param stb Stat buffer for that file. */ static void dkt_chks_show_one_file(DKT_CHKS_J *j, dkChar const *fn, dk3_stat_t const *stb) { dkChar bu[256]; /* Corrected file name. */ dkChar const *p1 = NULL; /* File name or option name. */ dkChar const *p2 = NULL; /* Option argument. */ char md[256]; /* Checksum buffer. */ #if DK3_CHAR_SIZE > 1 char *ptr = NULL; /* Walk through md. */ #endif int res = 0; $? "+ dkt_chks_show_one_file \"%s\"", TR_STR(fn) if(j->f_sz) { $? ". size" if(dk3ma_um_to_string(bu, DK3_SIZEOF(bu,dkChar), stb->sz)) { if(j->f_first) { dk3sf_initialize_stdout(); } j->f_first = 0; $? ". report size" dk3sf_fputs(bu, stdout); dk3sf_fputc(dkT(' '), stdout); dk3sf_fputs(fn, stdout); dk3sf_fputc(dkT('\n'), stdout); } else { /* ERROR */ dk3app_log_i1(j->app, DK3_LL_ERROR, 162); j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } else { res = dk3checksum_build_app(md, sizeof(md), fn, j->mdt, j->mde, j->app); if(res) { $? ". checksum ok" if(j->f_first) { $? ". is first" dk3sf_initialize_stdout(); /* PRINT DIGEST TYPE AND ENCODING */ p1 = dk3checksum_get_type_name(j->mdt); p2 = dk3enc_get_data_encoding_name(j->mde); dk3sf_fputs((j->kwnl)[5], stdout); dk3sf_fputs(p1, stdout); dk3sf_fputs((j->kwnl)[4], stdout); dk3sf_fputs(p2, stdout); dk3sf_fputc(dkT('\n'), stdout); } j->f_first = 0; $? ". now print message digest" #if DK3_CHAR_SIZE > 1 ptr = md; while(*ptr) { dk3sf_fputc((dkChar)(*(ptr++)), stdout); } #else fputs(md, stdout); #endif dk3sf_fputc(dkT(' '), stdout); dk3sf_fputs(fn, stdout); dk3sf_fputc(dkT('\n'), stdout); } else { $? "! checksum failed" /* ERROR: Failed to build checksum! */ j->exval = DKT_RESULT_ERR_INPUT; } } $? "- dkt_chks_show_one_file" } /** Process one directory. @param j Job structure. @param fn Directory name. @param stb Stat buffer containing information about directory. */ static void dkt_chks_show_directory(DKT_CHKS_J *j, dkChar const *fn, dk3_stat_t *stb) { DKT_CHKS_DIR *curdir = NULL; /* Current directory chain element. */ DKT_CHKS_DIR *nextdir = NULL; /* Next directory chain element. */ dkChar const *en = NULL; /* Current directory entry name. */ dk3_stat_t const *es = NULL; /* Current directory entry stat. */ int can_go_sub = 0; /* Flag: Can go down into sub. */ $? "+ dkt_chks_show_directory \"%s\"", TR_STR(fn) curdir = dkt_chks_dir_new(NULL, fn, stb, j->app); if(curdir) { while(curdir) { if(dk3dir_get_next_directory(curdir->dir)) { en = dk3dir_get_fullname(curdir->dir); es = dk3dir_get_stat(curdir->dir); if((en) && (es)) { can_go_sub = 1; #if DK3_HAVE_INODES nextdir = curdir; while((nextdir) && (can_go_sub)) { if((nextdir->ds).inode == es->inode) { if((nextdir->ds).device == es->device) { can_go_sub = 0; } } if(can_go_sub) { nextdir = (DKT_CHKS_DIR *)(nextdir->parent); } } #endif if(can_go_sub) { nextdir = dkt_chks_dir_new(curdir, en, es, j->app); if(nextdir) { curdir = nextdir; } else { /* ERROR: Memory */ j->exval = DKT_RESULT_ERR_MEMORY; } } } else { /* BUG */ j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } else { $? ". no more subdirectories, process files, go up" while(dk3dir_get_next_file(curdir->dir)) { en = dk3dir_get_fullname(curdir->dir); es = dk3dir_get_stat(curdir->dir); if((en) && (es)) { switch((es->ft) & (~(DK3_FT_SYMLINK))) { case DK3_FT_REGULAR: { dkt_chks_show_one_file(j, en, es); } break; default: { /* ERROR: Not a regular file! */ dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 23, 24, en); j->exval = DKT_RESULT_ERR_FILENAME; } break; } } else { /* BUG */ j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } nextdir = (DKT_CHKS_DIR *)(curdir->parent); dkt_chks_dir_delete(curdir); curdir = nextdir; } } } else { /* ERROR */ j->exval = DKT_RESULT_ERR_MEMORY; } $? "- dkt_chks_show_directory" } /** Process one file name. @param j Job structure. @param fn File name. */ static void dkt_chks_process_one_filename(DKT_CHKS_J *j, dkChar const *fn) { dk3_stat_t stb; /* Stat buffer for file. */ $? "+ dkt_chks_process_one_filename \"%s\"", TR_STR(fn) if(dk3sf_stat_app(&stb, fn, j->app)) { switch((stb.ft) & (~(DK3_FT_SYMLINK))) { case DK3_FT_DIRECTORY: { $? ". directory" dkt_chks_show_directory(j, fn, &stb); } break; case DK3_FT_REGULAR: { $? ". file" dkt_chks_show_one_file(j, fn, &stb); } break; default: { $? ". non-regular" /* ERROR: Not a regular file! */ dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 23, 24, fn); j->exval = DKT_RESULT_ERR_FILENAME; } break; } } else { j->exval = DKT_RESULT_ERR_FILENAME; } $? "- dkt_chks_process_one_filename" } /** Handler function for file list lines. @param vj Job structure. @param il Input line. @return 1 on success, 0 on error (can continue), -1 on error (abort). */ static int dkt_chks_line_handler_1(void *vj, dkChar *il) { int back = 1; int found = 0; /* Flag: At least one file found. */ dk3_dir_t *fne = NULL; /* File name expander. */ dkChar const *en = NULL; /* Entry name. */ dkChar *p1 = NULL; /* Start of input line. */ DKT_CHKS_J *j = NULL; /* Job structure. */ j = (DKT_CHKS_J *)vj; p1 = dk3str_start(il, NULL); if(p1) { dk3str_delnl(p1); dk3str_chomp(p1, NULL); dk3str_correct_filename(p1); if(dk3sf_must_expand(p1)) { fne = dk3dir_fne_open_app(p1, j->app); if(fne) { while(dk3dir_get_next_directory(fne)) { found = 1; en = dk3dir_get_fullname(fne); if(en) { dkt_chks_process_one_filename(j, en); } else { /* BUG */ j->exval = DKT_RESULT_ERR_FILENAME; } } while(dk3dir_get_next_file(fne)) { found = 1; en = dk3dir_get_fullname(fne); if(en) { dkt_chks_process_one_filename(j, en); } else { /* BUG */ j->exval = DKT_RESULT_ERR_FILENAME; } } dk3dir_close(fne); if(!found) { /* ERROR: No file name matching pattern! */ dk3app_log_i3(j->app, DK3_LL_ERROR, 215, 216, p1); j->exval = DKT_RESULT_ERR_FILENAME; } } else { /* ERROR: File name expander! */ j->exval = DKT_RESULT_ERR_FILENAME; } } else { dkt_chks_process_one_filename(j, p1); } } else { j->exval = DKT_RESULT_ERR_INPUT; } return back; } /** Process a file name list, either from file or standard input. @param j Job structure. */ static void dkt_chks_process_filename_list(DKT_CHKS_J *j) { dkChar bu[DK3_MAX_PATH]; /* Private copy of file name. */ dkChar ib[DK3_MAX_PATH]; /* Input line buffer. */ dkChar const *arg = NULL; /* Current argument. */ dkChar const *en = NULL; /* Entry name. */ dk3_dir_t *fne = NULL; /* File name expander. */ dk3_stat_t const *es = NULL; /* Entry stat buffer. */ int found = 0; /* Flag: At least one found. */ int nfn = 0; /* Number of file names. */ int i = 0; /* Current file name index. */ $? "+ dkt_chks_process_filename_list" if(dk3opt_get_num_args(j->opt) > 0) { $? ". file list from files" nfn = dk3opt_get_num_args(j->opt); 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) { 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; (void)dk3stream_process_filename_lines_app( (void *)j, dkt_chks_line_handler_1, en, ib, DK3_SIZEOF(ib,dkChar), dk3app_get_encoding(j->app), j->enc_f, j->app ); } break; default: { j->exval = DKT_RESULT_ERR_FILENAME; dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 23, 24, en); } break; } } else { /* BUG */ j->exval = DKT_RESULT_ERR_FILENAME; } } if(!found) { /* ERROR: No such file! */ j->exval = DKT_RESULT_ERR_FILENAME; dk3app_log_i3(j->app, DK3_LL_ERROR, 215, 216, bu); } dk3dir_close(fne); } else { /* ERROR: Failed to expand file name. */ j->exval = DKT_RESULT_ERR_FILENAME; } } else { dk3stream_process_filename_lines_app( (void *)j, dkt_chks_line_handler_1, bu, ib, DK3_SIZEOF(ib,dkChar), dk3app_get_encoding(j->app), j->enc_f, j->app ); } } 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 { $? ". file list from stdin" (void)dk3app_process_stdin_lines( j->app, (void *)j, dkt_chks_line_handler_1, ib, DK3_SIZEOF(ib,dkChar), j->enc_s ); } $? "- dkt_chks_process_filename_list" } /** Process the command line arguments. @param j Job structure. */ static void dkt_chks_process_argv_arguments(DKT_CHKS_J *j) { int nfn = 0; /* Number of file names available. */ int i = 0; /* Current argument index. */ int found = 0; /* Flag: At least one file found. */ dk3_dir_t *fne = NULL; /* File name expander. */ dkChar const *arg = NULL; /* Current argument. */ dkChar const *en = NULL; /* Entry name. */ dkChar bu[DK3_MAX_PATH]; /* Private file name copy. */ if((nfn = dk3opt_get_num_args(j->opt)) > 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) { while(dk3dir_get_next_directory(fne)) { found = 1; en = dk3dir_get_fullname(fne); if(en) { dkt_chks_process_one_filename(j, en); } else { /* BUG */ j->exval = DKT_RESULT_ERR_FILENAME; } } while(dk3dir_get_next_file(fne)) { found = 1; en = dk3dir_get_fullname(fne); if(en) { dkt_chks_process_one_filename(j, en); } else { /* BUG */ j->exval = DKT_RESULT_ERR_FILENAME; } } dk3dir_close(fne); if(!found) { /* ERROR: No file name matching pattern! */ j->exval = DKT_RESULT_ERR_FILENAME; dk3app_log_i3(j->app, DK3_LL_ERROR, 215, 216, bu); } } else { j->exval = DKT_RESULT_ERR_FILENAME; } } else { dkt_chks_process_one_filename(j, bu); } } else { /* ERROR: Name too long! */ j->exval = DKT_RESULT_ERR_FILENAME; dk3app_log_i3(j->app, DK3_LL_ERROR, 65, 66, arg); } } else { /* BUG */ j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } } else { dkt_chks_process_one_filename(j, (j->kwnl)[4]); } } /** Run checksum building or checking. @param j Job structure. */ static void dkt_chks_run(DKT_CHKS_J *j) { $? "+ dkt_chks_run" if(j->f_chk) { $? ". check" dkt_chks_check(j); } else { $? ". create list" if(j->f_lst) { $? ". file name list" dkt_chks_process_filename_list(j); } else { $? ". direct arg" dkt_chks_process_argv_arguments(j); } } $? "- dkt_chks_run" } int dkt_check( dk3_app_t *app, dkChar const *sn, dkChar const * const *msg, dkChar const * const *kwnl, int what ) { DKT_CHKS_J j; int back = DKT_RESULT_ERR_UNSPECIFIC; $? "+ dkt_check %d", what dkt_chks_job_init(&j); j.app = app; j.msg = msg; j.kwnl = kwnl; j.f_sz = what; j.enc_s = dk3app_get_input_stdin_encoding(app); j.enc_f = dk3app_get_input_file_encoding(app); dkt_tool_read_conf(app, sn, (void *)(&j), dkt_chks_conf_line); if(dkt_chks_process_arguments(&j)) { j.exval = DKT_RESULT_OK; dkt_chks_run(&j); } dkt_chks_job_cleanup(&j); back = j.exval; $? "- dkt_check %d", back return back; } /* vim: set ai sw=2 : */