%% options copyright owner = Dirk Krause copyright year = 2017-xxxx SPDX-License-Identifier: BSD-3-Clause %% module /** @file dk-lines.c The dk-lines program. The dk_lines_help_text array below shows details. */ #ifndef DK4CONF_H_INCLUDED #include "dk4conf.h" #endif #include #if DK4_HAVE_STDLIB_H #ifndef STDLIB_H_INCLUDED #include #define STDLIB_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_SYS_TYPES_H #ifndef SYS_TYPES_H_INCLUDED #include #define SYS_TYPES_H_INCLUDED 1 #endif #endif #if DK4_HAVE_SIGNAL_H #ifndef SIGNAL_H_INCLUDED #include #define SIGNAL_H_INCLUDED 1 #endif #endif #ifndef DK4UNUSED_H_INCLUDED #include "dk4unused.h" #endif #ifndef DK4TYPES_H_INCLUDED #include "dk4types.h" #endif #ifndef DK4MEM_H_INCLUDED #include "dk4mem.h" #endif #ifndef DK4ENC_H_INCLUDED #include "dk4enc.h" #endif #ifndef DK4STRD_H_INCLUDED #include "dk4strd.h" #endif #ifndef DK4STRDA_H_INCLUDED #include "dk4strda.h" #endif #ifndef DK4DIR_H_INCLUDED #include "dk4dir.h" #endif #ifndef DK4PATHD_H_INCLUDED #include "dk4pathd.h" #endif #ifndef DK4APP_H_INCLUDED #include "dk4app.h" #endif #ifndef DK4AOPT_H_INCLUDED #include "dk4aopt.h" #endif #ifndef DK4MEMA_H_INCLUDED #include "dk4mema.h" #endif #ifndef DK4VERS_H_INCLUDED #include "dk4vers.h" #endif #ifndef DK4MAADU_H_INCLUDED #include "dk4maadu.h" #endif #ifndef DK4MAIDDDU_H_INCLUDED #include "dk4maidddu.h" #endif #ifndef DK4MAODD_H_INCLUDED #include "dk4maodd.h" #endif #ifndef DK4FPUT_H_INCLUDED #include "dk4fput.h" #endif #ifndef DK4TSPDK_H_INCLUDED #include "dk4tspdk.h" #endif #ifndef DK4FOPD_H_INCLUDED #include "dk4fopd.h" #endif #ifndef DK4FOPDA_H_INCLUDED #include "dk4fopda.h" #endif #ifndef DK4ISADM_H_INCLUDED #include "dk4isadm.h" #endif #ifndef DK4TIME_H_INCLUDED #include "dk4time.h" #endif #ifndef DK4TIMEDK_H_INCLUDED #include "dk4timedk.h" #endif #ifndef DK4MPL_H_INCLUDED #include "dk4mpl.h" #endif #ifndef DK4WMAIN_H_INCLUDED #include "dk4wmain.h" #endif $!trace-include #ifndef DK_LINES_BUFFER_SIZE /** Text buffer size. */ #define DK_LINES_BUFFER_SIZE 4096 #endif /** Ring buffer element. */ typedef struct { dkChar *str; /**< Line text. */ dk4_um_t lineno; /**< Line number. */ size_t buflen; /**< Length of str buffer (number of dkChar). */ } rbe_t; /** Range specification types. */ enum { RANGE_NONE_NONE = 0 , /**< No ranges specified. */ RANGE_NONE_POS , /**< No start, positive end. */ RANGE_NONE_NEG , /**< No start, negative end. */ RANGE_POS_NONE , /**< Positive start, no end. */ RANGE_POS_POS , /**< Positive start, positive end. */ RANGE_POS_NEG , /**< Positive start, negative end. */ RANGE_NEG_NONE , /**< Negative start, no end. */ RANGE_NEG_POS , /**< Negative start, positive end. */ RANGE_NEG_NEG , /**< Negative start, negative end. */ }; /** Input line buffer. */ static dkChar default_input_line_buffer[DK_LINES_BUFFER_SIZE]; /** Buffer to store current date and time. */ static dkChar date_buffer[64]; /** Default text, used if help text file is not found. */ static const dkChar * const dk_lines_help_text[] = { $!text macro=dkT,preprocessor Select lines from text dk-lines [] [] Options: -i encoding Expected input encoding, one from: plain, ascii, ansi, utf-8, utf-16, utf-16le, utf-16be, c32, c32le, c32be. -f string Show lines lexicographically equal or larger than string. -t string Show lines lexicographically equal or smaller than string. -l spec Specification of line numbers in format / (inclusive) to pass through, applied after -f and -t processing. Examples: 5 (5 and following), 5/8 (5, 6, 7, 8), /8 (1...8), 5/-3 (5 and following except final 2 lines), -5 (final 5 lines), -5/-3 (3 lines before the final 2 lines), /-3 (all lines except the final 2), 5/-3 (5 and following except final 2 lines), -5/8 (final 5 lines but not above line 8). -v Verbose mode: Error message on SIGPIPE. --help Show this short help text. --manual *** SHOW FULL MANUAL. *** --version Show version information. --license Show license information. http://sourceforge.net/p/dktools/wiki/dk-lines/ $!end }; /** License conditions. */ static const dkChar * const dk_lines_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://sourceforge.net/p/dktools/wiki/Home/ 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 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: 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. Neither the name of the copyright holder nor the names of its 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 HOLDER 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 $!end }; /** Constant texts used by program, not localized. */ static const dkChar * const dk_lines_kwnl[] = { $!string-table macro=dkT # # 0 Program group name # dktools # # 1 Help text file name # dk-lines.txt # # 2 String table file # dk-lines.str # # 3 Long option for line size # line-size # # 4 Long option to use current date # today # # 5 File name separator # / # # 6 File open mode # rb # # 7 File name when processing stdin # # # # $!end }; /** Constant texts used by program, replaced by localized texts if available. */ static const dkChar * const dk_lines_kw_def[] = { $!string-table macro=dkT # # 0 ERROR: Failed to set up masked signal set for SIGPIPE # Failed to set up masked signal set for SIGPIPE! # # 1 ERROR: Failed to set up masked signal set for SIGINT # Failed to set up masked signal set for SIGINT! # # 2 ERROR: Failed to set up masked signal set for SIGTERM # Failed to set up masked signal set for SIGTERM! # # 3 ERROR: Failed to restore SIGTERM settings! # Failed to restore SIGTERM settings! # # 4 ERROR: Failed to restore SIGINT settings # Failed to restore SIGTERM settings! # # 5 ERROR: Failed to restore SIGPIPE settings # Failed to restore SIGPIPE settings! # # 6 ERROR: Illegal line number 0! # Illegal line number 0! # # 7 8 ERROR: Number ... too large! # Number too large: " "! # # 9 10 ERROR: Not a number: ...! # Not a number: " "! # # 11 ERROR: Start line number larger than end number! # Start line number larger than end number! # # 12 ERROR: Invalid line range specification (empty string)! # Invalid line range specification (empty string)! # # 13 ERROR: Missing line range specification! # Missing line range specification! # # 14 15 ERROR: Illegal input encoding! # Illegal input encoding: " "! # # 16 ERROR: Missing input encoding! # Missing input encoding! # # 17 ERROR: Invalid line size 0! # Invalid line size 0! # # 18 ERROR: Missing line size specification! # Missing line size specification! # # 19 ERROR: Can not use --today with both --from and --to! # Can not use --today with both --from and --to! # # 20 ERROR: Input line too long! # Input line too long! # # 21 ERROR: Overflow in line number! # Too many lines, overflow in line number! # # 22 ERROR: Overflow in line range calculation! # Overflow in line range calculation! # # 23 ERROR: End line number is 1 (bug)! # End line number is 1 (should not happen, bug)! # # 24 ERROR: End line number too large! # End line number too large, numeric overflow! # # 25 ERROR: Start line number too large! # Start line number too large, numeric overflow! # # 26 ERROR: Ring buffer size 0! # Ring buffer size is 0 (should not happen, bug)! # # 27 ERROR: Failed to obtain file name! # Failed to obtain file name (should not happen, bug)! # # 28 ERROR: Stopped by SIGPIPE! # Program stopped by SIGPIPE! # # 29 ERROR: I/O errors while writing output! # I/O errors while writing output! # # 30 ERROR: Stopped by interrupt! # Program stopped by interrupt! # # 31 32 33 Error messages for encoding/decoding/processing errors # Encoding error! Decoding error! Processing error! # # 34 ... 39 Error messages with detailed position # Encoding error!\n\tByte: Decoding error!\n\tByte: Processing error!\n\tByte: ,\n\tCharacter: ,\n\tPosition in line: . $!end }; /** Options used by program. */ static const dk4_option_specification_t dk_lines_options[] = { /* Input encoding. */ { dkT('i'), dkT("input-encoding"), DK4_OPT_ARG_STRING }, /* Only lines matching start text or lexicagraphically after text. */ { dkT('f'), dkT("from"), DK4_OPT_ARG_STRING }, /* Only lines matching end text or lexicographically before text. */ { dkT('t'), dkT("to"), DK4_OPT_ARG_STRING }, /* Line number selection _after_ applying from and to rules. First line is line 1, final line is line -1. dk-lines -l 5/8 Lines 5, 6, 7 and 8 dk-lines -l 5 Lines 5, ... dk-lines -l /8 Lines 1, ... 8 dk-lines -l -8/-5 8 before last ... 5 before last dk-lines -l -8 Final 8 lines dk-lines -l /-5 Skip final 5 lines dk-lines -l -8/125 Final 8 lines but not above line 125 dk-lines -l 125/-8 Lines from 125 but skip final 8 lines dk-lines without -l option All lines */ { dkT('l'), dkT("lines"), DK4_OPT_ARG_STRING }, /* Verbose flag (write notification on SIGPIPE). */ { dkT('v'), dkT("verbose"), DK4_OPT_ARG_NONE }, /* Treat multiple files as one stream. */ { dkT('o'), dkT("one-stream"), DK4_OPT_ARG_NONE }, /* Line buffer size. */ { dkT('\0'), dkT("line-size"), DK4_OPT_ARG_SIZE }, /** Use current date to filter lines. */ { dkT('\0'), dkT("today"), DK4_OPT_ARG_NONE } }; #if DK4_ON_WINDOWS /** File name separator. */ static const dkChar fnsep[] = { #if DK4_HAVE_BACKSLASH_AS_SEP dkT("\\") #else dkT("/") #endif }; #endif /** Ring buffer used for negative line numbers. */ static rbe_t *pribu = NULL; /** Application structure. */ static dk4_app_t *app = NULL; /** Pointer to localized messages array. */ static const dkChar * const *msgs = dk_lines_kw_def; /** Start range. */ static const dkChar *t_from = NULL; /** End of range. */ static const dkChar *t_to = NULL; /** Input line buffer to use. */ static dkChar *ilb = default_input_line_buffer; /** Start line number. */ static dk4_um_t l_start = (dk4_um_t)0UL; /** End line number. */ static dk4_um_t l_end = (dk4_um_t)0UL; /** Current line number. */ static dk4_um_t lineno = (dk4_um_t)0UL; /** Length of --from text. */ static size_t sz_t_from = 0; /** Length of --to text. */ static size_t sz_t_to = 0; /** Ring buffer size (number of elements). */ static size_t sz_ribu = 0; /** Current position in ring buffer. */ static size_t pos_ribu = 0; /** Size of input line buffer. */ static size_t sz_ilb = DK4_SIZEOF(default_input_line_buffer,dkChar); /** Number of usable elements in the dk_lines_kw_def array and in msg. */ static size_t sz_msg = (sizeof(dk_lines_kw_def)/sizeof(DK4_PDKCHAR) - 1); /** Number of elements in the dk_lines_options array. */ static const size_t dk_lines_sz_options = sizeof(dk_lines_options)/sizeof(dk4_option_specification_t); /** Line handling type. */ static int l_type = RANGE_NONE_NONE; /** Exit status code. */ static int exval = EXIT_FAILURE; /** Flag: Write errors. */ static int had_write_error = 0; /** Expected input encoding when reading from file. */ static int eie_file = #if DK4_ON_WINDOWS DK4_FILE_ENCODING_WIN1252 #else DK4_FILE_ENCODING_PLAIN #endif ; /** Expected input encoding on standard input. */ static int eie_stdin = #if DK4_ON_WINDOWS DK4_FILE_ENCODING_WIN1252 #else DK4_FILE_ENCODING_PLAIN #endif ; /** Verbose flag (report SIGPIPE or unusual large line buffer size). */ static int verbose = 0; /** Flag: At least one cyle in the ring buffer was completed. */ static int cycle_completed = 0; /** Flag: Treat multiple files as one stream. */ static int one_stream = 0; /** Number of decoding errors already reported. */ static int num_r_decoding_e = 0; /** Maximum number of decoding errors to report unless verbose output required. */ static const int max_r_decoding_e = 3; /** Number of encoding errors already reported. */ static int num_r_encoding_e = 0; /** Maximum number of encoding errors to report unless verbose output required. */ static const int max_r_encoding_e = 3; /** Number of processing errors already reported. */ static int num_r_processing_e = 0; /** Maximum number of processing errors to report unless verbose output required. */ static const int max_r_processing_e = 3; #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; /** Special function to pass a volatile pointer. Some C compiler does not handle direct assignments to volatile variables correctly. @param ptr Address of volatile variable. @return Same address. */ 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 DK4_ARG_UNUSED(signo) ) { DK4_UNUSED_ARG(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 DK4_ARG_UNUSED(signo) ) { DK4_UNUSED_ARG(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 DK4_ARG_UNUSED(signo) ) { DK4_UNUSED_ARG(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( #ifdef SIGPIPE int check_pipe #else int DK4_ARG_UNUSED(check_pipe) #endif ) { int back = 1; #ifndef SIGPIPE DK4_UNUSED_ARG(check_pipe) #else 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; } /** Retrieve value from text. @param pdest Address of result variable to fill. @param vtxt Text containing the value. @param pback Address of success variable to reset on error. */ static void retrieve_number(dk4_um_t *pdest, const dkChar *vtxt, int *pback) { dk4_er_t er; dk4_um_t u = (dk4_um_t)0UL; const dkChar *pe = NULL; $? "+ retrieve_number \"%!ds\"", TR_STR(vtxt) dk4error_init(&er); if (0 != dk4ma_input_dk_dec_dk4_um_t(&u, vtxt, &pe, 1, &er)) { if ((dk4_um_t)0UL < u) { *pdest = u; } else { $? "! 0" *pback = 0; /* ERROR: Illegal line number 0 */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 6); } } else { *pback = 0; switch (er.ec) { case DK4_E_MATH_OVERFLOW : { $? "! overflow" /* ERROR: Number too large */ dk4app_log_3(app, msgs, sz_msg, DK4_LL_ERROR, 7, 8, vtxt); } break; default : { $? "! not a number" /* ERROR: Not a number */ dk4app_log_3(app, msgs, sz_msg, DK4_LL_ERROR, 9, 10, vtxt); } break; } } $? "- retrieve_number" } #if TRACE_DEBUG static void trace_type_start_end(int t, unsigned long s, unsigned long e) { $? ". type=%d start=%lu end=%lu", t, s, e } #endif /** Process line number range. @param ps Text containing start line number. @param pe Text containing end line number. @return 1 on success, 0 on error. */ static int process_line_range_items(const dkChar *ps, const dkChar *pe) { int back = 1; $? "+ process_line_range_items \"%!ds\" \"%!ds\"", TR_STR(ps), TR_STR(pe) /* Retrieve pure data from texts */ if (NULL != ps) { $? ". ps != NULL" if (NULL != pe) { $? ". ps != NULL, pe != NULL" if (dkT('-') != *ps) { $? ". ps pos" if (dkT('-') != *pe) { $? ". ps pos, pe pos" l_type = RANGE_POS_POS; retrieve_number(&l_start, ps, &back); retrieve_number(&l_end, pe, &back); if (l_start > l_end) { $? "! start > end" back = 0; /* ERROR: start line number larger than end */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 11); } } else { $? ". ps pos, pe neg" pe++; l_type = RANGE_POS_NEG; retrieve_number(&l_start, ps, &back); retrieve_number(&l_end, pe, &back); } } else { $? ". ps neg" ps++; if (dkT('-') != *pe) { $? ". ps neg, pe pos" l_type = RANGE_NEG_POS; retrieve_number(&l_start, ps, &back); retrieve_number(&l_end, pe, &back); } else { $? ". ps neg, pe neg" pe++; l_type = RANGE_NEG_NEG; retrieve_number(&l_start, ps, &back); retrieve_number(&l_end, pe, &back); if (l_end > l_start) { $? "! end > start" back = 0; /* ERROR: End line number larger than start */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 11); } } } } else { $? ". ps != NULL, pe == NULL" if (dkT('-') != *ps) { $? ". ps pos, pe none" l_type = RANGE_POS_NONE; retrieve_number(&l_start, ps, &back); } else { $? ". ps neg, pe none" ps++; l_type = RANGE_NEG_NONE; retrieve_number(&l_start, ps, &back); } } } else { $? ". ps == NULL" if (NULL != pe) { $? ". ps == NULL, pe != NULL" if (dkT('-') != *pe) { $? ". ps none, pe pos" l_type = RANGE_NONE_POS; retrieve_number(&l_end, pe, &back); } else { $? ". ps none, pe neg" pe++; l_type = RANGE_NONE_NEG; retrieve_number(&l_end, pe, &back); } } else { $? ". ps == NULL, pe == NULL" } } /* Corrections for full ranges */ if (0 != back) { $? ". t=%d s=%lu e=%lu", l_type, (unsigned long)l_start, (unsigned long)l_end $!trace-code trace_type_start_end(l_type, l_start, l_end); switch (l_type) { case RANGE_NONE_NEG : { if ((dk4_um_t)1UL == l_end) { l_type = RANGE_NONE_NONE; l_end = (dk4_um_t)0UL; $!trace-code trace_type_start_end(l_type, l_start, l_end); } } break; case RANGE_POS_NEG : { if ((dk4_um_t)1UL == l_end) { l_type = RANGE_POS_NONE; l_end = (dk4_um_t)0UL; $!trace-code trace_type_start_end(l_type, l_start, l_end); if ((dk4_um_t)1UL == l_start) { l_type = RANGE_NONE_NONE; l_start = (dk4_um_t)0UL; $!trace-code trace_type_start_end(l_type,l_start,l_end); } } else { if ((dk4_um_t)1UL == l_start) { l_type = RANGE_NONE_NEG; l_start = (dk4_um_t)0UL; $!trace-code trace_type_start_end(l_type,l_start,l_end); } } } break; case RANGE_NEG_NEG : { if ((dk4_um_t)1UL == l_end) { l_type = RANGE_NEG_NONE; l_end = (dk4_um_t)0UL; $!trace-code trace_type_start_end(l_type, l_start, l_end); } } break; case RANGE_POS_NONE : { if ((dk4_um_t)1UL == l_start) { l_start = (dk4_um_t)0UL; l_type = RANGE_NONE_NONE; $!trace-code trace_type_start_end(l_type, l_start, l_end); } } break; case RANGE_POS_POS : { if ((dk4_um_t)1UL == l_start) { l_start = (dk4_um_t)0UL; l_type = RANGE_NONE_POS; $!trace-code trace_type_start_end(l_type, l_start, l_end); } } break; default : { /* Empty by intent */ $!trace-code trace_type_start_end(l_type, l_start, l_end); } break; } } $? "- process_line_range_items %d", back return back; } /** Process line number range. @param range Text containing the line number range. */ static int process_line_range(const dkChar *range) { dkChar buf[64*sizeof(dk4_um_t)]; dkChar *p1 = NULL; dkChar *p2 = NULL; int back = 0; $? "+ process_line_range \"%!ds\"", range back = dk4str_cpy_s(buf, DK4_SIZEOF(buf,dkChar), range, NULL); if (0 != back) { p1 = dk4str_start(buf, NULL); if (NULL != p1) { $? ". p1=\"%!ds\"", p1 p2 = dk4str_chr(p1, dkT('/')); if (NULL != p2) { $? ". p2=\"%!ds\"", p2 *(p2++) = dkT('\0'); p2 = dk4str_start(p2, NULL); $? ". p2=\"%!ds\"", TR_STR(p2) p1 = dk4str_start(p1, NULL); $? ". p1=\"%!ds\"", TR_STR(p1) } $? ". p1=\"%!ds\" p2=\"%!ds\"", TR_STR(p1), TR_STR(p2) back = process_line_range_items(p1, p2); } else { $? "! empty string" /* ERROR: Empty string */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 12); back = 0; } } $? "- process_line_range %d", back return back; } /** Retrieve configuration from command line arguments. @return 1 on success, 0 on error. */ static int process_command_line_arguments(void) { dk4_time_t timer = (dk4_time_t)0UL; const dkChar *ptr = NULL; size_t sz = 0; int back = 1; int res = 0; int iec = 0; $? "+ process_command_line_arguments" /* --verbose */ if (0 != dk4app_opt_is_set_short(app, dkT('v'), NULL)) { verbose = 1; } /* --one-stream */ if (0 != dk4app_opt_is_set_short(app, dkT('o'), NULL)) { one_stream = 1; } /* --from */ if (0 != dk4app_opt_is_set_short(app, dkT('f'), NULL)) { t_from = dk4app_opt_get_string_ptr_short(app, dkT('f'), NULL); sz_t_from = dk4str_len(t_from); } /* --to */ if (0 != dk4app_opt_is_set_short(app, dkT('t'), NULL)) { t_to = dk4app_opt_get_string_ptr_short(app, dkT('t'), NULL); sz_t_to = dk4str_len(t_to); } /* --lines */ if (0 != dk4app_opt_is_set_short(app, dkT('l'), NULL)) { ptr = dk4app_opt_get_string_ptr_short(app, dkT('l'), NULL); if (NULL != ptr) { back = process_line_range(ptr); } else { $? "! missing arg for --lines" /* ERROR: Failed to retrieve line range argument */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 13); back = 0; } } /* --input-encoding */ if (0 != dk4app_opt_is_set_short(app, dkT('i'), NULL)) { ptr = dk4app_opt_get_string_ptr_short(app, dkT('i'), NULL); if (NULL != ptr) { res = dk4enc_find(&iec, NULL, ptr, NULL); if (0 != res) { eie_stdin = iec; eie_file = iec; } else { /* ERROR: Illegal input encoding */ dk4app_log_3(app, msgs, sz_msg, DK4_LL_ERROR, 14, 15, ptr); } } else { back = 0; /* ERROR: Failed to retrieve input encoding */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 16); } } /* --line-size */ if (0 != dk4app_opt_is_set_long(app, dk_lines_kwnl[3], NULL)) { if (0 != dk4app_opt_get_size_long(&sz, app, dk_lines_kwnl[3], NULL)) { if (0 < sz) { if (DK4_SIZEOF(default_input_line_buffer,dkChar) < sz) { sz_ilb = sz; } #if TRACE_DEBUG else { $? ". not larger than existing" } #endif } else { $? "! line size 0" /* ERROR: Line size 0 */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 17); back = 0; } } else { $? "! missing arg for --line-size" /* ERROR: Failed to retrieve line size */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 18); back = 0; } } /* --today */ if (0 != dk4app_opt_is_set_long(app, dk_lines_kwnl[4], NULL)) { if ((NULL == t_from) || (NULL == t_to)) { dk4time_get(&timer); res = dk4time_as_text( date_buffer, DK4_SIZEOF(date_buffer,dkChar), &timer, NULL ); if (0 != res) { date_buffer[10] = dkT('\0'); if (NULL == t_from) { t_from = date_buffer; sz_t_from = dk4str_len(t_from); } if (NULL == t_to ) { t_to = date_buffer; sz_t_to = dk4str_len(t_to); } } } else { /* ERROR: --today used with --from and --to */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 19); } } $? "- process_command_line_arguments %d", back return back; } /** Report an error with position. @param i1 Index of first message part in array. @param i2 Index of alternative first message part. @param bno Current byte number. @param lno Current line number. @param cno Current character number. @param cil Current character number within line. @param fn File name processed. */ static void report_with_position( size_t i1, size_t i2, dk4_um_t bno, dk4_um_t lno, dk4_um_t cno, dk4_um_t cil, const dkChar *fn ) { dkChar b1[8*(1+sizeof(dk4_um_t))+16]; dkChar b2[8*(1+sizeof(dk4_um_t))+16]; dkChar b3[8*(1+sizeof(dk4_um_t))+16]; const dkChar *oldlogname = NULL; dk4_um_t oldlogline = (dk4_um_t)0UL; const size_t szb3 = DK4_SIZEOF(b3,dkChar); const size_t szb2 = DK4_SIZEOF(b2,dkChar); const size_t szb1 = DK4_SIZEOF(b1,dkChar); int allbuffersok = 0; oldlogname = dk4app_get_log_source_file(app); oldlogline = dk4app_get_log_source_line(app); dk4app_set_log_source_file(app, fn); dk4app_set_log_source_line(app, lno); if (0 != dk4ma_write_decimal_unsigned(b3, szb3, cil, 0, NULL)) { if (0 != dk4ma_write_decimal_unsigned(b2, szb2, cno, 0, NULL)) { if (0 != dk4ma_write_decimal_unsigned(b1, szb1, bno, 0, NULL)) { allbuffersok = 1; } } } if (0 != allbuffersok) { dk4app_log_7( app, msgs, sz_msg, DK4_LL_ERROR, i1, 37, 38, 39, b1, b2, b3 ); } else { dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, i2); } dk4app_set_log_source_line(app, oldlogline); dk4app_set_log_source_file(app, oldlogname); } static void report_errors_from_tsp( dk4_er_t *er_en, dk4_er_t *er_pr, const dkChar *fn ) { if (NULL != er_en) { switch (er_en->ec) { case DK4_E_DECODING_FAILED : { if ((0 != verbose) || (max_r_decoding_e > num_r_decoding_e)) { report_with_position( 35, 32, er_en->dt.fpos.byteno, er_en->dt.fpos.lineno, er_en->dt.fpos.charno, er_en->dt.fpos.charinline, fn ); num_r_decoding_e++; if (num_r_decoding_e > max_r_decoding_e) { num_r_decoding_e = max_r_decoding_e; } } } break; case DK4_E_ENCODING_FAILED : { if ((0 != verbose) || (max_r_encoding_e > num_r_encoding_e)) { report_with_position( 34, 31, er_en->dt.fpos.byteno, er_en->dt.fpos.lineno, er_en->dt.fpos.charno, er_en->dt.fpos.charinline, fn ); num_r_encoding_e++; if (num_r_encoding_e > max_r_encoding_e) { num_r_encoding_e = max_r_encoding_e; } } } break; } } if (NULL != er_pr) { if (DK4_E_NONE != er_pr->ec) { if ((0 != verbose) || (max_r_processing_e > num_r_processing_e)) { report_with_position( 36, 33, er_pr->dt.fpos.byteno, er_pr->dt.fpos.lineno, er_pr->dt.fpos.charno, er_pr->dt.fpos.charinline, fn ); num_r_processing_e++; if (num_r_processing_e > max_r_processing_e) { num_r_processing_e = max_r_processing_e; } } } } } static int write_text_line(const dkChar *line, dk4_er_t *erp) { int back = DK4_TSP_RES_OK; if (0 == dk4fputs(line, stdout, erp)) { had_write_error = 1; back = DK4_TSP_RES_ERROR; } if (0 == dk4fputc(dkT('\n'), stdout, erp)) { had_write_error = 1; back = DK4_TSP_RES_ERROR; } return back; } /** Add new line to ring buffer, write line stored previously. @param line New line to add. @param erp Error report. @return DK4_TSP_RES_OK on success, DK4_TSP_RES_ERROR on error. */ static int ribu_add_and_print(const dkChar *line, dk4_er_t *erp) { dkChar *ptr; size_t sz; int back = DK4_TSP_RES_OK; $? "+ ribu_add_and_print" sz = dk4str_len(line); if ((SIZE_MAX) > sz) { if ((NULL != pribu) && (0 < sz_ribu)) { if (NULL != pribu[pos_ribu].str) { $? ". ring buffer [%u] used =", (unsigned)pos_ribu $? ". \"%!ds\"", pribu[pos_ribu].str if (0 != cycle_completed) { back = write_text_line(pribu[pos_ribu].str, erp); } if (sz > pribu[pos_ribu].buflen) { $? ". allocate new buffer" ptr = dk4str_dup_app(line, app); if (NULL != ptr) { dk4mem_free(pribu[pos_ribu].str); pribu[pos_ribu].str = ptr; pribu[pos_ribu].buflen = sz; } else { back = DK4_TSP_RES_ERROR; dk4error_set_simple_error_code( erp, DK4_E_MEMORY_ALLOCATION_FAILED ); /* ERROR: Memory allocation failed */ } } else { $? ". reuse existing buffer" (void)dk4str_cpy_s( pribu[pos_ribu].str, (1 + pribu[pos_ribu].buflen), line, NULL ); } } else { $? ". ring buffer [%u] empty", (unsigned)pos_ribu pribu[pos_ribu].str = dk4str_dup_app(line, app); pribu[pos_ribu].buflen = sz; if (NULL == pribu[pos_ribu].str) { back = DK4_TSP_RES_ERROR; dk4error_set_simple_error_code( erp, DK4_E_MEMORY_ALLOCATION_FAILED ); pribu[pos_ribu].buflen = 0; /* ERROR: Memory allocation failed */ } } $? ". ring buffer [%u] now =", (unsigned)pos_ribu $? ". \"%!ds\"", pribu[pos_ribu].str pribu[pos_ribu].lineno = lineno; pos_ribu++; if (pos_ribu >= sz_ribu) { pos_ribu = 0; cycle_completed = 1; } $? ". next position %u", (unsigned)pos_ribu } else { back = DK4_TSP_RES_ERROR; dk4error_set_simple_error_code(erp, DK4_E_BUG); /* ERROR: No ring buffer */ } } else { back = DK4_TSP_RES_ERROR; dk4error_set_simple_error_code(erp, DK4_E_MATH_OVERFLOW); /* ERROR: Line too long */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 20); } $? "- ribu_add_and_print %d", back return back; } /** Add new line to ring buffer, do not print line previously stored. @param line New line to add. @param erp Error report. @return DK4_TSP_RES_OK on success, DK4_TSP_RES_ERROR on error. */ static int ribu_add_silently(const dkChar *line, dk4_er_t *erp) { dkChar *ptr; size_t sz; int back = DK4_TSP_RES_OK; $? "+ ribu_add_silently \"%!ds\"", line sz = dk4str_len(line); if ((SIZE_MAX) > sz) { if ((NULL != pribu) && (0 < sz_ribu)) { $? ". pribu" if (NULL != pribu[pos_ribu].str) { $? ". have existing string" $? ". ring buffer [%u] old =", (unsigned)pos_ribu $? ". \"%!ds\"", pribu[pos_ribu].str $? ". buf lgt = %lu", (unsigned long)(pribu[pos_ribu].buflen) $? ". line = \"%!ds\"", line if (sz > pribu[pos_ribu].buflen) { $? ". need new string" ptr = dk4str_dup_app(line, app); if (NULL != ptr) { $? ". memory allocated" dk4mem_free(pribu[pos_ribu].str); pribu[pos_ribu].str = ptr; pribu[pos_ribu].buflen = sz; } else { $? "! memory" dk4mem_release(pribu[pos_ribu].str); pribu[pos_ribu].buflen = 0; pribu[pos_ribu].lineno = (dk4_um_t)0UL; back = DK4_TSP_RES_ERROR; dk4error_set_simple_error_code( erp, DK4_E_MEMORY_ALLOCATION_FAILED ); /* ERROR: Memory allocation failed */ } } else { $? ". use existing buffer" (void)dk4str_cpy_s( pribu[pos_ribu].str, (1 + pribu[pos_ribu].buflen), line, NULL ); } } else { $? ". no existing string" pribu[pos_ribu].str = dk4str_dup_app(line, app); pribu[pos_ribu].buflen = sz; if (NULL == pribu[pos_ribu].str) { $? "! memory" back = DK4_TSP_RES_ERROR; dk4error_set_simple_error_code( erp, DK4_E_MEMORY_ALLOCATION_FAILED ); pribu[pos_ribu].buflen = 0; /* ERROR: Memory allocation failed */ } } $? ". ring buffer [%u] now =", (unsigned)pos_ribu $? ". \"%!ds\"", pribu[pos_ribu].str pribu[pos_ribu].lineno = lineno; pos_ribu++; if (pos_ribu >= sz_ribu) { pos_ribu = 0; } } else { $? "! pribu" back = DK4_TSP_RES_ERROR; dk4error_set_simple_error_code(erp, DK4_E_BUG); /* ERROR: No ring buffer */ } } else { back = DK4_TSP_RES_ERROR; dk4error_set_simple_error_code(erp, DK4_E_MATH_OVERFLOW); /* ERROR: Line too long */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 20); } $? "- ribu_add_silently %d", back return back; } static int dk_lines_line_handler( void *DK4_ARG_UNUSED(obj), dkChar *line, dk4_um_t xlineno, dk4_er_t *erp ) { dk4_er_t er; size_t sz = 0; int back = DK4_TSP_RES_OK; int ok = 1; DK4_UNUSED_ARG(obj) $? "+ dk_lines_line_handler" dk4app_set_log_source_line(app, xlineno); dk4str_delnl(line); $? ". line = \"%!ds\"", line if ((NULL != t_from) && (0 < sz_t_from)) { #if VERSION_BEFORE_2017_11_20 sz = dk4str_len(line); if (sz < sz_t_from) { ok = 0; } else { if (0 > dk4str_ncmp(line, t_from, sz_t_from)) { ok = 0; } } #endif if (0 > dk4str_cmp(line, t_from)) { ok = 0; } } if ((0 != ok) && (NULL != t_to) && (0 < sz_t_to)) { #if VERSION_BEFORE_2017_11_20 if (0 == sz) { sz = dk4str_len(line); } if (sz < sz_t_to) { if (0 < dk4str_ncmp(line, t_to, sz)) { ok = 0; } } else { if (0 < dk4str_ncmp(line, t_to, sz_t_to)) { ok = 0; } } #endif sz = dk4str_len(line); if (sz < sz_t_to) { if (0 < dk4str_cmp(line, t_to)) { ok = 0; } } else { if (0 < dk4str_ncmp(line, t_to, sz_t_to)) { ok = 0; } } } if (0 != ok) { dk4error_init(&er); lineno = dk4ma_um_add(lineno, (dk4_um_t)1UL, &er); if (DK4_E_NONE == er.ec) { $? ". adding line %lu \"%!ds\"", (unsigned long)lineno, line switch (l_type) { case RANGE_NONE_NONE : { back = write_text_line(line, erp); } break; case RANGE_NONE_POS : { if (lineno <= l_end) { back = write_text_line(line, erp); } } break; case RANGE_NONE_NEG : { back = ribu_add_and_print(line, erp); } break; case RANGE_POS_NONE : { if (l_start <= lineno) { back = write_text_line(line, erp); } } break; case RANGE_POS_POS : { if ((l_start <= lineno) && (lineno <= l_end)) { back = write_text_line(line, erp); } } break; case RANGE_POS_NEG : { if (l_start <= lineno) { back = ribu_add_and_print(line, erp); } } break; case RANGE_NEG_NONE : { back = ribu_add_silently(line, erp); } break; case RANGE_NEG_POS : { back = ribu_add_silently(line, erp); } break; case RANGE_NEG_NEG : { back = ribu_add_silently(line, erp); } break; } } else { back = DK4_TSP_RES_ERROR; dk4error_copy(erp, &er); /* ERROR: Overflow in line number */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 21); } } $? "- dk_lines_line_handler %d", back return back; } /** Flush the ring buffer after finishing file or stream. */ static void flush_ring_buffer(void) { dk4_er_t er; dk4_um_t umax; size_t i; size_t idx; size_t max; switch (l_type) { case RANGE_NONE_NONE : { /* Empty by intent, ring buffer not used */ } break; case RANGE_NONE_POS : { /* Empty by intent, ring buffer not used */ } break; case RANGE_NONE_NEG : case RANGE_POS_NEG : { /* Empty by intent, lines of interest already printed */ } break; case RANGE_POS_NONE : { /* Empty by intent, ring buffer not used */ } break; case RANGE_POS_POS : { /* Empty by intent, ring buffer not used */ } break; case RANGE_NEG_NONE : { idx = pos_ribu; for (i = 0; i < sz_ribu; i++) { $? ". loop pass %u / %u", (unsigned)i, (unsigned)sz_ribu if (NULL != pribu[idx].str) { $? ". have string" $? ". line number %lu", (unsigned long)(pribu[idx].lineno) if (0 == dk4fputs(pribu[idx].str, stdout, NULL)) { had_write_error = 1; } if (0 == dk4fputc(dkT('\n'), stdout, NULL)) { had_write_error = 1; } } #if TRACE_DEBUG else { $? ". no string" } #endif idx++; if (idx >= sz_ribu) { idx = 0; } } } break; case RANGE_NEG_POS : { idx = pos_ribu; for (i = 0; i < sz_ribu; i++) { if ((NULL != pribu[idx].str) && (pribu[idx].lineno <= l_end)) { if (0 == dk4fputs(pribu[idx].str, stdout, NULL)) { had_write_error = 1; } if (0 == dk4fputc(dkT('\n'), stdout, NULL)) { had_write_error = 1; } } idx++; if (idx >= sz_ribu) { idx = 0; } } } break; case RANGE_NEG_NEG : { dk4error_init(&er); umax = dk4ma_um_add( dk4ma_um_sub(l_start, l_end, &er), (dk4_um_t)1UL, &er ); if (DK4_E_NONE == er.ec) { if ((dk4_um_t)(SIZE_MAX) >= umax) { max = (size_t)umax; idx = pos_ribu; for (i = 0; i < max; i++) { if (NULL != pribu[idx].str) { if (0 == dk4fputs(pribu[idx].str, stdout, NULL)) { had_write_error = 1; } if (0 == dk4fputc(dkT('\n'), stdout, NULL)) { had_write_error = 1; } } idx++; if (idx >= sz_ribu) { idx = 0; } } } else { exval = EXIT_FAILURE; /* ERROR: Overflow in line range calculation */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 22); } } else { exval = EXIT_FAILURE; /* ERROR: Overflow in line range calculation */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 22); } } break; } } /** Process file contents. @param fipo File, opened for binary read access. @param fn File name (for diagnostics). @param eie Expected input encoding. */ static void run_for_file_with_name(FILE *fipo, const dkChar *fn, int eie) { dk4_tspdk_t tsp; /* Text stream processor */ dk4_er_t er_en; /* Error report for enc/decoding */ dk4_er_t er_pr; /* Error report for processing */ dk4_er_t er; /* General error report */ const dkChar *oldlogname = NULL; /* Old source file */ dk4_um_t oldlogline = 0UL; /* Old source line */ size_t i; /* Traverse ring buffer elements */ int c; /* Current character retrieved */ int res; /* Operation result */ int cc; /* Flag: Can continue */ unsigned char uc; /* Byte added to processor */ $? "+ run_for_file_with_name" oldlogname = dk4app_get_log_source_file(app); oldlogline = dk4app_get_log_source_line(app); dk4app_set_log_source_file(app, fn); dk4app_set_log_source_line(app, (dk4_um_t)0UL); /* Initialize handling of one file */ if (0 == one_stream) { lineno = (dk4_um_t)0UL; pos_ribu = 0; if (NULL != pribu) { for (i = 0; i < sz_ribu; i++) { pribu[i].str = NULL; pribu[i].lineno = (dk4_um_t)0UL; pribu[i].buflen = 0; } } } /* Process file contents in loop */ dk4error_init(&er_en); dk4error_init(&er_pr); dk4error_init(&er); res = dk4tspdk_setup_line( &tsp, NULL, dk_lines_line_handler, ilb, sz_ilb, dk4app_get_encoding(app), eie, &er ); if (0 != res) { cc = 1; while(1 == cc) { cc = -1; if (0 != sig_can_continue(1)) { c = fgetc(fipo); if (EOF != c) { cc = 1; uc = (unsigned char)c; switch (dk4tspdk_add_one_byte(&tsp, uc)) { case DK4_TSP_RES_FATAL : { cc = -1; exval = EXIT_FAILURE; dk4tspdk_get_errors(&er_en, &er_pr, &tsp); report_errors_from_tsp(&er_en, &er_pr, fn); } break; case DK4_TSP_RES_ERROR : { exval = EXIT_FAILURE; dk4tspdk_get_errors(&er_en, &er_pr, &tsp); report_errors_from_tsp(&er_en, &er_pr, fn); } break; } } else { cc = 0; } } } /* Flush data from text processor */ if (0 == cc) { switch (dk4tspdk_finish(&tsp)) { case DK4_TSP_RES_FATAL : case DK4_TSP_RES_ERROR : { exval = EXIT_FAILURE; dk4tspdk_get_errors(&er_en, &er_pr, &tsp); report_errors_from_tsp(&er_en, &er_pr, fn); } break; } } } dk4app_set_log_source_line(app, (dk4_um_t)0UL); /* Flush data from ring buffer */ if (0 == one_stream) { flush_ring_buffer(); } /* Clean up resources */ if (0 == one_stream) { cycle_completed = 0; if (NULL != pribu) { for (i = 0; i < sz_ribu; i++) { dk4mem_release(pribu[i].str); pribu[i].lineno = (dk4_um_t)0UL; pribu[i].buflen = 0; } } } dk4app_set_log_source_line(app, oldlogline); dk4app_set_log_source_file(app, oldlogname); $? "- run_for_file_with_name" } /** Open and process a file. @param fn File name to process. */ static void run_for_filename(const dkChar *fn) { const dkChar *oldlogname = NULL; FILE *fipo = NULL; dk4_um_t oldlogline = (dk4_um_t)0UL; int tests = DK4_FOPEN_SC_USER; $? "+ run_for_filename \"%!ds\"", fn oldlogname = dk4app_get_log_source_file(app); oldlogline = dk4app_get_log_source_line(app); dk4app_set_log_source_file(app, fn); dk4app_set_log_source_line(app, (dk4_um_t)0UL); if (dk4isadmin()) { tests = DK4_FOPEN_SC_PRIVILEGED; } $? ". going to open \"%!ds\" \"%!ds\"", fn, dk_lines_kwnl[6] fipo = dk4fopen_app(fn, dk_lines_kwnl[6], tests, app); if (NULL != fipo) { run_for_file_with_name(fipo, fn, eie_file); fclose(fipo); } else { exval = EXIT_FAILURE; } dk4app_set_log_source_line(app, oldlogline); dk4app_set_log_source_file(app, oldlogname); $? "- run_for_filename" } #if DK4_ON_WINDOWS static void expand_filename_and_run(const dkChar *pattern) { dkChar buf[DK4_MAX_PATH]; dk4_dir_t *fne; const dkChar *shf; const dkChar *pth; const size_t szbuf = DK4_SIZEOF(buf,dkChar); int any_file_found = 0; int cc; int ok; $? "+ expand_filename_and_run \"%!ds\"", pattern fne = dk4app_fne_open(pattern, app); if (NULL != fne) { pth = dk4dir_get_path(fne); cc = 1; while (1 == cc) { if (0 != sig_can_continue(1)) { shf = dk4dir_next_file(fne); if (NULL != shf) { $? ". file name \"%!ds\"", shf any_file_found = 1; if (NULL != pth) { $? ". must constract full path" ok = 1; if (0 == dk4str_cpy_s(buf, szbuf, pth, NULL)) { ok = 0; } if(0 != dk4str_cat_s(buf, szbuf, fnsep, NULL)) { ok = 0; } if(0 != dk4str_cat_s(buf, szbuf, shf, NULL)) { ok = 0; } if (1 == ok) { $? ". process \"%!ds\"", buf run_for_filename(buf); } else { $? ". path too long" /* ERROR: Path too long */ exval = EXIT_FAILURE; dk4app_log_base3(app,DK4_LL_ERROR,100,105,pattern); } } else { $? ". run for short file name" run_for_filename(shf); } } else { cc = 0; } } else { cc = -1; } } dk4dir_close(fne); if (0 == any_file_found) { exval = EXIT_FAILURE; dk4app_log_base3(app, DK4_LL_ERROR, 100, 107, pattern); } } else { $? "! failed to open fne" exval = EXIT_FAILURE; /* ERROR: Failed to open file name expander (already reported) */ } $? "- expand_filename_and_run" } #endif /** Allocate ring buffer used for negative line numbers. @return 1 on success, 0 on error. */ static int allocate_ring_buffer(void) { size_t i; int back = 1; $? "+ allocate_ring_buffer" switch (l_type) { case RANGE_NONE_NEG : case RANGE_POS_NEG : case RANGE_NEG_NONE : case RANGE_NEG_POS : case RANGE_NEG_NEG : { $? ". need ring buffer" back = 0; switch (l_type) { case RANGE_NONE_NEG : case RANGE_POS_NEG : { if ((dk4_um_t)(SIZE_MAX) >= l_end) { if ((dk4_um_t)1UL < l_end) { sz_ribu = (size_t)(l_end - (dk4_um_t)1UL); } else { $? "! BUG l_end == 1" /* BUG: l_end is 1, should not happen */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 23); } } else { $? "! numeric overflow" /* ERROR: Numeric overflow */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 24); } } break; default : { if ((dk4_um_t)(SIZE_MAX) >= l_start) { sz_ribu = (size_t)l_start; } else { $? "! numeric overflow" /* ERROR: Numeric overflow */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 25); } } break; } if (0 < sz_ribu) { $? ". size %lu", (unsigned long)sz_ribu pribu = dk4mem_new_app(rbe_t,sz_ribu,app); if (NULL != pribu) { $? ". buffer allocated" back = 1; for (i = 0; i < sz_ribu; i++) { pribu[i].str = NULL; pribu[i].lineno = (dk4_um_t)0UL; pribu[i].buflen = 0; } } else { $? "! allocation failed" /* ERROR: Memory allocation failed (already reported) */ } } else { $? "! invalid size 0" /* Invalid ring buffer size */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 26); } } break; default : { $? ". no ring buffer needed" /* Empty by intent */ } break; } $? "- allocate_ring_buffer %d", back return back; } /** Allocate ring buffer if necessary and process input. */ static void run_for_input(void) { const dkChar *fnptr; size_t xi; int argc; int i; $? "+ run_for_input" if (0 != allocate_ring_buffer()) { /* Initialize handling of one large data stream */ if (0 != one_stream) { lineno = (dk4_um_t)0UL; pos_ribu = 0; if (NULL != pribu) { for (xi = 0; xi < sz_ribu; xi++) { pribu[xi].str = NULL; pribu[xi].lineno = (dk4_um_t)0UL; pribu[xi].buflen = 0; } } } argc = dk4app_get_argc(app); if (0 < argc) { $? ". have file names" for (i = 0; ((i < argc) && (0 != sig_can_continue(1))); i++) { fnptr = dk4app_get_argv(app, i); if (NULL != fnptr) { $? ". file name \"%!ds\"", fnptr #if DK4_ON_WINDOWS if (0 != dk4path_must_expand(fnptr)) { expand_filename_and_run(fnptr); } else { #endif run_for_filename(fnptr); #if DK4_ON_WINDOWS } #endif } else { $? "! failed to obtain file name" exval = EXIT_FAILURE; /* ERROR: Did not obtain file name */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 27); } } } else { $? ". no file names specified" run_for_file_with_name(stdin, dk_lines_kwnl[7], eie_stdin); } /* Flush ring buffer and clean up resources for large data stream */ if (0 != one_stream) { /* Flush ring buffer */ flush_ring_buffer(); /* Release ring buffer resources */ cycle_completed = 0; if (NULL != pribu) { for (xi = 0; xi < sz_ribu; xi++) { dk4mem_release(pribu[xi].str); pribu[xi].lineno = (dk4_um_t)0UL; pribu[xi].buflen = 0; } } } } #if TRACE_DEBUG else { $? "! failed to allocate ring buf" } #endif if (NULL != pribu) { dk4mem_free(pribu); } $? "- run_for_input" } /** Run the functionality. */ static void run_the_functions(void) { dkChar *mybuf = NULL; $? "+ run_the_functions" $? ". running" if (0 != process_command_line_arguments()) { /* If a line buffer larger than default is required, allocate the buffer and run */ if (DK4_SIZEOF(default_input_line_buffer,dkChar) < sz_ilb) { mybuf = dk4mem_new_app(dkChar,sz_ilb,app); if (NULL != mybuf) { ilb = mybuf; run_for_input(); dk4mem_free(mybuf); } else { $? "! memory allocation failed" exval = EXIT_FAILURE; } } /* Otherwise run with default buffer */ else { run_for_input(); } } else { exval = EXIT_FAILURE; } $? "- run_the_functions" } #if DK4_HAVE_SIGACTION /** Set signal handlers and run. */ static void run_with_signals(void) { #ifdef SIGPIPE struct sigaction opipe; #endif struct sigaction oint; struct sigaction oterm; #ifdef SIGPIPE struct sigaction npipe; #endif struct sigaction nint; 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)) { $? "! sigemptyset SIGPIPE" /* ERROR: Failed to set up masked signal set for SIGPIPE */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 0); goto finished; } if (0 != sigaddset(&npipe.sa_mask, SIGPIPE)) { $? "! sigaddset SIGPIPE" /* ERROR: Failed to set up masked signal set for SIGPIPE */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 0); goto finished; } if (0 != sigaction(SIGPIPE, &npipe, &opipe)) { $? "! sigaction SIGPIPE" /* ERROR: Failed to set up signal handler for SIGPIPE */ dk4app_log_1(app, msgs, 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)) { $? "! sigemptyset SIGINT" /* ERROR: Failed to set up masked signal set for SIGINT */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 1); goto restore_old_pipe; } if (0 != sigaddset(&nint.sa_mask, SIGINT)) { $? "! sigaddset SIGINT" /* ERROR: Failed to set up masked signal set for SIGINT */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 1); goto restore_old_pipe; } if (0 != sigaction(SIGINT, &nint, &oint)) { $? "! sigaction SIGINT" /* ERROR: Failed to set up signal handler for SIGINT */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 1); 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)) { $? "! sigemptyset SIGTERM" /* ERROR: Failed to set up masked signal set for SIGTERM */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 2); goto restore_old_int; } if (0 != sigaddset(&nterm.sa_mask, SIGTERM)) { $? "! sigaddset SIGTERM" /* ERROR: Failed to set up masked signal set for SIGTERM */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 2); goto restore_old_int; } if (0 != sigaction(SIGTERM, &nterm, &oterm)) { $? "! sigaction SIGTERM" /* ERROR: Failed to set up signal handler for SIGTERM */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 2); goto restore_old_int; } success = 1; run_the_functions(); /* Restore signal handling for SIGTERM. */ if (0 != sigaction(SIGTERM, &oterm, NULL)) { $? "! sigaction SIGTERM" /* ERROR: Failed to restore old SIGTERM settings */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 3); success = 0; } /* Restore signal handling for SIGINT. */ restore_old_int: if (0 != sigaction(SIGINT, &oint, NULL)) { $? "! sigaction SIGINT" /* ERROR: Failed to restore old SIGINT settings */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 4); success = 0; } #ifdef SIGPIPE /* Restore signal handling for SIGPIPE. */ restore_old_pipe: if (0 != sigaction(SIGPIPE, &opipe, NULL)) { $? "! sigaction SIGPIPE" /* ERROR: Failed to restore old SIGPIPE settings */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 5); success = 0; } #endif /* Set exit status code if error occured. */ finished: if (0 == success) { $? "! failure ins signal handling" exval = EXIT_FAILURE; } } #else #if DK4_HAVE_SIGSET /** Set signal handlers and run. */ static void run_with_signals(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); run_the_functions(); sigset(SIGTERM, oldterm); sigset(SIGINT, oldint); #ifdef SIGPIPE sigset(SIGPIPE, oldpipe); #endif } #else #if DK4_HAVE_SIGNAL /** Set signal handlers and run. */ static void run_with_signals(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); run_the_functions(); signal(SIGTERM, oldterm); signal(SIGINT, oldint); #ifdef SIGPIPE signal(SIGPIPE, oldpipe); #endif } #else /** Set signal handlers and run. */ static void run_with_signals(void) { run_the_functions(); } #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 { dk4_sig_atomic_t shp; $!trace-init dk-lines.deb $? "+ main" dk4fput_initialize_stdout(); dk4fput_initialize_stderr(); app = dk4app_open_cmd( argc, argv, dk_lines_options, dk_lines_sz_options, dk_lines_kwnl[0], DKT_VERSION_DK, dk_lines_kwnl[1], dk_lines_help_text, dk_lines_license_text ); if (NULL != app) { sz_msg = dk4app_string_table_size(dk_lines_kw_def); msgs = dk4app_string_table(app, dk_lines_kwnl[2], dk_lines_kw_def); eie_file = dk4app_get_file_in_encoding(app); eie_stdin = dk4app_get_stdin_encoding(app); if (0 != dk4app_can_run_normally(app)) { exval = EXIT_SUCCESS; run_with_signals(); #ifdef SIGPIPE shp = sig_read_atomic(&sig_had_pipe); #else shp = 0; #endif if (0 != shp) { if (0 != verbose) { $? "! stopped by SIGPIPE" /* ERROR: Stopped by SIGPIPE */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 28); } exval = EXIT_FAILURE; } else { if (0 != had_write_error) { $? "! write error occured" /* ERROR: Had write error */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 29); exval = EXIT_FAILURE; } } if (0 != sig_read_atomic(&sig_had_int)) { $? "! stopped by SIGINT" /* ERROR: Stopped by interrupt */ dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 30); } } else { if (0 != dk4app_help_version_license(app)) { exval = EXIT_SUCCESS; } } dk4app_close(app); } fflush(stdout); fflush(stderr); dk4fput_cleanup_stderr(); dk4fput_cleanup_stdout(); $? "- main %d", exval $!trace-end exit(exval); return exval; } /* vim: set ai sw=4 ts=4 : */