%% options copyright owner = Dirk Krause copyright year = 2011-2014 license = bsd %% module #include "dk3all.h" #include "dk3ufi.h" #include "dkt.h" $!trace-include /** Job structure for dkt ls. */ 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. */ dkChar const *poall; /**< Print order, allocated. */ dkChar const *po; /**< Print order from args. */ int *ft; /**< File type enabled. */ dk3_um_t sld; /**< Symlink depth. */ dk3_um_t s_byt; /**< Summary bytes. */ dk3_um_t s_fil; /**< Summary files. */ dk3_um_t s_dir; /**< Summary directories. */ size_t sz_sz; /**< Length of size. */ size_t sz_nl; /**< Length of nlink. */ size_t sz_dev; /**< Length of device number. */ size_t sz_ino; /**< Length of inode number. */ size_t sz_uid; /**< Length of UID number. */ size_t sz_gid; /**< Length of GID number. */ int exval; /**< Exit status code. */ int o_ssl; /**< Option: Show symlinks. */ int o_sum; /**< Option: Show summary. */ int o_fs; /**< Option: Stay on file system. */ int o_rec; /**< Option: Recursive. */ int o_der; /**< Option: Dereference symlinks. */ int o_sz; /**< Option: Show size. */ int mdt; /**< Message digest type. */ int mde; /**< Message digest encoding. */ int e_mat; /**< Math error. */ } DKT_LS_J; /** Directory chain member. */ struct _dkt_ls_dir_ { struct _dkt_ls_dir_ *parent; /**< Parent chain member. */ dk3_stat_t ds; /**< Stat for directory. */ dk3_stat_t dso; /**< Original directory stat. */ dk3_dir_t *dir; /**< Directory. */ dk3_um_t sl; /**< Symlink level. */ }; /** Short name for directory chain member. */ typedef struct _dkt_ls_dir_ DKT_LS_DIR; /** Configuratin keywords for dkt ls. */ static dkChar const * const dkt_ls_conf_kw[] = { /* 0 */ dkT("print-order"), /* 1 */ dkT("message-digest"), /* 2 */ dkT("reset"), NULL }; /** Options used by dkt ls. */ static dk3_option_t const dkt_ls_options[] = { { dkT('R'), dkT("reset"), 0 }, { dkT('m'), dkT("message-digest"), 1 }, { dkT('d'), dkT("dereference-symlinks"), 0 }, { dkT('s'), dkT("summary"), 0 }, { dkT('f'), dkT("stay-on-filesystem"), 0 }, { dkT('l'), dkT("symlink-depth"), 1 }, { dkT('t'), dkT("type"), 1 }, { dkT('r'), dkT("recursive"), 0 }, { dkT('p'), dkT("print-order"), 1 } }; /** Number of options in the dkt_ls_options array. */ static size_t const dkt_ls_szoptions = sizeof(dkt_ls_options)/sizeof(dk3_option_t); /** Reset job structure. @param j Structure to reset. */ static void dkt_ls_job_reset(DKT_LS_J *j) { int i; /* Used to traverse j->ft array. */ dk3_release(j->poall); j->po = NULL; j->sld = DK3_UM_0; j->o_ssl = 1; for(i = 0; i <= DK3_FT_MAX; i++) { (j->ft)[i] = 1; } j->o_sum = 0; j->o_fs = 0; j->o_rec = 0; j->mdt = DK3_MD_TYPE_SHA_1; j->mde = DK3_DATA_ENCODING_ASCII85; j->o_der = 0; } /** Initialize job structure. @param j Job structure. */ static void dkt_ls_job_init(DKT_LS_J *j) { j->opt = NULL; j->exval = DKT_RESULT_ERR_UNSPECIFIC; j->po = NULL; j->poall = NULL; j->sld = DK3_UM_0; j->s_byt = DK3_UM_0; j->s_fil = DK3_UM_0; j->s_dir = DK3_UM_0; j->o_ssl = 1; j->o_sum = 0; j->o_fs = 0; j->o_rec = 0; j->mdt = DK3_MD_TYPE_SHA_1; j->mde = DK3_DATA_ENCODING_ASCII85; j->o_der = 0; j->o_sz = 0; j->sz_sz = 10; j->sz_nl = 2; j->sz_dev = 10; j->sz_ino = 10; j->sz_uid = 5; j->sz_gid = 5; j->e_mat = 0; } /** Clean up job structure. @param j Job structure. */ static void dkt_ls_job_cleanup(DKT_LS_J *j) { if(j->opt) { dk3opt_close(j->opt); } j->opt = NULL; dk3_release(j->poall); } /** Set list of file types to show. @param j Job structure. @param v String containing file type characters. @return 1 on success, 0 on error (illegal characters). */ static int dkt_ls_set_filetypes(DKT_LS_J *j, dkChar const *v) { int back = 0; int i = 0; /* Used to traverse j->ft array. */ dkChar const *ptr = NULL; /* Current character to process. */ dkChar errft[2]; /* Used to show illegal type in message. */ errft[0] = dkT('\0'); if(v) { back = 1; j->o_ssl = 0; for(i = 0; i <= DK3_FT_MAX; i++) { (j->ft)[i] = 0; } ptr = v; while(*ptr) { switch(*ptr) { case dkT('l'): { j->o_ssl = 1; } break; case dkT('f'): { (j->ft)[DK3_FT_REGULAR] = 1; } break; case dkT('d'): { (j->ft)[DK3_FT_DIRECTORY] = 1; } break; case dkT('p'): { (j->ft)[DK3_FT_FIFO] = 1; } break; case dkT('c'): { (j->ft)[DK3_FT_SPECIAL_CHAR] = 1; } break; case dkT('b'): { (j->ft)[DK3_FT_SPECIAL_BLOCK] = 1; } break; case dkT('s'): { (j->ft)[DK3_FT_SOCKET] = 1; } break; case dkT('D'): { (j->ft)[DK3_FT_DOOR] = 1; } break; case dkT('e'): { (j->ft)[DK3_FT_EVENT_PORT] = 1; } break; case dkT('O'): { (j->ft)[DK3_FT_XENIX_SPECIAL] = 1; } break; case dkT('N'): { (j->ft)[DK3_FT_XENIX_SEMAPHORE] = 1; } break; case dkT('M'): { (j->ft)[DK3_FT_XENIX_SHARED_DATA] = 1; } break; case dkT('C'): { (j->ft)[DK3_FT_MUX_CHAR] = 1; } break; case dkT('B'): { (j->ft)[DK3_FT_MUX_BLOCK] = 1; } break; case dkT('v'): { (j->ft)[DK3_FT_VXFS_COMPRESSED] = 1; } break; case dkT('w'): { (j->ft)[DK3_FT_WHITEOUT] = 1; } break; case dkT('n'): { (j->ft)[DK3_FT_NETWORK_SPECIAL] = 1; } break; case dkT('a'): { (j->ft)[DK3_FT_ACL_SHADOW] = 1; } break; case dkT('!'): { (j->ft)[DK3_FT_BAD_SYMLINK] = 1; } break; default: { back = 0; /* ERROR: File type character unknown! */ errft[0] = *ptr; errft[1] = dkT('\0'); dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 5, 6, errft); } break; } ptr++; } } return back; } /** Save print order specification, allocate memory. @param j Job structure. @param v New print order. @return 1 on success, 0 on error. */ static int dkt_ls_set_print_order_allocated(DKT_LS_J *j, dkChar const *v) { int back = 0; dkChar *ns = NULL; /* New string, dynamically allocated. */ if(v) { ns = dk3str_dup_app(v, j->app); if(ns) { if(j->poall) { dk3_delete(j->poall); } j->poall = ns; j->po = j->poall; } else { j->exval = DKT_RESULT_ERR_MEMORY; } } return back; } #if 0 /** Set digest type and encoding for checksums. @param j Job structure. @param txt Text containing digest type and encoding. @param verb Flag: Verbose. @return 1 on success, 0 on error. */ static int dkt_ls_set_md_type(DKT_LS_J *j, dkChar const *txt, int verb) { int back = 0; int cst = DK3_MD_TYPE_SHA_1; /* Message digest type. */ int et = DK3_DATA_ENCODING_ASCII85; /* Binary-to-text encoding. */ dkChar bu[DK3_MAX_PATH]; /* File name buffer. */ dkChar *p1 = NULL; /* Digest. */ dkChar *p2 = NULL; /* Encoding. */ bu[0] = dkT('\0'); if(dk3str_len(txt) < DK3_SIZEOF(bu,dkChar)) { dk3str_cpy_not_overlapped(bu, txt); p1 = dk3str_start(bu, 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); } if((cst = dk3checksum_get_type_app(p1, j->app)) > -1) { back = 1; if(p2) { back = 0; if((et = dk3enc_get_type_app(p2, j->app)) > -1) { back = 1; } else { if(verb) { j->exval = DKT_RESULT_ERR_OPTION; } } } } else { if(verb) { j->exval = DKT_RESULT_ERR_OPTION; } } } else { /* ERROR: Empty string! */ if(verb) { dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 7); j->exval = DKT_RESULT_ERR_OPTION; } } if(back) { j->mdt = cst; j->mde = et; } } else { /* ERROR: String too long! */ dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 8, 9, txt); } return back; } #endif /** Save symlink depth. @param j Job structure. @param v Number, "unlimited" or boolean. @param verb Flag: Verbose. @return 1 on success, 0 on error. */ static int dkt_ls_set_symlink_depth(DKT_LS_J *j, dkChar const *v, int verb) { int back = 0; dk3_um_t um = DK3_UM_0; /* Symlink depth. */ if(v) { #if VERSION_BEFORE_20140716 if(dk3ma_string_to_um(&um, v)) #else if (0 != dk3ma_um_from_string(&um, v, NULL)) #endif { j->sld = um; back = 1; } else { if(dk3str_is_bool(v)) { if(dk3str_is_on(v)) { j->sld = DK3_UM_MAX; } else { j->sld = DK3_UM_0; } back = 1; } else { if(dk3str_cmp((j->kwnl)[2], v) == 0) { j->sld = DK3_UM_MAX; back = 1; } } } if(!back) { /* ERROR: Illegal symlink depth! */ if(verb) { dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 10, 11, v); j->exval = DKT_RESULT_ERR_OPTION; } } } return back; } /** 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_ls_conf_line(void *jv, dkChar const *k, dkChar const *v) { int back = 0; DKT_LS_J *j; j = (DKT_LS_J *)jv; switch(dk3str_array_index(dkt_ls_conf_kw, k, 0)) { case 0: { back = dkt_ls_set_print_order_allocated(j, v); } break; case 1: { if(v) { /* back = dkt_ls_set_md_type(j, v, 0); */ back = dkt_tool_set_md_type( &(j->mdt), &(j->mde), &(j->exval), v, j->app, j->msg, 0 ); } } break; case 2: { back = 1; dkt_ls_job_reset(j); } break; } return back; } /** Process the command line arguments. @param j Job structure. @return 1 on success, 0 on error. */ static int dkt_ls_process_arguments(DKT_LS_J *j) { int back = 0; int xargc = 0; /* Number of command line arguments. */ dkChar const *arg = NULL; /* Current command line argument. */ dkChar const * const *xargv = NULL; /* Command line arguments array. */ $? "+ dkt_ls_process_arguments" xargc = dk3app_get_argc(j->app); xargv = dk3app_get_argv(j->app); xargv++; xargv++; xargc--; xargc--; j->opt = dk3opt_open_app( dkt_ls_options, dkt_ls_szoptions, dkT('\0'), NULL, xargc, xargv, j->app ); if(j->opt) { if(0 == dk3opt_get_error_code(j->opt)) { $? ". ok" back = 1; if(dk3opt_is_set(j->opt, dkT('R'))) { dkt_ls_job_reset(j); } if(dk3opt_is_set(j->opt, dkT('m'))) { $? ". message digest" arg = dk3opt_get_short_arg(j->opt, dkT('m')); if(arg) { if(!dkt_tool_set_md_type( &(j->mdt), &(j->mde), &(j->exval), arg, j->app, j->msg, 1 ) ) { back = 0; $? "! failed" } } else { back = 0; $? "! failed" j->exval = DKT_RESULT_ERR_OPTION; } } if(dk3opt_is_set(j->opt, dkT('d'))) { $? ". dereference symlinks" j->o_der = 1; } if(dk3opt_is_set(j->opt, dkT('s'))) { $? ". summary" j->o_sum = 1; } if(dk3opt_is_set(j->opt, dkT('f'))) { $? ". stay on filesystem" j->o_fs = 1; } if(dk3opt_is_set(j->opt, dkT('l'))) { $? ". symlink depth" arg = dk3opt_get_short_arg(j->opt, dkT('l')); if(arg) { if(!dkt_ls_set_symlink_depth(j, arg, 1)) { back = 0; $? "! failed" } } else { back = 0; $? "! failed" j->exval = DKT_RESULT_ERR_OPTION; } } if(dk3opt_is_set(j->opt, dkT('t'))) { $? ". file types" arg = dk3opt_get_short_arg(j->opt, dkT('t')); if(arg) { if(!dkt_ls_set_filetypes(j, arg)) { back = 0; $? "! failed" } } else { back = 0; $? "! failed" j->exval = DKT_RESULT_ERR_OPTION; } } if(dk3opt_is_set(j->opt, dkT('r'))) { $? ". recursive" j->o_rec = 1; } if(dk3opt_is_set(j->opt, dkT('p'))) { $? ". print order" arg = dk3opt_get_short_arg(j->opt, dkT('p')); if(arg) { j->po = arg; } else { back = 0; $? "! failed" j->exval = DKT_RESULT_ERR_OPTION; } } } else { j->exval = DKT_RESULT_ERR_OPTION; } } else { j->exval = DKT_RESULT_ERR_OPTION; } $? "- dkt_ls_process_arguments %d", back return back; } /** Set the o_sz flag if the print order contains an 's'. @param j Job structure. */ static void dkt_ls_check_for_size(DKT_LS_J *j) { dkChar const *ptr = NULL; /* Used to traverse print order. */ if(!(j->po)) { j->po = (j->kwnl)[3]; } ptr = j->po; j->o_sz = 0; while(*ptr) { if(*ptr == dkT('s')) { j->o_sz = 1; } ptr++; } } /** Print one maximum length unsigned integer. @param j Job structure. @param uf Value for file (link target). @param ul Value for link. @param sz Maximum length used so far. @param gr Increase value for length. @param islink Flag: Use link value. */ static void dkt_ls_print_um( DKT_LS_J *j, dk3_um_t uf, dk3_um_t ul, size_t *sz, size_t gr, int islink ) { dkChar buffer[64]; /* Buffer for number. */ size_t used = 0; /* Length needed for number. */ size_t i = 0; /* Used in for loop to fill space. */ buffer[0] = dkT('\0'); if(dk3ma_um_to_string(buffer, 64, ((islink) ? ul : uf))) { used = dk3str_len(buffer); while((*sz) < used) { *sz = *sz + gr; } for(i = used; i < (*sz); i++) { dk3sf_fputc(dkT(' '), stdout); } dk3sf_fputs(buffer, stdout); } else { j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } #if DK3_ON_WINDOWS /** Obtain reparse point character to print. @param j Job structure. @param stb Stat buffer for a file. @param is_link Flag: Is link. @return File type character. */ static dkChar dkt_ls_get_reparse_char(DKT_LS_J *j, dk3_stat_t const *stb) { dkChar back = dkT('-'); switch(stb->cReparse) { case 'r': { back = dkT('r'); } break; case 'm': { back = dkT('m'); } break; case 'l': { back = dkT('l'); } break; } return back; } #endif /** Obtain type character to print. @param j Job structure. @param stb Stat buffer for a file. @param is_link Flag: Is link. @return File type character. */ static dkChar dkt_ls_get_type_char(DKT_LS_J *j, dk3_stat_t const *stb, int is_link) { dkChar back = dkT('-'); if(is_link) { back = dkT('l'); if((stb->ai) & DK3_STAT_AI_FAR_LINK) { back = dkT('L'); } if(((stb->ft) & (~(DK3_FT_SYMLINK))) == DK3_FT_BAD_SYMLINK) { back = dkT('!'); } } else { switch((stb->ft) & (~(DK3_FT_SYMLINK))) { case DK3_FT_DIRECTORY: { back = dkT('d'); } break; case DK3_FT_FIFO: { back = dkT('p'); } break; case DK3_FT_SPECIAL_CHAR: { back = dkT('c'); } break; case DK3_FT_SPECIAL_BLOCK: { back = dkT('b'); } break; case DK3_FT_SOCKET: { back = dkT('s'); } break; case DK3_FT_DOOR: { back = dkT('D'); } break; case DK3_FT_EVENT_PORT: { back = dkT('e'); } break; case DK3_FT_XENIX_SPECIAL: { back = dkT('O'); } break; case DK3_FT_XENIX_SEMAPHORE: { back = dkT('N'); } break; case DK3_FT_XENIX_SHARED_DATA: { back = dkT('M'); } break; case DK3_FT_MUX_CHAR: { back = dkT('C'); } break; case DK3_FT_MUX_BLOCK: { back = dkT('B'); } break; case DK3_FT_VXFS_COMPRESSED: { back = dkT('v'); } break; case DK3_FT_NETWORK_SPECIAL: { back = dkT('n'); } break; case DK3_FT_WHITEOUT: { back = dkT('w'); } break; case DK3_FT_ACL_SHADOW: { back = dkT('a'); } break; case DK3_FT_BAD_SYMLINK: { back = dkT('!'); } break; } } return back; } /** Print file permissions. @param j Job structure. @param perms Permissions in DK3_PERM_xxx notation. */ static void dkt_ls_print_permissions(DKT_LS_J *j, int perms) { #if DK3_ON_WINDOWS dkChar bu[4]; bu[0] = dkT('-'); bu[1] = dkT('-'); bu[2] = dkT('-'); bu[3] = dkT('\0'); if(perms & DK3_FPERM_U_READ) { bu[0] = dkT('r'); } if(perms & DK3_FPERM_U_WRITE) { bu[1] = dkT('w'); } if(perms & DK3_FPERM_U_EXEC) { bu[2] = dkT('x'); } dk3sf_fputs(bu, stdout); #else dkChar bu[10]; bu[0] = dkT('-'); bu[1] = dkT('-'); bu[2] = dkT('-'); bu[3] = dkT('-'); bu[4] = dkT('-'); bu[5] = dkT('-'); bu[6] = dkT('-'); bu[7] = dkT('-'); bu[8] = dkT('-'); bu[9] = dkT('\0'); if(perms & DK3_FPERM_U_READ) { bu[0] = dkT('r'); } if(perms & DK3_FPERM_U_WRITE) { bu[1] = dkT('w'); } if(perms & DK3_FPERM_U_EXEC) { bu[2] = dkT('x'); } if(perms & DK3_FPERM_G_READ) { bu[3] = dkT('r'); } if(perms & DK3_FPERM_G_WRITE) { bu[4] = dkT('w'); } if(perms & DK3_FPERM_G_EXEC) { bu[5] = dkT('x'); } if(perms & DK3_FPERM_O_READ) { bu[6] = dkT('r'); } if(perms & DK3_FPERM_O_WRITE) { bu[7] = dkT('w'); } if(perms & DK3_FPERM_O_EXEC) { bu[8] = dkT('x'); } if(perms & DK3_FPERM_SUID) { if(perms & DK3_FPERM_U_EXEC) { bu[2] = dkT('s'); } else { bu[2] = dkT('S'); } } if(perms & DK3_FPERM_SGID) { if(perms & DK3_FPERM_G_EXEC) { bu[5] = dkT('s'); } else { bu[5] = dkT('S'); } } if(perms & DK3_FPERM_VTX) { if(perms & DK3_FPERM_O_EXEC) { bu[8] = dkT('t'); } else { bu[8] = dkT('T'); } } dk3sf_fputs(bu, stdout); #endif } /** Print a time stamp. @param j Job structure. @param timer The timestamp to print. */ static void dkt_ls_print_time(DKT_LS_J *j, dk3_time_t const *timer) { dkChar bu[128]; bu[0] = dkT('\0'); if(dk3sf_time_convert_app(bu, DK3_SIZEOF(bu,dkChar), timer, j->app)) { dk3sf_fputs(bu, stdout); } else { j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } /** Print checksum (message digest converted to text) for a file. @param j Job structure. @param stb Stat buffer for file. @param fn File name. @param il Flag: Is link. */ static void dkt_ls_print_checksum( DKT_LS_J *j, dk3_stat_t const *stb, dkChar const *fn, int il ) { char csb[256]; /* Checksum buffer. */ size_t s = 0; /* Text checksum length. */ size_t i = 0; /* Index of current character to process. */ #if DK3_CHAR_SIZE > 1 dkChar dkcsb[sizeof(csb)]; /* Checksum buffer as 16-bit characters. */ dkcsb[0] = dkT('\0'); #endif csb[0] = '\0'; s = dk3checksum_length(j->mdt, j->mde); s--; if((!(il)) && (((stb->ft) & (~(DK3_FT_SYMLINK))) == DK3_FT_REGULAR)) { if(dk3checksum_build_app(csb, sizeof(csb), fn, j->mdt, j->mde, j->app)) { #if DK3_CHAR_SIZE > 1 if(dk3str_cnv_c8_to_str_app(dkcsb, sizeof(csb), csb, j->app)) { dk3sf_fputs(dkcsb, stdout); } #else fputs(csb, stdout); #endif } else { for(i = 0; i < s; i++) { dk3sf_fputc(dkT('-'), stdout); } j->exval = DKT_RESULT_ERR_INPUT; } } else { for(i = 0; i < s; i++) { dk3sf_fputc(dkT('-'), stdout); } } } /** Add entry size to summary. @param j Job structure. @param isdir Flag: Entry is directory. @param st Stat buffer. @param pr Flag: Dereference symlinks. */ static void dkt_ls_add_summary( DKT_LS_J *j, int isdir, dk3_stat_t const *st, int pr ) { int is_lnk = 0; /* Flag: Entry is symbolic link. */ $? "+ dkt_ls_add_summary isdir=%d", isdir if(isdir) { j->s_dir = dk3ma_um_add_ok(j->s_dir, DK3_UM_1, &(j->e_mat)); } else { j->s_fil = dk3ma_um_add_ok(j->s_fil, DK3_UM_1, &(j->e_mat)); } is_lnk = 0; if(j->o_der) { if((st->ft) & DK3_FT_SYMLINK) { is_lnk = 1; } } if(pr) { is_lnk = 0; } if(is_lnk) { j->s_byt = dk3ma_um_add_ok(j->s_byt, st->lsz, &(j->e_mat)); } else { j->s_byt = dk3ma_um_add_ok(j->s_byt, st->sz, &(j->e_mat)); } $? "- dkt_ls_add_summary" } /** Print unique file identifier. @param job Job structure. @param arg File name to print information for. */ static void dkt_ls_print_ufi(DKT_LS_J *job, dkChar const *arg) { dkChar b1[64]; dkChar b2[64]; #if DK3_ON_WINDOWS dkChar b3[64]; #endif dk3_ufi_t ufib; if(dk3ufi_get_app(&ufib, arg, job->app)) { #if DK3_ON_WINDOWS dk3sf_sprintf3(b1, dkT("%08lx"), (long)(ufib.volser)); dk3sf_sprintf3(b2, dkT("%08lx"), (long)(ufib.indhigh)); dk3sf_sprintf3(b3, dkT("%08lx"), (long)(ufib.indlow)); dk3sf_fputs(b1, stdout); dk3sf_fputc(dkT(':'), stdout); dk3sf_fputs(b2, stdout); dk3sf_fputc(dkT(':'), stdout); dk3sf_fputs(b3, stdout); #else if(dk3ma_um_to_hex_string(b1, DK3_SIZEOF(b1,dkChar), ufib.device, 1)) { if(dk3ma_um_to_hex_string(b2, DK3_SIZEOF(b2,dkChar), ufib.inode, 1)) { dk3sf_fputs(b1, stdout); dk3sf_fputc(dkT(':'), stdout); dk3sf_fputs(b2, stdout); } else { if(sizeof(dk3_um_t) > 4) { dk3sf_fputs((job->kwnl)[16], stdout); } else { dk3sf_fputs((job->kwnl)[17], stdout); } } } else { if(sizeof(dk3_um_t) > 4) { dk3sf_fputs((job->kwnl)[16], stdout); } else { dk3sf_fputs((job->kwnl)[17], stdout); } } #endif } else { #if DK3_ON_WINDOWS dk3sf_fputs((job->kwnl)[15], stdout); #else if(sizeof(dk3_um_t) > 4) { dk3sf_fputs((job->kwnl)[16], stdout); } else { dk3sf_fputs((job->kwnl)[17], stdout); } #endif } } /** Report one directory entry. If the directory was found in a chain add the entry size to the directory information. @param j Job structure. @param stb Stat buffer of entry. @param arg Entry name. @param dir Directory chain member, may be NULL. @param isprim Flag: Primary entry (ignore DK3_FT_SYMLINK). */ static void dkt_ls_report_entry( DKT_LS_J *j, dk3_stat_t const *stb, dkChar const *arg, DKT_LS_DIR *dir, int isprim ) { int report_this = 1; /* Flag: Report this entry. */ dkChar const *item_to_show = NULL; /* File name. */ int is_link = 0; /* Flag: Entry is a symbolic link. */ int is_first = 0; /* Flag: First column. */ int last_was_type = 0; /* Flag: Last column was type. */ int show_file_index = 0; /* Index in j->ft for file type. */ is_link = 0; if((stb->ft) & DK3_FT_SYMLINK) { is_link = 1; } if(isprim) { is_link = 0; } if(j->o_der) { is_link = 0; } if(is_link) { if(j->o_ssl) report_this = 1; else report_this = 0; } else { show_file_index = ((stb->ft) & (~(DK3_FT_SYMLINK))); if(show_file_index <= DK3_FT_MAX) { report_this = (j->ft)[show_file_index]; } } if(report_this) { is_first = 1; last_was_type = 0; item_to_show = j->po; if(!(item_to_show)) { item_to_show = (j->kwnl)[3]; } while(*item_to_show) { if(!is_first) { if(!((*item_to_show == dkT('p')) && (last_was_type))) { dk3sf_fputc(dkT(' '), stdout); } } is_first = 0; last_was_type = 0; switch(*item_to_show) { case dkT('n'): { $? ". name" dk3sf_fputs(arg, stdout); } break; case dkT('s'): { $? ". size" dkt_ls_print_um( j, stb->sz, stb->lsz, &(j->sz_sz), 5, is_link ); } break; case dkT('t'): { $? ". type" dk3sf_fputc(dkt_ls_get_type_char(j, stb, is_link), stdout); #if DK3_ON_WINDOWS dk3sf_fputc(dkt_ls_get_reparse_char(j, stb), stdout); #endif last_was_type = 1; } break; case dkT('p'): { $? ". permissions" dkt_ls_print_permissions(j, ((is_link) ? stb->lperm : stb->perm)); } break; case dkT('x'): { $? ". message digest" dkt_ls_print_checksum(j, stb, arg, is_link); } break; case dkT('l'): { $? ". number of links" dkt_ls_print_um( j, stb->nlink, stb->lnlink, &(j->sz_nl), 1, is_link ); } break; case dkT('c'): { $? ". creation" dkt_ls_print_time( j, ((is_link) ? &(stb->lcre) : &(stb->cre)) ); } break; case dkT('m'): { $? ". modification" dkt_ls_print_time( j, ((is_link) ? &(stb->lmod) : &(stb->mod)) ); } break; case dkT('a'): { $? ". access" dkt_ls_print_time( j, ((is_link) ? &(stb->lacc) : &(stb->acc)) ); } break; case dkT('u'): { $? ". UID" dkt_ls_print_um( j, stb->u, stb->lu, &(j->sz_uid), 5, is_link ); } break; case dkT('g'): { $? ". GID" dkt_ls_print_um( j, stb->g, stb->lg, &(j->sz_gid), 5, is_link ); } break; case dkT('d'): { $? ". device" dkt_ls_print_um( j, stb->device, stb->ldevice, &(j->sz_dev), 5, is_link ); } break; case dkT('r'): { $? ". relative device" dkt_ls_print_um( j, stb->rdev, stb->lrdev, &(j->sz_dev), 5, is_link ); } break; case dkT('i'): { $? ". inode" dkt_ls_print_um( j, stb->inode, stb->linode, &(j->sz_ino), 5, is_link ); } break; case dkT('f'): { dkt_ls_print_ufi(j, arg); } break; #if DK3_ON_WINDOWS case dkT('w'): { dkChar bu[16]; unsigned long dw; $? ". windows reparse tag" dw = 0UL; if(stb->cReparse) { dw = stb->dwReparse; } dk3sf_sprintf3(bu, dkT("%08lx"), dw); dk3sf_fputs(bu, stdout); } break; #endif } item_to_show++; } dk3sf_fputc(dkT('\n'), stdout); } } /** Delete directory information struct, release memory. @param dp Struct to delete. */ static void dkt_ls_dir_delete(DKT_LS_DIR *dp) { $? "+ dkt_ls_dir_delete %s", TR_PTR(dp) if(dp) { if(dp->parent) { $? ". have parent" dp->parent = NULL; } if(dp->sl) { dp->sl = DK3_UM_0; } if(dp->dir) { $? ". must close dir" dk3dir_close(dp->dir); dp->dir = NULL; } dk3_delete(dp); } $? "- dkt_ls_dir_delete" } /** Create directory information struct, allocate memory. @param j Job structure. @param fn File name. @param stb Stat buffer containing information about \a fn. @param parent Parent directory chain element. @return Pointer to new struct on success, NULL on error. */ static DKT_LS_DIR * dkt_ls_dir_new( DKT_LS_J *j,dkChar const *fn,dk3_stat_t const *stb,DKT_LS_DIR *parent ) { DKT_LS_DIR *back = NULL; back = dk3_new_app(DKT_LS_DIR,1,j->app); if(back) { back->parent = parent; back->sl = DK3_UM_0; back->dir = dk3dir_open_app(fn, j->app); if(back->dir) { if(stb) { dk3mem_cpy(&(back->ds), stb, sizeof(dk3_stat_t)); dk3mem_cpy(&(back->dso), stb, sizeof(dk3_stat_t)); } else { if(dk3sf_stat_app(&(back->ds), fn, j->app)) { dk3mem_cpy(&(back->dso), &(back->ds), sizeof(dk3_stat_t)); } else { dkt_ls_dir_delete(back); back = NULL; j->exval = DKT_RESULT_ERR_FILENAME; } } } else { dkt_ls_dir_delete(back); back = NULL; j->exval = DKT_RESULT_ERR_OPENDIR; } if(back) { if(parent) { back->sl = parent->sl; if((stb->ft) & DK3_FT_SYMLINK) { back->sl += DK3_UM_1; } } } } else { j->exval = DKT_RESULT_ERR_MEMORY; } return back; } /** Check whether or not to go into a subdirectory. @param j Job structure. @param curdir Current directory chain element. @param fn File name. @param stb Stat buffer. @return 1 for change into directory, 0 for not. */ static int dkt_ls_check_go_sub( DKT_LS_J *j, DKT_LS_DIR *curdir, dkChar const *fn, dk3_stat_t const *stb ) { int back = 0; int sllok = 0; /* Flag: Symbolic link level ok. */ #if DK3_HAVE_INODES DKT_LS_DIR *cdir; /* Current directory chain element. */ #endif if((j->o_rec) || (j->o_sz)) { if((stb->ft) & DK3_FT_SYMLINK) { if(j->o_der) { if(j->sld == DK3_UM_MAX) { sllok = 1; } if(curdir) { if(curdir->sl < j->sld) { sllok = 1; } } if(sllok) { back = 1; #if DK3_HAVE_INODES cdir = curdir; while((back) && (cdir)) { if((cdir->ds).device == stb->device) { if((cdir->ds).inode == stb->inode) { back = 0; } } cdir = cdir->parent; } #endif } } } else { back = 1; } } if(back) { if(j->o_fs) { if(curdir) { if((curdir->ds).device != stb->device) { back = 0; } } } } #if DK3_ON_WINDOWS if(0x00 != stb->cReparse) { back = 0; } #endif return back; } /** Propagate directory entry size into directory size. @param j Job structure. @param dp Destination (directory) stat information. @param sp Source (directory entry) stat information. */ static void dkt_ls_propagate_size(DKT_LS_J *j, dk3_stat_t *dp, dk3_stat_t const *sp) { dk3_um_t addval = DK3_UM_0; addval = sp->sz; if((sp->ft) & DK3_FT_SYMLINK) { addval = sp->lsz; if(j->o_der) { addval = sp->sz; } } dp->sz = dk3ma_um_add_ok(dp->sz, addval, &(j->e_mat)); } /** Process a directory recursively. @param j Job structure. @param arg Directory name. @param stb Stat buffer for directory. @param isprim Flag: Is primary (ignore DK3_FT_SYMLINK). */ static void dkt_ls_process_dir_rec( DKT_LS_J *j, dkChar const *arg, dk3_stat_t const *stb, int isprim ) { DKT_LS_DIR *curdir = NULL; /* Current dir to process. */ DKT_LS_DIR *nextdir = NULL; /* Next dir to process. */ dkChar const *en = NULL; /* Entry name. */ dk3_stat_t const *es = NULL; /* Stat for entry. */ int isdir = 0; /* Flag: Is directory. */ $? "+ dkt_ls_process_dir_rec \"%s\"", TR_STR(arg) curdir = dkt_ls_dir_new(j, arg, stb, NULL); while(curdir) { if(dk3dir_get_next_directory(curdir->dir)) { $? ". have subdir" en = dk3dir_get_fullname(curdir->dir); es = dk3dir_get_stat(curdir->dir); if((en) && (es)) { /* Go sub or report */ if(dkt_ls_check_go_sub(j, curdir, en, es)) { nextdir = dkt_ls_dir_new(j, en, es, curdir); if(nextdir) { curdir = nextdir; } } else { if(j->o_rec) { dkt_ls_report_entry(j, es, en, curdir, 0); } isdir = 1; if((es->ft) & DK3_FT_SYMLINK) { isdir = 0; if(j->o_der) { isdir = 1; } } dkt_ls_add_summary(j, isdir, es, 0); dkt_ls_propagate_size(j, &(curdir->ds), es); } } else { /* BUG */ j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } else { $? ". no more subdirs" /* Process all non-directory contents. */ while(dk3dir_get_next_file(curdir->dir)) { en = dk3dir_get_fullname(curdir->dir); es = dk3dir_get_stat(curdir->dir); if((en) && (es)) { if(j->o_rec) { dkt_ls_report_entry(j, es, en, curdir, 0); } dkt_ls_add_summary(j, 0, es, 0); dkt_ls_propagate_size(j, &(curdir->ds), es); } else { /* BUG */ j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } /* Report this directory (if necessary) and go up one level. */ nextdir = curdir->parent; en = dk3dir_get_directory_name(curdir->dir); if(en) { if((j->o_rec) || (!(curdir->parent))) { dkt_ls_report_entry(j, &(curdir->ds), en, curdir->parent, isprim); } dkt_ls_add_summary(j, 1, &(curdir->dso), 0); } else { /* BUG */ j->exval = DKT_RESULT_ERR_UNSPECIFIC; } if(nextdir) { dkt_ls_propagate_size(j, &(nextdir->ds), &(curdir->ds)); } dkt_ls_dir_delete(curdir); curdir = nextdir; } } $? "- dkt_ls_process_dir_rec" } #if DK3_ON_WINDOWS /** Check whether a text ends on a double quote. @param str Text to check. @return 1 for ends on double quote, 0 otherwise. */ static int dkt_ls_ends_on_quote(dkChar const *str) { dkChar const *ptr; int back = 0; $? "+ dkt_ls_ends_on_quote \"%s\"", str if(str) { ptr = str; while(*ptr) { if(dkT('"') == *(ptr++)) { back = 1; } else { back = 0; } } } $? "- dkt_ls_ends_on_quote %d", back return back; } #endif /** Process one file name from command line arguments. We have a real file name here. @param j Job structure. @param arg File name. @param addbs Allow testing with additional backspace. */ static void dkt_ls_process_one_real_name_add_bs(DKT_LS_J *j, dkChar const *arg, int addbs) { #if DK3_ON_WINDOWS dkChar cofn[DK3_MAX_PATH]; #endif dk3_stat_t stb; /* Stat buffer. */ dk3_dir_t *dir = NULL; /* Directory. */ dkChar const *en = NULL; /* Entry name. */ dk3_stat_t const *es = NULL; /* Entry stat buffer. */ DKT_LS_DIR mydir; /* Structure for go-sub test. */ int isdir = 0; /* Flag: Is directory. */ int gosub = 0; /* Flag: Must go into dir. */ $? "+ dkt_ls_process_one_real_name_add_bs \"%s\"", TR_STR(arg) if(dk3sf_stat_app(&stb, arg, ((addbs) ? (NULL) : (j->app)))) { switch((stb.ft) & (~(DK3_FT_SYMLINK))) { case DK3_FT_DIRECTORY: { $? ". directory \"%s\"", arg gosub = 0; if((j->o_sz) || (j->o_rec)) { #if DK3_ON_WINDOWS if(0x00 == stb.cReparse) { gosub = 1; } #else gosub = 1; #endif } if(gosub) { $? ". gosub" dkt_ls_process_dir_rec(j, arg, &stb, 1); } else { $? ". no gosub" #if DK3_ON_WINDOWS if(0x00 == stb.cReparse) { #endif mydir.parent = NULL; mydir.sl = DK3_UM_0; mydir.dir = NULL; dk3mem_cpy(&(mydir.ds), &stb, sizeof(dk3_stat_t)); dk3mem_cpy(&(mydir.dso), &stb, sizeof(dk3_stat_t)); dir = dk3dir_open_app(arg, j->app); if(dir) { $? ". dir" while(dk3dir_get_next_directory(dir)) { en = dk3dir_get_fullname(dir); $? ". en=\"%s\"", en es = dk3dir_get_stat(dir); if((en) && (es)) { dkt_ls_report_entry(j, es, en, NULL, 0); isdir = 1; if((es->ft) & DK3_FT_SYMLINK) { isdir = 0; if(j->o_der) { isdir = 1; } } dkt_ls_add_summary(j, isdir, es, 0); } else { j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } while(dk3dir_get_next_file(dir)) { en = dk3dir_get_fullname(dir); $? ". en=\"%s\"", en es = dk3dir_get_stat(dir); if((en) && (es)) { dkt_ls_report_entry(j, es, en, NULL, 0); dkt_ls_add_summary(j, 0, es, 0); } else { j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } dk3dir_close(dir); } else { $? "! dir" j->exval = DKT_RESULT_ERR_FILENAME; #if DK3_ON_WINDOWS if(dkt_ls_ends_on_quote(arg)) { dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 73); dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 74); } #endif } dk3mem_cpy(&stb, &(mydir.ds), sizeof(dk3_stat_t)); #if DK3_ON_WINDOWS } #endif dkt_ls_report_entry(j, &stb, arg, NULL, 1); dkt_ls_add_summary(j, 1, &stb, 1); } } break; default: { $? ". arg = \"%s\"", arg dkt_ls_report_entry(j, &stb, arg, NULL, 1); dkt_ls_add_summary(j, 0, &stb, 1); } break; } } else { #if DK3_ON_WINDOWS if(addbs) { if((1 + dk3str_len(arg)) < DK3_SIZEOF(cofn,dkChar)) { dk3str_cpy_not_overlapped(cofn, arg); dk3str_cat(cofn, dk3app_not_localized(20)); dkt_ls_process_one_real_name_add_bs(j, cofn, 0); } else { j->exval = DKT_RESULT_ERR_FILENAME; } } else { j->exval = DKT_RESULT_ERR_FILENAME; if(dkt_ls_ends_on_quote(arg)) { dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 73); dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 74); } } #else j->exval = DKT_RESULT_ERR_FILENAME; #endif } $? "- dkt_ls_process_one_real_name_add_bs" } #if DK3_ON_WINDOWS /** Check whether a given file name is a candidate for appending a backslash (i.e. C: or \\server\share). @param fn File name to check. @return 1 for drives or shares, 0 otherwise. */ static int dkt_ls_add_bs_candidate(dkChar const *fn) { dkChar const *ptr; int bs = 0; int lbs = 0; int back = 0; $? "+ dkt_ls_add_bs_candidate %s", fn if(fn) { if((dkT('a') <= fn[0]) && (dkT('z') >= fn[0])) { back = 1; } if((dkT('A') <= fn[0]) && (dkT('Z' >= fn[0]))) { back = 1; } if(back) { back = 0; if(dkT(':') == fn[1]) { if(dkT('\0') == fn[2]) { back = 1; } } } if(0 == back) { if(dkT('\\') == fn[0]) { if(dkT('\\') == fn[1]) { ptr = &(fn[2]); while(*ptr) { if(dkT('\\') == *ptr) { bs++; lbs = 1; } else { lbs = 0; } ptr++; } if(1 == bs) { if(0 == lbs) { back = 1; } } } } } } $? "- dkt_ls_add_bs_candidate %d", back return back; } #endif /** Process one file name from command line arguments. We have a real file name here. @param j Job structure. @param arg File name. */ static void dkt_ls_process_one_real_name(DKT_LS_J *j, dkChar const *arg) { #if DK3_ON_WINDOWS if(dkt_ls_add_bs_candidate(arg)) { dkt_ls_process_one_real_name_add_bs(j, arg, 1); } else { dkt_ls_process_one_real_name_add_bs(j, arg, 0); } #else dkt_ls_process_one_real_name_add_bs(j, arg, 0); #endif } /** Process the current directory. @param j Job structure. */ static void dkt_ls_process_current_directory(DKT_LS_J *j) { dk3_stat_t stb; /* Stat buffer. */ dk3_dir_t *dir = NULL; /* Directory. */ DKT_LS_DIR mydir; /* Structure for go-sub test. */ dkChar const *en = NULL; /* Entry name. */ dk3_stat_t const *es = NULL; /* Entry stat buffer. */ int isdir = 0; /* Flag: Is directory. */ if(dk3sf_stat_app(&stb, (j->kwnl)[4], j->app)) { dir = dk3dir_open_app((j->kwnl)[4], j->app); if(dir) { mydir.parent = NULL; dk3mem_cpy(&(mydir.ds), &stb, sizeof(dk3_stat_t)); dk3mem_cpy(&(mydir.dso), &stb, sizeof(dk3_stat_t)); mydir.sl = DK3_UM_0; mydir.dir = NULL; while(dk3dir_get_next_directory(dir)) { isdir = 1; en = dk3dir_get_shortname(dir); $? ". en = \"%s\"", en es = dk3dir_get_stat(dir); if((en) && (es)) { if(dkt_ls_check_go_sub(j, &mydir, en, es)) { dkt_ls_process_dir_rec(j, en, es, 0); } else { dkt_ls_report_entry(j, es, en, NULL, 0); if((es->ft) & DK3_FT_SYMLINK) { isdir = 0; if(j->o_der) { isdir = 1; } } dkt_ls_add_summary(j, isdir, es, 0); } } else { /* BUG */ j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } while(dk3dir_get_next_file(dir)) { en = dk3dir_get_shortname(dir); $? ". en = \"%s\"", en es = dk3dir_get_stat(dir); if((en) && (es)) { dkt_ls_report_entry(j, es, en, NULL, 0); dkt_ls_add_summary(j, 0, es, 0); } else { /* BUG */ } } dk3dir_close(dir); } else { j->exval = DKT_RESULT_ERR_GETCWD; } } else { /* ERROR: Stat failed */ j->exval = DKT_RESULT_ERR_GETCWD; } } /** Process one file name from command line arguments. Correct the file name and expand if necessary. @param j Job structure. @param arg File name. */ static void dkt_ls_process_one_file_name(DKT_LS_J *j, dkChar const *arg) { dkChar bu[DK3_MAX_PATH]; /* Private file name copy. */ dkChar const *en = NULL; /* Entry name. */ dk3_dir_t *fne = NULL; /* File name expander. */ int found = 0; /* Flag: Name(s) found. */ $? "+ dkt_ls_process_one_file_name" if(dk3str_len(arg) < DK3_SIZEOF(bu,dkChar)) { dk3str_cpy_not_overlapped(bu, arg); dk3str_correct_filename(bu); if(dk3sf_must_expand(bu)) { $? ". must expand file name" fne = dk3dir_fne_open_app(bu, j->app); if(fne) { $? ". file name expander ok" if(dk3dir_get_number_of_directories(fne) > 0) { found = 1; $? ". ndir = %lu", (unsigned long)dk3dir_get_number_of_directories(fne) } if(dk3dir_get_number_of_files(fne) > 0) { found = 1; $? ". nfile = %lu", (unsigned long)dk3dir_get_number_of_files(fne) } if(found) { $? ". at least one file or directory found" while(dk3dir_get_next_directory(fne)) { en = dk3dir_get_fullname(fne); if(en) { dkt_ls_process_one_real_name(j, en); } else { /* BUG */ j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } while(dk3dir_get_next_file(fne)) { en = dk3dir_get_fullname(fne); if(en) { dkt_ls_process_one_real_name(j, en); } else { /* BUG */ j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } } else { $? "! no file or directory 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 { $? "! no file name expander" j->exval = DKT_RESULT_ERR_MEMORY; } } else { $? ". no expansion necessary" dkt_ls_process_one_real_name(j, bu); } } else { /* Name too long! */ dk3app_log_i3(j->app, DK3_LL_ERROR, 65, 66, arg); j->exval = DKT_RESULT_ERR_FILENAME; } $? "- dkt_ls_process_one_file_name" } /** Print summary before exiting. @param j Job structure. */ static void dkt_ls_print_summary(DKT_LS_J *j) { int ok = 0; size_t sz1 = 0; /* Length of "Directories:" */ size_t sz2 = 0; /* Length of "Files:" */ size_t sz3 = 0; /* Length of "Bytes:2 */ size_t sz4 = 0; /* Length of number of directories. */ size_t sz5 = 0; /* Length of number of files. */ size_t sz6 = 0; /* Length of number of bytes. */ size_t sm1 = 0; /* Maximum left row. */ size_t sm2 = 0; /* Maximum right row. */ size_t i = 0; /* Used in loop to fill space. */ dkChar b1[64]; /* Buffer for number of directories. */ dkChar b2[64]; /* Buffer for number of files. */ dkChar b3[64]; /* Buffer for number of bytes */ $? "+ dkt_ls_print_summary" b1[0] = b2[0] = b3[0] = dkT('\0'); if(dk3ma_um_to_string(b1, DK3_SIZEOF(b1,dkChar), j->s_dir)) { $? ". s_dir" if(dk3ma_um_to_string(b2, DK3_SIZEOF(b2,dkChar), j->s_fil)) { $? ". s_fil" if(dk3ma_um_to_string(b3, DK3_SIZEOF(b3,dkChar), j->s_byt)) { $? ". s_byt" ok = 1; sz1 = dk3str_len((j->msg)[12]); sz2 = dk3str_len((j->msg)[13]); sz3 = dk3str_len((j->msg)[14]); sz4 = dk3str_len(b1); sz5 = dk3str_len(b2); sz6 = dk3str_len(b3); sm1 = sz1; if(sz2 > sm1) sm1 = sz2; if(sz3 > sm1) sm1 = sz3; sm2 = sz4; if(sz5 > sm2) sm2 = sz5; if(sz6 > sm2) sm2 = sz6; dk3sf_fputs((j->msg)[12], stdout); $? ". here (1)" for(i = sz1; i < sm1; i++) { dk3sf_fputc(dkT(' '), stdout); } $? ". here (2)" for(i = sz4; i < sm2; i++) { dk3sf_fputc(dkT(' '), stdout); } $? ". here (3)" dk3sf_fputs(b1, stdout); $? ". here (4)" dk3sf_fputc(dkT('\n'), stdout); $? ". here (5)" dk3sf_fputs((j->msg)[13], stdout); for(i = sz2; i < sm1; i++) { dk3sf_fputc(dkT(' '), stdout); } for(i = sz5; i < sm2; i++) { dk3sf_fputc(dkT(' '), stdout); } dk3sf_fputs(b2, stdout); dk3sf_fputc(dkT('\n'), stdout); dk3sf_fputs((j->msg)[14], stdout); for(i = sz3; i < sm1; i++) { dk3sf_fputc(dkT(' '), stdout); } for(i = sz6; i < sm2; i++) { dk3sf_fputc(dkT(' '), stdout); } dk3sf_fputs(b3, stdout); dk3sf_fputc(dkT('\n'), stdout); } } } if(!ok) { j->exval = DKT_RESULT_ERR_UNSPECIFIC; } $? "- dkt_ls_print_summary" } /** Do the directory listing. @param j Job structure. */ static void dkt_ls_run(DKT_LS_J *j) { int nfn = 0; /* Number of file names. */ int i = 0; /* Current file index. */ dkChar const *arg = NULL; /* Current file name. */ $? "+ dkt_ls_run" dk3sf_initialize_stdout(); dkt_ls_check_for_size(j); 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) { dkt_ls_process_one_file_name(j, arg); } else { /* BUG */ j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } } else { dkt_ls_process_current_directory(j); } if(j->o_sum) { dkt_ls_print_summary(j); } $? "- dkt_ls_run" } int dkt_ls( dk3_app_t *app, dkChar const *sn, dkChar const * const *msg, dkChar const * const *kwnl ) { int back = DKT_RESULT_ERR_UNSPECIFIC; int ft[DK3_FT_MAX+1]; int i = 0; DKT_LS_J j; $? "+ dkt_ls" j.app = app; j.msg = msg; j.kwnl = kwnl; j.ft = ft; dkt_ls_job_init(&j); /* Enable all file types by default. */ for(i = 0; i <= DK3_FT_MAX; i++) { ft[i] = 1; } dkt_tool_read_conf(app, sn, (void *)(&j), dkt_ls_conf_line); if(dkt_ls_process_arguments(&j)) { j.exval = DKT_RESULT_OK; dkt_ls_run(&j); } back = j.exval; dkt_ls_job_cleanup(&j); $? "- dkt_ls %d", back return back; } /* vim: set ai sw=2 : */