%% options copyright owner = Dirk Krause copyright year = 2015-xxxx license = bsd %% header /** @file dk-ls.h Header file for dk-ls program. */ #include "dk4md.h" #include "dk4fileit.h" #ifdef __cplusplus extern "C" { #endif /** Set message digest type and message digest encoding. @param mdtype Address of message digest type variable. @param mdenc Address of message digest encoding variable. @param buffer Buffer containing textual description. @param app Application structure for diagnostics, may be NULL. @param msg Message string array. @param szmsg Size of message string array. */ void dk_ls_set_md( int *mdtype, int *mdenc, dkChar *buffer, dk4_app_t *app, const dkChar * const *msg, size_t szmsg ); /** Read information about symbolic link target. @param bptr Buffer for path name. @param szb Size of buffer (number of dkChar). @param fn Link filename. @return 1 on success, 0 on error. */ int dk_ls_readlink(dkChar *bptr, size_t szb, const dkChar *fn); /** Report error while attempting to read size. @param app Application structure for diagnostics. @param fn File name. @param ch Data chooser. @param msg Constant message texts. @param szmsg Size of msg array. */ void dk_ls_report_fileinfo_size_error( dk4_app_t *app, const dkChar *fn, int ch, const dkChar * const *msg, size_t szmsg ); /** Report error while attempting to read timestamp. @param app Application structure for diagnostics. @param fn File name. @param ch Data chooser. @param msg Constant message texts. @param szmsg Size of msg array. */ void dk_ls_report_fileinfo_timestamp_error( dk4_app_t *app, const dkChar *fn, int ch, const dkChar * const *msg, size_t szmsg ); #ifdef __cplusplus } #endif %% module #include "dk4conf.h" #if DK4_ON_WINDOWS #ifndef WINDOWS_H_INCLUDED #include #define WINDOWS_H_INCLUDED #endif #endif #if DK4_HAVE_SYS_TYPES_H #ifndef SYS_TYPES_H_INCLUDED #include #define SYS_TYPES_H_INCLUDED 1 #endif #endif #if DK4_HAVE_UNISTD_H #ifndef UNISTD_H_INCLUDED #include #define UNISTD_H_INCLUDED 1 #endif #endif #if DK4_HAVE_PROCESS_H #ifndef PROCESS_H_INCLUDED #include #define PROCESS_H_INCLUDED 1 #endif #endif #if DK4_HAVE_IO_H #ifndef IO_H_INCLUDED #include #define IO_H_INCLUDED 1 #endif #endif #if DK4_HAVE_FCNTL_H #ifndef FCNTL_H_INCLUDED #include #define FCNTL_H_INCLUDED 1 #endif #endif #if DK4_HAVE_SIGNAL_H #ifndef SIGNAL_H_INCLUDED #include #define SIGNAL_H_INCLUDED 1 #endif #endif #include "dk4types.h" #include "dk4const.h" #include "dk4vers.h" #include "dk4app.h" #include "dk4aopt.h" #include "dk4mem.h" #include "dk4mema.h" #include "dk4fput.h" #include "dk-ls.h" #include "dk4mpl.h" #include "dk4stat.h" #include "dk4statd.h" #include "dk4strd.h" #include "dk4fileit.h" #include "dk4filei.h" #include "dk4fileia.h" #include "dk4masz.h" #include "dk4maadu.h" #include "dk4maodd.h" #include "dk4maodh.h" #include "dk4numl.h" #include "dk4dira.h" #include "dk4pathd.h" #include "dk4fopda.h" #include "dk4symlink.h" $!trace-include /** Default help text, used if help text file is not found. */ static const dkChar * const dk_ls_help_text[] = { $!text macro=dkT,preprocessor List files dk-ls [] [] Options: -p ... Output column order, see below. -r Recursive listing. -t ... File types to list. -m ... Message digest type. -f Stay on current file system. -s Show summary at end. --help Show this short help text. --manual *** SHOW FULL MANUAL. *** --version Show version information. --license Show license information. Column order characters: n=name, s=size, t=type, p=permissions/attributes, c=creation, m=modification, a=access, l=links, u=UID, g=GID, d=device, i=inode, r=relative device, w=reparse tag (Widnows), x=message digest. Default: ``mtpn'' (``mtn'' on Windows). File type characters: d=directories, f=non-directory entries. Message digest types: md5, sha-1, sha-224, sha-256, sha-384, sha-512, ripemd-160. Optionally add encoding hex or a85, i.e. ``sha-1.hex''. http://dktools.sourceforge.net $!end }; /** License conditions. */ static const dkChar * const dk_ls_license_text[] = { $!text macro=dkT,preprocessor This software uses code from the following projects, either directly or as a library: dktools Dirk Krause's tools and libraries. See http://dktools.sourceforge.net/ for more information. #if DK4_HAVE_ZLIB_H zlib Data compression library. See http://www.zlib.net/ for more information. #endif #if DK4_HAVE_BZLIB_H bzip2 Data compression program and library. See http://www.bzip.org/ for more information. #endif #if DK4_HAVE_OPENSSL_MD5_H && DK4_HAVE_OPENSSL_SHA_H && DK4_HAVE_OPENSSL_RIPEMD_H OpenSSL Cryptographic toolkit, used to build file checksums here. See http://www.openssl.org/ for more information. This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/). This product includes cryptographic software written by Eric Young (eay@cryptsoft.com). This product includes software written by Tim Hudson (tjh@cryptsoft.com). #endif All the licenses below apply to the program. Licenses for used libraries are shown as found on my Scientific Linux 6.x computer in the /usr/share/doc directory on 2015-04-01. Check the project homepages of the used libraries for additional information and/or updated license terms. DK tools and libraries license ============================== Copyright (c) 2015-2016, Dirk Krause All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Dirk Krause nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #if DK4_HAVE_ZLIB_H Zlib license ============ (C) 1995-2004 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu #endif #if DK4_HAVE_BZLIB_H Bzip2 and libbzip2 library license ================================== This program, "bzip2", the associated library "libbzip2", and all documentation, are copyright (C) 1996-2007 Julian R Seward. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 3. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 4. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Julian Seward, jseward@bzip.org bzip2/libbzip2 version 1.0.5 of 10 December 2007 #endif #if DK4_HAVE_OPENSSL_MD5_H && DK4_HAVE_OPENSSL_SHA_H && DK4_HAVE_OPENSSL_RIPEMD_H OpenSSL license =============== LICENSE ISSUES -------------- The OpenSSL toolkit stays under a dual license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit. See below for the actual license texts. Actually both licenses are BSD-style Open Source licenses. In case of any license issues related to OpenSSL please contact openssl-core@openssl.org. OpenSSL License --------------- Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgment: "This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)" 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact openssl-core@openssl.org. 5. Products derived from this software may not be called "OpenSSL" nor may "OpenSSL" appear in their names without prior written permission of the OpenSSL Project. 6. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/)" THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. This product includes cryptographic software written by Eric Young (eay@cryptsoft.com). This product includes software written by Tim Hudson (tjh@cryptsoft.com). Original SSLeay License ----------------------- Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved. This package is an SSL implementation written by Eric Young (eay@cryptsoft.com). The implementation was written so as to conform with Netscapes SSL. This library is free for commercial and non-commercial use as long as the following conditions are aheared to. The following conditions apply to all code found in this distribution, be it the RC4, RSA, lhash, DES, etc., code; not just the SSL code. The SSL documentation included with this distribution is covered by the same copyright terms except that the holder is Tim Hudson (tjh@cryptsoft.com). Copyright remains Eric Young's, and as such any Copyright notices in the code are not to be removed. If this package is used in a product, Eric Young should be given attribution as the author of the parts of the library used. This can be in the form of a textual message at program startup or in documentation (online or textual) provided with the package. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgement: "This product includes cryptographic software written by Eric Young (eay@cryptsoft.com)" The word 'cryptographic' can be left out if the rouines from the library being used are not cryptographic related :-). 4. If you include any Windows specific code (or a derivative thereof) from the apps directory (application code) you must include an acknowledgement: "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The licence and distribution terms for any publically available version or derivative of this code cannot be changed. i.e. this code cannot simply be copied and put under another distribution licence [including the GNU Public Licence.] #endif $!end }; /** Constant texts used by program, not localized. */ static const dkChar * const dk_ls_kwnl[] = { $!string-table macro=dkT # # 0 Program group name # dktools # # 1 Help text file name # dk-ls.txt # # 2 String table file # dk-ls.str # # 3 Default set of information to show # (type, permissions, modification time, size, name) # mtpn # # 4 Current directory # . # # 5 Path component separator on Windows # \\ # # 6 Path component separator on non-Windows # / # # 7 String between link and target # -> # # 8 Default print order for Windows # mtn # # 9 Preference name for message digest # message.digest # # 10 Preference name for print order # print.order # # 11 Error message: Application startup failed! # ERROR: Application startup failed!\n $!end }; /** Constant texts used by program, replaced by localized texts if available. */ static const dkChar * const dk_ls_kw_def[] = { $!string-table macro=dkT # # 0 Error message: Failed to set up signal handlers! # Failed to set up signal handlers! # # 1 Error message: Failed to restore previous signal handlers! # Failed to restore previous signal handlers! # # 2 Text "OVERFLOW" to show instead of the size # OVERFLOW # # 3 4 5 Failed to retrieve file size information. # Failed to obtain file size for symbolic link:\n\t" Failed to obtain file size for target:\n\t" "!\n\tInformation not available! # # 6 Path grows too long # Path grows too long while recursing directory! # # 7 8 9 Failed to obtain timestamp! # Failed to obtain timestamp for symbolic link:\n\t" Failed to obtain timestamp for target:\n\t" "!\nInformation not available! # # 10 # Failed to obtain file type and attributes (bug)! # # 11 # Invalid text in print order! # # 12 13 Failed to find target for symbolic link! # Failed to find target for symbolic link:\n\t" "! # # 14 15 Failed to find unique file identifier # Failed to obtain unique file identifier:\n\t" "! # # 16 17 18 File names too long in summary. # File names too long for expansion:\n\t" ",\n\t" "! # # 19 Message digest type not supported! # Message digest type not supported! # # 20 21 Message digest type not supported! # Message digest type not supported: " "! # # 22 23 Not a message digest type name! # Not a message digest type name: " "! # # 24 25 Invalid encoding for message digest! # Invalid encoding for message digest: " "! # # 26 Error message: Failed to init digest ctx # Failed to initialize digest context (error from library function)! # # 27 Error message: Failed to init message digest ctx # Failed to initialize message digest context! # # 28 Error message: Failed to add bytes to ctx # Failed to add data to message digest! # # 29 Error message: Failed to finish message digest # Failed to finish message digest! # # 30, 31, 32 # \# Number of directories: \# Number of files: \# Total number of bytes: $!end }; #if DK4_ON_WINDOWS /** Buffer to store symbolic link destination. */ static dkChar dk_ls_sl_target[32784]; #else /** Buffer to store symbolic link destination. */ static dkChar dk_ls_sl_target[DK4_MAX_PATH]; #endif /** Options used by program. */ static const dk4_option_specification_t dk_ls_options[] = { /* Reset (ignore preferences). */ { dkT('R'), dkT("reset"), DK4_OPT_ARG_NONE }, /* Print order. */ { dkT('p'), dkT("print-order"), DK4_OPT_ARG_STRING }, /* Recursive listing. */ { dkT('r'), dkT("recursive"), DK4_OPT_ARG_NONE }, /* For recursive listings, stay on the current file system. */ { dkT('f'), dkT("stay-on-filesystem"), DK4_OPT_ARG_NONE }, /* File types to show (all others are ignored). */ { dkT('t'), dkT("types"), DK4_OPT_ARG_STRING }, { dkT('s'), dkT("summary"), DK4_OPT_ARG_NONE }, #if DK4_HAVE_OPENSSL_MD5_H && DK4_HAVE_OPENSSL_SHA_H && DK4_HAVE_OPENSSL_RIPEMD_H { dkT('m'), dkT("message-digest"), DK4_OPT_ARG_STRING }, #endif }; /** Buffer for print order from command line. */ static dkChar po_buf[32]; /** Application structure. */ static dk4_app_t *app = NULL; /** Print order. */ static const dkChar *print_order = NULL; /** Message texts, uses localized texts if possible. */ static const dkChar * const *dk_ls_msg = dk_ls_kw_def; /** Error report for bytes counting. */ static dk4_er_t er_bytes; /** Error report for files counting. */ static dk4_er_t er_files; /** Error report for directory counting. */ static dk4_er_t er_dirs; /** Size found so far. */ static dk4_um_t size_bytes = (dk4_um_t)0UL; /** Number of files shown. */ static dk4_um_t num_files = (dk4_um_t)0UL; /** Number of directories shown. */ static dk4_um_t num_dirs = (dk4_um_t)0UL; /** Number of elements in the dk_ls_kw_def array and in dk_ls_msg. */ static size_t dk_ls_sz_msg = (sizeof(dk_ls_kw_def)/sizeof(DK4_PDKCHAR) - 1); #if DK4_ON_WINDOWS /** Limit for incrementing path construction buffer. On Windows systems a special notation allows very long path names up to 32767 characters. */ static const size_t dk_ls_max_path = 32784U; #else /** Limit for incrementing path construction buffer. On non-Windows systems the path is restricted to DK4_MAX_PATH. */ static const size_t dk_ls_max_path = 2 * DK4_MAX_PATH; #endif /** Number of elements in the dk_ls_options array. */ static const size_t dk_ls_sz_options = sizeof(dk_ls_options)/sizeof(dk4_option_specification_t); /** Flag: Size output wanted. */ static int size_wanted = 0; /** Flag: Name output wanted. */ static int name_wanted = 0; /** Flag: Show non-directories. */ static int show_files = 1; /** Flag: Show directories. */ static int show_dirs = 1; /** Flag: Recursive listing. */ static int recursive = 0; /** Flag: Show summary. */ static int show_summary = 0; /** Flag: Stay on file system when recursing directories. */ static int stay_on_fs = 0; /** Message digest type. */ static int digest_type = DK4_MD_SHA1; /** Message digest encoding. */ static int digest_enco = DK4_BINARY_TO_TEXT_ENCODING_HEX; /** Flag: Unsupported digest type already reported. */ static int digest_erep = 0; /** Flag: Write error occured while writing to standard output. */ static int write_error = 0; /** Exit status code. */ static int exval = EXIT_FAILURE; /** Flag: Illegal characters in print order reported. */ static int print_order_error_reported = 0; #ifdef SIGPIPE /** Indicator: SIGPIPE signal received. */ static DK4_VOLATILE dk4_sig_atomic_t sig_had_pipe = 0; #endif /** Indicator: SIGINT signal received. */ static DK4_VOLATILE dk4_sig_atomic_t sig_had_int = 0; /** Indicator: SIGTERM signal received. */ static DK4_VOLATILE dk4_sig_atomic_t sig_had_term = 0; /** Pass a volatile pointer to an atomic integer. This function is necessary as some compilers mis-optimize direct access to volatile variables (at least if you believe one of the coding standards). @param ptr Address of atomic integer variable. @return The unmodified pointer. */ static DK4_VOLATILE dk4_sig_atomic_t * sig_pass_pointer(DK4_VOLATILE dk4_sig_atomic_t *ptr) { return ptr; } #ifdef SIGPIPE /** Handler for SIGPIPE signal. @param signo Signal number (always SIGPIPE, ignored). */ static void sig_handler_pipe(int signo) { *sig_pass_pointer(&sig_had_pipe) = 1; } #endif /** Handler for SIGINT signal. @param signo Signal number (always SIGINT, ignored). */ static void sig_handler_int(int signo) { *sig_pass_pointer(&sig_had_int) = 1; } /** Handler for SIGTERM signal. @param signo Signal number (always SIGTERM, ignored). */ static void sig_handler_term(int signo) { *sig_pass_pointer(&sig_had_term) = 1; } /** Read value from volatile atomic type. This function is necessary as some compilers mis-optimize direct access to volatile variables (at least if you believe one of the coding standards). @param ap Pointer to volatile atomic variable. @return Contents of the variable. */ static dk4_sig_atomic_t sig_read_atomic(DK4_VOLATILE dk4_sig_atomic_t *ap) { return (*ap); } /** Check whether we can continue or if a signal was received. @param check_pipe Flag: Check for occured SIGPIPE signal too. @return 1 if the program can continue, 0 if a signal was received. */ static int sig_can_continue(int check_pipe) { int back = 1; #ifdef SIGPIPE if (0 != check_pipe) { if (0 != sig_read_atomic(&sig_had_pipe)) { back = 0; } } #endif if (0 != sig_read_atomic(&sig_had_int )) { back = 0; } if (0 != sig_read_atomic(&sig_had_term)) { back = 0; } return back; } /** Check whether we can continue. @return 1 for yes, 0 for no. */ static int dk_ls_can_continue(void) { int back = 1; if (0 == sig_can_continue(1)) { back = 0; } return back; } /** Node type for a directory. */ typedef struct _dk_ls_node_t_ { struct _dk_ls_node_t_ *parent; /**< Pointer to parent node. */ struct _dk_ls_node_t_ *child; /**< Pointer to current child. */ dk4_dir_t *dirptr; /**< Directory. */ dk4_file_info_t fi; /**< File information. */ dk4_um_t size; /**< Directory size. */ int level; /**< Processing level. */ int ovsize; /**< Flag: Overflow in size calc. */ } dk_ls_node_t; /** Destroy node object, release memory. @param cn Node to destroy. */ static void dk_ls_delete_node(dk_ls_node_t *cn) { $? "+ dk_ls_delete_node" if (NULL != cn) { cn->parent = NULL; cn->child = NULL; if (NULL != cn->dirptr) { dk4dir_close(cn->dirptr); } cn->dirptr = NULL; dk4mem_free(cn); } $? "- dk_ls_delete_node" } /** Create new new directory node. @param dn Directory name for node. @param pa Parent node. @param level Processing level. @return Pointer to new node on success, NULL on error. */ static dk_ls_node_t * dk_ls_new_node(const dkChar *dn, dk_ls_node_t *pa, int level) { dk4_er_t er; dk_ls_node_t *back = NULL; int om = DK4_DIR_OPEN_SORTED; int ch = DK4_FILE_INFO_CONTENTS_DATA_TARGET; $? "+ dk_ls_new_node \"%!ds\" PTR=%d", TR_STR(dn), TR_IPTR(pa) back = dk4mem_new_app(dk_ls_node_t,1,app); if (NULL != back) { DK4_MEMRES(back, sizeof(dk_ls_node_t)); back->parent = pa; back->child = NULL; back->dirptr = NULL; back->level = level; back->size = (dk4_um_t)0UL; back->ovsize = 0; if (3 < back->level) { back->level = 3; } om |= DK4_DIR_SYMLINK_DIR_AS_FILE; dk4error_init(&er); back->dirptr = dk4dir_open_app(dn, om, app); if (NULL == back->dirptr) { $? "! back->dirptr" exval = EXIT_FAILURE; dk_ls_delete_node(back); back = NULL; } if (NULL != back) { $? ". node ok, retrieve file info" dk4fileinfo_init(&(back->fi)); if (0 != dk4fileinfo_app(&(back->fi), dn, app)) { if (0 == dk4fileinfo_size(&(back->size), &(back->fi), ch, NULL)) { back->size = (dk4_um_t)0UL; $? "! file size" /* ERROR: Failed to retrieve directory size */ dk_ls_report_fileinfo_size_error(app,dn,ch,dk_ls_msg,dk_ls_sz_msg); exval = EXIT_FAILURE; } } else { $? "! fileinfo" /* ERROR: Failed to retrieve file information, already reported */ exval = EXIT_FAILURE; } } else { $? "! directory" /* ERROR: Failed to open directory, already reported */ exval = EXIT_FAILURE; } } else { $? "! allocation" /* ERROR: Allocation failed, already reported */ exval = EXIT_FAILURE; } $? "- dk_ls_new_node PTR=%d", TR_IPTR(back) return back; } /** Construct a path name consisting of directory and file or subdirectory name into an existing buffer. @param bufptr Buffer address. @param szbuf Size of buffer. @param dirname Leading part of path name. @param filename Trailing part of path name. @param erp Error report, may be NULL. @return 1 on success, 0 on error (buffer too short). */ static int dk_ls_dir_and_file( dkChar *bufptr, size_t szbuf, const dkChar *dirname, const dkChar *filename ) { int back = 0; int nsep = 1; /* Flag: Separator needed */ $? "+ dk_ls_dir_and_file \"%!ds\" \"%!ds\"", TR_STR(dirname), TR_STR(filename) if (NULL != dirname) { if (0 != dk4str_cpy_s(bufptr, szbuf, dirname, NULL)) { while(dkT('\0') != *dirname) { #if DK4_ON_WINDOWS if (dkT('\\') == *dirname) #else if (dkT('/') == *dirname) #endif { nsep = 0; } else { nsep = 1; } dirname++; } if (0 != nsep) { #if DK4_ON_WINDOWS if (0 != dk4str_cat_s(bufptr, szbuf, dk_ls_kwnl[5], NULL)) #else if (0 != dk4str_cat_s(bufptr, szbuf, dk_ls_kwnl[6], NULL)) #endif { back = 1; } } else { back = 1; } if (0 != back) { back = 0; if (0 != dk4str_cat_s(bufptr, szbuf, filename, NULL)) { back = 1; } else { $? "! BUG: Buffer too small" /* BUG: Buffer too small */ exval = EXIT_FAILURE; } } else { $? "! BUG: Buffer too small" /* BUG: Buffer too small */ exval = EXIT_FAILURE; } } else { $? "! BUG: Buffer too small" /* BUG: Buffer too small */ exval = EXIT_FAILURE; } } else { back = dk4str_cpy_s(bufptr, szbuf, filename, NULL); if (0 == back) { $? "! BUG: Buffer too small" /* BUG: Buffer too small */ exval = EXIT_FAILURE; } } $? "- dk_ls_dir_and_file %d", back return back; } /** Construct a file or directory name, resize buffer if necessary. @param bufptr Address of pointer to buffer. @param szbufptr Address of buffer size variable. @param dirname Directory part of path to construct. @param filename File name part of path to construct. @return 1 on success, 0 on error. */ static int dk_ls_construct_path( dkChar **bufptr, size_t *szbufptr, const dkChar *dirname, const dkChar *filename ) { dk4_er_t er; /* Error code */ dkChar *nb; /* New buffer */ size_t lgt; /* Required buffer size */ int back = 0; $? "+ dk_ls_construct_path \"%!ds\" \"%!ds\"",TR_STR(dirname),TR_STR(filename) dk4error_init(&er); lgt = dk4str_len(filename); if (NULL != dirname) { lgt = dk4ma_size_t_add(lgt, dk4str_len(dirname), &er); lgt = dk4ma_size_t_add(lgt, 2, &er); } if (DK4_E_NONE == er.ec) { if (lgt <= *szbufptr) { /* Keep current buffer. */ $? ". can keep current buffer" back = dk_ls_dir_and_file(*bufptr, *szbufptr, dirname, filename); } else { if (dk_ls_max_path > lgt) { /* Allocate new larger buffer. */ $? ". need new buffer" nb = dk4mem_new_app(dkChar, lgt, app); if (NULL != nb) { $? ". allocation" dk4mem_release(*bufptr); *bufptr = nb; *szbufptr = lgt; back = dk_ls_dir_and_file(*bufptr, *szbufptr, dirname, filename); } else { $? "! allocation" /* ERROR: Memory allocation failed, already reported */ exval = EXIT_FAILURE; } } else { /* ERROR: Path size too long */ dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 6); exval = EXIT_FAILURE; } } } else { /* ERROR: Overflow in size calculation */ dk4app_log_base1(app, DK4_LL_ERROR, 82); exval = EXIT_FAILURE; } $? "- dk_ls_construct_path %d", back return back; } static void dk_ls_write_decimal_number(dk4_um_t value, size_t width) { dkChar buf[16 + 8 * sizeof(dk4_um_t)]; dk4_er_t er; size_t sl; int res; dk4error_init(&er); res = dk4ma_write_decimal_unsigned( buf, DK4_SIZEOF(buf,dkChar), value, 0, &er ); if (0 != res) { sl = dk4str_len(buf); while (sl++ < width) { if (0 == dk4fputc(dkT(' '), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } if (0 == dk4fputs(buf, stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } else { $? "! BUG: Failed to write decimal to buffer" /* ERROR: Failed to write decimal number to buffer (bug) */ exval = EXIT_FAILURE; } } static void dk_ls_write_hexadecimal_number(dk4_um_t value, size_t width) { dkChar buf[16 + 8 * sizeof(dk4_um_t)]; dk4_er_t er; size_t sl; int res; $? "+ dk_ls_write_hexadecimal_number" dk4error_init(&er); res = dk4ma_write_hex_unsigned( buf, DK4_SIZEOF(buf,dkChar), value, width, &er ); if (0 != res) { $? ". string created" sl = dk4str_len(buf); while (sl++ < width) { $? ". fill text" if (0 == dk4fputc(dkT('0'), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } if (0 == dk4fputs(buf, stdout, NULL)) { $? "! dk4fputs" write_error = 1; exval = EXIT_FAILURE; } } else { $? "! dk4ma_write_hex_unsigned" /* BUG: Failed to write hexadecimal number to buffer */ exval = EXIT_FAILURE; } $? "- dk_ls_write_hexadecimal_number" } /** Write file timestamp. @param fiptr File information. @param fn File name. @param what Choose the timestamp. @param issl Flag: Object is symbolic link. */ static void dk_ls_write_timestamp( dk4_file_info_t *fiptr, const dkChar *fn, int what, int issl ) { dk4_er_t er; dkChar buf[64]; int ch; int res; dk4error_init(&er); ch = ( (0 != issl) ? DK4_FILE_INFO_CONTENTS_DATA_LINK : DK4_FILE_INFO_CONTENTS_DATA_TARGET ); res = dk4fileinfo_timestamp(buf,DK4_SIZEOF(buf,dkChar),fiptr,ch,what,&er); if (0 != res) { if (0 == dk4fputs(buf, stdout, &er)) { write_error = 1; exval = EXIT_FAILURE; } } else { /* ERRROR: Failed to obtain time */ dk_ls_report_fileinfo_timestamp_error( app, fn, ch, dk_ls_msg, dk_ls_sz_msg ); exval = EXIT_FAILURE; } } /** Write indicator for unkown data. @param width Field width. @param compl Fill complete field. */ static void dk_ls_write_unknown(size_t width, int compl) { if (0 != compl) { while (0 < width--) { if (0 == dk4fputc(dkT('?'), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } } else { while (1 < width--) { if (0 == dk4fputc(dkT(' '), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } if (0 == dk4fputc(dkT('?'), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } } static int dk_ls_can_create_md(const dkChar *fn, dk4_file_info_t *fiptr) { int back = 0; if (0 == dk4fileinfo_is_symlink(fiptr)) { #if DK4_ON_WINDOWS if (0 == (FILE_ATTRIBUTE_REPARSE_POINT & (fiptr->fattr))) { if (0 == (FILE_ATTRIBUTE_DIRECTORY & (fiptr->fattr))) { if (0 == (FILE_ATTRIBUTE_DEVICE & (fiptr->fattr))) { back = 1; } } } #else if (0 != (DK4_FILE_INFO_CONTENTS_DATA_TARGET & (fiptr->contents))) { if (S_IFREG == (S_IFMT & ((fiptr->tstb).st_mode))) { back = 1; } } #endif } return back; } /** Write message digest. @param fn File name. @param fiptr File information pointer. @param digest_type Message digest type. @param digest_enco Message digest encoding. @param app Application structure for diagnostics. @param dk_ls_msg Message texts array. @param dk_ls_sz_msg Size of message texts error. */ static void dk_ls_write_md( const dkChar *fn, dk4_file_info_t *fiptr, int digest_type, int digest_enco, dk4_app_t *app, const dkChar * const *dk_ls_msg, size_t dk_ls_sz_msg ) { char buf[4096]; /* Buffer to read file */ dk4_md_ctx_t ctx; /* Message digest context */ dk4_er_t er; /* Error report */ FILE *fipo; /* Input file to process */ const char *resptr; /* Pointer to encoded digest buffer */ size_t rdbytes; /* Number of bytes read */ int mdok = 0; /* Flag: Digesting ok so far */ if (0 != dk_ls_can_create_md(fn, fiptr)) { $? ". can create md" dk4error_init(&er); if (0 != dk4md_ctx_init(&ctx, digest_type, digest_enco, &er)) { $? ". ctx initialized" fipo = dk4fopen_app(fn, dkT("rb"), DK4_FOPEN_SC_IS_REGULAR, app); if (NULL != fipo) { $? ". file opened" mdok = 1; do { if (0 != dk_ls_can_continue()) { rdbytes = fread(buf, 1, sizeof(buf), fipo); if (0 < rdbytes) { $? ". bytes found" dk4error_init(&er); if (1 != dk4md_ctx_add(&ctx, buf, rdbytes, &er)) { mdok = 0; $? "! ctx add" /* ERROR: Digest failed */ switch (er.ec) { case DK4_E_NOT_SUPPORTED : { dk4app_log_1(app,dk_ls_msg,dk_ls_sz_msg,DK4_LL_ERROR,19); } break; default : { dk4app_log_1(app,dk_ls_msg,dk_ls_sz_msg,DK4_LL_ERROR,28); } break; } } } } else { mdok = 0; rdbytes = 0; } } while((0 < rdbytes) && (1 == mdok)); fclose(fipo); if (1 == mdok) { $? ". mdok" resptr = NULL; rdbytes = 0; dk4error_init(&er); if (1 == dk4md_ctx_finish(&resptr, &rdbytes, &ctx, &er)) { $? ". ctx finished" if ((NULL != resptr) && (0 < rdbytes)) { $? ". results" while(rdbytes--) { if (0 == dk4fputc((dkChar)(*(resptr++)), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; $? "! write error" } } } else { $? "! results" mdok = 0; /* ERROR: No digest obtained */ dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 29); } } else { $? "! ctx finish" mdok = 0; /* ERROR: Failed to obtain digest */ dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 29); } } else { $? "! mdok" } } else { $? "! fopen" /* ERROR: Failed to open file, already reported */ } } else { $? "! ctx init" /* ERROR: Failed to initialize digest ctx */ switch (er.ec) { case DK4_E_NOT_SUPPORTED : { dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 19); } break; case DK4_E_SYSTEM : { dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 26); } break; default : { dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 27); } break; } } } else { $? ". do not create md" } if (0 == mdok) { rdbytes = dk4md_size(digest_type, digest_enco, NULL); while (rdbytes--) { if (0 == dk4fputc(dkT('x'), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } } } /** Write information about file to standard output. @param fn File name. @param fiptr File information. @param use_size Flag: Use specified size. @param size Size to use. @param ovsize Flag: Numeric overflow in size calculation. */ static void dk_ls_process_item_information( const dkChar *fn, dk4_file_info_t *fiptr, int use_size, dk4_um_t size, int ovsize ) { dkChar abuf[8*sizeof(dk4_um_t) + 16]; /* Attributes */ dkChar buf[8*sizeof(dk4_um_t) + 16]; /* Numbers */ dk4_er_t er; /* Error report */ const dkChar *piptr; /* Print order */ dk4_um_t fs; /* File size */ size_t numl; /* Number length */ size_t slen; /* String length */ size_t tlen; /* First part length */ size_t i; /* Index variable */ int issl; /* Flag: Is symbolic link */ int res; /* Result from information retrieval */ int showi; /* Flag: Show this item */ int habuf; /* Flag: Data in abuf */ int last_was_n; /* Flag: n was last character */ dkChar lasti; /* Last information printed */ $? "+ dk_ls_process_item_information \"%!ds\" PTR=%d", TR_STR(fn), TR_IPTR(fiptr) last_was_n = habuf = 0; if ((NULL != fn) && (NULL != fiptr)) { $? ". args ok" showi = 1; res = dk4fileinfo_type_attributes( buf, DK4_SIZEOF(buf,dkChar), fiptr, &er ); if (0 != res) { showi = 0; if (dkT('d') == buf[1]) { if (0 != show_dirs) { showi = 1; if (0 != show_summary) { num_dirs = dk4ma_um_add(num_dirs, 1, &er_dirs); } } } else { if (0 != show_files) { showi = 1; if (0 != show_summary) { num_files = dk4ma_um_add(num_files, 1, &er_files); } } } } else { $? "! BUG: Failed to write attributes to buffer" /* BUG: Failed to write file attributes to buffer */ dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 10); exval = EXIT_FAILURE; } if (0 != showi) { $? ". show file details" issl = dk4fileinfo_is_symlink(fiptr); lasti = dkT('\0'); piptr = print_order; while (dkT('\0') != *piptr) { $? ". piptr %!dc", *piptr last_was_n = 0; if ((int)(*piptr) <= 255) { $? ". ok" switch ((char)(*piptr)) { case 'n' : { $? ". name" if (dkT('\0') != lasti) { if (0 == dk4fputc(dkT(' '), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } if (0 == dk4fputs(fn, stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } last_was_n = 1; } break; case 'N' : { $? ". name" if (dkT('\0') != lasti) { if (0 == dk4fputc(dkT(' '), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } if (0 == dk4fputs(fn, stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } break; case 's' : { $? ". size" if (dkT('\0') != lasti) { if (0 == dk4fputc(dkT(' '), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } res = 0; if (0 != use_size) { if (0 != ovsize) { /* Show Overflow */ dk4error_set_simple_error_code(&er_bytes, DK4_E_MATH_OVERFLOW); slen = dk4str_len(dk_ls_msg[2]); numl = dk4numlength(sizeof(dk4_um_t), 0); tlen = (numl - slen) / 2; for (i = 0; i < tlen; i++) { if (0 == dk4fputc(dkT('-'), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } if (0 == dk4fputs(dk_ls_msg[2], stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } for (i = (tlen + slen); i < numl; i++) { if (0 == dk4fputc(dkT('-'), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } } else { /* Show size */ fs = size; res = 1; } } else { dk4error_init(&er); res = dk4fileinfo_size( &fs, fiptr, ( (0 != issl) ? (DK4_FILE_INFO_CONTENTS_DATA_LINK) : (DK4_FILE_INFO_CONTENTS_DATA_TARGET) ), &er ); if (0 == res) { /* ERROR: Failed to obtain size */ $? "! file size" dk_ls_report_fileinfo_size_error( app, fn, ( (0 != issl) ? (DK4_FILE_INFO_CONTENTS_DATA_LINK) : (DK4_FILE_INFO_CONTENTS_DATA_TARGET) ), dk_ls_msg, dk_ls_sz_msg ); } } if (0 != res) { numl = dk4numlength(sizeof(dk4_um_t), 0); dk_ls_write_decimal_number(fs, numl); if (0 != show_summary) { size_bytes = dk4ma_um_add(size_bytes, fs, &er_bytes); } } } break; case 't' : { $? ". type, permissions" dk4error_init(&er); if (0 == habuf) { habuf = dk4fileinfo_type_attributes( abuf, DK4_SIZEOF(abuf,dkChar), fiptr, &er ); } if (0 != habuf) { if (dkT('\0') != lasti) { if (0 == dk4fputc(dkT(' '), stdout, &er)) { write_error = 1; exval = EXIT_FAILURE; } } if (0 == dk4fputc(abuf[0], stdout, &er)) { write_error = 1; exval = EXIT_FAILURE; } if (0 == dk4fputc(abuf[1], stdout, &er)) { write_error = 1; exval = EXIT_FAILURE; } } else { /* ERROR: Failed to obtain file type */ dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 10); } } break; case 'p' : { if (0 == habuf) { habuf = dk4fileinfo_type_attributes( abuf, DK4_SIZEOF(abuf,dkChar), fiptr, &er ); } if (0 != habuf) { if ((dkT('\0') != lasti) && (dkT('t') != lasti)) { if (0 == dk4fputc(dkT(' '), stdout, &er)) { write_error = 1; exval = EXIT_FAILURE; } } if (0 == dk4fputs(&(abuf[2]), stdout, &er)) { write_error = 1; exval = EXIT_FAILURE; } } else { /* ERROR: Failed to obtain file type */ dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 10); } } break; case 'l' : { $? ". number of links" fs = (dk4_um_t)1UL; if (0 != issl) { if (0 != (DK4_FILE_INFO_CONTENTS_DATA_LINK & fiptr->contents)) { #if DK4_ON_WINDOWS fs = (dk4_um_t)((fiptr->linfo).nNumberOfLinks); #else fs = (dk4_um_t)((fiptr->lstb).st_nlink); #endif } } else { if (0 != (DK4_FILE_INFO_CONTENTS_DATA_TARGET & fiptr->contents)) { #if DK4_ON_WINDOWS fs = (dk4_um_t)((fiptr->tinfo).nNumberOfLinks); #else fs = (dk4_um_t)((fiptr->tstb).st_nlink); #endif } } #if DK4_ON_WINDOWS if ((DWORD)0UL == fiptr->resio) { #endif dk_ls_write_decimal_number( fs, #if DK4_ON_WINDOWS dk4numlength(sizeof(DWORD), 0) #else dk4numlength(sizeof(nlink_t), 0) #endif ); #if DK4_ON_WINDOWS } else { slen = dk4numlength(sizeof(DWORD), 0); for (i = 1; i < slen; i++) { if (0 == dk4fputc(dkT(' '), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } if (0 == dk4fputc(dkT('?'), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } #endif } break; case 'c' : { $? ". creation timestamp" if (dkT('\0') != lasti) { if (0 == dk4fputc(dkT(' '), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } dk_ls_write_timestamp(fiptr, fn, DK4_FILE_INFO_TIME_CREATE, issl); } break; case 'm' : { $? ". modification timestamp" if (dkT('\0') != lasti) { if (0 == dk4fputc(dkT(' '), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } dk_ls_write_timestamp(fiptr, fn, DK4_FILE_INFO_TIME_MODIFY, issl); } break; case 'a' : { $? ". access timestamp" if (dkT('\0') != lasti) { if (0 == dk4fputc(dkT(' '), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } dk_ls_write_timestamp(fiptr, fn, DK4_FILE_INFO_TIME_ACCESS, issl); } break; #if !DK4_ON_WINDOWS case 'u' : { $? ". owner UID" if (dkT('\0') != lasti) { if (0 == dk4fputc(dkT(' '), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } if (0 != issl) { if (0 != (DK4_FILE_INFO_CONTENTS_DATA_LINK & fiptr->contents)) { dk_ls_write_decimal_number( (fiptr->lstb).st_uid, dk4numlength(sizeof(uid_t), 0) ); } else { dk_ls_write_unknown(dk4numlength(sizeof(uid_t), 0), 0); } } else { if (0 != (DK4_FILE_INFO_CONTENTS_DATA_TARGET & fiptr->contents)) { dk_ls_write_decimal_number( (fiptr->tstb).st_uid, dk4numlength(sizeof(uid_t), 0) ); } else { dk_ls_write_unknown(dk4numlength(sizeof(uid_t), 0), 0); } } } break; #endif #if !DK4_ON_WINDOWS case 'g' : { $? ". owner GID" if (dkT('\0') != lasti) { if (0 == dk4fputc(dkT(' '), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } if (0 != issl) { if (0 != (DK4_FILE_INFO_CONTENTS_DATA_LINK & fiptr->contents)) { dk_ls_write_decimal_number( (fiptr->lstb).st_gid, dk4numlength(sizeof(gid_t), 0) ); } else { dk_ls_write_unknown(dk4numlength(sizeof(gid_t), 0), 0); } } else { if (0 != (DK4_FILE_INFO_CONTENTS_DATA_TARGET & fiptr->contents)) { dk_ls_write_decimal_number( (fiptr->tstb).st_gid, dk4numlength(sizeof(gid_t), 0) ); } else { dk_ls_write_unknown(dk4numlength(sizeof(gid_t), 0), 0); } } } break; #endif case 'd' : { $? ". device number" if (dkT('\0') != lasti) { if (0 == dk4fputc(dkT(' '), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } #if DK4_ON_WINDOWS if (0 != issl) { if (0 != (DK4_FILE_INFO_CONTENTS_DATA_LINK & fiptr->contents)) { if ((DWORD)0UL == fiptr->resio) { dk_ls_write_hexadecimal_number( (fiptr->linfo).dwVolumeSerialNumber, 2*sizeof(DWORD) ); } else { dk_ls_write_unknown(2*sizeof(DWORD), 1); } } else { dk_ls_write_unknown(2*sizeof(DWORD), 1); } } else { if (0 != (DK4_FILE_INFO_CONTENTS_DATA_TARGET & fiptr->contents)) { if ((DWORD)0UL == fiptr->resio) { dk_ls_write_hexadecimal_number( (fiptr->tinfo).dwVolumeSerialNumber, 2*sizeof(DWORD) ); } else { dk_ls_write_unknown(2*sizeof(DWORD), 1); } } else { dk_ls_write_unknown(2*sizeof(DWORD), 1); } } #else if (0 != issl) { if (0 != (DK4_FILE_INFO_CONTENTS_DATA_LINK & fiptr->contents)) { dk_ls_write_hexadecimal_number( (fiptr->lstb).st_dev, 2*sizeof(dev_t) ); } else { dk_ls_write_unknown(2*sizeof(dev_t), 1); } } else { if (0 != (DK4_FILE_INFO_CONTENTS_DATA_TARGET & fiptr->contents)) { dk_ls_write_hexadecimal_number( (fiptr->tstb).st_dev, 2*sizeof(dev_t) ); } else { dk_ls_write_unknown(2*sizeof(dev_t), 1); } } #endif } break; case 'r' : { $? ". relative device number" #if DK4_ON_WINDOWS if (0 != issl) { if (0 != (DK4_FILE_INFO_CONTENTS_DATA_LINK & fiptr->contents)) { if ((DWORD)0UL == fiptr->resio) { dk_ls_write_hexadecimal_number( (fiptr->linfo).dwVolumeSerialNumber, 2*sizeof(DWORD) ); } else { dk_ls_write_unknown(2*sizeof(DWORD), 1); } } else { dk_ls_write_unknown(2*sizeof(DWORD), 1); } } else { if (0 != (DK4_FILE_INFO_CONTENTS_DATA_TARGET & fiptr->contents)) { if ((DWORD)0UL == fiptr->resio) { dk_ls_write_hexadecimal_number( (fiptr->tinfo).dwVolumeSerialNumber, 2*sizeof(DWORD) ); } else { dk_ls_write_unknown(2*sizeof(DWORD), 1); } } else { dk_ls_write_unknown(2*sizeof(DWORD), 1); } } #else if (0 != issl) { if (0 != (DK4_FILE_INFO_CONTENTS_DATA_LINK & fiptr->contents)) { dk_ls_write_hexadecimal_number( (fiptr->lstb).st_rdev, 2*sizeof(dev_t) ); } else { dk_ls_write_unknown(2*sizeof(dev_t), 1); } } else { if (0 != (DK4_FILE_INFO_CONTENTS_DATA_TARGET & fiptr->contents)) { dk_ls_write_hexadecimal_number( (fiptr->tstb).st_rdev, 2*sizeof(dev_t) ); } else { dk_ls_write_unknown(2*sizeof(dev_t), 1); } } #endif } break; case 'i' : { $? ". inode number" if (dkT('\0') != lasti) { if (dkT('d') != lasti) { if (0 == dk4fputc(dkT(' '), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } else { if (0 == dk4fputc(dkT(':'), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } } #if DK4_ON_WINDOWS if (0 != issl) { if (0 != (DK4_FILE_INFO_CONTENTS_DATA_LINK & fiptr->contents)) { if ((DWORD)0UL == fiptr->resio) { dk_ls_write_hexadecimal_number( (fiptr->linfo).nFileIndexHigh, 2*sizeof(DWORD) ); dk_ls_write_hexadecimal_number( (fiptr->linfo).nFileIndexLow, 2*sizeof(DWORD) ); } else { dk_ls_write_unknown(4*sizeof(DWORD), 1); } } else { dk_ls_write_unknown(4*sizeof(DWORD), 1); } } else { if (0 != (DK4_FILE_INFO_CONTENTS_DATA_TARGET & fiptr->contents)) { if ((DWORD)0UL == fiptr->resio) { dk_ls_write_hexadecimal_number( (fiptr->tinfo).nFileIndexHigh, 2*sizeof(DWORD) ); dk_ls_write_hexadecimal_number( (fiptr->tinfo).nFileIndexLow, 2*sizeof(DWORD) ); } else { dk_ls_write_unknown(4*sizeof(DWORD), 1); } } else { dk_ls_write_unknown(4*sizeof(DWORD), 1); } } #else if (0 != issl) { if (0 != (DK4_FILE_INFO_CONTENTS_DATA_LINK & fiptr->contents)) { dk_ls_write_hexadecimal_number( (fiptr->lstb).st_ino, 2*sizeof(ino_t) ); } else { dk_ls_write_unknown(2*sizeof(ino_t), 1); } } else { if (0 != (DK4_FILE_INFO_CONTENTS_DATA_TARGET & fiptr->contents)) { dk_ls_write_hexadecimal_number( (fiptr->tstb).st_ino, 2*sizeof(ino_t) ); } else { dk_ls_write_unknown(2*sizeof(ino_t), 1); } } #endif } break; #if DK4_ON_WINDOWS case 'w' : { $? ". windows reparse point tag" if (dkT('\0') != lasti) { if (0 == dk4fputc(dkT(' '), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } dk_ls_write_hexadecimal_number( fiptr->rppnt, 2*sizeof(DWORD) ); } break; #endif case 'x' : { if (0 != dk4md_check_type(digest_type)) { if (dkT('\0') != lasti) { if (0 == dk4fputc(dkT(' '), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } dk_ls_write_md( fn, fiptr, digest_type, digest_enco, app, dk_ls_msg, dk_ls_sz_msg ); } else { /* ERROR (once) Message digest type not supported */ if (0 == digest_erep) { dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 19); digest_erep = 1; } } } break; default : { /* ERROR (once): Invalid text in -p argument */ if (0 == print_order_error_reported) { dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 11); print_order_error_reported = 1; } exval = EXIT_FAILURE; } break; } } else { /* ERROR (once): Invalid text in -p argument */ if (0 == print_order_error_reported) { dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 11); print_order_error_reported = 1; } exval = EXIT_FAILURE; } lasti = *(piptr++); } if (0 != last_was_n) { if (0 != dk4fileinfo_is_symlink(fiptr)) { res = dk4symlink_target( dk_ls_sl_target, DK4_SIZEOF(dk_ls_sl_target,dkChar), fn, NULL ); if (0 != res) { if (0 == dk4fputs(dk_ls_kwnl[7], stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } if (0 == dk4fputs(dk_ls_sl_target, stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } else { /* ERROR: Failed to find symlink target */ dk4app_log_3(app,dk_ls_msg,dk_ls_sz_msg,DK4_LL_ERROR,12,13,fn); exval = EXIT_FAILURE; } } } if (0 == dk4fputc(dkT('\n'), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } else { $? ". skipping file" } } else { $? "! args" } $? "- dk_ls_process_item_information" } /** Process non-directory file. @param fn File name. @param level Processing level (0=cmd line arg, 1=direct child, 2 and above=further indicrect siblings). */ static void dk_ls_process_non_directory(const dkChar *fn, int level) { dk4_file_info_t fili; dk4fileinfo_init(&fili); if (0 != dk4fileinfo_app(&fili, fn, app)) { dk_ls_process_item_information(fn, &fili, 0, (dk4_um_t)0UL, 0); } else { /* ERROR: Failed to obtain information about file, already reported */ exval = EXIT_FAILURE; } } /** Process one directory item. @param dirnode Node for the items parent directory. @param filename Path name for item. */ static void dk_ls_process_item_for_node( dk_ls_node_t *dirnode, const dkChar *filename ) { dk4_file_info_t fili; dk4_er_t er; dk4_um_t fs; int ch = DK4_FILE_INFO_CONTENTS_DATA_TARGET; $? "+ dk_ls_process_item_for_node PTR=%d \"%!ds\"", TR_IPTR(dirnode), filename if ((0 >= dirnode->level) || (0 != recursive) || (0 != size_wanted)) { dk4error_init(&er); dk4fileinfo_init(&fili); if (0 != dk4fileinfo_app(&fili, filename, app)) { if (0 != dk4fileinfo_is_symlink(&fili)) { ch = DK4_FILE_INFO_CONTENTS_DATA_LINK ; } if (0 != dk4fileinfo_size(&fs, &fili, ch, &er)) { dirnode->size = dk4ma_um_add(dirnode->size, fs, &er); if (DK4_E_MATH_OVERFLOW == er.ec) { dirnode->ovsize = 1; } } else { /* ERROR: Failed to retrieve size */ $? "! file size" dk_ls_report_fileinfo_size_error( app, filename, ch, dk_ls_msg, dk_ls_sz_msg ); fs = (dk4_um_t)0UL; exval = EXIT_FAILURE; } if ((0 >= dirnode->level ) || (0 != recursive)) { /* Show file information */ dk_ls_process_item_information( filename, &fili, 0, (dk4_um_t)0UL, 0 ); } } else { /* ERROR: Failed to obtain file information, already reported */ exval = EXIT_FAILURE; } } $? "- dk_ls_process_item_for_node" } /** Process directory. Unless required otherwise by a non-zero value for recursive, show contents and directory for level=0, directory itself for level=1. @param dn Directory name. @param level Processing level (0=cmd line arg, 1=direct child, 2 and above=further siblings). */ static void dk_ls_process_directory(const dkChar *dn, int level) { dk4_ufi_t pdufi; /* UFI for parent directory */ dk4_ufi_t sdufi; /* UFI for sub directory */ dk4_er_t er; /* Error report for size addition */ dkChar *mybu; /* Buffer for path name construction */ #if VERSION_BEFORE_20150821 dk_ls_node_t *rn; /* Root node */ #endif dk_ls_node_t *cn; /* Current node */ dk_ls_node_t *on; /* Other node */ const dkChar *pa; /* Directory path */ const dkChar *ndn; /* Next directory name */ size_t szmb; /* Size of mybu (number of dkChar) */ int gosub; /* Flag: Go into sub directory */ int ufich; /* Channel to retrieve UFI */ $? "+ dk_ls_process_directory \"%!ds\" %d", dn, level /* Allocate path name construction buffer. */ ufich = DK4_FILE_INFO_CONTENTS_DATA_TARGET; szmb = DK4_MAX_PATH; mybu = dk4mem_new_app(dkChar,szmb,app); /* If path name construction buffer available, run normally. */ if (NULL != mybu) { #if VERSION_BEFORE_20150821 rn = cn = dk_ls_new_node(dn, NULL, level); #else cn = dk_ls_new_node(dn, NULL, level); #endif while (NULL != cn) { $? ". still dir to process" if (0 != dk_ls_can_continue()) { $? ". can continue" ndn = dk4dir_next_dir(cn->dirptr); if (NULL != ndn) { /* Handle sub directory. */ pa = dk4dir_get_path(cn->dirptr); if (0 != dk_ls_construct_path(&mybu, &szmb, pa, ndn)) { /* Decide whether or not to go into subdirectory */ gosub = 0; #if 0 /* 2015-04-14 There is no reason to open a node for the subdirectory unless recursive output or size calculation is wanted. */ if (0 == cn->level) { /* Processing cmd line argument */ gosub = 1; } #endif if (0 != recursive) { /* Recursive output wanted */ gosub = 1; } if (0 != size_wanted) { /* Size calculation required */ if (0 != show_dirs) { /* ... and directory information wanted */ gosub = 1; } } if (0 != gosub) { if (0 != dk4ufi_get(&sdufi, mybu, NULL)) { if (0 != stay_on_fs) { /* Check for same filesystem */ if (0 != dk4fileinfo_ufi(&pdufi, &(cn->fi), ufich, NULL)) { #if DK4_ON_WINDOWS if (pdufi.ser != sdufi.ser) { gosub = 0; } #else if (pdufi.dev != sdufi.dev) { gosub = 0; } #endif } else { /* ERROR: Failed to obtain file identifier */ dk4app_log_3( app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 14, 15, dk4dir_get_path(cn->dirptr) ); gosub = 0; exval = EXIT_FAILURE; } } if (0 != gosub) { /* Check for symlink loop */ on = cn; while ((0 != gosub) && (NULL != on)) { if (0 != dk4fileinfo_ufi(&pdufi, &(on->fi), ufich, NULL)) { #if DK4_ON_WINDOWS if (pdufi.ser == sdufi.ser) { if (pdufi.inh == sdufi.inh) { if (pdufi.inl == sdufi.inl) { gosub = 0; } } } #else if (pdufi.dev == sdufi.dev) { if (pdufi.ino == sdufi.ino) { gosub = 0; } } #endif } else { /* ERROR: Failed to retrieve UFI for directory */ dk4app_log_3( app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 14, 15, dk4dir_get_path(on->dirptr) ); exval = EXIT_FAILURE; gosub = 0; } on = on->parent; } } } else { /* ERROR: Failed to retrieve unique file identifier */ dk4app_log_3( app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 14, 15, mybu ); exval = EXIT_FAILURE; gosub = 0; } } if (0 != gosub) { /* Go into subdirectory. */ on = dk_ls_new_node( mybu, cn, ((3 >= cn->level) ? (1 + cn->level) : 3) ); if (NULL != on) { cn->child = on; cn = on; } else { /* ERROR: Failed to create node, already reported */ exval = EXIT_FAILURE; } } else { /* Do not go into subdirectory, probably show directory details. */ dk_ls_process_item_for_node(cn, mybu); } } else { /* ERROR: Failed to construct path, already reported */ exval = EXIT_FAILURE; } } else { /* Finalize node, report non-directory entries and the directories, finally move to parent node. */ if ((0 != size_wanted) || (0 != show_files)) { if (0 != dk_ls_can_continue()) { do { ndn = dk4dir_next_file(cn->dirptr); if (NULL != ndn) { $? ". file \"%!ds\"", ndn pa = dk4dir_get_path(cn->dirptr); if (0 != dk_ls_construct_path(&mybu, &szmb, pa, ndn)) { dk_ls_process_item_for_node(cn, mybu); } else { /* ERROR: Failed to construct path, already reported */ exval = EXIT_FAILURE; } } } while((0 != dk_ls_can_continue()) && (NULL != ndn)) ; } } if (0 != dk_ls_can_continue()) { $? ". can continue" if ((0 != size_wanted) || (0 != show_dirs)) { if ((1 >= cn->level) || (0 != recursive)) { dk_ls_process_item_information( dk4dir_get_path(cn->dirptr), &(cn->fi), 1, cn->size, cn->ovsize ); } } } on = cn->parent; if (NULL != on) { on->child = NULL; dk4error_init(&er); on->size = dk4ma_size_t_add(on->size, cn->size, &er); if (DK4_E_NONE != er.ec) { on->ovsize = 1; } if (0 != cn->ovsize) { on->ovsize = 1; } } dk_ls_delete_node(cn); cn = on; } } else { /* Abort: Close current node and move to parent. No output. */ $? ". abort processing" on = cn->parent; if (NULL != on) { on->child = NULL; } dk_ls_delete_node(cn); cn = on; } } } else { $? "! mybu" /* ERROR: Allocation failed, already reported */ exval = EXIT_FAILURE; } /* Release path name construction buffer. */ if (NULL != mybu) { dk4mem_release(mybu); } $? "- dk_ls_process_directory" } /** Check whether the specified file name is a directory. @param fn File name. @return 1 for directories, 0 for non-directory entries, -1 on errors. */ static int dk_ls_is_directory(const dkChar *fn) { #if DK4_ON_WINDOWS DWORD dwattr; HANDLE ha; LONG lasterror; #if DK4_CHAR_SIZE > 1 WIN32_FIND_DATAW finddata; #else WIN32_FIND_DATAA finddata; #endif #else dk4_stat_t stb; dk4_er_t er; #endif int back = -1; $? "+ dk_ls_is_directory \"%!ds\"", fn if (NULL != fn) { #if DK4_ON_WINDOWS #if DK4_CHAR_SIZE > 1 dwattr = GetFileAttributesW(fn); #else dwattr = GetFileAttributesA(fn); #endif if (INVALID_FILE_ATTRIBUTES != dwattr) { if (0 != (FILE_ATTRIBUTE_DIRECTORY & dwattr)) { back = 1; } else { back = 0; } } else { lasterror = GetLastError(); #if DK4_CHAR_SIZE > 1 ha = FindFirstFileW(fn, &finddata); #else ha = FindFirstFileA(fn, &finddata); #endif if (INVALID_HANDLE_VALUE != ha) { if (0 != (FILE_ATTRIBUTE_DIRECTORY & (finddata.dwFileAttributes))) { back = 1; } else { back = 0; } FindClose(ha); } else { /* ERROR: Failed to retrieve file attributes */ dk4fileinfo_report_win_failed(app, fn, lasterror); exval = EXIT_FAILURE; } } #else dk4error_init(&er); if (0 != dk4stat(&stb, fn, &er)) { if (0 != dk4stat_is_directory(&stb, &er)) { back = 1; } else { back = 0; } } else { /* ERROR: Failed to retrieve file information */ dk4fileinfo_report_stat_failed(app, fn, er.dt.iDetails1); exval = EXIT_FAILURE; } #endif } $? "- dk_ls_is_directory %d", back return back; } /* !!!!! For files explicitly mentioned on the command line processing starts here, probably after name expansion. */ /** Run for one file or directory directly named on the command line. @param arg Path name. */ static void run_for_one_name(const dkChar *arg) { $? "+ run_for_one_name \"%!ds\"", arg switch (dk_ls_is_directory(arg)) { case 1 : { $? ". directory" dk_ls_process_directory(arg, 0); } break; case 0 : { $? ". not a directory" if (0 != show_files) { dk_ls_process_non_directory(arg, 0); } } break; default : { $? "! no information" /* ERROR: Failed to obtain information about file, already reported */ exval = EXIT_FAILURE; } break; } $? "- run_for_one_name" } /** Process command line arguments. @param xargc Number of command line arguments. */ static void run_for_command_line_arguments(int xargc) { #if DK4_ON_WINDOWS dkChar buf[DK4_MAX_PATH]; dk4_dir_t *fne; const dkChar *pth; const dkChar *fn; int cc2; #endif const dkChar *xargv; int i; int cc = 1; $? "+ run_for_command_line_arguments %d", xargc for (i = 0; ((i < xargc) && (1 == cc)); i++) { $? ". loop start" if (0 != dk_ls_can_continue()) { $? ". can continue" xargv = dk4app_get_argv(app, i); $? ". xargv \"%!ds\"", xargv #if DK4_ON_WINDOWS if (0 != dk4path_must_expand(xargv)) { /* !!!!! For file names we need to expand processing starts here. */ fne = dk4app_fne_open(xargv, app); if (NULL != fne) { cc2 = 1; while (1 == cc2) { if (0 != dk_ls_can_continue()) { fn = dk4dir_next_dir(fne); if (NULL != fn) { pth = dk4dir_get_path(fne); if (NULL != pth) { if (0 != dk4str_cpy_s(buf,DK4_SIZEOF(buf,dkChar),pth,NULL)) { if (0!=dk4str_cat_s(buf,DK4_SIZEOF(buf,dkChar),dk_ls_kwnl[5],NULL)) { if (0 != dk4str_cat_s(buf,DK4_SIZEOF(buf,dkChar),fn,NULL)) { dk_ls_process_directory(buf, 0); } else { /* ERROR: File name too long! */ dk4app_log_5( app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 16, 17, 18, pth, fn ); exval = EXIT_FAILURE; } } else { /* ERROR: File name too long! */ dk4app_log_base3(app, DK4_LL_ERROR, 98, 99, pth); exval = EXIT_FAILURE; } } else { /* ERROR: File name too long! */ dk4app_log_base3(app, DK4_LL_ERROR, 98, 99, pth); exval = EXIT_FAILURE; } } else { run_for_one_name(fn); } } else { cc2 = 0; } } else { cc2 = 0; } } cc2 = 1; while (1 == cc2) { if (0 != dk_ls_can_continue()) { fn = dk4dir_next_file(fne); if (NULL != fn) { pth = dk4dir_get_path(fne); if (NULL != pth) { if (0 != dk4str_cpy_s(buf,DK4_SIZEOF(buf,dkChar),pth,NULL)) { if (0!=dk4str_cat_s(buf,DK4_SIZEOF(buf,dkChar),dk_ls_kwnl[5],NULL)) { if (0 != dk4str_cat_s(buf,DK4_SIZEOF(buf,dkChar),fn,NULL)) { dk_ls_process_non_directory(buf, 0); } else { /* ERROR: File name too long! */ dk4app_log_5( app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 16, 17, 18, pth, fn ); exval = EXIT_FAILURE; } } else { /* ERROR: File name too long! */ dk4app_log_base3(app, DK4_LL_ERROR, 98, 99, pth); exval = EXIT_FAILURE; } } else { /* ERROR: File name too long! */ dk4app_log_base3(app, DK4_LL_ERROR, 98, 99, pth); exval = EXIT_FAILURE; } } else { run_for_one_name(fn); } } else { cc2 = 0; } } else { cc2 = 0; } } dk4dir_close(fne); } else { /* ERROR: Failed to expand file name, already reported */ exval = EXIT_FAILURE; } } else { #endif $? ". UNIX/Linux" run_for_one_name(xargv); #if DK4_ON_WINDOWS } #endif } else { cc = 0; } } $? "- run_for_command_line_arguments" } /* !!!!! If no file names was given on the command line processing starts here. */ /** Show contents of current directory. */ static void run_for_current_dir_contents(void) { dk4_er_t er; dk4_dir_t *dirptr; const dkChar *fn; int cc; int om; $? "+ run_for_current_dir_contents" dk4error_init(&er); om = DK4_DIR_OPEN_SORTED | DK4_DIR_SYMLINK_DIR_AS_FILE; dirptr = dk4dir_open_app(dk_ls_kwnl[4], om, app); if (NULL != dirptr) { if ((0 != recursive) || (0 != show_dirs)) { cc = 1; while(1 == cc) { if (0 != dk_ls_can_continue()) { fn = dk4dir_next_dir(dirptr); if (NULL != fn) { if ((0 != recursive) || (0 != size_wanted)) { dk_ls_process_directory(fn, 1); } else { dk_ls_process_non_directory(fn, 1); } } else { cc = 0; } } else { cc = 0; } } } if (0 != show_files) { cc = 1; while (1 == cc) { if (0 != dk_ls_can_continue()) { fn = dk4dir_next_file(dirptr); if (NULL != fn) { dk_ls_process_non_directory(fn, 1); } else { cc = 0; } } else { cc = 0; } } } dk4dir_close(dirptr); } else { /* ERROR: Failed to open current directory, already reported */ exval = EXIT_FAILURE; } $? "- run_for_current_dir_contents" } /** Process command line arguments (if any). */ static void dk_ls_normal_run(void) { dkChar mdbuf[32]; dkChar *bptr; int res; int xargc; int mdset; $? "+ dk_ls_normal_run" exval = EXIT_SUCCESS; /* Process options. */ if (0 != dk4app_opt_is_set_short(app, dkT('p'), NULL)) { res = dk4app_opt_get_string_short( po_buf, DK4_SIZEOF(po_buf,dkChar), app, dkT('p'), NULL ); if (0 != res) { print_order = po_buf; } } if (NULL == print_order) { if (0 == dk4app_opt_is_set_short(app, dkT('R'), NULL)) { res = dk4app_pref_get( po_buf, DK4_SIZEOF(po_buf,dkChar), app, dk_ls_kwnl[10], 0 ); if (0 != res) { dk4str_rtwh(po_buf, NULL); print_order = dk4str_start(po_buf, NULL); } } } if (NULL == print_order) { #if DK4_ON_WINDOWS print_order = dk_ls_kwnl[8]; #else print_order = dk_ls_kwnl[3]; #endif } if (NULL != dk4str_chr(print_order, dkT('s'))) { size_wanted = 1; } if (NULL != dk4str_chr(print_order, dkT('n'))) { name_wanted = 1; } if (0 != dk4app_opt_is_set_short(app, dkT('r'), NULL)) { recursive = 1; } if (0 != dk4app_opt_is_set_short(app, dkT('f'), NULL)) { stay_on_fs = 1; } if (0 != dk4app_opt_is_set_short(app, dkT('t'), NULL)) { res = dk4app_opt_get_string_short( mdbuf, DK4_SIZEOF(mdbuf,dkChar), app, dkT('t'), NULL ); if (0 != res) { bptr = mdbuf; show_files = 0; show_dirs = 0; while (dkT('\0') != *bptr) { if (dkT('d') == *bptr) { show_dirs = 1; } if (dkT('f') == *bptr) { show_files = 1; } bptr++; } } } mdset = 0; if (0 != dk4app_opt_is_set_short(app, dkT('m'), NULL)) { res = dk4app_opt_get_string_short( mdbuf, DK4_SIZEOF(mdbuf,dkChar), app, dkT('m'), NULL ); if (0 != res) { /* Process string, find digest and encoding */ mdset = 1; dk_ls_set_md( &digest_type, &digest_enco, mdbuf, app, dk_ls_msg, dk_ls_sz_msg ); } } if (0 != dk4app_opt_is_set_short(app, dkT('s'), NULL)) { show_summary = 1; } if ((0 == mdset) && (0 == dk4app_opt_is_set_short(app, dkT('R'), NULL))) { res = dk4app_pref_get( mdbuf, DK4_SIZEOF(mdbuf,dkChar), app, dk_ls_kwnl[9], 0 ); if (0 != res) { dk_ls_set_md( &digest_type, &digest_enco, mdbuf, app, dk_ls_msg, dk_ls_sz_msg ); } } /* Process command line arguments or current directory contents. */ xargc = dk4app_get_argc(app); if (0 < xargc) { $? ". process command line arguments" run_for_command_line_arguments(xargc); } else { $? ". no command line args, use current dir" run_for_current_dir_contents(); } if (0 != show_summary) { if (((dk4_um_t)0UL != num_dirs) && (DK4_E_NONE == er_dirs.ec)) { if (0 == dk4fputs(dk_ls_msg[30], stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } dk_ls_write_decimal_number(num_dirs,dk4numlength(sizeof(dk4_um_t),0)); if (0 == dk4fputc(dkT('\n'), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } if (((dk4_um_t)0UL != num_files) && (DK4_E_NONE == er_files.ec)) { if (0 == dk4fputs(dk_ls_msg[31], stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } dk_ls_write_decimal_number(num_files,dk4numlength(sizeof(dk4_um_t),0)); if (0 == dk4fputc(dkT('\n'), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } if (((dk4_um_t)0UL != size_bytes) && (DK4_E_NONE == er_bytes.ec)) { if (0 == dk4fputs(dk_ls_msg[32], stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } dk_ls_write_decimal_number(size_bytes,dk4numlength(sizeof(dk4_um_t),0)); if (0 == dk4fputc(dkT('\n'), stdout, NULL)) { write_error = 1; exval = EXIT_FAILURE; } } } $? "- dk_ls_normal_run" } #if DK4_HAVE_SIGACTION /** Set signal handlers and run. */ static void dk_ls_run_with_signal_handlers(void) { #ifdef SIGPIPE struct sigaction opipe; #endif struct sigaction oint; #ifdef SIGPIPE struct sigaction npipe; #endif struct sigaction nint; struct sigaction oterm; struct sigaction nterm; int success = 0; #ifdef SIGPIPE /* Set up signal handling for SIGPIPE. */ DK4_MEMRES(&npipe, sizeof(npipe)); npipe.sa_handler = sig_handler_pipe; npipe.sa_flags = 0; if (0 != sigemptyset(&npipe.sa_mask)) { /* ERROR: Failed to set up masked signal set for SIGPIPE */ dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 0); goto finished; } if (0 != sigaddset(&npipe.sa_mask, SIGPIPE)) { /* ERROR: Failed to set up masked signal set for SIGPIPE */ dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 0); goto finished; } if (0 != sigaction(SIGPIPE, &npipe, &opipe)) { /* ERROR: Failed to set up signal handler for SIGPIPE */ dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 0); goto finished; } #endif /* Set up signal handling for SIGINT. */ DK4_MEMRES(&nint, sizeof(nint)); nint.sa_handler = sig_handler_int; nint.sa_flags = 0; if (0 != sigemptyset(&nint.sa_mask)) { /* ERROR: Failed to set up masked signal set for SIGINT */ dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 0); goto restore_old_pipe; } if (0 != sigaddset(&nint.sa_mask, SIGINT)) { /* ERROR: Failed to set up masked signal set for SIGINT */ dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 0); goto restore_old_pipe; } if (0 != sigaction(SIGINT, &nint, &oint)) { /* ERROR: Failed to set up signal handler for SIGINT */ dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 0); goto restore_old_pipe; } /* Set up signal handling for SIGTERM */ DK4_MEMRES(&nterm, sizeof(nterm)); nterm.sa_handler = sig_handler_term; nterm.sa_flags = 0; if (0 != sigemptyset(&nterm.sa_mask)) { /* ERROR: Failed to set up masked signal set for SIGTERM */ dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 0); goto restore_old_int; } if (0 != sigaddset(&nterm.sa_mask, SIGTERM)) { /* ERROR: Failed to set up masked signal set for SIGTERM */ dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 0); goto restore_old_int; } if (0 != sigaction(SIGTERM, &nterm, &oterm)) { /* ERROR: Failed to set up signal handler for SIGTERM */ dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 0); goto restore_old_int; } success = 1; dk_ls_normal_run(); /* Restore signal handling for SIGTERM. */ if (0 != sigaction(SIGTERM, &oterm, NULL)) { /* ERROR: Failed to restore old SIGTERM settings */ dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 1); success = 0; } /* Restore signal handling for SIGINT. */ restore_old_int: if (0 != sigaction(SIGINT, &oint, NULL)) { /* ERROR: Failed to restore old SIGPIPE settings */ dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 1); success = 0; } #ifdef SIGPIPE /* Restore signal handling for SIGPIPE. */ restore_old_pipe: if (0 != sigaction(SIGPIPE, &opipe, NULL)) { /* ERROR: Failed to restore old SIGPIPE settings */ dk4app_log_1(app, dk_ls_msg, dk_ls_sz_msg, DK4_LL_ERROR, 1); success = 0; } #endif /* Set exit status code if error occured. */ finished: if (0 == success) { exval = EXIT_FAILURE; } } #else #if DK4_HAVE_SIGSET /** Set signal handlers and run. */ static void dk_ls_run_with_signal_handlers(void) { #ifdef SIGPIPE dk4_sig_handler_t *oldpipe = NULL; #endif dk4_sig_handler_t *oldint = NULL; dk4_sig_handler_t *oldterm = NULL; #ifdef SIGPIPE oldpipe = sigset(SIGPIPE, sig_handler_pipe); #endif oldint = sigset(SIGINT, sig_handler_int); oldterm = sigset(SIGTERM, sig_handler_term); dk_ls_normal_run(); sigset(SIGTERM, oldterm); sigset(SIGINT, oldint); #ifdef SIGPIPE sigset(SIGPIPE, oldpipe); #endif } #else #if DK4_HAVE_SIGNAL /** Set signal handlers and run. */ static void dk_ls_run_with_signal_handlers(void) { #ifdef SIGPIPE dk4_sig_handler_t *oldpipe = NULL; #endif dk4_sig_handler_t *oldint = NULL; dk4_sig_handler_t *oldterm = NULL; #ifdef SIGPIPE oldpipe = signal(SIGPIPE, sig_handler_pipe); #endif oldint = signal(SIGINT, sig_handler_int); oldterm = signal(SIGTERM, sig_handler_term); dk_ls_normal_run(); signal(SIGTERM, oldterm); signal(SIGINT, oldint); #ifdef SIGPIPE signal(SIGPIPE, oldpipe); #endif } #else /** Set signal handlers and run. */ static void dk_ls_run_with_signal_handlers(void) { dk_ls_normal_run(); } #endif #endif #endif /** Main function. @param argc Number of command line arguments. @param argv Command line arguments array. @return 0 on success, all other values indicate errors. */ #if DK4_CHAR_SIZE > 1 int wmain(int argc, wchar_t *argv[]) #else int main(int argc, char *argv[]) #endif { $!trace-init /tmp/dk-ls.deb $? "+ main" dk4fput_initialize_stdout(); dk4fput_initialize_stderr(); dk4error_init(&er_dirs); dk4error_init(&er_files); dk4error_init(&er_bytes); app = dk4app_open_cmd( argc, argv, dk_ls_options, dk_ls_sz_options, dk_ls_kwnl[0], DKT_VERSION_DK, dk_ls_kwnl[1], dk_ls_help_text, dk_ls_license_text ); if (NULL != app) { $? ". app" dk_ls_sz_msg = dk4app_string_table_size(dk_ls_kw_def); dk_ls_msg = dk4app_string_table(app, dk_ls_kwnl[2], dk_ls_kw_def); if (0 != dk4app_can_run_normally(app)) { $? ". normal" dk_ls_run_with_signal_handlers(); } else { if (0 != dk4app_help_version_license(app)) { $? ". help/vers/lic" exval = EXIT_SUCCESS; } } dk4app_close(app); } else { $? "! app" dk4fputs(dk_ls_kwnl[11], stdout, NULL); fflush(stdout); } fflush(stdout); fflush(stderr); dk4fput_cleanup_stderr(); dk4fput_cleanup_stdout(); $? "- main %d", exval $!trace-end exit(exval); return exval; }