%% options copyright owner = Dirk Krause copyright year = 2011-2014 license = bsd %% module /** @file dkt-sort.c The "dkt sort" command. -b Ignore leading blanks. -c Ignore case -n Sort by leading signed integer (numeric). -u Sort by leading unsigned integer. -f Sort by leading floating point number. -s Lines without leading int for floating point numbers are printed before lines with values found. -w Normalize lines (whitespace sequences replaced by space). -m Merge equal lines. -r Reverse search order. -i ie Input encoding. -p Same as -i plain. */ #include "dk3all.h" #include "dkt.h" $!trace-include /** Sort job structure. */ typedef struct { dk3_app_t *app; /**< Application. */ dkChar const * const *msg; /**< Localized messages. */ dkChar const * const *kwnl; /**< Keywords not localized. */ dk3_option_set_t *opt; /**< Option set. */ dk3_sto_t *sto; /**< Storage to keep lines. */ dk3_sto_it_t *sit; /**< Storage iterator. */ dkChar *b1; /**< First buffer. */ dkChar *b2; /**< Buffer for normalization. */ size_t linesz; /**< Line size. */ int exval; /**< Exit status code. */ int enc_s; /**< Encoding on stdin. */ int enc_f; /**< Encoding on files. */ int c_sort; /**< Sort criteria for storage. */ int c_merg; /**< Sort criteria for merging. */ int o_sort; /**< Simple sort criteria. */ int o_merg; /**< Option: Merge. */ int o_norm; /**< Option: Normalize. */ int o_igbl; /**< Option: Ignore leading blanks. */ } DKT_SORT_J; /** Information about one stored line. */ typedef struct { dkChar const *ori; /**< Original line as found. */ dkChar const *nrm; /**< Normalized version. */ dkChar const *stt; /**< Start of text in ori. */ char hv; /**< Flag: Have value. */ union { double d; /**< Double value. */ dk3_im_t i; /**< Signed integer value. */ dk3_um_t u; /**< Unsigned integer value. */ } v; /**< Value for sorting. */ } DKT_SORT_L; /** Configuration file options. */ static dkChar const * const dkt_sort_long_opt[] = { dkT("stdin-encoding"), dkT("file-encoding"), dkT("reset"), dkT("line-size"), NULL }; /** Data for the option set. */ static dk3_option_t const dkt_sort_options[] = { { dkT('R'), dkT("reset"), 0 }, { dkT('i'), dkT("input-encoding"), 1 }, { dkT('p'), dkT("plain"), 0 }, { dkT('l'), dkT("line-size"), 1 }, { dkT('b'), dkT("ignore-leading-blanks"), 0 }, { dkT('c'), dkT("ignore-case"), 0 }, { dkT('n'), dkT("integer"), 0 }, { dkT('u'), dkT("unsigned"), 0 }, { dkT('f'), dkT("float"), 0 }, { dkT('w'), dkT("normalize-text"), 0 }, { dkT('m'), dkT("merge"), 0 }, { dkT('e'), dkT("merge-exact"), 0 }, { dkT('r'), dkT("reverse"), 0 }, { dkT('s'), dkT("missing-value-first"), 0}, }; /** Number of options in the dkt_sort_options array. */ static size_t const dkt_sort_szoptions = sizeof(dkt_sort_options)/sizeof(dk3_option_t); /** Compare two sort line structures. @param l Left structure. @param r Right structure. @param cr Comparison criteria. @return Comparison result. */ static int dkt_sort_compare(void const *l, void const *r, int cr) { int back = 0; int mycr = 0; /* My criteria. */ DKT_SORT_L const *pl = NULL; /* Left object. */ DKT_SORT_L const *pr = NULL; /* Right object. */ $? "+ dkt_sort_compare %d", cr if(l) { if(r) { pl = (DKT_SORT_L const *)l; pr = (DKT_SORT_L const *)r; mycr = cr & DKT_SORT_CRITERIA_MASK; switch(mycr) { case 1: case 2: case 3: { $? ". compare by value" if(pl->hv) { if(pr->hv) { switch(mycr) { case 1: { $? ". signed integer" if((pl->v).i > (pr->v).i) { back = 1; } else { if((pl->v).i < (pr->v).i) { back = -1; } } } break; case 2: { $? ". unsigned integer" if((pl->v).u > (pr->v).u) { back = 1; } else { if((pl->v).u < (pr->v).u) { back = -1; } } } break; case 3: { $? ". double" if((pl->v).d > (pr->v).d) { back = 1; } else { if((pl->v).d < (pr->v).d) { back = -1; } } } break; } } else { back = -1; if(cr & DKT_SORT_NOVALUE_FIRST) { back = 1; } } } else { if(pr->hv) { back = 1; if(cr & DKT_SORT_NOVALUE_FIRST) { back = -1; } } } } break; } if(back == 0) { if(cr & DKT_SORT_NORMALIZE) { $? ". compare normalized" if(pl->nrm) { if(pr->nrm) { $? ". nrm \"%s\"<>\"%s\"", pl->nrm, pr->nrm if(cr & DKT_SORT_CASEINS) { back = dk3str_casecmp(pl->nrm, pr->nrm); } else { back = dk3str_cmp(pl->nrm, pr->nrm); } } else { back = 1; } } else { if(pr->nrm) { back = -1; } else { $? "! no normalized texts there" } } } } if(back == 0) { if(cr & DKT_SORT_IGNORE_LWHS) { $? ". no leading whs" if(pl->stt) { if(pr->stt) { $? ". stt \"%s\"<>\"%s\"", pl->stt, pr->stt if(cr & DKT_SORT_CASEINS) { back = dk3str_casecmp(pl->stt, pr->stt); } else { back = dk3str_cmp(pl->stt, pr->stt); } } else { back = 1; } } else { if(pr->stt) { back = -1; } else { $? "! no start of text" } } } } if(back == 0) { if(cr & DKT_SORT_EXACT) { $? ". exact comparison" if(pl->ori) { if(pr->ori) { $? ". ori \"%s\"<>\"%s\"", pl->ori, pr->ori if(cr & DKT_SORT_CASEINS) { back = dk3str_casecmp(pl->ori, pr->ori); } else { back = dk3str_cmp(pl->ori, pr->ori); } } else { back = 1; } } else { if(pr->ori) { back = -1; } else { $? "! not texts" } } } } $? ". correct %d", back if(back > 0) { back = 1; } else { if(back < 0) { back = -1; } } } else { back = 1; } } else { if(r) { back = -1; } else { $? "! no pointers at all" } } if(cr & DKT_SORT_INVERT) { $? ". must invert" back = 0 - back; } $? "- dkt_sort_compare %d", back return back; } /** Delete a line information structure. @param lp Structure to delete. */ static void dkt_sort_line_delete(DKT_SORT_L *lp) { $? "+ dkt_sort_line_delete" if(lp) { dk3_release(lp->ori); dk3_release(lp->nrm); lp->stt = NULL; lp->hv = '\0'; dk3_delete(lp); } $? "- dkt_sort_line_delete" } /** Create a line information structure. @param j Job structure. @param ori Original line. @param nrm Normalized line. @return Pointer to new structure on success, NULL on error. */ DKT_SORT_L * dkt_sort_line_new(DKT_SORT_J *j, dkChar const *ori, dkChar const *nrm) { DKT_SORT_L *back = NULL; int ok = 0; /* Flag: Conversion ok. */ dk3_im_t im = DK3_IM_0; /* Value for line. */ dk3_um_t um = DK3_UM_0; /* Value for line. */ double db = 0.0; /* Value for line. */ back = dk3_new_app(DKT_SORT_L,1,j->app); if(back) { back->ori = NULL; back->nrm = NULL; back->stt = NULL; back->hv = '\0'; back->ori = dk3str_dup_app(ori, j->app); if(back->ori) { if(nrm) { back->nrm = dk3str_dup_app(nrm, j->app); if(back->nrm) { ok = 1; } } else { ok = 1; } } if(ok) { if(j->o_igbl) { back->stt = dk3str_start(back->ori, NULL); } switch(j->o_sort) { case DKT_SORT_INTEGER: { #if VERSION_BEFORE_20140716 if(dk3ma_string_to_im(&im, back->ori)) #else if (0 != dk3ma_im_from_string(&im, back->ori, NULL)) #endif { (back->v).i = im; $? ". found int" back->hv = 'y'; } else { $? ". no int" } } break; case DKT_SORT_UNSIGNED: { #if VERSION_BEFORE_20140716 if(dk3ma_string_to_um(&um, back->ori)) #else if (0 != dk3ma_um_from_string(&um, back->ori, NULL)) #endif { (back->v).u = um; $? ". found unsigned" back->hv = 'y'; } else { $? ". no unsigned" } } break; case DKT_SORT_DOUBLE: { #if VERSION_BEFORE_20140716 if(dk3sf_sscanf3(back->ori, (j->kwnl)[1], &db) == 1) #else if(0 != dk3ma_d_from_string(&db, back->ori, NULL)) #endif { (back->v).d = db; $? ". found double" back->hv = 'y'; } else { $? ". no double" } } break; } } else { dkt_sort_line_delete(back); back = NULL; } } return back; } /** Initialize job structure. @param j Job structure. */ static void dkt_sort_job_init(DKT_SORT_J *j) { j->opt = NULL; j->exval = DKT_RESULT_ERR_UNSPECIFIC; j->linesz = DKT_SORT_LINE_SIZE; j->enc_s = dk3app_get_input_stdin_encoding(j->app); j->enc_f = dk3app_get_input_file_encoding(j->app); j->c_sort = 0; j->c_merg = 0; j->o_merg = 0; j->o_sort = 0; j->o_norm = 0; j->o_igbl = 0; j->sto = NULL; j->sit = NULL; } /** Reset job structure (--reset or -R option). @param j Job structure. */ static void dkt_sort_job_reset(DKT_SORT_J *j) { j->linesz = DKT_SORT_LINE_SIZE; j->enc_s = dk3app_get_default_stdin_encoding(j->app); j->enc_f = dk3app_get_default_file_encoding(j->app); } /** Clean up job structure. @param j Job structure. */ static void dkt_sort_job_cleanup(DKT_SORT_J *j) { DKT_SORT_L *lp = NULL; if(j->sit) { dk3sto_it_reset(j->sit); while((lp = (DKT_SORT_L *)dk3sto_it_next(j->sit)) != NULL) { dkt_sort_line_delete(lp); } dk3sto_it_close(j->sit); } j->sit = NULL; if(j->sto) { dk3sto_close(j->sto); } j->sto = NULL; if(j->opt) { dk3opt_close(j->opt); j->opt = NULL; } } /** Set line size. @param j Job structure. @param t Text containing the new line size. @return 1 on success, 0 on error. */ static int dkt_sort_set_linesize(DKT_SORT_J *j, dkChar const *t) { #if VERSION_BEFORE_21040809 int back = 0; unsigned u = 0; /* Used for conversion. */ size_t s = 0; /* New size. */ if(dk3sf_sscanf3(t, (j->kwnl)[0], &u) == 1) { s = (size_t)u; if((unsigned)s == u) { back = u; j->linesz = s; } } return back; #else dk3_um_t um = DK3_UM_0; int back = 0; size_t sz = 0; #if VERSION_BEFORE_20140716 if (dk3ma_string_to_um(&um, t)) #else if (0 != dk3ma_um_from_string(&um, t, NULL)) #endif { sz = dk3ma_um_to_sz(um, NULL); if (0 != sz) { j->linesz = sz; back = 1; } } return back; #endif } /** Process one key/value pair. @param jv Pointer to job structure casted to void *. @param k Key. @param v Value. @return 1 to indicate success. */ static int dkt_sort_conf_line(void *jv, dkChar const *k, dkChar const *v) { int back = 0; DKT_SORT_J *j; $? "+ dkt_sort_conf_line \"%s\"=\"%s\"", TR_STR(k), TR_STR(v) j = (DKT_SORT_J *)jv; switch(dk3str_array_index(dkt_sort_long_opt, k, 0)) { case 0: { if(v) { $? ". stdin encoding" back = dkt_tool_set_encoding( j->app, &(j->enc_s), v, dk3app_get_input_stdin_encoding(j->app) ); } } break; case 1: { $? ". file encoding" if(v) { back = dkt_tool_set_encoding( j->app, &(j->enc_f), v, dk3app_get_input_file_encoding(j->app) ); } } break; case 2: { $? ". reset" back = 1; dkt_sort_job_reset(j); } break; case 3: { if(v) { back = dkt_sort_set_linesize(j, v); } } break; } $? "- dkt_sort_conf_line %d", back return back; } /** Process the command line arguments. @param j Job structure. @return 1 on success, 0 on error. */ static int dkt_sort_process_arguments(DKT_SORT_J *j) { int back = 0; int xargc = 0; /* Number of cmd line args. */ dkChar const * const *xargv = NULL; /* Cmd line args array. */ xargc = dk3app_get_argc(j->app); xargv = dk3app_get_argv(j->app); xargv++; xargv++; xargc--; xargc--; j->opt = dk3opt_open_app( dkt_sort_options, dkt_sort_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_sort_job_reset(j); } if(dk3opt_is_set(j->opt, dkT('n'))) { j->o_sort = j->c_sort = DKT_SORT_INTEGER; if(dk3opt_is_set(j->opt, dkT('u')) || dk3opt_is_set(j->opt, dkT('f'))) { back = 0; /* ERROR: Options -n -u -f are exclusive! */ dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 2); } } else { if(dk3opt_is_set(j->opt, dkT('u'))) { j->o_sort = j->c_sort = DKT_SORT_UNSIGNED; if(dk3opt_is_set(j->opt, dkT('f'))) { back = 0; /* ERROR: Options -n -u -f are exclusive! */ dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 2); } } else { if(dk3opt_is_set(j->opt, dkT('f'))) { j->o_sort = j->c_sort = DKT_SORT_DOUBLE; } } } j->c_sort |= DKT_SORT_EXACT; if(dk3opt_is_set(j->opt, dkT('s'))) { j->c_sort |= DKT_SORT_NOVALUE_FIRST; } if(dk3opt_is_set(j->opt, dkT('c'))) { j->c_sort |= DKT_SORT_CASEINS; } if(dk3opt_is_set(j->opt, dkT('r'))) { j->c_sort |= DKT_SORT_INVERT; } if(dk3opt_is_set(j->opt, dkT('w'))) { j->c_sort |= DKT_SORT_NORMALIZE; j->o_norm = 1; if(dk3opt_is_set(j->opt, dkT('b'))) { back = 0; /* ERROR: -w and -b are exlusive! */ dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 3); } } else { if(dk3opt_is_set(j->opt, dkT('b'))) { j->c_sort |= DKT_SORT_IGNORE_LWHS; j->o_igbl = 1; } } j->c_merg = j->c_sort; /* Same options for merge as for sort. */ if(dk3opt_is_set(j->opt, dkT('e'))) { j->c_sort |= DKT_SORT_MERGE; j->c_merg |= DKT_SORT_MERGE; j->o_merg = 1; if(dk3opt_is_set(j->opt, dkT('m'))) { back = 0; /* ERROR: -e and -m are exclusive! */ dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 4); } } else { if(dk3opt_is_set(j->opt, dkT('m'))) { j->c_sort |= DKT_SORT_MERGE; j->c_merg |= DKT_SORT_MERGE; j->o_merg = 1; if((j->c_merg) & (DKT_SORT_NORMALIZE | DKT_SORT_IGNORE_LWHS)) { $? ". no need for exact comparison" j->c_merg &= (~(DKT_SORT_EXACT)); } } } } } return back; } /** Handler function for one line. @param vj Job structure casted to void *. @param il Input line to process. @return 1=OK, 0=error, can continue, -1=error, abort processing. */ static int dkt_sort_line_handler(void *vj, dkChar *il) { int back = 0; int must_add = 1; /* Flag: Must add line. */ DKT_SORT_J *j = NULL; /* Job structure. */ DKT_SORT_L ltest; DKT_SORT_L *lnew = NULL; /* New line structure. */ dk3_im_t im = DK3_IM_0; /* Line value. */ dk3_um_t um = DK3_UM_0; /* Line value. */ double db = 0.0; /* Line value. */ $? "+ dkt_sort_line_handler \"%s\"", TR_STR(il) j = (DKT_SORT_J *)vj; dk3str_delnl(il); /* Create normalized version. */ if(j->o_norm) { $? ". Normalized comparison" dk3str_cpy_not_overlapped(j->b2, il); dk3str_normalize(j->b2, NULL, dkT(' ')); } /* Check for merging whether or not to add this line. */ if(j->o_merg) { $? ". Merge" ltest.ori = il; ltest.nrm = NULL; if(j->o_norm) { ltest.nrm = j->b2; } ltest.stt = NULL; if(j->o_igbl) { ltest.stt = dk3str_start(il, NULL); } ltest.hv = '\0'; switch(j->o_sort) { case DKT_SORT_INTEGER: { #if VERSION_BEFORE_20140716 if(dk3ma_string_to_im(&im, il)) #else if (0 != dk3ma_im_from_string(&im, il, NULL)) #endif { ltest.v.i = im; $? ". found int" ltest.hv = 'y'; } else { $? ". no int" } } break; case DKT_SORT_UNSIGNED: { #if VERSION_BEFORE_20140716 if(dk3ma_string_to_um(&um, il)) #else if (0 != dk3ma_um_from_string(&um, il, NULL)) #endif { ltest.v.u = um; $? ". found unsigned" ltest.hv = 'y'; } else { $? ". no unsigned" } } break; case DKT_SORT_DOUBLE: { #if VERSION_BEFORE_20140716 if(dk3sf_sscanf3(il, (j->kwnl)[1], &db) == 1) #else if(0 != dk3ma_d_from_string(&db, il, NULL)) #endif { ltest.v.d = db; $? ". found double" ltest.hv = 'y'; } else { $? ". no double" } } break; } lnew = (DKT_SORT_L *)dk3sto_it_find_like( j->sit, (void *)(<est), j->c_merg ); if(lnew) { $? ". line found" must_add = 0; } } /* Attept to add the line. */ if(must_add) { $? ". must add" lnew = dkt_sort_line_new(j, il, ((j->o_norm) ? (j->b2) : NULL)); if(lnew) { if(dk3sto_add(j->sto, (void *)lnew)) { back = 1; } else { dkt_sort_line_delete(lnew); back = -1; } } else { back = -1; } } else { back = 1; } $? "- dkt_sort_line_handler %d", back return back; } /** Process file names specified as command line arguments. @param j Job structure. @param nfn Number of file names. @return 1 on success, 0 on error. */ static int dkt_sort_process_files(DKT_SORT_J *j, int nfn) { int back = 1; int i = 0; /* Index of current file name. */ int res = 0; /* Line processing result. */ dkChar const *fn = NULL; /* File name. */ dk3_dir_t *fne = NULL; /* File name expander. */ dkChar const *en = NULL; /* Full file name. */ dkChar bu[DK3_MAX_PATH]; /* File name buffer. */ for(i = 0; i < nfn; i++) { fn = dk3opt_get_arg(j->opt, i); 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) > 0) { while(dk3dir_get_next_file(fne)) { en = dk3dir_get_fullname(fne); if(en) { res = dk3stream_process_filename_lines_app( (void *)j, dkt_sort_line_handler, en, j->b1, j->linesz, dk3app_get_encoding(j->app), j->enc_f, j->app ); if(res < 0) { back = 0; } } } } else { back = 0; /* ERROR: No matching file name */ dk3app_log_i3(j->app, DK3_LL_ERROR, 215, 216, fn); } dk3dir_close(fne); } } else { res = dk3stream_process_filename_lines_app( (void *)j, dkt_sort_line_handler, bu, j->b1, j->linesz, dk3app_get_encoding(j->app), j->enc_f, j->app ); if(res < 1) { back = 0; } } } else { back = 0; /* ERROR: File name too long */ dk3app_log_i3(j->app, DK3_LL_ERROR, 65, 66, fn); } } else { /* BUG */ } } return back; } /** Process data from standard input. @param j Job structure. @return 1 on success, 0 on error. */ static int dkt_sort_process_stdin(DKT_SORT_J *j) { int back = 0; back = dk3app_process_stdin_lines( j->app, (void *)j, dkt_sort_line_handler, j->b1, j->linesz, j->enc_s ); return back; } /** Get input data. @param j Job structure. @return 1 on success, 0 on error. */ static int dkt_sort_get_with_buffers(DKT_SORT_J *j) { int back = 0; int nfn = 0; /* Number of file names. */ $? "+ dkt_sort_get_with_buffers" nfn = dk3opt_get_num_args(j->opt); if(nfn) { back = dkt_sort_process_files(j, nfn); } else { back = dkt_sort_process_stdin(j); } $? "- dkt_sort_get_with_buffers %d", back return back; } /** Use allocated buffers to process input. @param j Job structure. @return 1 on success, 0 on error. */ static int dkt_sort_get_with_allocated_buffers(DKT_SORT_J *j) { int back = 0; dkChar *b1 = NULL; /* Allocated buffer 1. */ dkChar *b2 = NULL; /* Allocated buffer 2. */ b1 = dk3_new_app(dkChar,j->linesz,j->app); if(b1) { b2 = dk3_new_app(dkChar,j->linesz,j->app); if(b2) { j->b1 = b1; j->b2 = b2; back = dkt_sort_get_with_buffers(j); j->b1 = NULL; j->b2 = NULL; dk3_delete(b2); } dk3_delete(b1); } return back; } /** Use builtin buffers to process input. @param j Job structure. @return 1 on success, 0 on error. */ static int dkt_sort_get_with_builtin_buffers(DKT_SORT_J *j) { int back = 0; dkChar b1[DKT_SORT_LINE_SIZE]; /* Buffer 1. */ dkChar b2[DKT_SORT_LINE_SIZE]; /* Buffer 2. */ size_t oldsz = 0; /* Line size. */ j->b1 = b1; j->b2 = b2; oldsz = j->linesz; j->linesz = DKT_SORT_LINE_SIZE; back = dkt_sort_get_with_buffers(j); j->linesz = oldsz; j->b1 = NULL; j->b2 = NULL; return back; } /** Read input from named files or from standard input. @param j Job structure. @return 1 on success, 0 on error. */ static int dkt_sort_get_input(DKT_SORT_J *j) { int back = 0; if(j->linesz > DKT_SORT_LINE_SIZE) { back = dkt_sort_get_with_allocated_buffers(j); } else { back = dkt_sort_get_with_builtin_buffers(j); } return back; } /** Input was read successfully, now produce output. @param j Job structure. */ static void dkt_sort_produce_output(DKT_SORT_J *j) { DKT_SORT_L *lp = NULL; /* Current line to process. */ dkChar const *ptr = NULL; /* Line text. */ register int initialized = 0; /* Flag: stdout initialized. */ dk3sto_it_reset(j->sit); while((lp = (DKT_SORT_L *)dk3sto_it_next(j->sit)) != NULL) { if(!initialized) { dk3sf_initialize_stdout(); initialized = 1; } ptr = lp->ori; if(j->o_merg) { if((j->c_merg) & DKT_SORT_NORMALIZE) { if(!((j->c_merg) & DKT_SORT_EXACT)) { if(lp->nrm) { ptr = lp->nrm; } } } else { if((j->c_merg) & DKT_SORT_IGNORE_LWHS) { if(!((j->c_merg) & DKT_SORT_EXACT)) { if(lp->stt) { ptr = lp->stt; } } } } } if(ptr) { dk3sf_fputs(ptr, stdout); dk3sf_fputc(dkT('\n'), stdout); } } } /** Create storage and iterator to save input lines. @param j Job structure. @return 1 on success, 0 on error. */ static int dkt_sort_create_storage(DKT_SORT_J *j) { int back = 0; j->sto = dk3sto_open_app(j->app); if(j->sto) { dk3sto_set_comp(j->sto, dkt_sort_compare, j->c_sort); j->sit = dk3sto_it_open(j->sto); if(j->sit) { back = 1; } } return back; } int dkt_sort( dk3_app_t *app, dkChar const *sn, dkChar const * const *msg, dkChar const * const *kwnl ) { int back = DKT_RESULT_ERR_UNSPECIFIC; DKT_SORT_J j; $? "+ dkt_sort" j.app = app; j.msg = msg; j.kwnl = kwnl; dkt_sort_job_init(&j); dkt_tool_read_conf(app, sn, (void *)(&j), dkt_sort_conf_line); if(dkt_sort_process_arguments(&j)) { if(dkt_sort_create_storage(&j)) { if(dkt_sort_get_input(&j)) { dkt_sort_produce_output(&j); } } } dkt_sort_job_cleanup(&j); $? "- dkt_sort %d", back return back; } /* vim: set ai sw=2 : */