%% options copyright owner = Dirk Krause copyright year = 2011-2014 license = bsd %% module #include "dk3all.h" #include "dkt.h" $!trace-include /** Size of date line in tape file. */ #define DKT_TAPE_FILE_LINE_SIZE 64 /** Size of line in tape set file. */ #define DKT_TAPE_SET_FILE_LINE_SIZE 1024 /** INformation about tape usages. */ typedef struct { int tn; /**< Tape number. */ int delay; /**< Delay time. */ int f_c; /**< Flag: Used in this cycle. */ } DKT_TAPE_USAGE_INFO; /** Information for one tape (backup media). */ typedef struct { int Y; /**< Year of previous use. */ int M; /**< Month of previous use. */ int D; /**< Day of previous use. */ int h; /**< Hour of previous use. */ int m; /**< Minute of previous use. */ int s; /**< Second of previous use. */ int u; /**< Number of times the tape was used. */ } DKT_TAPE_INFO; /** Information from the tape file. */ typedef struct { DKT_TAPE_INFO tapes[10]; /**< Information about tape usages. */ int ct; /**< Current tape. */ } DKT_TAPE_FILE; /** Job structure for tape-set, tape, tape-report. */ typedef struct { DKT_TAPE_FILE tf; /**< Tape file structure. */ dk3_app_t *app; /**< Application structure. */ dkChar const * const *msg; /**< Localized texts. */ dkChar const * const *kwnl; /**< Keywords not localized. */ dkChar const *ofn; /**< Original file name. */ dkChar const *fn; /**< Real file name. */ dk3_option_set_t *opt; /**< Command line option set. */ int act; /**< Operation: 0=get, 1=confirm. */ int pl_o; /**< Plain text output to stdout. */ int f_cy; /**< Flag: Cycle completed. */ int e_f; /**< Encoding for file. */ int e_o; /**< Encoding for stdout. */ int exv; /**< Exit status code to set. */ int line; /**< Line number. */ } DKT_TAPE_J; /** Tapeset configuration structure. */ typedef struct { dkChar *st[DKT_TAPE_SET_FILE_LINE_SIZE / 2]; /**< String parts. */ dkChar il[DKT_TAPE_SET_FILE_LINE_SIZE]; /**< Input line. */ dkChar bu[DKT_TAPE_SET_FILE_LINE_SIZE]; /**< Modified copy. */ DKT_TAPE_J *job; /**< Job structure. */ size_t nst; /**< Number of string parts. */ int ct; /**< Current tape set. */ int lineno; /**< Current line number in file. */ } DKT_TAPE_SET_CONF; /** Configuration file key names. */ static dkChar const * const dkt_tape_long_options[] = { $!string-table macro=dkT # # 0 # reset # # 1 # file-encoding # # 2 # plain # # 3 # ascii $!end }; /** Commands for dkt tape. */ dkChar const * const dkt_tape_commands[] = { $!string-table macro=dkT # # 0 # g$et # # 1 # c$onfirm $!end }; /** Options used by dkt tape. */ static dk3_option_t const dkt_tape_options[] = { { dkT('p'), dkT("plain"), 0 }, /* Plain file contents. */ { dkT('a'), dkT("ascii"), 0 }, /* Plain output. */ { dkT('R'), dkT("reset"), 0 }, /* Reset all options. */ { dkT('f'), dkT("file-encoding"), 1 } /* Specify file encoding. */ }; /** Number of options in the dkt_tape_options array. */ static size_t const dkt_tape_szoptions = sizeof(dkt_tape_options)/sizeof(dk3_option_t); /** Options used by dkt tape-report. */ static dk3_option_t const dkt_tape_report_options[] = { { dkT('p'), dkT("plain"), 0 }, /* Plain file contents. */ { dkT('R'), dkT("reset"), 0 }, /* Reset all options. */ { dkT('f'), dkT("file-encoding"), 1 } /* Specify file encoding. */ }; /** Number of options in the dkt_tape_options array. */ static size_t const dkt_tape_report_szoptions = sizeof(dkt_tape_report_options)/sizeof(dk3_option_t); /** Tape numbers in order of usage. This implements the 10 tape rotation scheme. At every time we will have backups of different ages. During 200 backups each of the 10 tapes is used 20 times. */ static unsigned const dkt_tape_numbers[] = { 1, 2, 3, 4, 5, 1, 2, 3, 4, 6, 1, 2, 3, 4, 7, 1, 2, 3, 4, 8, 2, 3, 4, 5, 6, 2, 3, 4, 5, 7, 2, 3, 4, 5, 8, 2, 3, 4, 5, 9, 3, 4, 5, 6, 7, 3, 4, 5, 6, 8, 3, 4, 5, 6, 9, 3, 4, 5, 6, 10, 4, 5, 6, 7, 8, 4, 5, 6, 7, 9, 4, 5, 6, 7, 10, 4, 5, 6, 7, 1, 5, 6, 7, 8, 9, 5, 6, 7, 8, 10, 5, 6, 7, 8, 1, 5, 6, 7, 8, 2, 6, 7, 8, 9, 10, 6, 7, 8, 9, 1, 6, 7, 8, 9, 2, 6, 7, 8, 9, 3, 7, 8, 9, 10, 1, 7, 8, 9, 10, 2, 7, 8, 9, 10, 3, 7, 8, 9, 10, 4, 8, 9, 10, 1, 2, 8, 9, 10, 1, 3, 8, 9, 10, 1, 4, 8, 9, 10, 1, 5, 9, 10, 1, 2, 3, 9, 10, 1, 2, 4, 9, 10, 1, 2, 5, 9, 10, 1, 2, 6, 10, 1, 2, 3, 4, 10, 1, 2, 3, 5, 10, 1, 2, 3, 6, 10, 1, 2, 3, 7 }; /** Initialize a tape file before reading data. @param tf Tape file to initialize. */ static void dkt_tape_file_init(DKT_TAPE_FILE *tf) { size_t i; /* Current tape number. */ $? "+ dkt_tape_file_init" tf->ct = 0; for(i = 0; i < 10; i++) { tf->tapes[i].Y = 0; tf->tapes[i].M = 0; tf->tapes[i].D = 0; tf->tapes[i].h = 0; tf->tapes[i].m = 0; tf->tapes[i].s = 0; tf->tapes[i].u = 0; } $? "- dkt_tape_file_init" } /** Reset options in job structure (reset command). @param job Job structure to modify. */ static void dkt_tape_job_reset(DKT_TAPE_J *job) { job->e_f = dk3app_get_default_file_encoding(job->app); job->e_o = dk3app_get_input_stdin_encoding(job->app); job->pl_o = 0; } /** Initialize job structure before running the job. @param job Job structure to initialize. @param app Application structure for diagnostics. @param msg Localized message texts. @param kwnl Keywords, not localized. */ static void dkt_tape_job_init( DKT_TAPE_J *job, dk3_app_t *app, dkChar const * const *msg, dkChar const * const *kwnl ) { $? "+ dkt_tape_job_init" job->app = app; job->msg = msg; job->kwnl = kwnl; job->opt = NULL; job->f_cy = 0; job->act = -1; job->ofn = NULL; job->fn = NULL; job->line = 0; job->exv = DKT_RESULT_ERR_UNSPECIFIC; dkt_tape_job_reset(job); dkt_tape_file_init(&(job->tf)); $? "- dkt_tape_job_init" } /** Clean up job structure after running command. @param job job structure to clean up. */ static void dkt_tape_job_cleanup(DKT_TAPE_J *job) { $? "+ dkt_tape_job_cleanup" if(job->opt) { dk3opt_close(job->opt); job->opt = NULL; } $? "- dkt_tape_job_cleanup" } /** Process one line from configuration file. @param vj Job structure. @param key Key found in line. @param val Value found in line, may be NULL. @return 1 on success, 0 on error. */ static int dkt_tape_conf_line(void *vj, dkChar const *key, dkChar const *val) { DKT_TAPE_J *job; /* Job structure to modify. */ int back = 1; /* Return value. */ int act; /* Action to take. */ $? "+ dkt_tape_conf_line \"%s\"=\"%s\"", TR_STR(key), TR_STR(val) if((vj) && (key)) { job = (DKT_TAPE_J *)vj; act = dk3str_array_index(dkt_tape_long_options, key, 0); switch(act) { case 0: { dkt_tape_job_reset(job); } break; case 1: { if(val) { (void)dkt_tool_set_encoding( job->app, &(job->e_f), val, dk3app_get_input_file_encoding(job->app) ); } else { job->e_f = dk3app_get_default_file_encoding(job->app); } } break; case 2: { job->e_f = DK3_FILE_ENCODING_ASCII; } break; case 3: { job->pl_o = 1; } break; } } $? "- dkt_tape_conf_line %d", back return back; } /** Process command line arguments. @param job Job structure. @param f_get_conf Flag: First argument is get or confirm. @param rqt Command (0=tape, 1=tape-set, 2=tape-report). @return 1 on success, 0 on error. */ static int dkt_tape_process_arguments(DKT_TAPE_J *job, int f_get_conf, int rqt) { dkChar const * const *xargv; /* Command line arguments array. */ dkChar const *arg; /* Option argument. */ int back = 0; /* Return value. */ int xargc = 0; /* Number of command line arguments. */ int res = 0; /* Result of encoding search. */ $? "+ dkt_tape_process_arguments" xargc = dk3app_get_argc(job->app); xargv = dk3app_get_argv(job->app); xargc--; xargv++; job->opt = dk3opt_open_app( ((2 == rqt) ? dkt_tape_report_options : dkt_tape_options), ((2 == rqt) ? dkt_tape_report_szoptions : dkt_tape_szoptions), dkT('\0'), NULL, xargc, xargv, job->app ); if(job->opt) { if(0 == dk3opt_get_error_code(job->opt)) { $? ". option set ok" back = 1; if(dk3opt_is_set(job->opt, dkT('R'))) { $? ". reset" dkt_tape_job_reset(job); } if(dk3opt_is_set(job->opt, dkT('a'))) { $? ". -a" job->pl_o = 1; } if(dk3opt_is_set(job->opt, dkT('p'))) { $? ". -p" job->e_f = DK3_FILE_ENCODING_ASCII; if(dk3opt_is_set(job->opt, dkT('f'))) { $? "! -f" back = 0; /* ERROR: -f and -p are exclusive! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 65); job->exv = DKT_RESULT_ERR_OPTION; } } else { if(dk3opt_is_set(job->opt, dkT('f'))) { $? ". -f" arg = dk3opt_get_short_arg(job->opt, dkT('f')); if(arg) { $? ". arg=\"%s\"", arg res = dkt_tool_set_encoding( job->app, &(job->e_f), arg, dk3app_get_input_file_encoding(job->app) ); if(!(res)) { $? "! not a valid encoding" back = 0; job->exv = DKT_RESULT_ERR_OPTION; } } else { $? "! arg" back = 0; job->exv = DKT_RESULT_ERR_OPTION; /* ERROR: Option -f requires an argument! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 66); } } } if(back) { $? ". options ok" back = 0; if(f_get_conf) { $? ". sub-command and options" if(dk3opt_get_num_args(job->opt) > 2) { job->ofn = dk3opt_get_arg(job->opt, 2); job->act = dk3str_array_abbr( dkt_tape_commands, dk3opt_get_arg(job->opt,1), dkT('$') ,0 ); $? ". ofn=\"%s\"", TR_STR(job->ofn) if(0 <= job->act) { $? ". act=%d", job->act back = 1; } else { $? "! illegal sub command" /* ERROR: Illegal sub command */ job->exv = DKT_RESULT_ERR_OPTION; dk3app_log_3( job->app, DK3_LL_ERROR, job->msg, 67, 68, dk3opt_get_arg(job->opt, 1) ); } } else { $? "! too few options" /* ERROR: Too few options for command */ job->exv = DKT_RESULT_ERR_OPTION; dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 69); } } else { $? ". no sub-command" if(dk3opt_get_num_args(job->opt) > 1) { job->ofn = dk3opt_get_arg(job->opt, 1); back = 1; $? ". ofn=\"%s\"", TR_STR(job->ofn) } else { $? "! missing file name" /* ERROR: Missing file name */ job->exv = DKT_RESULT_ERR_OPTION; dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 25); } } } } else { /* ERROR: Error in command line options! */ job->exv = DKT_RESULT_ERR_OPTION; dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 70); } } if(back) { if(!(job->ofn)) { back = 0; /* ERROR: Missing file name! */ job->exv = DKT_RESULT_ERR_OPTION; dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 25); } } $? "- dkt_tape_process_arguments %d", back return back; } /** Process line for one tape. @param job Job structure. @param tape Tape structure. @param il Input line to process. @return 1 on success, 0 on error. */ static int dkt_tape_process_tape_data_line( DKT_TAPE_J *job, DKT_TAPE_INFO *tape, dkChar *il ) { dkChar *syear = NULL; /* String: Year. */ dkChar *smon = NULL; /* String: Month. */ dkChar *sday = NULL; /* String: Day. */ dkChar *shour = NULL; /* String: Hour. */ dkChar *smin = NULL; /* String: Minute. */ dkChar *ssec = NULL; /* String: Second. */ dkChar *sused = NULL; /* String: Number of times used. */ int i; /* Temporary conversion result. */ int back = 0; /* Return code. */ $? "+ dkt_tape_process_tape_data_line" syear = dk3str_start(il, NULL); if(syear) { shour = dk3str_next(syear, NULL); if(shour) { sused = dk3str_next(shour, NULL); if(sused) { smon = dk3str_chr(syear, dkT('-')); if(smon) { *(smon++) = dkT('\0'); sday = dk3str_chr(smon, dkT('-')); if(sday) { *(sday++) = dkT('\0'); smin = dk3str_chr(shour, dkT(':')); if(smin) { *(smin++) = dkT('\0'); ssec = dk3str_chr(smin, dkT(':')); if(ssec) { *(ssec++) = dkT('\0'); $? ". all strings found" #if VERSION_BEFORE_20140716 if(dk3sf_sscanf3(syear, dkT("%d"), &i)) #else if (0 != dk3ma_i_from_string(&i, syear, NULL)) #endif { tape->Y = i; #if VERSION_BEFORE_20140716 if(dk3sf_sscanf3(smon, dkT("%d"), &i)) #else if (0 != dk3ma_i_from_string(&i, smon, NULL)) #endif { tape->M = i; #if VERSION_BEFORE_20140716 if(dk3sf_sscanf3(sday, dkT("%d"), &i)) #else if (0 != dk3ma_i_from_string(&i, sday, NULL)) #endif { tape->D = i; #if VERSION_BEFORE_20140716 if(dk3sf_sscanf3(shour, dkT("%d"), &i)) #else if (0 != dk3ma_i_from_string(&i, shour, NULL)) #endif { tape->h = i; #if VERSION_BEFORE_20140716 if(dk3sf_sscanf3(smin, dkT("%d"), &i)) #else if (0 != dk3ma_i_from_string(&i, smin, NULL)) #endif { tape->m = i; #if VERSION_BEFORE_20140716 if(dk3sf_sscanf3(ssec, dkT("%d"), &i)) #else if (0 != dk3ma_i_from_string(&i, ssec, NULL)) #endif { tape->s = i; #if VERSION_BEFORE_20140716 if(dk3sf_sscanf3(sused, dkT("%d"), &i)) #else if (0 != dk3ma_i_from_string(&i, sused, NULL)) #endif { tape->u = i; $? ". all strings converted" back = 1; $? ". %d-%d-%d %d:%d:%d %d", tape->Y, tape->M, tape->D, tape->h, tape->m, tape->s, tape->u } } } } } } } } } } } } } } if(1 != back) { job->exv = DKT_RESULT_ERR_INPUT; /* ERROR: Not a tape file! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 27); } $? "- dkt_tape_process_tape_data_line %d", back return back; } /** Process one input line from tape file. @param obj Tape set configuration. @param il Input line. @return 1 on success, 0 on recoverable error, -1 on unrecoverable error. */ static int dkt_tape_line_handler(void *obj, dkChar *il) { DKT_TAPE_J *job; /* Job structure. */ int back = -1; /* Return code. */ int i; /* Temporary conversion result. */ job = (DKT_TAPE_J *)obj; job->line += 1; switch(job->line) { case 1: { #if VERSION_BEFORE_20140716 if(1 == dk3sf_sscanf3(il, dkT("%d"), &i)) #else if (0 != dk3ma_i_from_string(&i, il, NULL)) #endif { if(0 <= i) { back = 1; (job->tf).ct = i; } else { /* ERROR: Not a tape file! */ job->exv = DKT_RESULT_ERR_INPUT; dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 27); } } else { /* ERROR: Not a tape file! */ job->exv = DKT_RESULT_ERR_INPUT; dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 27); } } break; case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: { i = job->line; if(dkt_tape_process_tape_data_line(job, &((job->tf).tapes[i - 2]),il)) { back = 1; } } break; default: { back = 1; } break; } return back; } /** Read the tape file. @param job Job structure. @return 1 on success, 0 on error. */ static int dkt_tape_read_tapefile(DKT_TAPE_J *job) { dkChar bu[DKT_TAPE_FILE_LINE_SIZE]; /* Input line buffer. */ dk3_stat_t stb; /* File information. */ int back = 1; /* Return code. */ int res; /* Result from linewise processing. */ $? "+ dkt_tape_read_tapefile" if(dk3sf_stat_app(&stb, job->fn, NULL)) { $? ". file exists" switch((stb.ft) & (~(DK3_FT_SYMLINK))) { case DK3_FT_REGULAR: { $? ". regular file" res = dk3stream_process_filename_lines_app( (void *)job, dkt_tape_line_handler, job->fn, bu, DK3_SIZEOF(bu,dkChar), dk3app_get_encoding(job->app), job->e_f, job->app ); if(1 == res) { back = 1; } else { back = 0; job->exv = DKT_RESULT_ERR_INPUT; } } break; default: { $? "! not a regular file" back = 0; job->exv = DKT_RESULT_ERR_OPTION; /* ERROR: Not a regular file! */ dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 23, 24, job->fn); } break; } } else { $? ". no such file" } $? "- dkt_tape_read_tapefile %d", back return back; } /** Handle tape. @param job Job structure. */ static void dkt_tape_run_tape(DKT_TAPE_J *job) { dkChar bu[DKT_TAPE_FILE_LINE_SIZE]; /* Conversion result. */ char xb[DKT_TAPE_FILE_LINE_SIZE]; /* Conversion result. */ dk3_time_t timer; /* Current time. */ dk3_tm_t tm; /* Time splitted. */ FILE *fipo; /* Output file. */ dk3_stream_t *os; /* Ouptut stream. */ int conr; /* Conversion result. */ int i; /* Index. */ int llstderr; /* Log level stderr. */ $? "+ dkt_tape_run_tape" if(dkt_tape_read_tapefile(job)) { if(1 == job->act) { if(dk3sf_time(&timer)) { if(dk3sf_localtime_app(&tm, &timer, job->app)) { i = dkt_tape_numbers[(job->tf).ct] - 1; (job->tf).tapes[i].Y = tm.Y; (job->tf).tapes[i].M = tm.M; (job->tf).tapes[i].D = tm.D; (job->tf).tapes[i].h = tm.h; (job->tf).tapes[i].m = tm.m; (job->tf).tapes[i].s = tm.s; (job->tf).tapes[i].u += 1; (job->tf).ct += 1; if(200 <= (job->tf).ct) { (job->tf).ct = 0; for(i = 0; i < 10; i++) { (job->tf).tapes[i].u = 0; } /* INFO: Cycle completed, used next tape set. */ llstderr = dk3app_get_stderr_log_level(job->app); dk3app_set_stderr_log_level(job->app, DK3_LL_INFO); dk3app_log_1(job->app, DK3_LL_INFO, job->msg, 28); dk3app_set_stderr_log_level(job->app, llstderr); } fipo = dk3sf_fopen_app(job->fn, dk3app_not_localized(53), job->app); if(fipo) { os = dk3stream_open_file_app(fipo, DK3_STREAM_FLAG_WRITE, job->app); if(os) { $? ". output encoding %d", job->e_f dk3stream_set_output_encoding(os, job->e_f); dk3stream_write_byte_order_marker_if_necessary( os, dk3app_get_encoding(job->app) ); #if VERSION_BEFORE_20140716 dk3sf_sprintf3(bu, dkT("%d"), (job->tf).ct); #else conr = dk3ma_im_to_string( bu, DK3_SIZEOF(bu,dkChar), (dk3_im_t)((job->tf).ct) ); if (0 == conr) { bu[0] = dkT('\0'); } #endif dk3stream_strputs(os, bu, dk3app_get_encoding(job->app)); dk3stream_strnl(os); for(i = 0; i < 10; i++) { sprintf( xb, "%04d-%02d-%02d %02d:%02d:%02d %d", (job->tf).tapes[i].Y, (job->tf).tapes[i].M, (job->tf).tapes[i].D, (job->tf).tapes[i].h, (job->tf).tapes[i].m, (job->tf).tapes[i].s, (job->tf).tapes[i].u ); dk3str_c8_to_str_simple_app( bu, DK3_SIZEOF(bu,dkChar), xb, job->app ); dk3stream_strputs(os, bu, dk3app_get_encoding(job->app)); dk3stream_strnl(os); } dk3stream_close(os); } else { job->exv = DKT_RESULT_ERR_OUTPUT; } dk3sf_fclose_fn_app(fipo, job->fn, job->app); } else { job->exv = DKT_RESULT_ERR_OUTPUT; } } else { } } else { } } else { if(job->pl_o) { printf("%d\n", dkt_tape_numbers[(job->tf).ct]); } else { #if VERSION_BEFORE_20140716 dk3sf_sprintf3(bu, dkT("%d\n"), dkt_tape_numbers[(job->tf).ct]); dk3sf_initialize_stdout(); dk3sf_fputs(bu, stdout); #else if (dk3ma_im_to_string(bu,DK3_SIZEOF(bu,dkChar),(dk3_im_t)((job->tf).ct))) { dk3sf_initialize_stdout(); dk3sf_fputs(bu, stdout); dk3sf_fputc(dkT('\n'), stdout); } #endif } job->exv = DKT_RESULT_OK; } } $? "- dkt_tape_run_tape" } /** Process one input line from tape set file. @param obj Tape set configuration. @param il Input line. @return 1 on success, 0 on recoverable error, -1 on unrecoverable error. */ static int dkt_tape_set_line_handler(void *obj, dkChar *il) { DKT_TAPE_SET_CONF *tsc; /* Tape set configuration structure. */ DKT_TAPE_J *job; /* Job structure. */ int back = 0; /* Return code. */ int i; /* Temporary conversion result. */ $? "+ dkt_tape_set_line_handler \"%s\"", TR_STR(il) tsc = (DKT_TAPE_SET_CONF *)obj; job = tsc->job; dk3str_delnl(il); tsc->lineno += 1; switch(tsc->lineno) { case 1: { $? ". tape set names" dk3str_cpy(tsc->il, il); dk3str_cpy(tsc->bu, il); tsc->nst = dk3str_explode( tsc->st, (DKT_TAPE_SET_FILE_LINE_SIZE / 2 - 1), tsc->bu, NULL ); if(0 < tsc->nst) { $? ". tape set names line ok" back = 1; } else { $? "! problem in tape set names line" back = -1; job->exv = DKT_RESULT_ERR_INPUT; /* ERROR: Not a tape set file! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 29); } } break; case 2: { $? ". current tape" #if VERSION_BEFORE_20140716 if(1 == dk3sf_sscanf3(il, dkT("%d"), &i)) #else if (0 != dk3ma_i_from_string(&i, il, NULL)) #endif { if(0 <= i) { tsc->ct = i; if((0 < tsc->nst) && (i < (int)(tsc->nst))) { back = 1; $? ". current tape number line ok" } else { $? "! number too large" back = -1; job->exv = DKT_RESULT_ERR_INPUT; /* ERROR: Not a tape set file! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 31); } } else { $? "! negative number" back = -1; job->exv = DKT_RESULT_ERR_INPUT; /* ERROR: Not a tape set file! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 31); } } else { $? "! Not a number" back = -1; job->exv = DKT_RESULT_ERR_INPUT; /* ERROR: Not a tape set file! */ dk3app_log_i3(job->app, DK3_LL_ERROR, 141, 142, il); } } break; default: { $? "! line ignored" back = -1; /* ERROR: Not a tapeset file! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 30); } break; } $? "- dkt_tape_set_line_handler %d", back return back; } /** Handle tape set. @param job Job structure. */ static void dkt_tape_run_tapeset(DKT_TAPE_J *job) { DKT_TAPE_SET_CONF tsc; /* Tape set configuration. */ dkChar bu[DKT_TAPE_SET_FILE_LINE_SIZE]; /* Input line. */ char tx[DKT_TAPE_SET_FILE_LINE_SIZE]; /* Output buffer. */ FILE *fipo; /* Input file. */ dk3_stream_t *os; /* Output stream. */ size_t i; /* Index to initialize tsc. */ int res = 0; /* Operation result. */ $? "+ dkt_tape_run_tapeset" /* Initialize tsc. */ for(i = 0; i < (DKT_TAPE_SET_FILE_LINE_SIZE / 2); i++) { tsc.st[i] = NULL; } tsc.il[0] = dkT('\0'); tsc.bu[0] = dkT('\0'); tsc.job = job; tsc.ct = 0; tsc.lineno = 0; tsc.nst = 0; /* Read tape set file and process request. */ res = dk3stream_process_filename_lines_app( (void *)(&tsc), dkt_tape_set_line_handler, job->fn, bu, DK3_SIZEOF(bu,dkChar), dk3app_get_encoding(job->app), job->e_f, job->app ); if(1 == res) { if(1 == job->act) { $? ". confirm tape set" tsc.ct += 1; if(tsc.ct >= (int)(tsc.nst)) { tsc.ct = 0; } fipo = dk3sf_fopen_app(job->fn, dk3app_not_localized(53), job->app); if(fipo) { os = dk3stream_open_file_app(fipo, DK3_STREAM_FLAG_WRITE, job->app); if(os) { dk3stream_set_output_encoding(os, job->e_f); dk3stream_write_byte_order_marker_if_necessary( os, dk3app_get_encoding(job->app) ); dk3stream_strputs(os, tsc.il, dk3app_get_encoding(job->app)); dk3stream_strnl(os); #if VERSION_BEFORE_20140716 dk3sf_sprintf3(bu, dkT("%d"), tsc.ct); #else if (!(dk3ma_im_to_string(bu,DK3_SIZEOF(bu,dkChar),(dk3_im_t)(tsc.ct)))) { bu[0] = dkT('\0'); } #endif dk3stream_strputs(os, bu, dk3app_get_encoding(job->app)); dk3stream_strnl(os); dk3stream_close(os); } else { /* ERROR: Failed to open output stream! */ job->exv = DKT_RESULT_ERR_MEMORY; } dk3sf_fclose_fn_app(fipo, job->fn, job->app); } else { /* ERROR: Failed to open file! */ job->exv = DKT_RESULT_ERR_OUTPUT; } } else { $? ". write current tape set name" if((0 <= tsc.ct) && (tsc.ct < (int)(tsc.nst))) { if(job->pl_o) { res = dk3str_to_c8p_app( tx, sizeof(tx), tsc.st[tsc.ct], dk3app_get_encoding(job->app), job->app ); if(res) { fputs(tx, stdout); fputc('\n', stdout); } else { job->exv = DKT_RESULT_ERR_INPUT; } } else { dk3sf_initialize_stdout(); dk3sf_fputs(tsc.st[tsc.ct], stdout); dk3sf_fputc(dkT('\n'), stdout); } } else { job->exv = DKT_RESULT_ERR_INPUT; } } } else { job->exv = DKT_RESULT_ERR_INPUT; } $? "- dkt_tape_run_tapeset" } /** Compare two usage information structure. @param l Left object. @param r Right object. @param cr Comparison criteria (ignored). @return Comparison result. */ static int dkt_tape_comp_usage_info(void const *l, void const *r, int cr) { DKT_TAPE_USAGE_INFO const *pl; /* Left object. */ DKT_TAPE_USAGE_INFO const *pr; /* Right object. */ int back = 0; /* Return value. */ if(l) { if(r) { pl = (DKT_TAPE_USAGE_INFO const *)l; pr = (DKT_TAPE_USAGE_INFO const *)r; if(pl->delay < pr->delay) { back = -1; } else { if(pl->delay > pr->delay) { back = 1; } } if(0 == back) { if(pl->f_c) { if(!(pr->f_c)) { back = -1; } } else { if(pr->f_c) { back = 1; } } } if(0 == back) { if(pl->tn < pr->tn) { back = 1; } else { if(pl->tn > pr->tn) { back = -1; } } } } else { back = 1; } } else { if(r) { back = -1; } } return back; } /** Check tape information whether it is worth to print it. @param ti Tape information to check. @return 1 on success (print tape information), 0 otherwise. */ static int dkt_tape_check_tape_information(DKT_TAPE_INFO *ti) { int back = 0; if(ti->Y) back = 1; if(ti->M) back = 1; if(ti->D) back = 1; if(ti->h) back = 1; if(ti->m) back = 1; if(ti->s) back = 1; return back; } /** Report tape usage. @param job Job structure. */ static void dkt_tape_run_tapereport(DKT_TAPE_J *job) { dkChar bu[DKT_TAPE_FILE_LINE_SIZE]; /* Output line buffer. */ char xb[DKT_TAPE_FILE_LINE_SIZE]; /* Temporary buffer. */ DKT_TAPE_USAGE_INFO ui[] = { { 1, 0, 0}, { 2, 0, 0}, { 3, 0, 0}, { 4, 0, 0}, { 5, 0, 0}, { 6, 0, 0}, { 7, 0, 0}, { 8, 0, 0}, { 9, 0, 0}, { 10, 0, 0}, }; /* Information about last uses. */ dk3_sto_t *s_ui; /* Storage for usage information. */ dk3_sto_it_t *i_ui; /* Iterator through usage information. */ DKT_TAPE_USAGE_INFO *ptr; /* Current usage information to process. */ int i; /* Running index. */ int ti; /* Tape index in dkt_tape_numbers. */ int tn; /* Tape number. */ int ok; /* Everything ok. */ $? "+ dkt_tape_run_tapereport" if(dkt_tape_read_tapefile(job)) { /* Calculate delays */ for(i = 1; i < 200; i++) { ti = (job->tf).ct - i; if(0 <= ti) { tn = dkt_tape_numbers[ti]; if(0 == ui[tn - 1].delay) { ui[tn - 1].delay = i; ui[tn - 1].f_c = 1; } } else { tn = dkt_tape_numbers[200 + ti]; if(0 == ui[tn - 1].delay) { ui[tn - 1].delay = i; } } } /* Build sorted storage and write report. */ s_ui = dk3sto_open_app(job->app); if(s_ui) { dk3sto_set_comp(s_ui, dkt_tape_comp_usage_info, 0); i_ui = dk3sto_it_open(s_ui); if(i_ui) { ok = 1; for(i = 0; i < 10; i++) { if(!dk3sto_add(s_ui, (void *)(&(ui[i])))) { ok = 0; job->exv = DKT_RESULT_ERR_MEMORY; } } if(ok) { dk3sto_it_reset(i_ui); job->exv = DKT_RESULT_OK; dk3sf_initialize_stdout(); /* PRINT: Current tape: */ dk3sf_fputs((job->msg)[71], stdout); tn = dkt_tape_numbers[(job->tf).ct]; #if VERSION_BEFORE_20140716 dk3sf_sprintf3(bu, dkT("%d"), tn); #else if (!(dk3ma_im_to_string(bu, DK3_SIZEOF(bu,dkChar), (dk3_im_t)tn))) { bu[0] = dkT('\0'); } #endif dk3sf_fputs(bu, stdout); dk3sf_fputc(dkT('\n'), stdout); do { ptr = (DKT_TAPE_USAGE_INFO *)dk3sto_it_next(i_ui); if(ptr) { i = ptr->tn - 1; if(dkt_tape_check_tape_information(&((job->tf).tapes[i]))) { sprintf( xb, "%02d %04d-%02d-%02d %02d-%02d-%02d %02d", ptr->tn, (job->tf).tapes[i].Y, (job->tf).tapes[i].M, (job->tf).tapes[i].D, (job->tf).tapes[i].h, (job->tf).tapes[i].m, (job->tf).tapes[i].s, (job->tf).tapes[i].u ); } else { sprintf( xb, "%02d ********** ******** %02d", ptr->tn, (job->tf).tapes[i].u ); } dk3str_c8_to_str_simple_app( bu, DK3_SIZEOF(bu,dkChar), xb, job->app ); dk3sf_fputs(bu, stdout); if(!(ptr->f_c)) { /* PRINT: " previous cycle" */ dk3sf_fputs((job->msg)[72], stdout); } dk3sf_fputc(dkT('\n'), stdout); } } while(ptr); } dk3sto_it_close(i_ui); } else { job->exv = DKT_RESULT_ERR_MEMORY; } dk3sto_close(s_ui); } else { job->exv = DKT_RESULT_ERR_MEMORY; } } $? "- dkt_tape_run_tapereport" } /** Expand file name an invoke subroutine. @param job Job structure. @param act Sub-routine selector (0=tape, 1=tapeset, 2=tapereport). */ static void dkt_tape_expand_filename(DKT_TAPE_J *job, int act) { dk3_dir_t *fne; fne = dk3dir_fne_open_app(job->ofn, job->app); if(fne) { if(1 == dk3dir_get_number_of_files(fne)) { if(dk3dir_get_next_file(fne)) { job->fn = dk3dir_get_fullname(fne); if(job->fn) { switch(act) { case 2: { dkt_tape_run_tapereport(job); } break; case 1: { dkt_tape_run_tapeset(job); } break; default: { dkt_tape_run_tape(job); } break; } } else { job->exv = DKT_RESULT_ERR_OPTION; /* BUG: Must not happen! */ } } else { job->exv = DKT_RESULT_ERR_OPTION; /* BUG: Must not happen! */ } } else { job->exv = DKT_RESULT_ERR_OPTION; if(1 < dk3dir_get_number_of_files(fne)) { dk3app_log_i3(job->app, DK3_LL_ERROR, 168, 169, job->ofn); } else { dk3app_log_i3(job->app, DK3_LL_ERROR, 215, 216, job->ofn); } } dk3dir_close(fne); } else { job->exv = DKT_RESULT_ERR_OPENDIR; } } int dkt_tape( dk3_app_t *app, dkChar const *sn, dkChar const * const *msg, dkChar const * const *kwnl ) { DKT_TAPE_J job; int back; $? "+ dkt_tape" dkt_tape_job_init(&job, app, msg, kwnl); dkt_tool_read_conf(app, sn, (void *)(&job), dkt_tape_conf_line); if(dkt_tape_process_arguments(&job, 1, 0)) { if(dk3sf_must_expand(job.ofn)) { dkt_tape_expand_filename(&job, 0); } else { job.fn = job.ofn; dkt_tape_run_tape(&job); } } back = job.exv; dkt_tape_job_cleanup(&job); $? "- dkt_tape %d", back return back; } int dkt_tapeset( dk3_app_t *app, dkChar const *sn, dkChar const * const *msg, dkChar const * const *kwnl ) { DKT_TAPE_J job; int back; $? "+ dkt_tapeset" dkt_tape_job_init(&job, app, msg, kwnl); dkt_tool_read_conf(app, sn, (void *)(&job), dkt_tape_conf_line); if(dkt_tape_process_arguments(&job, 1, 1)) { if(dk3sf_must_expand(job.ofn)) { dkt_tape_expand_filename(&job, 1); } else { job.fn = job.ofn; dkt_tape_run_tapeset(&job); } } back = job.exv; dkt_tape_job_cleanup(&job); $? "- dkt_tapeset %d", back return back; } int dkt_tapereport( dk3_app_t *app, dkChar const *sn, dkChar const * const *msg, dkChar const * const *kwnl ) { DKT_TAPE_J job; int back; $? "+ dkt_tapereport" dkt_tape_job_init(&job, app, msg, kwnl); dkt_tool_read_conf(app, sn, (void *)(&job), dkt_tape_conf_line); if(dkt_tape_process_arguments(&job, 0, 2)) { if(dk3sf_must_expand(job.ofn)) { dkt_tape_expand_filename(&job, 2); } else { job.fn = job.ofn; dkt_tape_run_tapereport(&job); } } back = job.exv; dkt_tape_job_cleanup(&job); $? "- dkt_tapereport %d", back return back; }