1%%	options
2
3copyright owner	=	Dirk Krause
4copyright year	=	2017-xxxx
5SPDX-License-Identifier:	BSD-3-Clause
6
7
8%%	module
9
10/**	@file	dk-lines.c	The dk-lines program.
11	The dk_lines_help_text array below shows details.
12
13*/
14
15#ifndef	DK4CONF_H_INCLUDED
16#include "dk4conf.h"
17#endif
18
19#include <stdio.h>
20
21#if	DK4_HAVE_STDLIB_H
22#ifndef	STDLIB_H_INCLUDED
23#include <stdlib.h>
24#define	STDLIB_H_INCLUDED 1
25#endif
26#endif
27
28#if DK4_HAVE_UNISTD_H
29#ifndef UNISTD_H_INCLUDED
30#include <unistd.h>
31#define	UNISTD_H_INCLUDED 1
32#endif
33#endif
34
35#if DK4_HAVE_PROCESS_H
36#ifndef PROCESS_H_INCLUDED
37#include <process.h>
38#define	PROCESS_H_INCLUDED 1
39#endif
40#endif
41
42#if DK4_HAVE_IO_H
43#ifndef IO_H_INCLUDED
44#include <io.h>
45#define IO_H_INCLUDED 1
46#endif
47#endif
48
49#if DK4_HAVE_FCNTL_H
50#ifndef FCNTL_H_INCLUDED
51#include <fcntl.h>
52#define FCNTL_H_INCLUDED 1
53#endif
54#endif
55
56#if DK4_HAVE_SYS_TYPES_H
57#ifndef SYS_TYPES_H_INCLUDED
58#include <sys/types.h>
59#define SYS_TYPES_H_INCLUDED 1
60#endif
61#endif
62
63#if DK4_HAVE_SIGNAL_H
64#ifndef SIGNAL_H_INCLUDED
65#include <signal.h>
66#define SIGNAL_H_INCLUDED 1
67#endif
68#endif
69
70#ifndef	DK4UNUSED_H_INCLUDED
71#include <libdk4base/dk4unused.h>
72#endif
73
74#ifndef	DK4TYPES_H_INCLUDED
75#include <libdk4base/dk4types.h>
76#endif
77
78#ifndef	DK4MEM_H_INCLUDED
79#include <libdk4base/dk4mem.h>
80#endif
81
82#ifndef	DK4ENC_H_INCLUDED
83#include <libdk4c/dk4enc.h>
84#endif
85
86#ifndef	DK4STRD_H_INCLUDED
87#include <libdk4base/dk4strd.h>
88#endif
89
90#ifndef	DK4STRDA_H_INCLUDED
91#include <libdk4app/dk4strda.h>
92#endif
93
94#ifndef	DK4DIR_H_INCLUDED
95#include <libdk4c/dk4dir.h>
96#endif
97
98#ifndef	DK4PATHD_H_INCLUDED
99#include <libdk4c/dk4pathd.h>
100#endif
101
102#ifndef	DK4APP_H_INCLUDED
103#include <libdk4app/dk4app.h>
104#endif
105
106#ifndef	DK4AOPT_H_INCLUDED
107#include <libdk4app/dk4aopt.h>
108#endif
109
110#ifndef	DK4MEMA_H_INCLUDED
111#include <libdk4app/dk4mema.h>
112#endif
113
114#ifndef	DK4VERS_H_INCLUDED
115#include <libdk4base/dk4vers.h>
116#endif
117
118#ifndef	DK4MAADU_H_INCLUDED
119#include <libdk4ma/dk4maadu.h>
120#endif
121
122#ifndef	DK4MAIDDDU_H_INCLUDED
123#include <libdk4maiodd/dk4maidddu.h>
124#endif
125
126#ifndef	DK4MAODD_H_INCLUDED
127#include <libdk4maiodd/dk4maodd.h>
128#endif
129
130#ifndef	DK4FPUT_H_INCLUDED
131#include <libdk4c/dk4fput.h>
132#endif
133
134#ifndef	DK4TSPDK_H_INCLUDED
135#include <libdk4c/dk4tspdk.h>
136#endif
137
138#ifndef	DK4FOPD_H_INCLUDED
139#include <libdk4c/dk4fopd.h>
140#endif
141
142#ifndef	DK4FOPDA_H_INCLUDED
143#include <libdk4app/dk4fopda.h>
144#endif
145
146#ifndef	DK4ISADM_H_INCLUDED
147#include <libdk4c/dk4isadm.h>
148#endif
149
150#ifndef	DK4TIME_H_INCLUDED
151#include <libdk4c/dk4time.h>
152#endif
153
154#ifndef	DK4TIMEDK_H_INCLUDED
155#include <libdk4c/dk4timedk.h>
156#endif
157
158#ifndef	DK4MPL_H_INCLUDED
159#include <libdk4base/dk4mpl.h>
160#endif
161
162#ifndef	DK4WMAIN_H_INCLUDED
163#include <libdk4base/dk4wmain.h>
164#endif
165
166
167$!trace-include
168
169
170
171#ifndef	DK_LINES_BUFFER_SIZE
172/**	Text buffer size.
173*/
174#define	DK_LINES_BUFFER_SIZE	4096
175#endif
176
177
178
179/**	Ring buffer element.
180*/
181typedef struct {
182	dkChar		*str;		/**< Line text. */
183	dk4_um_t	 lineno;	/**< Line number. */
184	size_t		 buflen;	/**< Length of str buffer (number of dkChar). */
185} rbe_t;
186
187
188
189/**	Range specification types.
190*/
191enum {
192	RANGE_NONE_NONE		= 0 ,		/**< No ranges specified. */
193	RANGE_NONE_POS ,				/**< No start, positive end. */
194	RANGE_NONE_NEG ,				/**< No start, negative end. */
195	RANGE_POS_NONE ,				/**< Positive start, no end. */
196	RANGE_POS_POS ,					/**< Positive start, positive end. */
197	RANGE_POS_NEG ,					/**< Positive start, negative end. */
198	RANGE_NEG_NONE ,				/**< Negative start, no end. */
199	RANGE_NEG_POS ,					/**< Negative start, positive end. */
200	RANGE_NEG_NEG ,					/**< Negative start, negative end. */
201};
202
203
204
205/**	Input line buffer.
206*/
207static dkChar	default_input_line_buffer[DK_LINES_BUFFER_SIZE];
208
209
210
211/**	Buffer to store current date and time.
212*/
213static dkChar	date_buffer[64];
214
215
216
217/**	Default text, used if help text file is not found.
218*/
219static const dkChar * const	dk_lines_help_text[] = {
220$!text	macro=dkT,preprocessor
221
222Select lines from text
223
224dk-lines [<options>] [<files>]
225
226Options:
227-i encoding Expected input encoding, one from: plain, ascii, ansi, utf-8,
228            utf-16, utf-16le, utf-16be, c32, c32le, c32be.
229-f string   Show lines lexicographically equal or larger than string.
230-t string   Show lines lexicographically equal or smaller than string.
231-l spec     Specification of line numbers in format <start>/<end> (inclusive)
232            to pass through, applied after -f and -t processing. Examples:
233            5 (5 and following), 5/8 (5, 6, 7, 8), /8 (1...8), 5/-3 (5 and
234            following except final 2 lines), -5 (final 5 lines), -5/-3 (3 lines
235            before the final 2 lines), /-3 (all lines except the final 2),
236            5/-3 (5 and following except final 2 lines),
237            -5/8 (final 5 lines but not above line 8).
238-v          Verbose mode: Error message on SIGPIPE.
239--help      Show this short help text.  --manual   *** SHOW FULL MANUAL. ***
240--version   Show version information.   --license   Show license information.
241
242http://sourceforge.net/p/dktools/wiki/dk-lines/
243
244$!end
245};
246
247
248
249/**	License conditions.
250*/
251static const dkChar * const	dk_lines_license_text[] = {
252$!text	macro=dkT,preprocessor
253
254This software uses code from the following projects, either directly or as
255a library:
256
257dktools		Dirk Krause's tools and libraries.
258		See http://sourceforge.net/p/dktools/wiki/Home/
259		for more information.
260#if DK4_HAVE_ZLIB_H
261
262zlib		Data compression library.
263		See http://www.zlib.net/ for more information.
264#endif
265#if DK4_HAVE_BZLIB_H
266
267bzip2		Data compression program and library.
268		See http://www.bzip.org/ for more information.
269#endif
270
271All the licenses below apply to the program.
272Licenses for used libraries are shown as found on my Scientific Linux 6.x
273computer in the /usr/share/doc directory on 2015-04-01. Check the project
274homepages of the used libraries for additional information and/or updated
275license terms.
276
277
278DK tools and libraries license
279==============================
280Copyright (c) 2015-2016, Dirk Krause
281All rights reserved.
282
283Redistribution and use in source and binary forms, with or without
284modification, are permitted provided that the following conditions are met:
285
2861. Redistributions of source code must retain the above copyright notice,
287   this list of conditions and the following disclaimer.
2882. Redistributions in binary form must reproduce the above copyright
289   notice, this list of conditions and the following disclaimer in the
290   documentation and/or other materials provided with the distribution.
2913. Neither the name of the copyright holder nor the names of its
292   contributors may be used to endorse or promote products derived from
293   this software without specific prior written permission.
294
295THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
296``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
297LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
298A PARTICULAR PURPOSE ARE DISCLAIMED.
299IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
300DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
301(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
302SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
303CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
304LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
305OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
306SUCH DAMAGE.
307#if DK4_HAVE_ZLIB_H
308
309
310Zlib license
311============
312(C) 1995-2004 Jean-loup Gailly and Mark Adler
313
314This software is provided 'as-is', without any express or implied
315warranty.  In no event will the authors be held liable for any damages
316arising from the use of this software.
317
318Permission is granted to anyone to use this software for any purpose,
319including commercial applications, and to alter it and redistribute it
320freely, subject to the following restrictions:
321
3221. The origin of this software must not be misrepresented; you must not
323   claim that you wrote the original software. If you use this software
324   in a product, an acknowledgment in the product documentation would be
325   appreciated but is not required.
3262. Altered source versions must be plainly marked as such, and must not be
327   misrepresented as being the original software.
3283. This notice may not be removed or altered from any source distribution.
329
330Jean-loup Gailly        Mark Adler
331jloup@gzip.org          madler@alumni.caltech.edu
332#endif
333#if DK4_HAVE_BZLIB_H
334
335
336Bzip2 and libbzip2 library license
337==================================
338This program, "bzip2", the associated library "libbzip2", and all
339documentation, are copyright (C) 1996-2007 Julian R Seward.  All
340rights reserved.
341
342Redistribution and use in source and binary forms, with or without
343modification, are permitted provided that the following conditions
344are met:
345
3461. Redistributions of source code must retain the above copyright
347   notice, this list of conditions and the following disclaimer.
348
3492. The origin of this software must not be misrepresented; you must
350   not claim that you wrote the original software.  If you use this
351   software in a product, an acknowledgment in the product
352   documentation would be appreciated but is not required.
353
3543. Altered source versions must be plainly marked as such, and must
355   not be misrepresented as being the original software.
356
3574. The name of the author may not be used to endorse or promote
358   products derived from this software without specific prior written
359   permission.
360
361THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
362OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
363WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
364ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
365DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
366DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
367GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
368INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
369WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
370NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
371SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
372
373Julian Seward, jseward@bzip.org
374bzip2/libbzip2 version 1.0.5 of 10 December 2007
375#endif
376
377$!end
378};
379
380
381
382/**	Constant texts used by program, not localized.
383*/
384static const dkChar * const	dk_lines_kwnl[] = {
385$!string-table	macro=dkT
386#
387#	0			Program group name
388#
389dktools
390#
391#	1			Help text file name
392#
393dk-lines.txt
394#
395#	2			String table file
396#
397dk-lines.str
398#
399#	3			Long option for line size
400#
401line-size
402#
403#	4			Long option to use current date
404#
405today
406#
407#	5			File name separator
408#
409/
410#
411#	6			File open mode
412#
413rb
414#
415#	7			File name when processing stdin
416#
417<stdin>
418#
419#
420#
421$!end
422};
423
424
425
426/**	Constant texts used by program, replaced by localized texts
427	if available.
428*/
429static const dkChar * const     dk_lines_kw_def[] = {
430$!string-table	macro=dkT
431#
432#	0		ERROR: Failed to set up masked signal set for SIGPIPE
433#
434Failed to set up masked signal set for SIGPIPE!
435#
436#	1		ERROR: Failed to set up masked signal set for SIGINT
437#
438Failed to set up masked signal set for SIGINT!
439#
440#	2		ERROR: Failed to set up masked signal set for SIGTERM
441#
442Failed to set up masked signal set for SIGTERM!
443#
444#	3		ERROR: Failed to restore SIGTERM settings!
445#
446Failed to restore SIGTERM settings!
447#
448#	4		ERROR: Failed to restore SIGINT settings
449#
450Failed to restore SIGTERM settings!
451#
452#	5		ERROR: Failed to restore SIGPIPE settings
453#
454Failed to restore SIGPIPE settings!
455#
456#	6		ERROR: Illegal line number 0!
457#
458Illegal line number 0!
459#
460#	7	8	ERROR: Number ... too large!
461#
462Number too large: "
463"!
464#
465#	9	10	ERROR: Not a number: ...!
466#
467Not a number: "
468"!
469#
470#	11		ERROR: Start line number larger than end number!
471#
472Start line number larger than end number!
473#
474#	12		ERROR: Invalid line range specification (empty string)!
475#
476Invalid line range specification (empty string)!
477#
478#	13		ERROR: Missing line range specification!
479#
480Missing line range specification!
481#
482#	14	15	ERROR: Illegal input encoding!
483#
484Illegal input encoding: "
485"!
486#
487#	16		ERROR: Missing input encoding!
488#
489Missing input encoding!
490#
491#	17		ERROR: Invalid line size 0!
492#
493Invalid line size 0!
494#
495#	18		ERROR: Missing line size specification!
496#
497Missing line size specification!
498#
499#	19		ERROR: Can not use --today with both --from and --to!
500#
501Can not use --today with both --from and --to!
502#
503#	20		ERROR: Input line too long!
504#
505Input line too long!
506#
507#	21		ERROR: Overflow in line number!
508#
509Too many lines, overflow in line number!
510#
511#	22		ERROR: Overflow in line range calculation!
512#
513Overflow in line range calculation!
514#
515#	23		ERROR: End line number is 1 (bug)!
516#
517End line number is 1 (should not happen, bug)!
518#
519#	24		ERROR: End line number too large!
520#
521End line number too large, numeric overflow!
522#
523#	25		ERROR: Start line number too large!
524#
525Start line number too large, numeric overflow!
526#
527#	26		ERROR: Ring buffer size 0!
528#
529Ring buffer size is 0 (should not happen, bug)!
530#
531#	27		ERROR: Failed to obtain file name!
532#
533Failed to obtain file name (should not happen, bug)!
534#
535#	28		ERROR: Stopped by SIGPIPE!
536#
537Program stopped by SIGPIPE!
538#
539#	29		ERROR: I/O errors while writing output!
540#
541I/O errors while writing output!
542#
543#	30		ERROR: Stopped by interrupt!
544#
545Program stopped by interrupt!
546#
547#	31 32 33	Error messages for encoding/decoding/processing errors
548#
549Encoding error!
550Decoding error!
551Processing error!
552#
553#	34 ... 39	Error messages with detailed position
554#
555Encoding error!\n\tByte:
556Decoding error!\n\tByte:
557Processing error!\n\tByte:
558,\n\tCharacter:
559,\n\tPosition in line:
560.
561$!end
562};
563
564
565
566/**	Options used by program.
567*/
568static const dk4_option_specification_t dk_lines_options[] = {
569
570	/*	Input encoding.
571	*/
572	{ dkT('i'),		dkT("input-encoding"),	DK4_OPT_ARG_STRING },
573
574	/*	Only lines matching start text or lexicagraphically after text.
575	*/
576	{ dkT('f'),		dkT("from"),			DK4_OPT_ARG_STRING },
577
578	/*	Only lines matching end text or lexicographically before text.
579	*/
580	{ dkT('t'),		dkT("to"),				DK4_OPT_ARG_STRING },
581
582	/*	Line number selection _after_ applying from and to rules.
583		First line is line 1, final line is line -1.
584		dk-lines -l 5/8					Lines 5, 6, 7 and 8
585		dk-lines -l 5					Lines 5, ...
586		dk-lines -l /8					Lines 1, ... 8
587		dk-lines -l -8/-5				8 before last ... 5 before last
588		dk-lines -l -8					Final 8 lines
589		dk-lines -l /-5					Skip final 5 lines
590		dk-lines -l -8/125				Final 8 lines but not above line 125
591		dk-lines -l 125/-8				Lines from 125 but skip final 8 lines
592		dk-lines without -l option		All lines
593	*/
594	{ dkT('l'),		dkT("lines"),			DK4_OPT_ARG_STRING },
595
596	/*	Verbose flag (write notification on SIGPIPE).
597	*/
598	{ dkT('v'),		dkT("verbose"),			DK4_OPT_ARG_NONE },
599
600	/*	Treat multiple files as one stream.
601	*/
602	{ dkT('o'),		dkT("one-stream"),		DK4_OPT_ARG_NONE },
603
604	/*	Line buffer size.
605	*/
606	{ dkT('\0'),	dkT("line-size"),		DK4_OPT_ARG_SIZE },
607
608	/**	Use current date to filter lines.
609	*/
610	{ dkT('\0'),	dkT("today"),			DK4_OPT_ARG_NONE }
611};
612
613
614#if	DK4_ON_WINDOWS
615
616/**	File name separator.
617*/
618static const dkChar	fnsep[] = {
619#if DK4_HAVE_BACKSLASH_AS_SEP
620dkT("\\")
621#else
622dkT("/")
623#endif
624};
625
626#endif
627
628
629
630/**	Ring buffer used for negative line numbers.
631*/
632static rbe_t			*pribu	= NULL;
633
634
635
636/**	Application structure.
637*/
638static dk4_app_t		*app	= NULL;
639
640
641/**	Pointer to localized messages array.
642*/
643static const dkChar * const		*msgs	=	dk_lines_kw_def;
644
645
646
647/**	Start range.
648*/
649static const dkChar		*t_from	=	NULL;
650
651
652
653/**	End of range.
654*/
655static const dkChar		*t_to	=	NULL;
656
657
658/**	Input line buffer to use.
659*/
660static dkChar	*ilb	=	default_input_line_buffer;
661
662
663
664/**	Start line number.
665*/
666static dk4_um_t	l_start	=	(dk4_um_t)0UL;
667
668
669
670/**	End line number.
671*/
672static dk4_um_t	l_end	=	(dk4_um_t)0UL;
673
674
675
676/**	Current line number.
677*/
678static dk4_um_t	lineno	=	(dk4_um_t)0UL;
679
680
681
682/**	Length of --from text.
683*/
684static size_t	sz_t_from	=	0;
685
686
687
688/**	Length of --to text.
689*/
690static size_t	sz_t_to		=	0;
691
692
693
694/**	Ring buffer size (number of elements).
695*/
696static size_t	sz_ribu		=	0;
697
698
699
700/**	Current position in ring buffer.
701*/
702static size_t	pos_ribu	=	0;
703
704
705
706/**	Size of input line buffer.
707*/
708static size_t	sz_ilb	=	DK4_SIZEOF(default_input_line_buffer,dkChar);
709
710
711
712/**	Number of usable elements in the dk_lines_kw_def array and in msg.
713*/
714static size_t	sz_msg	=	(sizeof(dk_lines_kw_def)/sizeof(DK4_PDKCHAR) - 1);
715
716
717
718/**	Number of elements in the dk_lines_options array.
719*/
720static const size_t dk_lines_sz_options =
721sizeof(dk_lines_options)/sizeof(dk4_option_specification_t);
722
723
724
725/**	Line handling type.
726*/
727static	int		l_type	=	RANGE_NONE_NONE;
728
729
730
731/**	Exit status code.
732*/
733static	int		exval	=	EXIT_FAILURE;
734
735
736
737/**	Flag: Write errors.
738*/
739static	int		had_write_error	= 0;
740
741
742
743/**	Expected input encoding when reading from file.
744*/
745static	int		eie_file		=
746#if DK4_ON_WINDOWS
747	DK4_FILE_ENCODING_WIN1252
748#else
749	DK4_FILE_ENCODING_PLAIN
750#endif
751;
752
753
754
755/**	Expected input encoding on standard input.
756*/
757static	int		eie_stdin		=
758#if DK4_ON_WINDOWS
759	DK4_FILE_ENCODING_WIN1252
760#else
761	DK4_FILE_ENCODING_PLAIN
762#endif
763;
764
765
766
767/**	Verbose flag (report SIGPIPE or unusual large line buffer size).
768*/
769static	int		verbose	=	0;
770
771
772
773/**	Flag: At least one cyle in the ring buffer was completed.
774*/
775static	int		cycle_completed	=	0;
776
777
778
779/**	Flag: Treat multiple files as one stream.
780*/
781static	int		one_stream	=	0;
782
783
784
785/**	Number of decoding errors already reported.
786*/
787static int num_r_decoding_e		= 0;
788
789
790
791/**	Maximum number of decoding errors to report unless verbose output required.
792*/
793static const int max_r_decoding_e	= 3;
794
795
796
797/**	Number of encoding errors already reported.
798*/
799static int num_r_encoding_e		= 0;
800
801
802
803/**	Maximum number of encoding errors to report unless verbose output required.
804*/
805static const int max_r_encoding_e	= 3;
806
807
808
809/**	Number of processing errors already reported.
810*/
811static int num_r_processing_e		= 0;
812
813
814
815/**	Maximum number of processing errors to report unless
816	verbose output required.
817*/
818static const int max_r_processing_e	= 3;
819
820
821
822#ifdef SIGPIPE
823/**	Indicator: SIGPIPE signal received.
824*/
825static
826DK4_VOLATILE
827dk4_sig_atomic_t	sig_had_pipe	=	0;
828#endif
829
830/**	Indicator: SIGINT signal received.
831*/
832static
833DK4_VOLATILE
834dk4_sig_atomic_t	sig_had_int	=	0;
835
836/**	Indicator: SIGTERM signal received.
837*/
838static
839DK4_VOLATILE
840dk4_sig_atomic_t	sig_had_term	=	0;
841
842
843
844/**	Special function to pass a volatile pointer.
845	Some C compiler does not handle direct assignments to volatile variables
846	correctly.
847	@param	ptr	Address of volatile variable.
848	@return	Same address.
849*/
850static
851DK4_VOLATILE
852dk4_sig_atomic_t *
853sig_pass_pointer(DK4_VOLATILE dk4_sig_atomic_t *ptr)
854{
855	return ptr;
856}
857
858
859#ifdef SIGPIPE
860/**	Handler for SIGPIPE signal.
861	@param	signo	Signal number (always SIGPIPE, ignored).
862*/
863static
864void
865sig_handler_pipe(int DK4_ARG_UNUSED(signo) )
866{
867	DK4_UNUSED_ARG(signo)
868	*sig_pass_pointer(&sig_had_pipe) = 1;
869}
870#endif
871
872
873/**	Handler for SIGINT signal.
874	@param	signo	Signal number (always SIGINT, ignored).
875*/
876static
877void
878sig_handler_int(int DK4_ARG_UNUSED(signo) )
879{
880	DK4_UNUSED_ARG(signo)
881	*sig_pass_pointer(&sig_had_int) = 1;
882}
883
884
885/**	Handler for SIGTERM signal.
886	@param	signo	Signal number (always SIGTERM, ignored).
887*/
888static
889void
890sig_handler_term(int DK4_ARG_UNUSED(signo) )
891{
892	DK4_UNUSED_ARG(signo)
893	*sig_pass_pointer(&sig_had_term) = 1;
894}
895
896
897/**	Read value from volatile atomic type.
898	This function is necessary as some compilers mis-optimize
899	direct access to volatile variables (at least if you believe
900	one of the coding standards).
901	@param	ap	Pointer to volatile atomic variable.
902	@return	Contents of the variable.
903*/
904static
905dk4_sig_atomic_t
906sig_read_atomic(DK4_VOLATILE dk4_sig_atomic_t *ap)
907{
908	return (*ap);
909}
910
911
912
913/**	Check whether we can continue or if a signal was received.
914	@param	check_pipe	Flag: Check for occured SIGPIPE signal too.
915	@return	1 if the program can continue, 0 if a signal was received.
916*/
917static
918int
919sig_can_continue(
920#ifdef	SIGPIPE
921int check_pipe
922#else
923int DK4_ARG_UNUSED(check_pipe)
924#endif
925)
926{
927	int		back = 1;
928#ifndef SIGPIPE
929	DK4_UNUSED_ARG(check_pipe)
930#else
931	if (0 != check_pipe) {
932		if (0 != sig_read_atomic(&sig_had_pipe)) { back = 0; }
933	}
934#endif
935	if (0 != sig_read_atomic(&sig_had_int )) { back = 0; }
936	if (0 != sig_read_atomic(&sig_had_term)) { back = 0; }
937	return back;
938}
939
940
941
942/**	Retrieve value from text.
943	@param	pdest	Address of result variable to fill.
944	@param	vtxt	Text containing the value.
945	@param	pback	Address of success variable to reset on error.
946*/
947static
948void
949retrieve_number(dk4_um_t *pdest, const dkChar *vtxt, int *pback)
950{
951	dk4_er_t		 er;
952	dk4_um_t		 u	= (dk4_um_t)0UL;
953	const dkChar	*pe	= NULL;
954	$? "+ retrieve_number \"%!ds\"", TR_STR(vtxt)
955	dk4error_init(&er);
956	if (0 != dk4ma_input_dk_dec_dk4_um_t(&u, vtxt, &pe, 1, &er)) {
957		if ((dk4_um_t)0UL < u) {
958			*pdest = u;
959		}
960		else {								$? "! 0"
961			*pback = 0;
962			/* ERROR: Illegal line number 0 */
963			dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 6);
964		}
965	}
966	else {
967		*pback = 0;
968		switch (er.ec) {
969			case DK4_E_MATH_OVERFLOW : {	$? "! overflow"
970				/* ERROR: Number too large */
971				dk4app_log_3(app, msgs, sz_msg, DK4_LL_ERROR, 7, 8, vtxt);
972			} break;
973			default : {						$? "! not a number"
974				/* ERROR: Not a number */
975				dk4app_log_3(app, msgs, sz_msg, DK4_LL_ERROR, 9, 10, vtxt);
976			} break;
977		}
978	}
979	$? "- retrieve_number"
980}
981
982
983
984#if TRACE_DEBUG
985
986static
987void
988trace_type_start_end(int t, unsigned long s, unsigned long e)
989{
990	$? ". type=%d   start=%lu   end=%lu", t, s, e
991}
992
993#endif
994
995
996
997/**	Process line number range.
998	@param	ps	Text containing start line number.
999	@param	pe	Text containing end line number.
1000	@return	1 on success, 0 on error.
1001*/
1002static
1003int
1004process_line_range_items(const dkChar *ps, const dkChar *pe)
1005{
1006	int			 back	= 1;
1007	$? "+ process_line_range_items \"%!ds\" \"%!ds\"", TR_STR(ps), TR_STR(pe)
1008
1009	/*	Retrieve pure data from texts
1010	*/
1011	if (NULL != ps) {						$? ". ps != NULL"
1012		if (NULL != pe) {					$? ". ps != NULL, pe != NULL"
1013			if (dkT('-') != *ps) {			$? ". ps pos"
1014				if (dkT('-') != *pe) {		$? ". ps pos, pe pos"
1015					l_type = RANGE_POS_POS;
1016					retrieve_number(&l_start, ps, &back);
1017					retrieve_number(&l_end, pe, &back);
1018					if (l_start > l_end) {		$? "! start > end"
1019						back = 0;
1020						/* ERROR: start line number larger than end */
1021						dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 11);
1022					}
1023				}
1024				else {						$? ". ps pos, pe neg"
1025					pe++;
1026					l_type = RANGE_POS_NEG;
1027					retrieve_number(&l_start, ps, &back);
1028					retrieve_number(&l_end, pe, &back);
1029				}
1030			}
1031			else {							$? ". ps neg"
1032				ps++;
1033				if (dkT('-') != *pe) {		$? ". ps neg, pe pos"
1034					l_type = RANGE_NEG_POS;
1035					retrieve_number(&l_start, ps, &back);
1036					retrieve_number(&l_end, pe, &back);
1037				}
1038				else {						$? ". ps neg, pe neg"
1039					pe++;
1040					l_type = RANGE_NEG_NEG;
1041					retrieve_number(&l_start, ps, &back);
1042					retrieve_number(&l_end, pe, &back);
1043					if (l_end > l_start) {		$? "! end > start"
1044						back = 0;
1045						/* ERROR: End line number larger than start */
1046						dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 11);
1047					}
1048				}
1049			}
1050		}
1051		else {								$? ". ps != NULL, pe == NULL"
1052			if (dkT('-') != *ps) {			$? ". ps pos, pe none"
1053				l_type = RANGE_POS_NONE;
1054				retrieve_number(&l_start, ps, &back);
1055			}
1056			else {							$? ". ps neg, pe none"
1057				ps++;
1058				l_type = RANGE_NEG_NONE;
1059				retrieve_number(&l_start, ps, &back);
1060			}
1061		}
1062	}
1063	else {									$? ". ps == NULL"
1064		if (NULL != pe) {					$? ". ps == NULL, pe != NULL"
1065			if (dkT('-') != *pe) {			$? ". ps none, pe pos"
1066				l_type = RANGE_NONE_POS;
1067				retrieve_number(&l_end, pe, &back);
1068			}
1069			else {							$? ". ps none, pe neg"
1070				pe++;
1071				l_type = RANGE_NONE_NEG;
1072				retrieve_number(&l_end, pe, &back);
1073			}
1074		}
1075		else {								$? ". ps == NULL, pe == NULL"
1076		}
1077	}
1078
1079	/*	Corrections for full ranges
1080	*/
1081	if (0 != back) {
1082		$? ". t=%d s=%lu e=%lu", l_type, (unsigned long)l_start, (unsigned long)l_end
1083		$!trace-code trace_type_start_end(l_type, l_start, l_end);
1084		switch (l_type) {
1085			case RANGE_NONE_NEG : {
1086				if ((dk4_um_t)1UL == l_end) {
1087					l_type = RANGE_NONE_NONE;
1088					l_end  = (dk4_um_t)0UL;
1089					$!trace-code trace_type_start_end(l_type, l_start, l_end);
1090				}
1091			} break;
1092			case RANGE_POS_NEG : {
1093				if ((dk4_um_t)1UL == l_end) {
1094					l_type = RANGE_POS_NONE;
1095					l_end  = (dk4_um_t)0UL;
1096					$!trace-code trace_type_start_end(l_type, l_start, l_end);
1097					if ((dk4_um_t)1UL == l_start) {
1098						l_type  = RANGE_NONE_NONE;
1099						l_start = (dk4_um_t)0UL;
1100						$!trace-code trace_type_start_end(l_type,l_start,l_end);
1101					}
1102				}
1103				else {
1104					if ((dk4_um_t)1UL == l_start) {
1105						l_type  = RANGE_NONE_NEG;
1106						l_start = (dk4_um_t)0UL;
1107						$!trace-code trace_type_start_end(l_type,l_start,l_end);
1108					}
1109				}
1110			} break;
1111			case RANGE_NEG_NEG : {
1112				if ((dk4_um_t)1UL == l_end) {
1113					l_type = RANGE_NEG_NONE;
1114					l_end  = (dk4_um_t)0UL;
1115					$!trace-code trace_type_start_end(l_type, l_start, l_end);
1116				}
1117			} break;
1118			case RANGE_POS_NONE : {
1119				if ((dk4_um_t)1UL == l_start) {
1120					l_start = (dk4_um_t)0UL;
1121					l_type  = RANGE_NONE_NONE;
1122					$!trace-code trace_type_start_end(l_type, l_start, l_end);
1123				}
1124			} break;
1125			case RANGE_POS_POS : {
1126				if ((dk4_um_t)1UL == l_start) {
1127					l_start = (dk4_um_t)0UL;
1128					l_type  = RANGE_NONE_POS;
1129					$!trace-code trace_type_start_end(l_type, l_start, l_end);
1130				}
1131			} break;
1132			default : {
1133				/* Empty by intent */
1134				$!trace-code trace_type_start_end(l_type, l_start, l_end);
1135			} break;
1136		}
1137	}
1138	$? "- process_line_range_items %d", back
1139	return back;
1140}
1141
1142
1143
1144/**	Process line number range.
1145	@param	range	Text containing the line number range.
1146*/
1147static
1148int
1149process_line_range(const dkChar *range)
1150{
1151	dkChar		 buf[64*sizeof(dk4_um_t)];
1152	dkChar		*p1		= NULL;
1153	dkChar		*p2		= NULL;
1154	int			 back	= 0;
1155	$? "+ process_line_range \"%!ds\"", range
1156	back = dk4str_cpy_s(buf, DK4_SIZEOF(buf,dkChar), range, NULL);
1157	if (0 != back) {
1158		p1 = dk4str_start(buf, NULL);
1159		if (NULL != p1) {						$? ". p1=\"%!ds\"", p1
1160			p2 = dk4str_chr(p1, dkT('/'));
1161			if (NULL != p2) {					$? ". p2=\"%!ds\"", p2
1162				*(p2++) = dkT('\0');
1163				p2 = dk4str_start(p2, NULL);	$? ". p2=\"%!ds\"", TR_STR(p2)
1164				p1 = dk4str_start(p1, NULL);	$? ". p1=\"%!ds\"", TR_STR(p1)
1165			}	$? ". p1=\"%!ds\" p2=\"%!ds\"", TR_STR(p1), TR_STR(p2)
1166			back = process_line_range_items(p1, p2);
1167		}
1168		else {									$? "! empty string"
1169			/* ERROR: Empty string */
1170			dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 12);
1171			back = 0;
1172		}
1173	}
1174	$? "- process_line_range %d", back
1175	return back;
1176}
1177
1178
1179
1180/**	Retrieve configuration from command line arguments.
1181	@return	1 on success, 0 on error.
1182*/
1183static
1184int
1185process_command_line_arguments(void)
1186{
1187	dk4_time_t		 timer	= (dk4_time_t)0UL;
1188	const dkChar	*ptr	= NULL;
1189	size_t			 sz		= 0;
1190	int				 back	= 1;
1191	int				 res	= 0;
1192	int				 iec	= 0;
1193	$? "+ process_command_line_arguments"
1194
1195	/*	--verbose
1196	*/
1197	if (0 != dk4app_opt_is_set_short(app, dkT('v'), NULL)) {
1198		verbose = 1;
1199	}
1200
1201	/*	--one-stream
1202	*/
1203	if (0 != dk4app_opt_is_set_short(app, dkT('o'), NULL)) {
1204		one_stream = 1;
1205	}
1206
1207	/*	--from
1208	*/
1209	if (0 != dk4app_opt_is_set_short(app, dkT('f'), NULL)) {
1210		t_from	= dk4app_opt_get_string_ptr_short(app, dkT('f'), NULL);
1211		sz_t_from = dk4str_len(t_from);
1212	}
1213
1214	/*	--to
1215	*/
1216	if (0 != dk4app_opt_is_set_short(app, dkT('t'), NULL)) {
1217		t_to	= dk4app_opt_get_string_ptr_short(app, dkT('t'), NULL);
1218		sz_t_to = dk4str_len(t_to);
1219	}
1220
1221	/*	--lines
1222	*/
1223	if (0 != dk4app_opt_is_set_short(app, dkT('l'), NULL)) {
1224		ptr		= dk4app_opt_get_string_ptr_short(app, dkT('l'), NULL);
1225		if (NULL != ptr) {
1226			back = process_line_range(ptr);
1227		}
1228		else {			$? "! missing arg for --lines"
1229			/* ERROR: Failed to retrieve line range argument */
1230			dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 13);
1231			back = 0;
1232		}
1233	}
1234
1235	/*	--input-encoding
1236	*/
1237	if (0 != dk4app_opt_is_set_short(app, dkT('i'), NULL)) {
1238		ptr		= dk4app_opt_get_string_ptr_short(app, dkT('i'), NULL);
1239		if (NULL != ptr) {
1240			res = dk4enc_find(&iec, NULL, ptr, NULL);
1241			if (0 != res) {
1242				eie_stdin = iec;
1243				eie_file  = iec;
1244			}
1245			else {
1246				/* ERROR: Illegal input encoding */
1247				dk4app_log_3(app, msgs, sz_msg, DK4_LL_ERROR, 14, 15, ptr);
1248			}
1249		}
1250		else {
1251			back = 0;
1252			/* ERROR: Failed to retrieve input encoding */
1253			dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 16);
1254		}
1255	}
1256
1257	/*	--line-size
1258	*/
1259	if (0 != dk4app_opt_is_set_long(app, dk_lines_kwnl[3], NULL)) {
1260		if (0 != dk4app_opt_get_size_long(&sz, app, dk_lines_kwnl[3], NULL)) {
1261			if (0 < sz) {
1262				if (DK4_SIZEOF(default_input_line_buffer,dkChar) < sz) {
1263					sz_ilb = sz;
1264				}
1265#if TRACE_DEBUG
1266				else {			$? ". not larger than existing"
1267				}
1268#endif
1269			}
1270			else {				$? "! line size 0"
1271				/* ERROR: Line size 0 */
1272				dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 17);
1273				back = 0;
1274			}
1275		}
1276		else {					$? "! missing arg for --line-size"
1277			/* ERROR: Failed to retrieve line size */
1278			dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 18);
1279			back = 0;
1280		}
1281	}
1282
1283	/*	--today
1284	*/
1285	if (0 != dk4app_opt_is_set_long(app, dk_lines_kwnl[4], NULL)) {
1286		if ((NULL == t_from) || (NULL == t_to)) {
1287			dk4time_get(&timer);
1288			res = dk4time_as_text(
1289				date_buffer, DK4_SIZEOF(date_buffer,dkChar), &timer, NULL
1290			);
1291			if (0 != res) {
1292				date_buffer[10] = dkT('\0');
1293				if (NULL == t_from) {
1294					t_from = date_buffer;
1295					sz_t_from = dk4str_len(t_from);
1296				}
1297				if (NULL == t_to  ) {
1298					t_to   = date_buffer;
1299					sz_t_to   = dk4str_len(t_to);
1300				}
1301			}
1302		}
1303		else {
1304			/* ERROR: --today used with --from and --to */
1305			dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 19);
1306		}
1307	}
1308	$? "- process_command_line_arguments %d", back
1309	return back;
1310}
1311
1312
1313
1314/**	Report an error with position.
1315	@param	i1	Index of first message part in array.
1316	@param	i2	Index of alternative first message part.
1317	@param	bno	Current byte number.
1318	@param	lno	Current line number.
1319	@param	cno	Current character number.
1320	@param	cil	Current character number within line.
1321	@param	fn	File name processed.
1322*/
1323static
1324void
1325report_with_position(
1326	size_t			 i1,
1327	size_t			 i2,
1328	dk4_um_t		 bno,
1329	dk4_um_t		 lno,
1330	dk4_um_t		 cno,
1331	dk4_um_t		 cil,
1332	const dkChar	*fn
1333)
1334{
1335	dkChar			 b1[8*(1+sizeof(dk4_um_t))+16];
1336	dkChar			 b2[8*(1+sizeof(dk4_um_t))+16];
1337	dkChar			 b3[8*(1+sizeof(dk4_um_t))+16];
1338	const dkChar	*oldlogname		=	NULL;
1339	dk4_um_t		 oldlogline		=	(dk4_um_t)0UL;
1340	const size_t	 szb3			=	DK4_SIZEOF(b3,dkChar);
1341	const size_t	 szb2			=	DK4_SIZEOF(b2,dkChar);
1342	const size_t	 szb1			=	DK4_SIZEOF(b1,dkChar);
1343	int				 allbuffersok	=	0;
1344
1345	oldlogname =  dk4app_get_log_source_file(app);
1346	oldlogline =  dk4app_get_log_source_line(app);
1347	dk4app_set_log_source_file(app, fn);
1348	dk4app_set_log_source_line(app, lno);
1349	if (0 != dk4ma_write_decimal_unsigned(b3, szb3, cil, 0, NULL)) {
1350		if (0 != dk4ma_write_decimal_unsigned(b2, szb2, cno, 0, NULL)) {
1351			if (0 != dk4ma_write_decimal_unsigned(b1, szb1, bno, 0, NULL)) {
1352				allbuffersok = 1;
1353			}
1354		}
1355	}
1356	if (0 != allbuffersok) {
1357		dk4app_log_7(
1358			app, msgs, sz_msg, DK4_LL_ERROR, i1, 37, 38, 39, b1, b2, b3
1359		);
1360	}
1361	else {
1362		dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, i2);
1363	}
1364	dk4app_set_log_source_line(app, oldlogline);
1365	dk4app_set_log_source_file(app, oldlogname);
1366}
1367
1368
1369
1370static
1371void
1372report_errors_from_tsp(
1373	dk4_er_t		*er_en,
1374	dk4_er_t		*er_pr,
1375	const dkChar	*fn
1376)
1377{
1378	if (NULL != er_en) {
1379		switch (er_en->ec) {
1380			case DK4_E_DECODING_FAILED : {
1381				if ((0 != verbose) || (max_r_decoding_e > num_r_decoding_e)) {
1382					report_with_position(
1383						35, 32,
1384						er_en->dt.fpos.byteno, er_en->dt.fpos.lineno,
1385						er_en->dt.fpos.charno, er_en->dt.fpos.charinline,
1386						fn
1387					);
1388					num_r_decoding_e++;
1389					if (num_r_decoding_e > max_r_decoding_e) {
1390						num_r_decoding_e = max_r_decoding_e;
1391					}
1392				}
1393			} break;
1394			case DK4_E_ENCODING_FAILED : {
1395				if ((0 != verbose) || (max_r_encoding_e > num_r_encoding_e)) {
1396					report_with_position(
1397						34, 31,
1398						er_en->dt.fpos.byteno, er_en->dt.fpos.lineno,
1399						er_en->dt.fpos.charno, er_en->dt.fpos.charinline,
1400						fn
1401					);
1402					num_r_encoding_e++;
1403					if (num_r_encoding_e > max_r_encoding_e) {
1404						num_r_encoding_e = max_r_encoding_e;
1405					}
1406				}
1407			} break;
1408		}
1409	}
1410	if (NULL != er_pr) {
1411		if (DK4_E_NONE != er_pr->ec) {
1412			if ((0 != verbose) || (max_r_processing_e > num_r_processing_e)) {
1413				report_with_position(
1414					36, 33,
1415					er_pr->dt.fpos.byteno, er_pr->dt.fpos.lineno,
1416					er_pr->dt.fpos.charno, er_pr->dt.fpos.charinline,
1417					fn
1418				);
1419				num_r_processing_e++;
1420				if (num_r_processing_e > max_r_processing_e) {
1421					num_r_processing_e = max_r_processing_e;
1422				}
1423			}
1424		}
1425	}
1426}
1427
1428
1429
1430static
1431int
1432write_text_line(const dkChar *line, dk4_er_t *erp)
1433{
1434	int		 back = DK4_TSP_RES_OK;
1435	if (0 == dk4fputs(line, stdout, erp)) {
1436		had_write_error = 1;
1437		back = DK4_TSP_RES_ERROR;
1438	}
1439	if (0 == dk4fputc(dkT('\n'), stdout, erp)) {
1440		had_write_error = 1;
1441		back = DK4_TSP_RES_ERROR;
1442	}
1443	return back;
1444}
1445
1446
1447
1448/**	Add new line to ring buffer, write line stored previously.
1449	@param	line	New line to add.
1450	@param	erp		Error report.
1451	@return	DK4_TSP_RES_OK on success, DK4_TSP_RES_ERROR on error.
1452*/
1453static
1454int
1455ribu_add_and_print(const dkChar *line, dk4_er_t *erp)
1456{
1457	dkChar		*ptr;
1458	size_t		 sz;
1459	int			 back	= DK4_TSP_RES_OK;
1460	$? "+ ribu_add_and_print"
1461	sz = dk4str_len(line);
1462	if ((SIZE_MAX) > sz) {
1463		if ((NULL != pribu) && (0 < sz_ribu)) {
1464			if (NULL != pribu[pos_ribu].str) {
1465				$? ". ring buffer [%u] used =", (unsigned)pos_ribu
1466				$? ". \"%!ds\"", pribu[pos_ribu].str
1467				if (0 != cycle_completed) {
1468					back = write_text_line(pribu[pos_ribu].str, erp);
1469				}
1470				if (sz > pribu[pos_ribu].buflen) {	$? ". allocate new buffer"
1471					ptr = dk4str_dup_app(line, app);
1472					if (NULL != ptr) {
1473						dk4mem_free(pribu[pos_ribu].str);
1474						pribu[pos_ribu].str = ptr;
1475						pribu[pos_ribu].buflen = sz;
1476					}
1477					else {
1478						back = DK4_TSP_RES_ERROR;
1479						dk4error_set_simple_error_code(
1480							erp, DK4_E_MEMORY_ALLOCATION_FAILED
1481						);
1482						/* ERROR: Memory allocation failed */
1483					}
1484				}
1485				else {							$? ". reuse existing buffer"
1486					(void)dk4str_cpy_s(
1487						pribu[pos_ribu].str, (1 + pribu[pos_ribu].buflen),
1488						line, NULL
1489					);
1490				}
1491			}
1492			else {		$? ". ring buffer [%u] empty", (unsigned)pos_ribu
1493				pribu[pos_ribu].str = dk4str_dup_app(line, app);
1494				pribu[pos_ribu].buflen = sz;
1495				if (NULL == pribu[pos_ribu].str) {
1496					back = DK4_TSP_RES_ERROR;
1497					dk4error_set_simple_error_code(
1498						erp, DK4_E_MEMORY_ALLOCATION_FAILED
1499					);
1500					pribu[pos_ribu].buflen = 0;
1501					/* ERROR: Memory allocation failed */
1502				}
1503			}
1504			$? ". ring buffer [%u] now =", (unsigned)pos_ribu
1505			$? ". \"%!ds\"", pribu[pos_ribu].str
1506			pribu[pos_ribu].lineno = lineno;
1507			pos_ribu++;
1508			if (pos_ribu >= sz_ribu) {
1509				pos_ribu = 0;
1510				cycle_completed = 1;
1511			}	$? ". next position %u", (unsigned)pos_ribu
1512		}
1513		else {
1514			back = DK4_TSP_RES_ERROR;
1515			dk4error_set_simple_error_code(erp, DK4_E_BUG);
1516			/* ERROR: No ring buffer */
1517		}
1518	}
1519	else {
1520		back = DK4_TSP_RES_ERROR;
1521		dk4error_set_simple_error_code(erp, DK4_E_MATH_OVERFLOW);
1522		/* ERROR: Line too long */
1523		dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 20);
1524	}
1525	$? "- ribu_add_and_print %d", back
1526	return back;
1527}
1528
1529
1530
1531/**	Add new line to ring buffer, do not print line previously stored.
1532	@param	line	New line to add.
1533	@param	erp		Error report.
1534	@return	DK4_TSP_RES_OK on success, DK4_TSP_RES_ERROR on error.
1535*/
1536static
1537int
1538ribu_add_silently(const dkChar *line, dk4_er_t *erp)
1539{
1540	dkChar		*ptr;
1541	size_t		 sz;
1542	int			 back	= DK4_TSP_RES_OK;
1543	$? "+ ribu_add_silently \"%!ds\"", line
1544
1545	sz = dk4str_len(line);
1546	if ((SIZE_MAX) > sz) {
1547		if ((NULL != pribu) && (0 < sz_ribu)) {		$? ". pribu"
1548			if (NULL != pribu[pos_ribu].str) {		$? ". have existing string"
1549				$? ". ring buffer [%u] old =", (unsigned)pos_ribu
1550				$? ". \"%!ds\"", pribu[pos_ribu].str
1551				$? ". buf lgt = %lu", (unsigned long)(pribu[pos_ribu].buflen)
1552				$? ". line = \"%!ds\"", line
1553				if (sz > pribu[pos_ribu].buflen) {	$? ". need new string"
1554					ptr = dk4str_dup_app(line, app);
1555					if (NULL != ptr) {				$? ". memory allocated"
1556						dk4mem_free(pribu[pos_ribu].str);
1557						pribu[pos_ribu].str = ptr;
1558						pribu[pos_ribu].buflen = sz;
1559					}
1560					else {							$? "! memory"
1561						dk4mem_release(pribu[pos_ribu].str);
1562						pribu[pos_ribu].buflen = 0;
1563						pribu[pos_ribu].lineno = (dk4_um_t)0UL;
1564						back = DK4_TSP_RES_ERROR;
1565						dk4error_set_simple_error_code(
1566							erp, DK4_E_MEMORY_ALLOCATION_FAILED
1567						);
1568						/* ERROR: Memory allocation failed */
1569					}
1570				}
1571				else {								$? ". use existing buffer"
1572					(void)dk4str_cpy_s(
1573						pribu[pos_ribu].str, (1 + pribu[pos_ribu].buflen),
1574						line, NULL
1575					);
1576				}
1577			}
1578			else {									$? ". no existing string"
1579				pribu[pos_ribu].str = dk4str_dup_app(line, app);
1580				pribu[pos_ribu].buflen = sz;
1581				if (NULL == pribu[pos_ribu].str) {	$? "! memory"
1582					back = DK4_TSP_RES_ERROR;
1583					dk4error_set_simple_error_code(
1584						erp, DK4_E_MEMORY_ALLOCATION_FAILED
1585					);
1586					pribu[pos_ribu].buflen = 0;
1587					/* ERROR: Memory allocation failed */
1588				}
1589			}
1590			$? ". ring buffer [%u] now =", (unsigned)pos_ribu
1591			$? ". \"%!ds\"", pribu[pos_ribu].str
1592			pribu[pos_ribu].lineno = lineno;
1593			pos_ribu++;
1594			if (pos_ribu >= sz_ribu) {
1595				pos_ribu = 0;
1596			}
1597		}
1598		else {										$? "! pribu"
1599			back = DK4_TSP_RES_ERROR;
1600			dk4error_set_simple_error_code(erp, DK4_E_BUG);
1601			/* ERROR: No ring buffer */
1602		}
1603	}
1604	else {
1605		back = DK4_TSP_RES_ERROR;
1606		dk4error_set_simple_error_code(erp, DK4_E_MATH_OVERFLOW);
1607		/* ERROR: Line too long */
1608		dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 20);
1609	}
1610	$? "- ribu_add_silently %d", back
1611	return back;
1612}
1613
1614
1615
1616static
1617int
1618dk_lines_line_handler(
1619	void			*DK4_ARG_UNUSED(obj),
1620	dkChar			*line,
1621	dk4_um_t		 xlineno,
1622	dk4_er_t		*erp
1623)
1624{
1625	dk4_er_t	 er;
1626	size_t		 sz		= 0;
1627	int			 back	= DK4_TSP_RES_OK;
1628	int			 ok		= 1;
1629
1630	DK4_UNUSED_ARG(obj)
1631	$? "+ dk_lines_line_handler"
1632	dk4app_set_log_source_line(app, xlineno);
1633	dk4str_delnl(line);
1634	$? ". line = \"%!ds\"", line
1635	if ((NULL != t_from) && (0 < sz_t_from)) {
1636#if VERSION_BEFORE_2017_11_20
1637		sz = dk4str_len(line);
1638		if (sz < sz_t_from) {
1639			ok = 0;
1640		}
1641		else {
1642			if (0 > dk4str_ncmp(line, t_from, sz_t_from)) { ok = 0; }
1643		}
1644#endif
1645		if (0 > dk4str_cmp(line, t_from)) { ok = 0; }
1646	}
1647	if ((0 != ok) && (NULL != t_to) && (0 < sz_t_to)) {
1648#if VERSION_BEFORE_2017_11_20
1649		if (0 == sz) { sz = dk4str_len(line); }
1650		if (sz < sz_t_to) {
1651			if (0 < dk4str_ncmp(line, t_to, sz)) { ok = 0; }
1652		}
1653		else {
1654			if (0 < dk4str_ncmp(line, t_to, sz_t_to)) { ok = 0; }
1655		}
1656#endif
1657		sz = dk4str_len(line);
1658		if (sz < sz_t_to) {
1659			if (0 < dk4str_cmp(line, t_to)) { ok = 0; }
1660		}
1661		else {
1662			if (0 < dk4str_ncmp(line, t_to, sz_t_to)) { ok = 0; }
1663		}
1664	}
1665	if (0 != ok) {
1666		dk4error_init(&er);
1667		lineno = dk4ma_um_add(lineno, (dk4_um_t)1UL, &er);
1668		if (DK4_E_NONE == er.ec) {
1669			$? ". adding line %lu \"%!ds\"", (unsigned long)lineno, line
1670			switch (l_type) {
1671				case RANGE_NONE_NONE : {
1672					back = write_text_line(line, erp);
1673				} break;
1674				case RANGE_NONE_POS : {
1675					if (lineno <= l_end) {
1676						back = write_text_line(line, erp);
1677					}
1678				} break;
1679				case RANGE_NONE_NEG : {
1680					back = ribu_add_and_print(line, erp);
1681				} break;
1682				case RANGE_POS_NONE : {
1683					if (l_start <= lineno) {
1684						back = write_text_line(line, erp);
1685					}
1686				} break;
1687				case RANGE_POS_POS : {
1688					if ((l_start <= lineno) && (lineno <= l_end)) {
1689						back = write_text_line(line, erp);
1690					}
1691				} break;
1692				case RANGE_POS_NEG : {
1693					if (l_start <= lineno) {
1694						back = ribu_add_and_print(line, erp);
1695					}
1696				} break;
1697				case RANGE_NEG_NONE : {
1698					back = ribu_add_silently(line, erp);
1699				} break;
1700				case RANGE_NEG_POS : {
1701					back = ribu_add_silently(line, erp);
1702				} break;
1703				case RANGE_NEG_NEG : {
1704					back = ribu_add_silently(line, erp);
1705				} break;
1706			}
1707		}
1708		else {
1709			back = DK4_TSP_RES_ERROR;
1710			dk4error_copy(erp, &er);
1711			/* ERROR: Overflow in line number */
1712			dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 21);
1713		}
1714	}
1715
1716	$? "- dk_lines_line_handler %d", back
1717	return back;
1718}
1719
1720
1721
1722/**	Flush the ring buffer after finishing file or stream.
1723*/
1724static
1725void
1726flush_ring_buffer(void)
1727{
1728	dk4_er_t	er;
1729	dk4_um_t	umax;
1730	size_t		i;
1731	size_t		idx;
1732	size_t		max;
1733
1734	switch (l_type) {
1735		case RANGE_NONE_NONE : {
1736			/* Empty by intent, ring buffer not used */
1737		} break;
1738		case RANGE_NONE_POS : {
1739			/* Empty by intent, ring buffer not used */
1740		} break;
1741		case RANGE_NONE_NEG : case RANGE_POS_NEG : {
1742			/* Empty by intent, lines of interest already printed */
1743		} break;
1744		case RANGE_POS_NONE : {
1745			/* Empty by intent, ring buffer not used */
1746		} break;
1747		case RANGE_POS_POS : {
1748			/* Empty by intent, ring buffer not used */
1749		} break;
1750		case RANGE_NEG_NONE : {
1751			idx = pos_ribu;
1752			for (i = 0; i < sz_ribu; i++) {
1753				$? ". loop pass %u / %u", (unsigned)i, (unsigned)sz_ribu
1754				if (NULL != pribu[idx].str) {	$? ". have string"
1755					$? ". line number %lu", (unsigned long)(pribu[idx].lineno)
1756					if (0 == dk4fputs(pribu[idx].str, stdout, NULL)) {
1757						had_write_error = 1;
1758					}
1759					if (0 == dk4fputc(dkT('\n'), stdout, NULL)) {
1760						had_write_error = 1;
1761					}
1762				}
1763#if TRACE_DEBUG
1764				else {							$? ". no string"
1765				}
1766#endif
1767				idx++; if (idx >= sz_ribu) { idx = 0; }
1768			}
1769		} break;
1770		case RANGE_NEG_POS : {
1771			idx = pos_ribu;
1772			for (i = 0; i < sz_ribu; i++) {
1773				if ((NULL != pribu[idx].str) && (pribu[idx].lineno <= l_end)) {
1774					if (0 == dk4fputs(pribu[idx].str, stdout, NULL)) {
1775						had_write_error = 1;
1776					}
1777					if (0 == dk4fputc(dkT('\n'), stdout, NULL)) {
1778						had_write_error = 1;
1779					}
1780				}
1781				idx++; if (idx >= sz_ribu) { idx = 0; }
1782			}
1783		} break;
1784		case RANGE_NEG_NEG : {
1785			dk4error_init(&er);
1786			umax = dk4ma_um_add(
1787				dk4ma_um_sub(l_start, l_end, &er),
1788				(dk4_um_t)1UL,
1789				&er
1790			);
1791			if (DK4_E_NONE == er.ec) {
1792				if ((dk4_um_t)(SIZE_MAX) >= umax) {
1793					max = (size_t)umax;
1794					idx = pos_ribu;
1795					for (i = 0; i < max; i++) {
1796						if (NULL != pribu[idx].str) {
1797							if (0 == dk4fputs(pribu[idx].str, stdout, NULL)) {
1798								had_write_error = 1;
1799							}
1800							if (0 == dk4fputc(dkT('\n'), stdout, NULL)) {
1801								had_write_error = 1;
1802							}
1803						}
1804						idx++; if (idx >= sz_ribu) { idx = 0; }
1805					}
1806				}
1807				else {
1808					exval = EXIT_FAILURE;
1809					/* ERROR: Overflow in line range calculation */
1810					dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 22);
1811				}
1812			}
1813			else {
1814				exval = EXIT_FAILURE;
1815				/* ERROR: Overflow in line range calculation */
1816				dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 22);
1817			}
1818		} break;
1819	}
1820}
1821
1822
1823
1824/**	Process file contents.
1825	@param	fipo	File, opened for binary read access.
1826	@param	fn		File name (for diagnostics).
1827	@param	eie		Expected input encoding.
1828*/
1829static
1830void
1831run_for_file_with_name(FILE *fipo, const dkChar *fn, int eie)
1832{
1833	dk4_tspdk_t		 tsp;				/* Text stream processor */
1834	dk4_er_t		 er_en;				/* Error report for enc/decoding */
1835	dk4_er_t		 er_pr;				/* Error report for processing */
1836	dk4_er_t		 er;				/* General error report */
1837	const dkChar	*oldlogname	= NULL;	/* Old source file */
1838	dk4_um_t		 oldlogline	= 0UL;	/* Old source line */
1839	size_t			 i;					/* Traverse ring buffer elements */
1840	int				 c;					/* Current character retrieved */
1841	int				 res;				/* Operation result */
1842	int				 cc;				/* Flag: Can continue */
1843	unsigned char	 uc;				/* Byte added to processor */
1844	$? "+ run_for_file_with_name"
1845
1846	oldlogname =  dk4app_get_log_source_file(app);
1847	oldlogline =  dk4app_get_log_source_line(app);
1848	dk4app_set_log_source_file(app, fn);
1849	dk4app_set_log_source_line(app, (dk4_um_t)0UL);
1850	/*	Initialize handling of one file
1851	*/
1852	if (0 == one_stream) {
1853		lineno		= (dk4_um_t)0UL;
1854		pos_ribu	= 0;
1855		if (NULL != pribu) {
1856			for (i = 0; i < sz_ribu; i++) {
1857				pribu[i].str = NULL;
1858				pribu[i].lineno = (dk4_um_t)0UL;
1859				pribu[i].buflen = 0;
1860			}
1861		}
1862	}
1863
1864	/*	Process file contents in loop
1865	*/
1866	dk4error_init(&er_en);
1867	dk4error_init(&er_pr);
1868	dk4error_init(&er);
1869	res = dk4tspdk_setup_line(
1870		&tsp, NULL, dk_lines_line_handler, ilb, sz_ilb,
1871		dk4app_get_encoding(app), eie, &er
1872	);
1873	if (0 != res) {
1874		cc = 1;
1875		while(1 == cc) {
1876			cc = -1;
1877			if (0 != sig_can_continue(1)) {
1878				c = fgetc(fipo);
1879				if (EOF != c) {
1880					cc = 1;
1881					uc = (unsigned char)c;
1882					switch (dk4tspdk_add_one_byte(&tsp, uc)) {
1883						case DK4_TSP_RES_FATAL : {
1884							cc = -1;
1885							exval = EXIT_FAILURE;
1886							dk4tspdk_get_errors(&er_en, &er_pr, &tsp);
1887							report_errors_from_tsp(&er_en, &er_pr, fn);
1888						} break;
1889						case DK4_TSP_RES_ERROR : {
1890							exval = EXIT_FAILURE;
1891							dk4tspdk_get_errors(&er_en, &er_pr, &tsp);
1892							report_errors_from_tsp(&er_en, &er_pr, fn);
1893						} break;
1894					}
1895				}
1896				else {
1897					cc = 0;
1898				}
1899			}
1900		}
1901		/*	Flush data from text processor
1902		*/
1903		if (0 == cc) {
1904			switch (dk4tspdk_finish(&tsp)) {
1905				case DK4_TSP_RES_FATAL : case DK4_TSP_RES_ERROR : {
1906					exval = EXIT_FAILURE;
1907					dk4tspdk_get_errors(&er_en, &er_pr, &tsp);
1908					report_errors_from_tsp(&er_en, &er_pr, fn);
1909				} break;
1910			}
1911		}
1912	}
1913	dk4app_set_log_source_line(app, (dk4_um_t)0UL);
1914
1915	/*	Flush data from ring buffer
1916	*/
1917	if (0 == one_stream) {
1918		flush_ring_buffer();
1919	}
1920
1921	/*	Clean up resources
1922	*/
1923	if (0 == one_stream) {
1924		cycle_completed = 0;
1925		if (NULL != pribu) {
1926			for (i = 0; i < sz_ribu; i++) {
1927				dk4mem_release(pribu[i].str);
1928				pribu[i].lineno = (dk4_um_t)0UL;
1929				pribu[i].buflen = 0;
1930			}
1931		}
1932	}
1933	dk4app_set_log_source_line(app, oldlogline);
1934	dk4app_set_log_source_file(app, oldlogname);
1935	$? "- run_for_file_with_name"
1936}
1937
1938
1939
1940/**	Open and process a file.
1941	@param	fn	File name to process.
1942*/
1943static
1944void
1945run_for_filename(const dkChar *fn)
1946{
1947	const dkChar	*oldlogname	=	NULL;
1948	FILE			*fipo		=	NULL;
1949	dk4_um_t		 oldlogline	=	(dk4_um_t)0UL;
1950	int				 tests	= DK4_FOPEN_SC_USER;
1951	$? "+ run_for_filename \"%!ds\"", fn
1952	oldlogname =  dk4app_get_log_source_file(app);
1953	oldlogline =  dk4app_get_log_source_line(app);
1954	dk4app_set_log_source_file(app, fn);
1955	dk4app_set_log_source_line(app, (dk4_um_t)0UL);
1956	if (dk4isadmin()) { tests = DK4_FOPEN_SC_PRIVILEGED; }
1957	$? ". going to open \"%!ds\" \"%!ds\"", fn, dk_lines_kwnl[6]
1958	fipo = dk4fopen_app(fn, dk_lines_kwnl[6], tests, app);
1959	if (NULL != fipo) {
1960		run_for_file_with_name(fipo, fn, eie_file);
1961		fclose(fipo);
1962	}
1963	else {
1964		exval = EXIT_FAILURE;
1965	}
1966	dk4app_set_log_source_line(app, oldlogline);
1967	dk4app_set_log_source_file(app, oldlogname);
1968	$? "- run_for_filename"
1969}
1970
1971
1972
1973#if DK4_ON_WINDOWS
1974
1975static
1976void
1977expand_filename_and_run(const dkChar *pattern)
1978{
1979	dkChar			 buf[DK4_MAX_PATH];
1980	dk4_dir_t		*fne;
1981	const dkChar	*shf;
1982	const dkChar	*pth;
1983	const size_t	 szbuf	= DK4_SIZEOF(buf,dkChar);
1984	int				 any_file_found = 0;
1985	int				 cc;
1986	int				 ok;
1987	$? "+ expand_filename_and_run \"%!ds\"", pattern
1988	fne = dk4app_fne_open(pattern, app);
1989	if (NULL != fne) {
1990		pth = dk4dir_get_path(fne);
1991		cc = 1;
1992		while (1 == cc) {
1993			if (0 != sig_can_continue(1)) {
1994				shf = dk4dir_next_file(fne);
1995				if (NULL != shf) {				$? ". file name \"%!ds\"", shf
1996					any_file_found = 1;
1997					if (NULL != pth) {			$? ". must constract full path"
1998						ok = 1;
1999						if (0 == dk4str_cpy_s(buf, szbuf, pth, NULL)) {
2000							ok = 0;
2001						}
2002						if(0 != dk4str_cat_s(buf, szbuf, fnsep, NULL)) {
2003							ok = 0;
2004						}
2005						if(0 != dk4str_cat_s(buf, szbuf, shf, NULL)) {
2006							ok = 0;
2007						}
2008						if (1 == ok) {			$? ". process \"%!ds\"", buf
2009							run_for_filename(buf);
2010						}
2011						else {					$? ". path too long"
2012							/* ERROR: Path too long */
2013							exval = EXIT_FAILURE;
2014							dk4app_log_base3(app,DK4_LL_ERROR,100,105,pattern);
2015						}
2016					}
2017					else {						$? ". run for short file name"
2018						run_for_filename(shf);
2019					}
2020				}
2021				else {
2022					cc = 0;
2023				}
2024			}
2025			else {
2026				cc = -1;
2027			}
2028		}
2029		dk4dir_close(fne);
2030		if (0 == any_file_found) {
2031			exval = EXIT_FAILURE;
2032			dk4app_log_base3(app, DK4_LL_ERROR, 100, 107, pattern);
2033		}
2034	}
2035	else {										$? "! failed to open fne"
2036		exval = EXIT_FAILURE;
2037		/* ERROR: Failed to open file name expander (already reported) */
2038	}
2039	$? "- expand_filename_and_run"
2040}
2041
2042#endif
2043
2044
2045
2046/**	Allocate ring buffer used for negative line numbers.
2047	@return	1 on success, 0 on error.
2048*/
2049static
2050int
2051allocate_ring_buffer(void)
2052{
2053	size_t		 i;
2054	int			 back	= 1;
2055	$? "+ allocate_ring_buffer"
2056	switch (l_type) {
2057		case RANGE_NONE_NEG : case RANGE_POS_NEG : case RANGE_NEG_NONE :
2058		case RANGE_NEG_POS  : case RANGE_NEG_NEG : {	$? ". need ring buffer"
2059			back = 0;
2060			switch (l_type) {
2061				case RANGE_NONE_NEG : case RANGE_POS_NEG : {
2062					if ((dk4_um_t)(SIZE_MAX) >= l_end) {
2063						if ((dk4_um_t)1UL < l_end) {
2064							sz_ribu = (size_t)(l_end - (dk4_um_t)1UL);
2065						}
2066						else {			$? "! BUG l_end == 1"
2067							/* BUG: l_end is 1, should not happen */
2068							dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 23);
2069						}
2070					}
2071					else {				$? "! numeric overflow"
2072						/* ERROR: Numeric overflow */
2073						dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 24);
2074					}
2075				} break;
2076				default : {
2077					if ((dk4_um_t)(SIZE_MAX) >= l_start) {
2078						sz_ribu = (size_t)l_start;
2079					}
2080					else {				$? "! numeric overflow"
2081						/* ERROR: Numeric overflow */
2082						dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 25);
2083					}
2084				} break;
2085			}
2086			if (0 < sz_ribu) {			$? ". size %lu", (unsigned long)sz_ribu
2087				pribu = dk4mem_new_app(rbe_t,sz_ribu,app);
2088				if (NULL != pribu) {	$? ". buffer allocated"
2089					back = 1;
2090					for (i = 0; i < sz_ribu; i++) {
2091						pribu[i].str = NULL;
2092						pribu[i].lineno = (dk4_um_t)0UL;
2093						pribu[i].buflen = 0;
2094					}
2095				}
2096				else {					$? "! allocation failed"
2097					/* ERROR: Memory allocation failed (already reported) */
2098				}
2099			}
2100			else {						$? "! invalid size 0"
2101				/* Invalid ring buffer size */
2102				dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 26);
2103			}
2104		} break;
2105		default : {						$? ". no ring buffer needed"
2106			/* Empty by intent */
2107		} break;
2108	}
2109	$? "- allocate_ring_buffer %d", back
2110	return back;
2111}
2112
2113
2114
2115/**	Allocate ring buffer if necessary and process input.
2116*/
2117static
2118void
2119run_for_input(void)
2120{
2121	const dkChar	*fnptr;
2122	size_t			 xi;
2123	int				 argc;
2124	int				 i;
2125	$? "+ run_for_input"
2126	if (0 != allocate_ring_buffer()) {
2127		/*
2128			Initialize handling of one large data stream
2129		*/
2130		if (0 != one_stream) {
2131			lineno		= (dk4_um_t)0UL;
2132			pos_ribu	= 0;
2133			if (NULL != pribu) {
2134				for (xi = 0; xi < sz_ribu; xi++) {
2135					pribu[xi].str = NULL;
2136					pribu[xi].lineno = (dk4_um_t)0UL;
2137					pribu[xi].buflen = 0;
2138				}
2139			}
2140		}
2141		argc = dk4app_get_argc(app);
2142		if (0 < argc) {						$? ". have file names"
2143			for (i = 0; ((i < argc) && (0 != sig_can_continue(1))); i++) {
2144				fnptr = dk4app_get_argv(app, i);
2145				if (NULL != fnptr) {		$? ". file name \"%!ds\"", fnptr
2146#if DK4_ON_WINDOWS
2147					if (0 != dk4path_must_expand(fnptr)) {
2148						expand_filename_and_run(fnptr);
2149					}
2150					else {
2151#endif
2152						run_for_filename(fnptr);
2153#if DK4_ON_WINDOWS
2154					}
2155#endif
2156				}
2157				else {						$? "! failed to obtain file name"
2158					exval = EXIT_FAILURE;
2159					/* ERROR: Did not obtain file name */
2160					dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 27);
2161				}
2162			}
2163		}
2164		else {								$? ". no file names specified"
2165			run_for_file_with_name(stdin, dk_lines_kwnl[7], eie_stdin);
2166		}
2167		/*	Flush ring buffer and clean up resources for large data stream
2168		*/
2169		if (0 != one_stream) {
2170			/*
2171				Flush ring buffer
2172			*/
2173			flush_ring_buffer();
2174			/*
2175				Release ring buffer resources
2176			*/
2177			cycle_completed = 0;
2178			if (NULL != pribu) {
2179				for (xi = 0; xi < sz_ribu; xi++) {
2180					dk4mem_release(pribu[xi].str);
2181					pribu[xi].lineno = (dk4_um_t)0UL;
2182					pribu[xi].buflen = 0;
2183				}
2184			}
2185		}
2186	}
2187#if TRACE_DEBUG
2188	else {									$? "! failed to allocate ring buf"
2189	}
2190#endif
2191	if (NULL != pribu) {
2192		dk4mem_free(pribu);
2193	}
2194	$? "- run_for_input"
2195}
2196
2197
2198
2199/**	Run the functionality.
2200*/
2201static
2202void
2203run_the_functions(void)
2204{
2205	dkChar		*mybuf	= NULL;
2206	$? "+ run_the_functions"
2207	$? ". running"
2208	if (0 != process_command_line_arguments()) {
2209		/*
2210			If a line buffer larger than default is required,
2211			allocate the buffer and run
2212		*/
2213		if (DK4_SIZEOF(default_input_line_buffer,dkChar) < sz_ilb) {
2214			mybuf = dk4mem_new_app(dkChar,sz_ilb,app);
2215			if (NULL != mybuf) {
2216				ilb = mybuf;
2217				run_for_input();
2218				dk4mem_free(mybuf);
2219			}
2220			else {					$? "! memory allocation failed"
2221				exval = EXIT_FAILURE;
2222			}
2223		}
2224		/*	Otherwise run with default buffer
2225		*/
2226		else {
2227			run_for_input();
2228		}
2229	}
2230	else {
2231		exval = EXIT_FAILURE;
2232	}
2233	$? "- run_the_functions"
2234}
2235
2236
2237#if	DK4_HAVE_SIGACTION
2238/**	Set signal handlers and run.
2239*/
2240static
2241void
2242run_with_signals(void)
2243{
2244#ifdef SIGPIPE
2245	struct sigaction opipe;
2246#endif
2247	struct sigaction oint;
2248	struct sigaction oterm;
2249#ifdef SIGPIPE
2250	struct sigaction npipe;
2251#endif
2252	struct sigaction nint;
2253	struct sigaction nterm;
2254	int	 success = 0;
2255
2256#ifdef SIGPIPE
2257	/*	Set up signal handling for SIGPIPE.
2258	*/
2259	DK4_MEMRES(&npipe, sizeof(npipe));
2260	npipe.sa_handler = sig_handler_pipe;
2261	npipe.sa_flags = 0;
2262	if (0 != sigemptyset(&npipe.sa_mask)) {			$? "! sigemptyset SIGPIPE"
2263		/* ERROR: Failed to set up masked signal set for SIGPIPE */
2264		dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 0);
2265		goto finished;
2266	}
2267	if (0 != sigaddset(&npipe.sa_mask, SIGPIPE)) {	$? "! sigaddset SIGPIPE"
2268		/* ERROR: Failed to set up masked signal set for SIGPIPE */
2269		dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 0);
2270		goto finished;
2271	}
2272	if (0 != sigaction(SIGPIPE, &npipe, &opipe)) {	$? "! sigaction SIGPIPE"
2273		/* ERROR: Failed to set up signal handler for SIGPIPE */
2274		dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 0);
2275		goto finished;
2276	}
2277#endif
2278
2279	/*	Set up signal handling for SIGINT.
2280	*/
2281	DK4_MEMRES(&nint, sizeof(nint));
2282	nint.sa_handler = sig_handler_int;
2283	nint.sa_flags = 0;
2284	if (0 != sigemptyset(&nint.sa_mask)) {			$? "! sigemptyset SIGINT"
2285		/* ERROR: Failed to set up masked signal set for SIGINT */
2286		dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 1);
2287		goto restore_old_pipe;
2288	}
2289	if (0 != sigaddset(&nint.sa_mask, SIGINT)) {	$? "! sigaddset SIGINT"
2290		/* ERROR: Failed to set up masked signal set for SIGINT */
2291		dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 1);
2292		goto restore_old_pipe;
2293	}
2294	if (0 != sigaction(SIGINT, &nint, &oint)) {		$? "! sigaction SIGINT"
2295		/* ERROR: Failed to set up signal handler for SIGINT */
2296		dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 1);
2297		goto restore_old_pipe;
2298	}
2299
2300	/*	Set up signal handling for SIGTERM
2301	*/
2302	DK4_MEMRES(&nterm, sizeof(nterm));
2303	nterm.sa_handler = sig_handler_term;
2304	nterm.sa_flags = 0;
2305	if (0 != sigemptyset(&nterm.sa_mask)) {			$? "! sigemptyset SIGTERM"
2306		/* ERROR: Failed to set up masked signal set for SIGTERM */
2307		dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 2);
2308		goto restore_old_int;
2309	}
2310	if (0 != sigaddset(&nterm.sa_mask, SIGTERM)) {	$? "! sigaddset SIGTERM"
2311		/* ERROR: Failed to set up masked signal set for SIGTERM */
2312		dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 2);
2313		goto restore_old_int;
2314	}
2315	if (0 != sigaction(SIGTERM, &nterm, &oterm)) {	$? "! sigaction SIGTERM"
2316		/* ERROR: Failed to set up signal handler for SIGTERM */
2317		dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 2);
2318		goto restore_old_int;
2319	}
2320
2321	success = 1;
2322	run_the_functions();
2323
2324	/*	Restore signal handling for SIGTERM.
2325	*/
2326	if (0 != sigaction(SIGTERM, &oterm, NULL)) {	$? "! sigaction SIGTERM"
2327		/* ERROR: Failed to restore old SIGTERM settings */
2328		dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 3);
2329		success = 0;
2330	}
2331
2332	/*	Restore signal handling for SIGINT.
2333	*/
2334	restore_old_int:
2335	if (0 != sigaction(SIGINT, &oint, NULL)) {		$? "! sigaction SIGINT"
2336		/* ERROR: Failed to restore old SIGINT settings */
2337		dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 4);
2338		success = 0;
2339	}
2340
2341#ifdef SIGPIPE
2342	/*	Restore signal handling for SIGPIPE.
2343	*/
2344	restore_old_pipe:
2345	if (0 != sigaction(SIGPIPE, &opipe, NULL)) {	$? "! sigaction SIGPIPE"
2346		/* ERROR: Failed to restore old SIGPIPE settings */
2347		dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 5);
2348		success = 0;
2349	}
2350#endif
2351
2352	/*	Set exit status code if error occured.
2353	*/
2354	finished:
2355	if (0 == success) {						$? "! failure ins signal handling"
2356		exval = EXIT_FAILURE;
2357	}
2358}
2359#else
2360#if	DK4_HAVE_SIGSET
2361/**	Set signal handlers and run.
2362*/
2363static
2364void
2365run_with_signals(void)
2366{
2367#ifdef SIGPIPE
2368	dk4_sig_handler_t	*oldpipe = NULL;
2369#endif
2370	dk4_sig_handler_t	*oldint  = NULL;
2371	dk4_sig_handler_t	*oldterm = NULL;
2372
2373#ifdef SIGPIPE
2374	oldpipe = sigset(SIGPIPE, sig_handler_pipe);
2375#endif
2376	oldint  = sigset(SIGINT,  sig_handler_int);
2377	oldterm = sigset(SIGTERM, sig_handler_term);
2378	run_the_functions();
2379	sigset(SIGTERM, oldterm);
2380	sigset(SIGINT,  oldint);
2381#ifdef SIGPIPE
2382	sigset(SIGPIPE, oldpipe);
2383#endif
2384}
2385#else
2386#if	DK4_HAVE_SIGNAL
2387/**	Set signal handlers and run.
2388*/
2389static
2390void
2391run_with_signals(void)
2392{
2393#ifdef SIGPIPE
2394	dk4_sig_handler_t	*oldpipe = NULL;
2395#endif
2396	dk4_sig_handler_t	*oldint  = NULL;
2397	dk4_sig_handler_t	*oldterm = NULL;
2398#ifdef SIGPIPE
2399	oldpipe = signal(SIGPIPE, sig_handler_pipe);
2400#endif
2401	oldint  = signal(SIGINT,  sig_handler_int);
2402	oldterm = signal(SIGTERM, sig_handler_term);
2403	run_the_functions();
2404	signal(SIGTERM, oldterm);
2405	signal(SIGINT,  oldint);
2406#ifdef SIGPIPE
2407	signal(SIGPIPE, oldpipe);
2408#endif
2409}
2410#else
2411/**	Set signal handlers and run.
2412*/
2413static
2414void
2415run_with_signals(void)
2416{
2417	run_the_functions();
2418}
2419#endif
2420#endif
2421#endif
2422
2423
2424/**	Main function.
2425	@param	argc	Number of command line arguments.
2426	@param	argv	Command line arguments array.
2427	@return	0 on success, all other values indicate errors.
2428*/
2429#if DK4_CHAR_SIZE > 1
2430int wmain(int argc, wchar_t *argv[])
2431#else
2432int main(int argc, char *argv[])
2433#endif
2434{
2435	dk4_sig_atomic_t	shp;
2436	$!trace-init dk-lines.deb
2437	$? "+ main"
2438	dk4fput_initialize_stdout();
2439	dk4fput_initialize_stderr();
2440	app = dk4app_open_cmd(
2441		argc, argv, dk_lines_options, dk_lines_sz_options,
2442		dk_lines_kwnl[0], DKT_VERSION_DK,
2443		dk_lines_kwnl[1], dk_lines_help_text, dk_lines_license_text
2444	);
2445	if (NULL != app) {
2446		sz_msg	=	dk4app_string_table_size(dk_lines_kw_def);
2447		msgs	=	dk4app_string_table(app, dk_lines_kwnl[2], dk_lines_kw_def);
2448		eie_file  = dk4app_get_file_in_encoding(app);
2449		eie_stdin = dk4app_get_stdin_encoding(app);
2450		if (0 != dk4app_can_run_normally(app)) {
2451			exval = EXIT_SUCCESS;
2452			run_with_signals();
2453#ifdef	SIGPIPE
2454			shp = sig_read_atomic(&sig_had_pipe);
2455#else
2456			shp = 0;
2457#endif
2458			if (0 != shp) {
2459				if (0 != verbose) {				$? "! stopped by SIGPIPE"
2460					/* ERROR: Stopped by SIGPIPE */
2461					dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 28);
2462				}
2463				exval = EXIT_FAILURE;
2464			}
2465			else {
2466				if (0 != had_write_error) {		$? "! write error occured"
2467					/* ERROR: Had write error */
2468					dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 29);
2469					exval = EXIT_FAILURE;
2470				}
2471			}
2472			if (0 != sig_read_atomic(&sig_had_int)) {	$? "! stopped by SIGINT"
2473				/* ERROR: Stopped by interrupt */
2474				dk4app_log_1(app, msgs, sz_msg, DK4_LL_ERROR, 30);
2475			}
2476		}
2477		else {
2478			if (0 != dk4app_help_version_license(app)) {
2479				exval = EXIT_SUCCESS;
2480			}
2481		}
2482		dk4app_close(app);
2483	}
2484	fflush(stdout);
2485	fflush(stderr);
2486	dk4fput_cleanup_stderr();
2487	dk4fput_cleanup_stdout();
2488	$? "- main %d", exval
2489	$!trace-end
2490	exit(exval); return exval;
2491}
2492
2493/* vim: set ai sw=4 ts=4 : */
2494