1%%	options
2
3copyright owner	=	Dirk Krause
4copyright year	=	2015-xxxx
5SPDX-License-Identifier:	BSD-3-Clause
6
7%%	module
8
9#include "dk4conf.h"
10
11#if DK4_ON_WINDOWS
12#ifndef WINDOWS_H_INCLUDED
13#include <windows.h>
14#define WINDOWS_H_INCLUDED
15#endif
16#endif
17
18#if DK4_HAVE_SIGNAL_H
19#ifndef SIGNAL_H_INCLUDED
20#include <signal.h>
21#define SIGNAL_H_INCLUDED 1
22#endif
23#endif
24
25#if DK4_HAVE_IO_H
26#ifndef IO_H_INCLUDED
27#include <io.h>
28#define	IO_H_INCLUDED 1
29#endif
30#endif
31
32#if DK4_HAVE_FCNTL_H
33#ifndef FCNTL_H_INCLUDED
34#include <fcntl.h>
35#define	FCNTL_H_INCLUDED 1
36#endif
37#endif
38
39#include <libdk4base/dk4types.h>
40#include <libdk4base/dk4const.h>
41#include <libdk4base/dk4vers.h>
42#include <libdk4app/dk4app.h>
43#include <libdk4app/dk4aopt.h>
44#include <libdk4base/dk4mem.h>
45#include <libdk4app/dk4mema.h>
46#include <libdk4c/dk4fput.h>
47#include <libdk4base/dk4mpl.h>
48#include <libdk4maiodd/dk4maiddbl.h>
49#if 0
50#include <libdk4maiodh/dk4maidhmu.h>
51#include <libdk4maiodd/dk4maiddmu.h>
52#include <libdk4maiodd/dk4maiddmi.h>
53#endif
54#include <libdk4maiodd/dk4maidddu.h>
55#include <libdk4maiodh/dk4maidhdu.h>
56#include <libdk4maiodd/dk4maidddi.h>
57#include <libdk4maiodd/dk4maodd.h>
58#include <libdk4c/dk4enc.h>
59#include <libdk4base/dk4strd.h>
60#include <libdk4app/dk4strda.h>
61#include <libdk4c/dk4tspdk.h>
62#include <libdk4app/dk4fopda.h>
63#include <libdk4c/dk4pathd.h>
64#include <libdk4c/dk4filed.h>
65#include <libdk4base/dk4unused.h>
66
67#ifndef	DK4WMAIN_H_INCLUDED
68#include <libdk4base/dk4wmain.h>
69#endif
70
71
72$!trace-include
73
74
75
76/**	Structure to store one line, optionally with evaluation result.
77*/
78typedef struct {
79  dkChar		*line;	/**< Text line. */
80  dkChar		*fts;	/**< File type suffix. */
81  union {
82    dk4_um_t		 u;	/**< Unsigned number at start of line. */
83    dk4_im_t		 i;	/**< Signed number at start of line. */
84    double		 d;	/**< Floating point number at start of line. */
85  } value;			/**< Value at start of line. */
86  char			 h;	/**< Flag: Have value. */
87} dk_sort_line_t;
88
89
90/**	Default help text, shown if help file is not found.
91*/
92static const dkChar * const	dk_sort_help_text[] = {
93$!text	macro=dkT
94
95Sort text line by line
96
97dk-sort [<options>] [<file(s)>]
98
99Options:
100-i <encoding>	--input-encoding=<encoding>	Expected input encoding.
101-c		--case-insensitive		Case-insensitive comparisons.
102-b		--ignore-leading-blanks		Ignore leading whitespaces.
103-w		--normalize			Normalize text lines.
104-e		--suppress-empty-lines		Suppress output for empty lines.
105-t		--file-type			Sort by file type.
106-s <size>	--skip-words=<size>		Number of words to skip.
107-n		--number			Leading signed integer values.
108-u		--unsigned			Leading unsigned integer values.
109-x		--hex				Leading hexadecimal numbers.
110-f		--float				Leading floating point numbers.
111-v		--value-lines-first		Lines containing numbers first.
112-m		--merge-equal-lines		Merge equal lines.
113-r		--reverse			Reverse output order.
114-l <size>	--line-size=<size>		Line buffer sizes.
115-R		--reset				Ignore preferences values.
116
117--help						Show this short help text.
118--manual					*** SHOW FULL MANUAL. ***
119--version					Show version information.
120--license					Show license information.
121
122http://sourceforge.net/p/dktools/wiki/dk-sort/
123
124$!end
125};
126
127
128
129/**	License text.
130*/
131static const dkChar * const	dk_sort_license_text[] = {
132$!text	macro=dkT,preprocessor
133This software uses code from the following projects, either directly or as
134a library:
135
136dktools		Dirk Krause's tools and libraries.
137		See http://sourceforge.net/p/dktools/wiki/Home/
138		for more information.
139#if DK4_HAVE_ZLIB_H
140
141zlib		Data compression library.
142		See http://www.zlib.net/ for more information.
143#endif
144#if DK4_HAVE_BZLIB_H
145
146bzip2		Data compression program and library.
147		See http://www.bzip.org/ for more information.
148#endif
149
150All the licenses below apply to the program.
151Licenses for used libraries are shown as found on my Scientific Linux 6.x
152computer in the /usr/share/doc directory on 2015-04-01. Check the project
153homepages of the used libraries for additional information and/or updated
154license terms.
155
156
157DK tools and libraries license
158==============================
159Copyright (c) 2015-2016, Dirk Krause
160All rights reserved.
161
162Redistribution and use in source and binary forms, with or without
163modification, are permitted provided that the following conditions are met:
164
1651. Redistributions of source code must retain the above copyright notice,
166   this list of conditions and the following disclaimer.
1672. Redistributions in binary form must reproduce the above copyright
168   notice, this list of conditions and the following disclaimer in the
169   documentation and/or other materials provided with the distribution.
1703. Neither the name of the copyright holder nor the names of its
171   contributors may be used to endorse or promote products derived from
172   this software without specific prior written permission.
173
174THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
175``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
176LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
177A PARTICULAR PURPOSE ARE DISCLAIMED.
178IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
179DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
180(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
181SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
182CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
183LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
184OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
185SUCH DAMAGE.
186#if DK4_HAVE_ZLIB_H
187
188
189Zlib license
190============
191(C) 1995-2004 Jean-loup Gailly and Mark Adler
192
193This software is provided 'as-is', without any express or implied
194warranty.  In no event will the authors be held liable for any damages
195arising from the use of this software.
196
197Permission is granted to anyone to use this software for any purpose,
198including commercial applications, and to alter it and redistribute it
199freely, subject to the following restrictions:
200
2011. The origin of this software must not be misrepresented; you must not
202   claim that you wrote the original software. If you use this software
203   in a product, an acknowledgment in the product documentation would be
204   appreciated but is not required.
2052. Altered source versions must be plainly marked as such, and must not be
206   misrepresented as being the original software.
2073. This notice may not be removed or altered from any source distribution.
208
209Jean-loup Gailly        Mark Adler
210jloup@gzip.org          madler@alumni.caltech.edu
211#endif
212#if DK4_HAVE_BZLIB_H
213
214
215Bzip2 and libbzip2 library license
216==================================
217This program, "bzip2", the associated library "libbzip2", and all
218documentation, are copyright (C) 1996-2007 Julian R Seward.  All
219rights reserved.
220
221Redistribution and use in source and binary forms, with or without
222modification, are permitted provided that the following conditions
223are met:
224
2251. Redistributions of source code must retain the above copyright
226   notice, this list of conditions and the following disclaimer.
227
2282. The origin of this software must not be misrepresented; you must
229   not claim that you wrote the original software.  If you use this
230   software in a product, an acknowledgment in the product
231   documentation would be appreciated but is not required.
232
2333. Altered source versions must be plainly marked as such, and must
234   not be misrepresented as being the original software.
235
2364. The name of the author may not be used to endorse or promote
237   products derived from this software without specific prior written
238   permission.
239
240THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
241OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
242WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
243ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
244DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
245DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
246GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
247INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
248WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
249NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
250SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
251
252Julian Seward, jseward@bzip.org
253bzip2/libbzip2 version 1.0.5 of 10 December 2007
254#endif
255
256$!end
257};
258
259
260
261/**	Command line options.
262*/
263static const dk4_option_specification_t dk_sort_options[] = {
264  { dkT('R'), dkT("reset"),			DK4_OPT_ARG_NONE },
265  { dkT('i'), dkT("input-encoding"),		DK4_OPT_ARG_STRING },
266  { dkT('c'), dkT("case-insensitive"),		DK4_OPT_ARG_NONE },
267  { dkT('b'), dkT("ignore-leading-blanks"),	DK4_OPT_ARG_NONE },
268  { dkT('w'), dkT("normalize"),			DK4_OPT_ARG_NONE },
269  { dkT('t'), dkT("file-type"), 		DK4_OPT_ARG_NONE },
270  { dkT('s'), dkT("skip-words"),		DK4_OPT_ARG_SIZE },
271  { dkT('n'), dkT("number"),			DK4_OPT_ARG_NONE },
272  { dkT('u'), dkT("unsigned"),			DK4_OPT_ARG_NONE },
273  { dkT('x'), dkT("hex"),			DK4_OPT_ARG_NONE },
274  { dkT('f'), dkT("float"),			DK4_OPT_ARG_NONE },
275  { dkT('v'), dkT("value-lines-first"),		DK4_OPT_ARG_NONE },
276  { dkT('m'), dkT("merge-equal-lines"),		DK4_OPT_ARG_NONE },
277  { dkT('r'), dkT("reverse"),			DK4_OPT_ARG_NONE },
278  { dkT('l'), dkT("line-size"),			DK4_OPT_ARG_SIZE },
279  { dkT('e'), dkT("suppress-empty-lines"),	DK4_OPT_ARG_NONE }
280};
281
282
283
284/**	Number of elements in the dk_sort_options array.
285*/
286static const size_t dk_sort_sz_options =
287sizeof(dk_sort_options)/sizeof(dk4_option_specification_t);
288
289
290
291/**	Constant text strings used by the module, not localized.
292*/
293static const dkChar * const	dk_sort_kwnl[] = {
294$!string-table macro=dkT
295#
296#	0	Program group name
297#
298dktools
299#
300#	1	Help text file name
301#
302dk-sort.txt
303#
304#	2	String table file name
305#
306dk-sort.str
307#
308#	3	File name when processing standard input.
309#
310stdin
311#
312#	4	File open mode to read a file
313#
314rb
315#
316#	5	Backslash for file name expansion
317#
318\\
319#
320#	6	Preference name for line size
321#
322line.size
323$!end
324};
325
326
327
328/**	Text strings used by program, localized versions used if found.
329*/
330static const dkChar * const	dk_sort_kw_def[] = {
331$!string-table	macro=dkT
332#
333#	0	Error message: Failed to set up signal handlers!
334#
335Failed to set up signal handlers!
336#
337#	1	Error message: Failed to restore previous signal handlers!
338#
339Failed to restore previous signal handlers!
340#
341#	2 3 4	Error messages for encoding/decoding/processing errors
342#
343Encoding error!
344Decoding error!
345Processing error!
346#
347#	5 6 7 8 9 10	Error messages with detailed position
348#
349Encoding error!\n\tByte:
350Decoding error!\n\tByte:
351Processing error!\n\tByte:
352,\n\tCharacter:
353,\n\tPosition in line:
354.
355#
356#	11		Options -u, -x, -f mutually exclusive!
357#
358Options -u, -x, and -f are mutually exclusive!
359#
360#	12 13		Error: Illegal input encoding!
361#
362Illegal input encoding: "
363"!
364#
365#	14		Error: Failed to initialize text stream processing!
366#
367Failed to initialize text stream processing!
368$!end
369};
370
371
372
373/**	Default buffer to use if sizes up to 1024 are specified.
374*/
375static dkChar	input_line_buffer[1024];
376
377
378
379/**	Input buffer really to use.
380*/
381static dkChar	*ibptr	=	input_line_buffer;
382
383
384
385/**	Allocated input buffer address for line sizes larger than 1024.
386*/
387static dkChar	*allocated_buffer	=	NULL;
388
389
390
391/**	Localized message texts.
392*/
393static const dkChar * const	*dk_sort_msg	=	dk_sort_kw_def;
394
395
396
397/**	Application structure.
398*/
399static dk4_app_t	*app	=	NULL;
400
401
402
403/**	Line storage.
404*/
405static dk4_sto_t	*lsto	=	NULL;
406
407
408
409/**	Iterator to traverse line storage.
410*/
411static dk4_sto_it_t	*lsti	=	NULL;
412
413
414
415/**	Number of strings in dk_sort_msg array.
416*/
417static size_t			 dk_sort_sz_msg	=
418sizeof(dk_sort_kw_def)/sizeof(DK4_PCDKCHAR) - 1;
419
420
421/**	Size of ibptr buffer.
422*/
423static size_t	 ibsz	=	DK4_SIZEOF(input_line_buffer,dkChar);
424
425
426
427/**	Number of words to skip when processing file names.
428*/
429static size_t	 skip_words	=	0;
430
431
432
433/**	Flag: Case-insensitive comparison.
434*/
435static int	 case_insensitive	=	0;
436
437
438
439/**	How to handle blanks:
440	0 leave as is,
441	1 remove leading and trailing blanks,
442	2 normalize lines.
443*/
444static int	 handle_blanks		=	0;
445
446
447
448/**	How to handle leading values:
449	0 do not use numeric values,
450	1 use signed integers
451	2 use unsigned integers
452	3 use unsigned integers, specified in hexadecimal notation
453	4 use floating point numbers.
454*/
455static int	 handle_values		=	0;
456
457
458
459/**	Flag: Sort files type file type suffix.
460*/
461static int	 handle_file_type	=	0;
462
463
464
465/**	Flag: Show lines containing values before lines not containing
466	a value. Default is to show lines without a value first.
467*/
468static int	 value_first		=	0;
469
470
471
472/**	Flag: Merge equal lines.
473*/
474static int	 merge			=	0;
475
476
477
478/**	Flag: Reverse search order.
479*/
480static int	 reversed		=	0;
481
482
483
484/**	Flag: Suppress empty lines.
485*/
486static int	 suppress_empty		=	0;
487
488
489/**	Expected input encoding.
490*/
491static int	 input_encoding		=
492#if DK4_ON_WINDOWS
493	DK4_FILE_ENCODING_WIN1252
494#else
495	DK4_FILE_ENCODING_PLAIN
496#endif
497;
498
499
500
501/**	Flag: Already reported decoding errors.
502*/
503static int	dk_sort_had_decoding_errors	=	0;
504
505
506
507/**	Flag: Already reported encoding errors.
508*/
509static int	dk_sort_had_encoding_errors	=	0;
510
511
512
513/**	Flag: Already reported processing errors.
514*/
515static int	dk_sort_had_proc_errors	=	0;
516
517
518
519/**	Exit status code returned by the main function.
520*/
521static int exval	=	EXIT_FAILURE;
522
523
524
525#ifdef SIGPIPE
526/**	Indicator: SIGPIPE signal received.
527*/
528static
529DK4_VOLATILE
530dk4_sig_atomic_t	sig_had_pipe	=	0;
531#endif
532
533/**	Indicator: SIGINT signal received.
534*/
535static
536DK4_VOLATILE
537dk4_sig_atomic_t	sig_had_int	=	0;
538
539/**	Indicator: SIGTERM signal received.
540*/
541static
542DK4_VOLATILE
543dk4_sig_atomic_t	sig_had_term	=	0;
544
545
546
547/**	Pass a volatile pointer to an atomic integer.
548	This function is necessary as some compilers mis-optimize
549	direct access to volatile variables (at least if you believe
550	one of the coding standards).
551	@param	ptr	Address of atomic integer variable.
552	@return	The unmodified pointer.
553*/
554static
555DK4_VOLATILE
556dk4_sig_atomic_t *
557sig_pass_pointer(DK4_VOLATILE dk4_sig_atomic_t *ptr)
558{
559  return ptr;
560}
561
562
563
564#ifdef SIGPIPE
565/**	Handler for SIGPIPE signal.
566	@param	signo	Signal number (always SIGPIPE, ignored).
567*/
568static
569void
570sig_handler_pipe(int DK4_ARG_UNUSED(signo))
571{
572  DK4_UNUSED_ARG(signo)
573  *sig_pass_pointer(&sig_had_pipe) = 1;
574}
575#endif
576
577/**	Handler for SIGINT signal.
578	@param	signo	Signal number (always SIGINT, ignored).
579*/
580static
581void
582sig_handler_int(int DK4_ARG_UNUSED(signo) )
583{
584  DK4_UNUSED_ARG(signo)
585  *sig_pass_pointer(&sig_had_int) = 1;
586}
587
588/**	Handler for SIGTERM signal.
589	@param	signo	Signal number (always SIGTERM, ignored).
590*/
591static
592void
593sig_handler_term(int DK4_ARG_UNUSED(signo) )
594{
595  DK4_UNUSED_ARG(signo)
596  *sig_pass_pointer(&sig_had_term) = 1;
597}
598
599/**	Read value from volatile atomic type.
600	This function is necessary as some compilers mis-optimize
601	direct access to volatile variables (at least if you believe
602	one of the coding standards).
603	@param	ap	Pointer to volatile atomic variable.
604	@return	Contents of the variable.
605*/
606static
607dk4_sig_atomic_t
608sig_read_atomic(DK4_VOLATILE dk4_sig_atomic_t *ap)
609{
610  return (*ap);
611}
612
613/**	Check whether we can continue or if a signal was received.
614	@param	check_pipe	Flag: Check for occured SIGPIPE signal too.
615	@return	1 if the program can continue, 0 if a signal was received.
616*/
617static
618int
619sig_can_continue(
620#ifdef SIGPIPE
621	int check_pipe
622#else
623	int DK4_ARG_UNUSED(check_pipe)
624#endif
625)
626{
627  int		back = 1;
628#ifndef SIGPIPE
629  DK4_UNUSED_ARG(check_pipe)
630#else
631  if (0 != check_pipe) {
632    if (0 != sig_read_atomic(&sig_had_pipe)) { back = 0; }
633  }
634#endif
635  if (0 != sig_read_atomic(&sig_had_int )) { back = 0; }
636  if (0 != sig_read_atomic(&sig_had_term)) { back = 0; }
637  return back;
638}
639
640
641
642/**	Check whether we can continue.
643	@return	1 for yes, 0 for no.
644*/
645static
646int
647dk_sort_can_continue(void)
648{
649  int		back	=	1;
650  if (0 == sig_can_continue(1)) {
651    back = 0;
652  }
653  return back;
654}
655
656
657
658/**	Delete a storage node.
659	@param	ptr	Node to delete.
660*/
661static
662void
663dk_sort_delete_node(dk_sort_line_t *ptr)
664{
665  $? "+ dk_sort_delete_node"
666  if (NULL != ptr) {
667    dk4mem_release(ptr->line);
668    dk4mem_free(ptr);
669  }
670  $? "- dk_sort_delete_node"
671}
672
673
674
675/**	Create storage node for a text line.
676	@param	line	Text line to store.
677	@return	Pointer to new node on success, NULL on error.
678*/
679static
680dk_sort_line_t *
681dk_sort_new_node(dkChar *line)
682{
683  dk_sort_line_t	*back	=	NULL;
684  dkChar		*lines	=	NULL;	/* Start of line */
685  const dkChar		*endptr	=	NULL;	/* First text after value */
686  double		 d;			/* Evaluation double */
687  dk4_um_t		 u;			/* Evaluation unsigned */
688  dk4_im_t		 i;			/* Evaluation integer */
689  $? "+ dk_sort_new_node"
690  /*	Normalize or remove leading and trailing whitespace if required.
691  */
692  if (NULL != line) {
693    dk4str_delnl(line);
694    if (0 < handle_blanks) {
695      lines = dk4str_start(line, NULL);
696      if (NULL != lines) {
697        line = lines;
698        switch (handle_blanks) {
699          case 1: {
700	    dk4str_rtwh(line, NULL);
701	  } break;
702	  case 2: {
703	    dk4str_normalize(line, NULL);
704	  } break;
705        }
706      } else {
707        *line = dkT('\0');
708      }
709    }
710  }
711  if ((0 != suppress_empty) && (NULL != line)) {
712    lines = dk4str_start(line, NULL);
713    if (NULL == lines) {
714      line = NULL;
715    }
716  }
717
718  /*	Create node.
719  */
720  if (NULL != line) {
721    back = dk4mem_new_app(dk_sort_line_t,1,app);
722    if (NULL != back) {
723      back->fts	 = NULL;
724      back->h    = 0x00;
725      back->line = dk4str_dup_app(line, app);
726      if (NULL != back->line) {
727        if (0 != handle_file_type) {
728	  back->fts = dk4path_get_suffix(back->line, NULL);
729	}
730      } else {
731        dk_sort_delete_node(back);
732	back = NULL;
733	exval = EXIT_FAILURE;
734	/* ERROR: Memory, already reported */
735      }
736    } else {
737      exval = EXIT_FAILURE;
738      /* ERROR: Memory, already reported */
739    }
740  }
741
742  /*	Evaluate node if necessary.
743  */
744  if (NULL != back) {
745    switch (handle_values) {
746      case 4: {
747        d = 0.0;
748        if (0 != dk4ma_input_dk_double(&d, line, &endptr, 2, NULL)) {
749	  (back->value).d = d;
750	  back->h = 0x01;
751	}
752      } break;
753      case 3: {
754        u = (dk4_um_t)0UL;
755	if (0 != dk4ma_input_dk_hex_dk4_um_t(&u, line, &endptr, 2, NULL)) {
756	  (back->value).u = u;
757	  back->h = 0x01;
758	}
759      } break;
760      case 2: {
761        u = (dk4_um_t)0UL;
762	if (0 != dk4ma_input_dk_dec_dk4_um_t(&u, line, &endptr, 2, NULL)) {
763	  (back->value).u = u;
764	  back->h = 0x01;
765	}
766      } break;
767      case 1: {
768        i = (dk4_im_t)0L;
769	if (0 != dk4ma_input_dk_dec_dk4_im_t(&i, line, &endptr, 2, NULL)) {
770	  (back->value).i = i;
771	  back->h = 0x01;
772	}
773      } break;
774    }
775  }
776  $? "- dk_sort_new_node PTR=%d", TR_IPTR(back)
777  return back;
778}
779
780
781
782static
783int
784dk_sort_is_dot_file(const dkChar *fn)
785{
786  const dkChar	*fns	= NULL;
787  const dkChar	*ptr	= NULL;
788  int		 back	= 0;
789  $? "+ dk_sort_is_dot_file \"%s\"", fn
790  ptr = fn;
791  while (dkT('\0') != *ptr) {
792#if DK4_HAVE_BACKSLASH_AS_SEP
793    if (dkT('\\') == *ptr) { fns = ptr; }
794#else
795    if (dkT('/') == *ptr) { fns = ptr; }
796#endif
797    ptr++;
798  }
799  if (NULL == fns) { fns = fn; }
800  else { fns++; }
801  $? ". fns = \"%s\"", fns
802  if (dkT('.') == *fns) {
803    back = 1;
804  }
805#if DK4_ON_WINDOWS
806  if (dkT('_') == *fns) {
807    back = 1;
808  }
809#endif
810  $? "- dk_sort_is_dot_file %d", back
811  return back;
812}
813
814
815
816/**	Compare two storage nodes.
817	@param	l	Left node.
818	@param	r	Right node.
819	@param	cr	Comparison criteria, ignored here.
820	@return	Comparison result.
821*/
822static
823int
824dk_sort_compare_nodes(const void *l, const void *r, int DK4_ARG_UNUSED(cr) )
825{
826  int		 	 back = 0;
827  dk_sort_line_t	*pl;
828  dk_sort_line_t	*pr;
829  const dkChar		*fnleft;
830  const dkChar		*fnright;
831  $? "+ dk_sort_compare_nodes"
832  DK4_UNUSED_ARG(cr)
833  if (NULL != l) {
834    if (NULL != r) {
835      pl = (dk_sort_line_t *)l;
836      pr = (dk_sort_line_t *)r;
837
838      /*	Check file type suffix if required.
839      */
840      if (0 != handle_file_type) {
841        fnleft = dk4str_skip(pl->line, skip_words);
842	fnright = dk4str_skip(pr->line, skip_words);
843	if (NULL != fnleft) {		$? ". fnleft  = \"%s\"", fnleft
844	  if (NULL != fnright) {	$? ". fnright = \"%s\"", fnright
845	    if (0 != dk4file_is_directory(fnleft, NULL)) {
846	      if (0 == dk4file_is_directory(fnright, NULL)) {
847	        back = -1;
848	      }
849	    } else {
850	      if (0 != dk4file_is_directory(fnright, NULL)) {
851	        back = 1;
852	      }
853	    }
854	    if (0 == back) {
855	      if (0 != dk_sort_is_dot_file(fnleft)) {
856	        if (0 == dk_sort_is_dot_file(fnright)) {
857		  back = -1;
858		}
859	      } else {
860	        if (0 != dk_sort_is_dot_file(fnright)) {
861		  back = 1;
862		}
863	      }
864	    }
865	  } else {
866	    back = 1;
867	  }
868	} else {
869	  if (NULL != fnright) {
870	    back = -1;
871	  }
872	}
873        if (0 == back) {
874	  if (NULL != pl->fts) {
875	    if (NULL != pr->fts) {
876	      back = dk4str_pathcmp(pl->fts, pr->fts);
877#if DK4_HAVE_CASE_INSENSITIVE_PATHNAMES
878	      if (0 == back) {
879	        back = dk4str_cmp(pl->fts, pr->fts);
880	      }
881#endif
882	    } else {
883	      back = 1;
884	    }
885	  } else {
886	    if (NULL != pr->fts) {
887	      back = -1;
888	    }
889	  }
890	}
891      }
892
893      /*	Check evaluation if required.
894      */
895      if (0 == back) {
896        if (0 != handle_values) {
897          if (0x00 != pl->h) {
898	    if (0x00 != pr->h) {	/* both values */
899	      switch (handle_values) {
900	        case 4: {
901	          if ((pl->value).d < (pr->value).d) {
902		    back = -1;
903		  } else {
904		    if ((pl->value).d > (pr->value).d) {
905		      back = 1;
906		    }
907		  }
908	        } break;
909	        case 2: case 3: {
910	          if ((pl->value).u < (pr->value).u) {
911		    back = -1;
912		  } else {
913		    if ((pl->value).u > (pr->value).u) {
914		      back = 1;
915		    }
916		  }
917	        } break;
918	        case 1: {
919	          if ((pl->value).i < (pr->value).i) {
920		    back = -1;
921		  } else {
922		    if ((pl->value).i > (pr->value).i) {
923		      back = 1;
924		    }
925		  }
926	        } break;
927	      }
928	    } else {		/* only left value */
929	      back = ((0 != value_first) ? -1 : 1);
930	    }
931	  } else {
932	    if (0x00 != pr->h) {	/* only right value */
933	      back = ((0 != value_first) ? 1 : -1);
934	    }
935	  }
936        }
937      }
938
939      /*	String comparison
940      */
941      if (0 == back) {
942        if (0 != case_insensitive) {
943	  back = dk4str_casecmp(pl->line, pr->line);
944	  if (0 == back) {
945	    back = dk4str_cmp(pl->line, pr->line);
946	  }
947	} else {
948	  back = dk4str_cmp(pl->line, pr->line);
949	}
950      }
951
952      /*	Reverse order if necessary.
953      */
954      if (0 != reversed) { back = -1 * back; }
955    } else {
956      back = 1;
957    }
958  } else {
959    if (NULL != r) {
960      back = -1;
961    }
962  } $? "- dk_sort_compare_nodes %d", back
963  return back;
964}
965
966
967
968static
969int
970dk_sort_line_handler(
971  void		* DK4_ARG_UNUSED(obj),
972  dkChar	*line,
973  dk4_um_t	 DK4_ARG_UNUSED(lineno),
974  dk4_er_t	* DK4_ARG_UNUSED(erp)
975)
976{
977  dk_sort_line_t	*nptr;
978  int		 	 back	=	DK4_TSP_RES_OK;
979  $? "+ dk_sort_line_handler"
980  DK4_UNUSED_ARG(obj)
981  DK4_UNUSED_ARG(lineno)
982  DK4_UNUSED_ARG(erp)
983  nptr = dk_sort_new_node(line);
984  if (NULL != nptr) {
985    if (0 == dk4sto_add(lsto, nptr, NULL)) {
986      dk_sort_delete_node(nptr);
987      dk4app_log_base1(app, DK4_LL_ERROR, 90);
988      back = DK4_TSP_RES_FATAL;
989      exval = EXIT_FAILURE;
990    }
991  } $? "- dk_sort_line_handler %d", back
992  return back;
993}
994
995
996
997/**	Report an error with position.
998	@param	i1	Index of first message part in array.
999	@param	i2	Index of alternative first message part.
1000	@param	bno	Current byte number.
1001	@param	lno	Current line number.
1002	@param	cno	Current character number.
1003	@param	cil	Current character number within line.
1004	@param	fn	File name processed.
1005*/
1006static
1007void
1008dk_sort_report_with_position(
1009  size_t		 i1,
1010  size_t		 i2,
1011  dk4_um_t		 bno,
1012  dk4_um_t		 lno,
1013  dk4_um_t		 cno,
1014  dk4_um_t		 cil,
1015  const dkChar		*fn
1016)
1017{
1018  dkChar         b1[8*(1+sizeof(dk4_um_t))+16];
1019  dkChar         b2[8*(1+sizeof(dk4_um_t))+16];
1020  dkChar         b3[8*(1+sizeof(dk4_um_t))+16];
1021  const dkChar	*oldlogname	=	NULL;
1022  dk4_um_t	 oldlogline	=	(dk4_um_t)0UL;
1023  int		 allbuffersok	=	0;
1024
1025  oldlogname =  dk4app_get_log_source_file(app);
1026  oldlogline =  dk4app_get_log_source_line(app);
1027  dk4app_set_log_source_file(app, fn);
1028  dk4app_set_log_source_line(app, lno);
1029  if (0 != dk4ma_write_decimal_unsigned(b3,DK4_SIZEOF(b3,dkChar),cil,0,NULL)) {
1030  if (0 != dk4ma_write_decimal_unsigned(b2,DK4_SIZEOF(b2,dkChar),cno,0,NULL)) {
1031  if (0 != dk4ma_write_decimal_unsigned(b1,DK4_SIZEOF(b1,dkChar),bno,0,NULL)) {
1032    allbuffersok = 1;
1033  }
1034  }
1035  }
1036  if (0 != allbuffersok) {
1037    dk4app_log_7(
1038      app, dk_sort_msg, dk_sort_sz_msg, DK4_LL_ERROR, i1, 8, 9, 10, b1, b2, b3
1039    );
1040  } else {
1041    dk4app_log_1(app, dk_sort_msg, dk_sort_sz_msg, DK4_LL_ERROR, i2);
1042  }
1043  dk4app_set_log_source_line(app, oldlogline);
1044  dk4app_set_log_source_file(app, oldlogname);
1045}
1046
1047
1048
1049/**	Report errors from line processing.
1050	@param	er_en	Error report for encoding/decoding errors.
1051	@param	en_pr	Error report for processing errors.
1052	@param	fn	File name for error message.
1053*/
1054static
1055void
1056dk_sort_report_errors(
1057  dk4_er_t	*er_en,
1058  dk4_er_t	*er_pr,
1059  const dkChar	*fn
1060)
1061{
1062  $? "+ dk_sort_report_errors"
1063  if (NULL != er_en) {
1064    switch (er_en->ec) {
1065      case DK4_E_DECODING_FAILED : {
1066        switch (dk_sort_had_decoding_errors) {
1067	  case 0: case 1: case 2: {
1068	    dk_sort_report_with_position(
1069	      6, 3,
1070	      er_en->dt.fpos.byteno, er_en->dt.fpos.lineno,
1071	      er_en->dt.fpos.charno, er_en->dt.fpos.charinline,
1072	      fn
1073	    );
1074	  } break;
1075	}
1076	if (3 > dk_sort_had_decoding_errors) { dk_sort_had_decoding_errors++; }
1077      } break;
1078      case DK4_E_ENCODING_FAILED : {
1079        switch (dk_sort_had_encoding_errors) {
1080	  case 0: case 1: case 2: {
1081	    dk_sort_report_with_position(
1082	      5, 2,
1083	      er_en->dt.fpos.byteno, er_en->dt.fpos.lineno,
1084	      er_en->dt.fpos.charno, er_en->dt.fpos.charinline,
1085	      fn
1086	    );
1087	  } break;
1088	}
1089	if (3 > dk_sort_had_encoding_errors) { dk_sort_had_encoding_errors++; }
1090      } break;
1091    }
1092  }
1093  if (NULL != er_pr) {
1094    if (DK4_E_NONE != er_pr->ec) {
1095      switch (dk_sort_had_proc_errors) {
1096        case 0: case 1: case 2: {
1097	    dk_sort_report_with_position(
1098	      7, 4,
1099	      er_pr->dt.fpos.byteno, er_pr->dt.fpos.lineno,
1100	      er_pr->dt.fpos.charno, er_pr->dt.fpos.charinline,
1101	      fn
1102	    );
1103	} break;
1104      }
1105      if (3 > dk_sort_had_proc_errors) { dk_sort_had_proc_errors++; }
1106    }
1107  }
1108  $? "- dk_sort_report_errors"
1109}
1110
1111
1112
1113/**	Process one input file opened in binary mode.
1114	@param	fipo		File to process.
1115	@param	filename	File name.
1116*/
1117static
1118void
1119dk_sort_process_file(FILE *fipo, const dkChar *filename)
1120{
1121  dk4_tspdk_t		tsp;
1122  dk4_er_t		er_en;
1123  dk4_er_t		er_pr;
1124  dk4_er_t		er;
1125  int			res;
1126  int			cc;
1127  int			c;
1128  unsigned char		ub;
1129  $? "+ dk_sort_process_file"
1130  dk4error_init(&er_en);
1131  dk4error_init(&er_pr);
1132  dk4error_init(&er);
1133  res = dk4tspdk_setup_line(
1134    &tsp, NULL,
1135    dk_sort_line_handler,
1136    ibptr, ibsz,
1137    dk4app_get_encoding(app), input_encoding,
1138    &er
1139  );
1140  if (0 != res) {
1141    cc = 1;
1142    do {
1143      if (dk_sort_can_continue()) {
1144        c = fgetc(fipo);
1145	if (EOF != c) {
1146	  ub = (unsigned char)c;
1147	  switch (dk4tspdk_add_one_byte(&tsp, ub)) {
1148	    case DK4_TSP_RES_FATAL : {
1149	      cc = -1;
1150	      exval = EXIT_FAILURE;
1151	      dk4tspdk_get_errors(&er_en, &er_pr, &tsp);
1152	      dk_sort_report_errors(&er_en, &er_pr, filename);
1153	    } break;
1154	    case DK4_TSP_RES_ERROR : {
1155	      exval = EXIT_FAILURE;
1156	      dk4tspdk_get_errors(&er_en, &er_pr, &tsp);
1157	      dk_sort_report_errors(&er_en, &er_pr, filename);
1158	    } break;
1159	  }
1160	} else {
1161	  cc = 0;
1162	}
1163      } else {
1164        cc = -1;
1165      }
1166    } while (1 == cc);
1167    if (0 == cc) {
1168      switch (dk4tspdk_finish(&tsp)) {
1169        case DK4_TSP_RES_FATAL : {
1170	  exval = EXIT_FAILURE;
1171	  dk4tspdk_get_errors(&er_en, &er_pr, &tsp);
1172	  dk_sort_report_errors(&er_en, &er_pr, filename);
1173	} break;
1174	case DK4_TSP_RES_ERROR : {
1175	  exval = EXIT_FAILURE;
1176	  dk4tspdk_get_errors(&er_en, &er_pr, &tsp);
1177	  dk_sort_report_errors(&er_en, &er_pr, filename);
1178	} break;
1179      }
1180    }
1181  } else {
1182    /* ERROR: Failed to initialize text stream processor */
1183    dk4app_log_1(app, dk_sort_msg, dk_sort_sz_msg, DK4_LL_ERROR, 14);
1184    exval = EXIT_FAILURE;
1185  }
1186  $? "- dk_sort_process_file"
1187}
1188
1189
1190
1191/**	Process standard input.
1192*/
1193static
1194void
1195dk_sort_process_stdin(void)
1196{
1197#if DK4_ON_WINDOWS
1198  int	oldmode;
1199#endif
1200  $? "+ dk_sort_process_stdin"
1201#if DK4_ON_WINDOWS
1202  oldmode = _setmode(_fileno(stdin), _O_BINARY);
1203#endif
1204  dk_sort_process_file(stdin, dk_sort_kwnl[3]);
1205#if DK4_ON_WINDOWS
1206  _setmode(_fileno(stdin), oldmode);
1207#endif
1208  $? "- dk_sort_process_stdin"
1209}
1210
1211
1212
1213static
1214void
1215dk_sort_process_one_file_name(const dkChar *fn)
1216{
1217  FILE	*fipo;
1218  int	 tests	=	DK4_FOPEN_SC_IS_REGULAR;
1219  $? "+ dk_sort_process_one_file_name"
1220  fipo = dk4fopen_app(fn, dk_sort_kwnl[4], tests, app);
1221  if (NULL != fipo) {
1222    dk_sort_process_file(fipo, fn);
1223    fclose(fipo);
1224  } else {
1225    /* ERROR: Failed to open file, already reported */
1226    exval = EXIT_FAILURE;
1227  }
1228  $? "- dk_sort_process_one_file_name"
1229}
1230
1231
1232
1233#if DK4_ON_WINDOWS
1234static
1235void
1236dk_sort_expand_one_file_name(const dkChar *pattern)
1237{
1238  dkChar	 buf[DK4_MAX_PATH];
1239  dk4_dir_t	*fne;
1240  const dkChar	*shf;			/* Short file name */
1241  const dkChar	*pth;			/* Path */
1242  int		 any_file_found	= 0;	/* Flag: Any file found */
1243  int		 can_continue;
1244  $? "+ dk_sort_expand_one_file_name"
1245  fne = dk4app_fne_open(pattern, app);
1246  if (NULL != fne) {
1247    can_continue = 1;
1248    while ((0 != can_continue) && (NULL != (shf = dk4dir_next_file(fne)))) {
1249      any_file_found = 1;
1250      pth = dk4dir_get_path(fne);
1251      if (NULL != pth) {
1252        if (0 != dk4str_cpy_s(buf, DK4_SIZEOF(buf,dkChar), pth, NULL)) {
1253	  if(0 != dk4str_cat_s(buf,DK4_SIZEOF(buf,dkChar),dk_sort_kwnl[5],NULL))
1254	  {
1255	    if(0 != dk4str_cat_s(buf, DK4_SIZEOF(buf,dkChar), shf, NULL)) {
1256	      dk_sort_process_one_file_name(buf);
1257	    } else {
1258	      exval = EXIT_FAILURE;
1259	      /* ERROR: Path too long */
1260	      dk4app_log_base3(app, DK4_LL_ERROR, 100, 105, pattern);
1261	    }
1262	  }
1263	  else
1264	  {
1265	    exval = EXIT_FAILURE;
1266	    /* ERROR: Path too long */
1267	    dk4app_log_base3(app, DK4_LL_ERROR, 100, 105, pattern);
1268	  }
1269	} else {
1270	  exval = EXIT_FAILURE;
1271	  /* ERROR: Path too long! */
1272	  dk4app_log_base3(app, DK4_LL_ERROR, 100, 105, pattern);
1273	}
1274      } else {
1275        dk_sort_process_one_file_name(shf);
1276      }
1277      can_continue = dk_sort_can_continue();
1278      if (EXIT_FAILURE == exval) { can_continue = 0; }
1279    }
1280    dk4dir_close(fne);
1281    if (0 == any_file_found) {
1282      /* ERROR: No such file */
1283      dk4app_log_base3(app, DK4_LL_ERROR, 100, 107, pattern);
1284      exval = EXIT_FAILURE;
1285    }
1286  } else {
1287    /* ERROR: Failed to open file name expander (already reported) */
1288    exval = EXIT_FAILURE;
1289  }
1290  $? "- dk_sort_expand_one_file_name"
1291}
1292#endif
1293
1294
1295
1296/**	Process either the files specified on the command line
1297	or standard input otherwise.
1298*/
1299static
1300void
1301dk_sort_process_files(void)
1302{
1303  const dkChar	*fn;		/* Current file to process */
1304  int		 xargc;		/* Number of command line arguments */
1305  int		 i;		/* Current file index */
1306  $? "+ dk_sort_process_files"
1307  xargc = dk4app_get_argc(app);
1308  if (0 < xargc) {
1309    for (
1310      i = 0;
1311      ((i < xargc) && (0 != dk_sort_can_continue()) && (EXIT_SUCCESS == exval));
1312      i++
1313    )
1314    {
1315      fn = dk4app_get_argv(app, i);
1316      if (NULL != fn) {
1317#if DK4_ON_WINDOWS
1318        if (0 != dk4path_must_expand(fn)) {
1319	  dk_sort_expand_one_file_name(fn);
1320	} else {
1321#endif
1322	  dk_sort_process_one_file_name(fn);
1323#if DK4_ON_WINDOWS
1324	}
1325#endif
1326      }
1327    }
1328  } else {
1329    dk_sort_process_stdin();
1330  }
1331  $? "- dk_sort_process_files"
1332}
1333
1334
1335
1336/**	Write stored text lines to standard output.
1337*/
1338static
1339void
1340dk_sort_write_output(void)
1341{
1342  dk_sort_line_t	*nptr;
1343  const dkChar		*prevtext;
1344  int			 printthis;
1345  $? "+ dk_sort_write_output"
1346  if ((0 != dk_sort_can_continue()) && (EXIT_SUCCESS == exval)) {
1347    dk4sto_it_reset(lsti);
1348    prevtext = NULL;
1349    do {
1350      if ((0 != dk_sort_can_continue()) && (EXIT_SUCCESS == exval)) {
1351        nptr = (dk_sort_line_t *)dk4sto_it_next(lsti);
1352	if (NULL != nptr) {
1353	  printthis = 1;
1354	  if (0 != merge) {
1355	    if (NULL != prevtext) {
1356	      if (0 == dk4str_cmp(prevtext, nptr->line)) {
1357	        printthis = 0;
1358	      }
1359	    }
1360	    prevtext = nptr->line;
1361	  }
1362	  if (0 != printthis) {
1363	    if (0 == dk4fputs(nptr->line, stdout, NULL)) {
1364	      exval = EXIT_FAILURE;
1365	      nptr = NULL;
1366	    }
1367	    if (0 == dk4fputc(dkT('\n'), stdout, NULL)) {
1368	      exval = EXIT_FAILURE;
1369	      nptr = NULL;
1370	    }
1371	  }
1372	}
1373      } else {
1374        nptr = NULL;
1375      }
1376    } while (NULL != nptr);
1377  }
1378  $? "- dk_sort_write_output"
1379}
1380
1381
1382
1383/**	Run after setting signal handlers.
1384*/
1385static
1386void
1387dk_sort_normal_run(void)
1388{
1389  dkChar	buf[32];			/* Input encoding name */
1390  dk4_er_t	er;				/* Error report */
1391  dk_sort_line_t	*nopt;			/* Current node */
1392  size_t	myll		=	1024;	/* Line length */
1393  size_t	nskip		=	0;
1394  int		options_error	=	0;	/* Flag: Error in options */
1395  int		res		=	0;	/* Operations result */
1396  $? "+ dk_sort_normal_run"
1397  /*	Process command line options
1398  */
1399  if (0 != dk4app_opt_is_set_short(app, dkT('c'), NULL)) {
1400    case_insensitive = 1;
1401  }
1402  if (0 != dk4app_opt_is_set_short(app, dkT('b'), NULL)) {
1403    handle_blanks = 1;
1404  }
1405  if (0 != dk4app_opt_is_set_short(app, dkT('w'), NULL)) {
1406    handle_blanks = 2;
1407  }
1408  if (0 != dk4app_opt_is_set_short(app, dkT('t'), NULL)) {
1409    handle_file_type = 1;
1410  }
1411  if (0 != dk4app_opt_is_set_short(app, dkT('s'), NULL)) {
1412    if (0 != dk4app_opt_get_size_short(&nskip, app, dkT('s'), NULL)) {
1413      skip_words = nskip;
1414    }
1415  }
1416  if (0 != dk4app_opt_is_set_short(app, dkT('n'), NULL)) {
1417    handle_values = 1;
1418  }
1419  if (0 != dk4app_opt_is_set_short(app, dkT('u'), NULL)) {
1420    handle_values = 2;
1421  }
1422  if (0 != dk4app_opt_is_set_short(app, dkT('x'), NULL)) {
1423    handle_values = 3;
1424  }
1425  if (0 != dk4app_opt_is_set_short(app, dkT('f'), NULL)) {
1426    if ((2 != handle_values) && (3 != handle_values)) {
1427      handle_values = 4;
1428    } else {
1429      dk4app_log_1(app, dk_sort_msg, dk_sort_sz_msg, DK4_LL_ERROR, 11);
1430      options_error = 1;
1431    }
1432  }
1433  if (0 != dk4app_opt_is_set_short(app, dkT('v'), NULL)) {
1434    value_first = 1;
1435  }
1436  if (0 != dk4app_opt_is_set_short(app, dkT('m'), NULL)) {
1437    merge = 1;
1438  }
1439  if (0 != dk4app_opt_is_set_short(app, dkT('r'), NULL)) {
1440    reversed = 1;
1441  }
1442  if (0 != dk4app_opt_is_set_short(app, dkT('e'), NULL)) {
1443    suppress_empty = 1;
1444  }
1445  if (0 < dk4app_get_argc(app)) {
1446    input_encoding = dk4app_get_file_in_encoding(app);
1447  } else {
1448    input_encoding = dk4app_get_stdin_encoding(app);
1449  }
1450  if (0 != dk4app_opt_is_set_short(app, dkT('i'), NULL)) {
1451    res = dk4app_opt_get_string_short(
1452      buf, DK4_SIZEOF(buf,dkChar), app, dkT('i'), NULL
1453    );
1454    if (0 != res) {
1455      res = dk4enc_find(&input_encoding, NULL, buf, NULL);
1456      if (0 == res) {
1457        dk4app_log_3(app,dk_sort_msg,dk_sort_sz_msg,DK4_LL_ERROR,12,13,buf);
1458	options_error = 1;
1459      }
1460    }
1461  }
1462  res = 0;
1463  if (0 != dk4app_opt_is_set_short(app, dkT('l'), NULL)) {
1464    res = dk4app_opt_get_size_short(&myll, app, dkT('l'), NULL);
1465  }
1466  if (0 == res) {
1467    if (0 == dk4app_opt_is_set_short(app, dkT('R'), NULL)) {
1468      res = dk4app_pref_get_size(&myll, app, dk_sort_kwnl[6], 0);
1469    }
1470  }
1471  if ((0 != res) && (DK4_SIZEOF(input_line_buffer,dkChar) < myll)) {
1472    allocated_buffer = dk4mem_new_app(dkChar,myll,app);
1473    if (NULL != allocated_buffer) {
1474      ibptr = allocated_buffer;
1475      ibsz = myll;
1476    } else {
1477      /* ERROR: Memory allocation failed, already reported */
1478      options_error = 1;
1479    }
1480  }
1481
1482  /*	Processing part.
1483  */
1484  if (0 == options_error) {
1485    dk4error_init(&er);
1486    lsto = dk4sto_open(&er);
1487    if (NULL != lsto) {
1488      dk4sto_set_comp(lsto, dk_sort_compare_nodes, 0);
1489      lsti = dk4sto_it_open(lsto, &er);
1490      if (NULL != lsti) {
1491        exval = EXIT_SUCCESS;
1492        dk_sort_process_files();
1493	dk_sort_write_output();
1494        dk4sto_it_reset(lsti);
1495	do {
1496	  nopt = (dk_sort_line_t *)dk4sto_it_next(lsti);
1497	  if (NULL != nopt) {
1498	    dk_sort_delete_node(nopt);
1499	  }
1500	} while (NULL != nopt);
1501        dk4sto_it_close(lsti);
1502      } else {
1503        dk4app_log_base1(app, DK4_LL_ERROR, 90);
1504      }
1505      dk4sto_close(lsto);
1506    } else {
1507      dk4app_log_base1(app, DK4_LL_ERROR, 90);
1508    }
1509  }
1510
1511  /*	Cleanup: release dynamically allocated memory for input line buffer.
1512  */
1513  if (NULL != allocated_buffer) {
1514    dk4mem_free(allocated_buffer); allocated_buffer = NULL; ibsz = 1024;
1515  }
1516  $? "- dk_sort_normal_run"
1517}
1518
1519
1520
1521#if	DK4_HAVE_SIGACTION
1522/**	Set signal handlers and run.
1523*/
1524static
1525void
1526dk_sort_run_with_signal_handlers(void)
1527{
1528#ifdef SIGPIPE
1529  struct sigaction opipe;
1530#endif
1531  struct sigaction oint;
1532#ifdef SIGPIPE
1533  struct sigaction npipe;
1534#endif
1535  struct sigaction nint;
1536  struct sigaction oterm;
1537  struct sigaction nterm;
1538  int	 success = 0;
1539  $? "+ dk_sort_run_with_signal_handlers (sigaction)"
1540#ifdef SIGPIPE
1541  /*	Set up signal handling for SIGPIPE.
1542  */
1543  DK4_MEMRES(&npipe, sizeof(npipe));
1544  npipe.sa_handler = sig_handler_pipe;
1545  npipe.sa_flags = 0;
1546  if (0 != sigemptyset(&npipe.sa_mask)) {
1547    /* ERROR: Failed to set up masked signal set for SIGPIPE */
1548    dk4app_log_1(app, dk_sort_msg, dk_sort_sz_msg, DK4_LL_ERROR, 0);
1549    goto finished;
1550  }
1551  if (0 != sigaddset(&npipe.sa_mask, SIGPIPE)) {
1552    /* ERROR: Failed to set up masked signal set for SIGPIPE */
1553    dk4app_log_1(app, dk_sort_msg, dk_sort_sz_msg, DK4_LL_ERROR, 0);
1554    goto finished;
1555  }
1556  if (0 != sigaction(SIGPIPE, &npipe, &opipe)) {
1557    /* ERROR: Failed to set up signal handler for SIGPIPE */
1558    dk4app_log_1(app, dk_sort_msg, dk_sort_sz_msg, DK4_LL_ERROR, 0);
1559    goto finished;
1560  }
1561#endif
1562
1563  /*	Set up signal handling for SIGINT.
1564  */
1565  DK4_MEMRES(&nint, sizeof(nint));
1566  nint.sa_handler = sig_handler_int;
1567  nint.sa_flags = 0;
1568  if (0 != sigemptyset(&nint.sa_mask)) {
1569    /* ERROR: Failed to set up masked signal set for SIGINT */
1570    dk4app_log_1(app, dk_sort_msg, dk_sort_sz_msg, DK4_LL_ERROR, 0);
1571    goto restore_old_pipe;
1572  }
1573  if (0 != sigaddset(&nint.sa_mask, SIGINT)) {
1574    /* ERROR: Failed to set up masked signal set for SIGINT */
1575    dk4app_log_1(app, dk_sort_msg, dk_sort_sz_msg, DK4_LL_ERROR, 0);
1576    goto restore_old_pipe;
1577  }
1578  if (0 != sigaction(SIGINT, &nint, &oint)) {
1579    /* ERROR: Failed to set up signal handler for SIGINT */
1580    dk4app_log_1(app, dk_sort_msg, dk_sort_sz_msg, DK4_LL_ERROR, 0);
1581    goto restore_old_pipe;
1582  }
1583
1584  /*	Set up signal handling for SIGTERM
1585  */
1586  DK4_MEMRES(&nterm, sizeof(nterm));
1587  nterm.sa_handler = sig_handler_term;
1588  nterm.sa_flags = 0;
1589  if (0 != sigemptyset(&nterm.sa_mask)) {
1590    /* ERROR: Failed to set up masked signal set for SIGTERM */
1591    dk4app_log_1(app, dk_sort_msg, dk_sort_sz_msg, DK4_LL_ERROR, 0);
1592    goto restore_old_int;
1593  }
1594  if (0 != sigaddset(&nterm.sa_mask, SIGTERM)) {
1595    /* ERROR: Failed to set up masked signal set for SIGTERM */
1596    dk4app_log_1(app, dk_sort_msg, dk_sort_sz_msg, DK4_LL_ERROR, 0);
1597    goto restore_old_int;
1598  }
1599  if (0 != sigaction(SIGTERM, &nterm, &oterm)) {
1600    /* ERROR: Failed to set up signal handler for SIGTERM */
1601    dk4app_log_1(app, dk_sort_msg, dk_sort_sz_msg, DK4_LL_ERROR, 0);
1602    goto restore_old_int;
1603  }
1604
1605  success = 1;
1606  dk_sort_normal_run();
1607
1608  /*	Restore signal handling for SIGTERM.
1609  */
1610  if (0 != sigaction(SIGTERM, &oterm, NULL)) {
1611    /* ERROR: Failed to restore old SIGTERM settings */
1612    dk4app_log_1(app, dk_sort_msg, dk_sort_sz_msg, DK4_LL_ERROR, 1);
1613    success = 0;
1614  }
1615
1616  /*	Restore signal handling for SIGINT.
1617  */
1618  restore_old_int:
1619  if (0 != sigaction(SIGINT, &oint, NULL)) {
1620    /* ERROR: Failed to restore old SIGPIPE settings */
1621    dk4app_log_1(app, dk_sort_msg, dk_sort_sz_msg, DK4_LL_ERROR, 1);
1622    success = 0;
1623  }
1624
1625#ifdef SIGPIPE
1626  /*	Restore signal handling for SIGPIPE.
1627  */
1628  restore_old_pipe:
1629  if (0 != sigaction(SIGPIPE, &opipe, NULL)) {
1630    /* ERROR: Failed to restore old SIGPIPE settings */
1631    dk4app_log_1(app, dk_sort_msg, dk_sort_sz_msg, DK4_LL_ERROR, 1);
1632    success = 0;
1633  }
1634#endif
1635
1636  /*	Set exit status code if error occured.
1637  */
1638  finished:
1639  if (0 == success) { exval = EXIT_FAILURE; }
1640  $? "- dk_sort_run_with_signal_handlers (sigaction)"
1641}
1642#else
1643#if	DK4_HAVE_SIGSET
1644/**	Set signal handlers and run.
1645*/
1646static
1647void
1648dk_sort_run_with_signal_handlers(void)
1649{
1650#ifdef SIGPIPE
1651  dk4_sig_handler_t	*oldpipe = NULL;
1652#endif
1653  dk4_sig_handler_t	*oldint  = NULL;
1654  dk4_sig_handler_t	*oldterm = NULL;
1655  $? "+ dk_sort_run_with_signal_handlers (sigset)"
1656#ifdef SIGPIPE
1657  oldpipe = sigset(SIGPIPE, sig_handler_pipe);
1658#endif
1659  oldint  = sigset(SIGINT,  sig_handler_int);
1660  oldterm = sigset(SIGTERM, sig_handler_term);
1661  dk_sort_normal_run();
1662  sigset(SIGTERM, oldterm);
1663  sigset(SIGINT,  oldint);
1664#ifdef SIGPIPE
1665  sigset(SIGPIPE, oldpipe);
1666#endif
1667  $? "- dk_sort_run_with_signal_handlers (sigset)"
1668}
1669#else
1670#if	DK4_HAVE_SIGNAL
1671/**	Set signal handlers and run.
1672*/
1673static
1674void
1675dk_sort_run_with_signal_handlers(void)
1676{
1677#ifdef SIGPIPE
1678  dk4_sig_handler_t	*oldpipe = NULL;
1679#endif
1680  dk4_sig_handler_t	*oldint  = NULL;
1681  dk4_sig_handler_t	*oldterm = NULL;
1682  $? "+ dk_sort_run_with_signal_handlers (signal)"
1683#ifdef SIGPIPE
1684  oldpipe = signal(SIGPIPE, sig_handler_pipe);
1685#endif
1686  oldint  = signal(SIGINT,  sig_handler_int);
1687  oldterm = signal(SIGTERM, sig_handler_term);
1688  dk_sort_normal_run();
1689  signal(SIGTERM, oldterm);
1690  signal(SIGINT,  oldint);
1691#ifdef SIGPIPE
1692  signal(SIGPIPE, oldpipe);
1693#endif
1694  $? "- dk_sort_run_with_signal_handlers (signal)"
1695}
1696#else
1697/**	Set signal handlers and run.
1698*/
1699static
1700void
1701dk_sort_run_with_signal_handlers(void)
1702{
1703  dk_sort_normal_run();
1704}
1705#endif
1706#endif
1707#endif
1708
1709
1710
1711/**	Main function.
1712	@param	argc	Number of command line arguments.
1713	@param	argv	Command line arguments array.
1714	@return	0 on success, all other values indicate errors.
1715*/
1716#if DK4_CHAR_SIZE > 1
1717int wmain(int argc, wchar_t *argv[])
1718#else
1719int main(int argc, char *argv[])
1720#endif
1721{
1722  $!trace-init	dk-sort.deb
1723  $? "+ main"
1724  dk4fput_initialize_stdout();
1725  dk4fput_initialize_stderr();
1726  app = dk4app_open_cmd(
1727    argc, argv, dk_sort_options, dk_sort_sz_options,
1728    dk_sort_kwnl[0], DKT_VERSION_DK,
1729    dk_sort_kwnl[1], dk_sort_help_text, dk_sort_license_text
1730  );
1731  if (NULL != app) {
1732    dk_sort_sz_msg = dk4app_string_table_size(dk_sort_kw_def);
1733    dk_sort_msg    = dk4app_string_table(app, dk_sort_kwnl[2], dk_sort_kw_def);
1734    if (0 != dk4app_can_run_normally(app)) {
1735      $? ". normal processing"
1736      dk_sort_run_with_signal_handlers();
1737    } else {
1738      $? ". help/version/license"
1739      if (0 != dk4app_help_version_license(app)) {
1740        exval = EXIT_SUCCESS;
1741      }
1742    }
1743    dk4app_close(app);
1744  } else {
1745  }
1746  fflush(stdout);
1747  fflush(stderr);
1748  dk4fput_cleanup_stderr();
1749  dk4fput_cleanup_stdout();
1750  $? "- main %d", exval
1751  $!trace-end
1752  return exval;
1753}
1754
1755