1%%	options
2
3copyright owner	=	Dirk Krause
4copyright year	=	2015-xxxx
5SPDX-License-Identifier:	BSD-3-Clause
6
7
8
9%%	header
10
11/**	@file
12	Additional string functions for
13	8 bit characters.
14
15	Although some of the functions in this module duplicate
16	functionality from string.h functions, you should use
17	the functions from this module.
18	With DK4DK4_WIN_AVOID_CRT or DK4_WIN_DENY_CRT defined to non-zero
19	these functions call Windows API functions like
20	lstrcmpA() ... instead of CRT functions.
21*/
22
23#ifndef DK4CONF_H_INCLUDED
24#if DK4_BUILDING_DKTOOLS4
25#include "dk4conf.h"
26#else
27#include <dktools-4/dk4conf.h>
28#endif
29#endif
30
31#ifndef DK4TYPES_H_INCLUDED
32#if DK4_BUILDING_DKTOOLS4
33#include <libdk4base/dk4types.h>
34#else
35#include <dktools-4/dk4types.h>
36#endif
37#endif
38
39#ifndef DK4ERROR_H_INCLUDED
40#if DK4_BUILDING_DKTOOLS4
41#include <libdk4base/dk4error.h>
42#else
43#include <dktools-4/dk4error.h>
44#endif
45#endif
46
47#ifndef STDLIB_H_INCLUDED
48#include <stdlib.h>
49#define STDLIB_H_INCLUDED 1
50#endif
51
52
53#ifdef __cplusplus
54extern "C" {
55#endif
56
57/**	Copy string, check destination buffer size.
58
59	CRT on Windows: Optional.
60	@param	dst	Destination buffer address.
61	@param	sz	Destination buffer size.
62	@param	src	Source string.
63	@param	erp	Error report, may be NULL.
64	@return	1 on success, 0 on error.
65
66	Error codes:
67	- DK4_E_INVALID_ARGUMENTS<br>
68	  if dst or src is NULL or sz is 0,
69	- DK4_E_BUFFER_TOO_SMALL<br>
70	  if the dst buffer is too small.
71*/
72int
73
74dk4str8_cpy_s(char *dst, size_t sz, const char *src, dk4_er_t *erp);
75
76/**	Concatenate strings, check destination buffer size.
77
78	CRT on Windows: Optional.
79	@param	dst	Destination buffer address.
80	@param	sz	Destination buffer size.
81	@param	src	Source string.
82	@param	erp	Error report, may be NULL.
83	@return	1 on success, 0 on error.
84
85	Error codes:
86	- DK4_E_INVALID_ARGUMENTS<br>
87	  if dst or src is NULL or sz is 0,
88	- DK4_E_MATH_OVERFLOW<br>
89	  if a mathematical overflow occured in size calculation,
90	- DK4_E_BUFFER_TOO_SMALL<br>
91	  if the concatenated string doest not fit into the buffer.
92*/
93int
94
95dk4str8_cat_s(char *dst, size_t sz, const char *src, dk4_er_t *erp);
96
97/**	Copy characters within a string from right to left.
98	This copy operation is intended for moving characters within
99	one string from right to left, making the string shorter
100	as is. This function does not check buffer size or report
101	any error.
102	For normal string copy operations, use the dk4str8_cpy_s()
103	function instead.
104
105	CRT on Windows: Not used.
106	@param	dst	Destination buffer address.
107	@param	src	Source string.
108*/
109void
110
111dk4str8_cpy_to_left(char *dst, const char *src);
112
113/**	Find string length.
114
115	CRT on Windows: Optional.
116	@param	src	Source string.
117	@return	String length.
118*/
119size_t
120
121dk4str8_len(const char *src);
122
123/**	Compare two strings.
124
125	CRT on Windows: Optional.
126	@param	s1	Left string, may be NULL.
127	@param	s2	Right string, may be NULL.
128	@return	1 if s1>s2, 0 if s1==s2, -1 if s1<s2.
129*/
130int
131
132dk4str8_cmp(const char *s1, const char *s2);
133
134/**	Compare two strings.
135
136	CRT on Windows: Optional, disabling CRT probably degrades performance.
137	@param	s1	Left string, may be NULL.
138	@param	s2	Right string, may be NULL.
139	@param	n	Maximum number of characters to compare.
140	@return	1 if s1>s2, 0 if s1==s2, -1 if s1<s2.
141*/
142int
143
144dk4str8_ncmp(const char *s1, const char *s2, size_t n);
145
146/**	Compare two strings, case-insensitive comparison.
147
148	CRT on Windows: Optional.
149	@param	s1	Left string, may be NULL.
150	@param	s2	Right string, may be NULL.
151	@return	1 if s1>s2, 0 if s1==s2, -1 if s1<s2.
152*/
153int
154
155dk4str8_casecmp(const char *s1, const char *s2);
156
157/**	Compare two file path names.
158
159	CRT on Windows: Optional.
160	@param	s1	Left string, may be NULL.
161	@param	s2	Right string, may be NULL.
162	@return	1 if s1>s2, 0 if s1==s2, -1 if s1<s2.
163*/
164int
165
166dk4str8_pathcmp(const char *s1, const char *s2);
167
168/**	Find first occurance of character c in string s.
169
170	CRT on Windows: Optional, disabling CRT degrades performance.
171	@param	s	String to search for c.
172	@param	c	Character to find.
173	@return	Pointer to first occurance if found, NULL otherwise.
174*/
175char *
176
177dk4str8_chr(const char *s, char c);
178
179/**	Find last occurance of character c in string s.
180
181	CRT on Windows: Optional, disabling CRT degrades performance.
182	@param	s	String to search for c.
183	@param	c	Character to find.
184	@return	Pointer to last occurance if found, NULL otherwise.
185*/
186char *
187
188dk4str8_rchr(const char *s, char c);
189
190/**	Retrieve first token from *stringp.
191
192	CRT on Windows: Optional, disabling CRT degrades performance.
193	@param	stringp	Adress of string pointer.
194	@param	delim	String containing the delimiter set,
195			my be NULL to use the default delimiter
196			set (space, tabulator, carriage return and newline).
197	@return	Pointer to first token on success, NULL if no (further)
198	token found.
199*/
200char *
201
202dk4str8_sep(char **stringp, const char *delim);
203
204/**	Find first token in string.
205
206	CRT on Windows: Optional, disabling CRT degrades performance.
207	@param	src	String to search for tokens.
208	@param	delim	String containing the delimiter characters,
209			may be NULL to use the default delimiter set.
210	@return	Pointer to first token if found, NULL otherwise.
211*/
212char *
213
214dk4str8_start(const char *src, const char *delim);
215
216/**	Cut string after first token, find start of second token in string.
217
218	CRT on Windows: Optional, disabling CRT degrades performance.
219	@param	src	String to search for tokens,
220			normally the result of dk4str8_start() or a
221			previous dk4str8_next() call.
222	@param	delim	String containing the delimiter characters,
223			may be NULL to use the default delimiter set.
224	@return	Pointer to second token if found, NULL otherwise.
225*/
226char *
227
228dk4str8_next(char *src, const char *delim);
229
230/**	Split a string into tokens.
231
232	CRT on Windows: Optional, disabling CRT degrades performance.
233	@param	dpp	Pointer to string pointer array.
234	@param	szdpp	Size of destination string pointer array.
235	@param	src	Source string to process.
236	@param	delim	String containing delimiter characters,
237			may be NULL to use the default delimiter set.
238	@param	erp	Error report, may be NULL.
239	@return	Number of tokens found on success, 0 if no token
240	found or on error.
241
242	Error codes:
243	- DK4_E_INVALID_ARGUMENTS<br>
244	  for invalid function arguments,
245	- DK4_E_BUFFER_TOO_SMALL<br>
246	  with dt.mem.nelem set to the number of tokens in string if the dpp
247	  array is too short.
248*/
249size_t
250
251dk4str8_tokenize(
252  char **dpp, size_t szdpp, char *src, const char *delim, dk4_er_t *erp
253);
254
255/**	Normalize a string (remove leading and trailing delimiters,
256	replace delimiter sequences by just one delimiter).
257
258	CRT on Windows: Optional, disabling CRT degrades performance.
259	@param	src	Buffer containing the string to normalize.
260	@param	delim	String containing delimiter characters,
261			may be NULL to use the default delimiter set.
262*/
263void
264
265dk4str8_normalize(char *src, const char *delim);
266
267/**	Find index of a string in an array of strings.
268
269	CRT on Windows: Optional.
270	@param	arr	Array of strings.
271	@param	str	String to find in array.
272	@param	cs	Flag: Case-sensitive search (0=no, other=yes).
273	@return	Non-negative index value on success, -1 on error.
274*/
275int
276
277dk4str8_array_index(const char * const *arr, const char *str, int cs);
278
279/**	Find index of a string in an array of strings.
280
281	CRT on Windows: Optional, disabling CRT degrades performances
282	and reduces to upper-case conversion to characters 'a' to 'z'.
283	@param	arr	Array of string allowing abbreviations.
284	@param	spec	Special character indicating start of optional text.
285	@param	str	String to find in array.
286	@param	cs	Flag: Case-sensitive search (0=no, other=yes).
287	@return	Non-negative index value on success, -1 on error.
288*/
289int
290
291dk4str8_abbr_index(const char * const *arr, char spec, const char *str, int cs);
292
293/**	Check whether a text matches a pattern, the text may be abbreviated.
294
295	CRT on Windows: Optional, disabling CRT degrades performances
296	and reduces to upper-case conversion to characters 'a' to 'z'
297	in case-insensitive comparisons.
298 	@param	str	Text to check.
299 	@param	pattern	Pattern for comparison.
300 	@param	spec	Special character marking the abbreviation in the
301	pattern.
302 	@param	cs	Flag: Case sensitive (1) or not (0).
303 	@return	1 for a match, 0 otherwise.
304*/
305int
306
307dk4str8_is_abbr(const char *str, const char *pattern, char spec, int cs);
308
309/**	Check whether a string represents a boolean value.
310
311	CRT on Windows: Optional, disabling CRT degrades performances.
312	@param	str	String to check.
313	@return	1 if str represents a boolean value, 0 otherwise.
314*/
315int
316
317dk4str8_is_bool(const char *str);
318
319/**	Check whether a string represents the boolean value TRUE.
320
321	CRT on Windows: Optional, disabling CRT degrades performances.
322	@param	str	String to check.
323	@return	1 if str represents the boolean value TRUE, 0 otherwise.
324*/
325int
326
327dk4str8_is_on(const char *str);
328
329#if (DK4_ON_WINDOWS) || (DK4_HAVE_MALLOC && DK4_HAVE_FREE)
330
331/**	Duplicate string in dynamically allocated memory.
332
333	CRT on Windows: Optional, disabling CRT reduces functionality
334	to strings with length fitting to int and overall size in
335	bytes - including finalizer - fitting into a DWORD.
336	@param	src	String to duplicate.
337	@param	erp	Error report, may be NULL.
338	@return	Pointer to string in new memory on succes, NULL on error.
339	Use dk4mem_free() to release the memory after usage.
340
341	Error codes:
342	- DK4_E_INVALID_ARGUMENTS<br>
343	  if src is a NULL pointer,
344	- DK4_E_MATH_OVERFLOW<br>
345	  on mathematical overflow in size calculation,
346	- DK4_E_MEMORY<br>
347	  if no memory is available.
348*/
349char *
350
351dk4str8_dup(const char *src, dk4_er_t *erp);
352
353#endif
354
355
356/**	Remove trailing white spaces.
357
358	CRT on Windows: Optional, disabling CRT degrades performance.
359	@param	str	String to remove white spaces from.
360	@param	whsp	White spaces set.
361*/
362void
363
364dk4str8_rtwh(char *str, const char *whsp);
365
366/**	Convert character to uppercase.
367
368	CRT on Windows: Optional, disabling CRT degrades performances
369	and reduces functionality to characters 'a' to 'z'.
370	@param	c	Character to convert.
371	@return	Conversion result.
372*/
373char
374
375dk4str8_toupper(char c);
376
377/**	Convert character to lowercase.
378
379	CRT on Windows: Optional, disabling CRT degrades performances
380	and reduces functionality to characters 'A' to 'Z'.
381	@param	c	Character to convert.
382	@return	Conversion result.
383*/
384char
385
386dk4str8_tolower(char c);
387
388/**	Remove trailing newline from line.
389
390	CRT on Windows: Not used.
391	@param	lptr	Line pointer.
392*/
393void
394
395dk4str8_delnl(char *lptr);
396
397/**	Skip the first text words in a line, return pointer to start
398	of next word.
399
400	CRT on Windows: Optional, disabling CRT degrades performance.
401	@param	line	Line to process.
402	@param	skip	Number of words to skip.
403	@return	Pointer to next word on success, NULL on error.
404*/
405const char *
406
407dk4str8_skip(const char *line, size_t skip);
408
409/**	Sanitize string (overwrite using 0 bytes).
410
411	CRT on Windows: Optional.
412	@param	str	Address of string to sanitize.
413	@return	1 on success, 0 on error.
414*/
415int
416
417dk4str8_sanitize(char *str);
418
419/**	Sanitize string buffer (overwrite using 0 bytes).
420
421	CRT on Windows: Optional.
422	@param	str	Address of string  buffer.
423	@param	sz	Size of string buffer (number of elements).
424	@return	1 on success, 0 on error.
425*/
426int
427
428dk4str8_buffer_sanitize(char *str, size_t sz);
429
430/**	Sanitize dynamically allocated memory and release it.
431
432	CRT on Windows: Optional.
433	@param	str	Address of string to sanitize and release.
434	@return	1 on success, 0 on error.
435*/
436int
437
438dk4str8_free_sanitized(char *str);
439
440#ifdef __cplusplus
441}
442#endif
443
444
445/**	Sanitize string and release it, set pointer to NULL.
446	@param	p	Pointer to string to release.
447*/
448#define	dk4str8_release_sanitized(p) \
449do { if (NULL != p) { (void)dk4str8_free_sanitized(p); } p = NULL; } while (0)
450
451
452%%	module
453
454#include "dk4conf.h"
455
456#ifndef	DK4USE_H_INCLUDED
457#include <libdk4base/dk4use.h>
458#endif
459
460#if DK4_ON_WINDOWS && (DK4_WIN_AVOID_CRT || DK4_WIN_DENY_CRT)
461#ifndef WINDOWS_H_INCLUDED
462#include <windows.h>
463#define	WINDOWS_H_INCLUDED 1
464#endif
465#endif
466
467#include <libdk4base/dk4str8.h>
468
469#if DK4_HAVE_STRING_H
470#ifndef STRING_H_INCLUDED
471#include <string.h>
472#define STRING_H_INCLUDED 1
473#endif
474#endif
475
476#if DK4_HAVE_CTYPE_H
477#ifndef CTYPE_H_INCLUDED
478#include <ctype.h>
479#define	CTYPE_H_INCLUDED 1
480#endif
481#endif
482
483#if DK4_HAVE_ASSERT_H
484#ifndef	ASSERT_H_INCLUDED
485#include <assert.h>
486#define	ASSERT_H_INCLUDED 1
487#endif
488#endif
489
490
491#ifndef DK4NUMCO_H_INCLUDED
492#include <libdk4base/dk4numco.h>
493#endif
494
495#ifndef DK4MEM_H_INCLUDED
496#include <libdk4base/dk4mem.h>
497#endif
498
499#ifndef	DK4MEMRS_H_INCLUDED
500#include <libdk4base/dk4memrs.h>
501#endif
502
503
504$!trace-include
505
506
507
508/**	Constant strings used in the module.
509*/
510static const char * const	dk4str8_modkw[] = {
511$!string-table
512#
513#   0	Default delimiters.
514#
515 \t\r\n
516$!end
517};
518
519
520
521/**	First index of a keyword indicating true in the
522	dk4str8_boolkw array below.
523*/
524#define	DK4STR8_FIRST_TRUE	5
525
526/**	Keywords for boolean values.
527*/
528static const char * const	dk4str8_boolkw[] = {
529$!string-table
530#
531#  0...4	false
532#
533-
5340
535n$o
536of$f
537f$alse
538#
539#  5...		true
540#
541+
5421
543y$es
544on
545t$rue
546ok
547$!end
548};
549
550
551
552#if 0
553/**	Number of significant digits.
554*/
555static const int	dk4str8_dbl_digits	=	DK4_DBL_DIGITS;
556#endif
557
558
559void
560
561dk4str8_cpy_to_left(char *dst, const char *src)
562{
563  register		char	*dptr;
564  register const	char	*sptr;
565#if	DK4_USE_ASSERT
566  assert(NULL != dst);
567  assert(NULL != src);
568#endif
569  if (NULL != dst) {
570    *dst = '\0';
571    if (NULL != src) {
572      dptr = dst; sptr = src;
573      while('\0' != *sptr) { *(dptr++) = *(sptr++); }
574      *dptr = '\0';
575    }
576  }
577}
578
579
580
581size_t
582
583dk4str8_len(const char *src)
584{
585  if (NULL == src) {
586    return 0;
587  }
588#if DK4_ON_WINDOWS
589  /* +++ Windows */
590#if DK4_WIN_AVOID_CRT || DK4_WIN_DENY_CRT
591  return ((size_t)lstrlenA(src));
592#else
593  return (strlen(src));
594#endif
595  /* --- Windows */
596#else
597  /* +++ non-Windows */
598  return (strlen(src));
599  /* --- non-Windows */
600#endif
601}
602
603
604
605char
606
607dk4str8_toupper(char c)
608{
609#if DK4_ON_WINDOWS
610#if DK4_WIN_DENY_CRT
611  char	back;
612  back = c;
613  if (('a' <= c) && ('z' >= c)) {
614    back = 'A' + (c - 'a');
615  }
616  return back;
617#else
618  return (char)toupper((unsigned char)c);
619#endif
620#else
621  return (char)toupper((unsigned char)c);
622#endif
623}
624
625
626
627char
628
629dk4str8_tolower(char c)
630{
631#if DK4_ON_WINDOWS
632#if DK4_WIN_DENY_CRT
633  char	back;
634  back = c;
635  if (('A' <= c) && ('Z' >= c)) {
636    back = 'a' + (c - 'A');
637  }
638  return back;
639#else
640  return (char)tolower((unsigned char)c);
641#endif
642#else
643  return (char)tolower((unsigned char)c);
644#endif
645}
646
647
648
649char *
650
651dk4str8_chr(const char *s, char c)
652{
653  if (NULL == s) { return NULL; }
654#if DK4_ON_WINDOWS
655  /* +++ Windows */
656#if DK4_WIN_DENY_CRT
657  {
658    char *back = NULL;
659    while (('\0' != *s) && (NULL == back)) {
660      if (c == *s) {
661        back = (char *)s;
662      } else {
663        s++;
664      }
665    }
666    return back;
667  }
668#else
669#ifdef	__cplusplus
670  return ((char *)strchr(s, (int)((unsigned char)c)));
671#else
672  return (strchr(s, (int)((unsigned char)c)));
673#endif
674#endif
675  /* --- Windows */
676#else
677  /* +++ non-Windows */
678#ifdef	__cplusplus
679  return ((char *)strchr(s, (int)((unsigned char)c)));
680#else
681  return (strchr(s, (int)((unsigned char)c)));
682#endif
683  /* --- non-Windows */
684#endif
685}
686
687
688
689char *
690
691dk4str8_rchr(const char *s, char c)
692{
693  if (NULL == s) { return NULL; }
694#if DK4_ON_WINDOWS
695#if DK4_WIN_DENY_CRT
696  {
697    char *back = NULL;
698    while ('\0' != *s) {
699      if (c == *s) {
700        back = (char *)s;
701      }
702      s++;
703    }
704    return back;
705  }
706#else
707#ifdef	__cplusplus
708  return ((char *)strrchr(s, (int)((unsigned char)c)));
709#else
710  return (strrchr(s, (int)((unsigned char)c)));
711#endif
712#endif
713#else
714#ifdef	__cplusplus
715  return ((char *)strrchr(s, (int)((unsigned char)c)));
716#else
717  return (strrchr(s, (int)((unsigned char)c)));
718#endif
719#endif
720}
721
722
723
724int
725
726dk4str8_cpy_s(char *dst, size_t sz, const char *src, dk4_er_t *erp)
727{
728  int		back	= 0;
729#if	DK4_USE_ASSERT
730  assert(NULL != dst);
731  assert(0 < sz);
732#endif
733  if ((NULL != dst) && (0 < sz)) {
734    *dst = '\0';
735    if (NULL != src) {
736      if (dk4str8_len(src) < sz) {
737#if DK4_ON_WINDOWS
738        /* +++ Windows */
739#if DK4_WIN_AVOID_CRT || DK4_WIN_DENY_CRT
740        (void)lstrcpyA(dst, (char *)src);
741#else
742#if DK4_USE_PRAGMA_WARNING_DISABLE
743#pragma warning( push )
744#pragma warning( disable: 4996 )
745#endif
746        (void)strcpy(dst, src);
747#if DK4_USE_PRAGMA_WARNING_DISABLE
748#pragma warning( pop )
749#endif
750#endif
751        /* --- Windows */
752#else
753        /* +++ non-Windows */
754        (void)strcpy(dst, src);
755        /* --- non-Windows */
756#endif
757        back = 1;
758      } else {
759        *dst = '\0';
760        dk4error_set_simple_error_code(erp, DK4_E_BUFFER_TOO_SMALL);
761      }
762    } else {
763      dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
764    }
765  } else {
766    dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
767  }
768  return back;
769}
770
771
772
773int
774
775dk4str8_cat_s(char *dst, size_t sz, const char *src, dk4_er_t *erp)
776{
777  size_t	 la;
778  size_t	 lb;
779  int		 back	= 0;
780#if	DK4_USE_ASSERT
781  assert(NULL != dst);
782  assert(0 < sz);
783#endif
784  if ((NULL != dst) && (0 < sz) && (NULL != src)) {
785    la = dk4str8_len(dst);
786    lb = dk4str8_len(src);
787    if ((SIZE_MAX - la) >= lb) {
788      if ((la + lb) < sz) {
789#if DK4_ON_WINDOWS
790	/* +++ Windows */
791#if DK4_WIN_AVOID_CRT || DK4_WIN_DENY_CRT
792	(void)lstrcatA(dst, (char *)src);
793#else
794#if DK4_USE_PRAGMA_WARNING_DISABLE
795#pragma warning( push )
796#pragma warning( disable: 4996 )
797#endif
798	(void)strcat(dst, src);
799#if DK4_USE_PRAGMA_WARNING_DISABLE
800#pragma warning( pop )
801#endif
802#endif
803	/* --- Windows */
804#else
805	/* +++ non-Windows */
806        (void)strcat(dst, src);
807	/* --- non-Windows */
808#endif
809        back = 1;
810      } else {
811        dk4error_set_simple_error_code(erp, DK4_E_BUFFER_TOO_SMALL);
812      }
813    } else {
814      dk4error_set_simple_error_code(erp, DK4_E_MATH_OVERFLOW);
815    }
816  } else {
817    dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
818  }
819  return back;
820}
821
822
823
824int
825
826dk4str8_cmp(const char *s1, const char *s2)
827{
828  int		back = 0;
829  if (NULL != s1) {
830    if (NULL != s2) {
831#if DK4_ON_WINDOWS
832      /* +++ Windows */
833#if DK4_WIN_AVOID_CRT || DK4_WIN_DENY_CRT
834      back = lstrcmpA(s1, s2);
835#else
836      back = strcmp(s1, s2);
837#endif
838      /* --- Windows */
839#else
840      /* +++ non-Windows */
841      back = strcmp(s1, s2);
842      /* --- non-Windows */
843#endif
844      if (0 > back) back = -1;
845      if (0 < back) back =  1;
846    } else {
847      back = 1;
848    }
849  } else {
850    if (NULL != s2) {
851      back = -1;
852    }
853  }
854  return back;
855}
856
857
858
859int
860
861dk4str8_ncmp(const char *s1, const char *s2, size_t n)
862{
863  int		 back	= 0;
864  if (NULL != s1) {
865    if (NULL != s2) {
866#if (DK4_ON_WINDOWS && (DK4_WIN_AVOID_CRT || DK4_WIN_DENY_CRT) ) \
867|| (!(DK4_HAVE_WCSNCMP)) || (!(DK4_HAVE__WCSNCMP))
868      while((0 < n--) && (0 == back)) {
869        if ('\0' == *s1) {
870	  if ('\0' == *s2) {
871	    n = 0;
872	  } else {
873	    back = -1;
874	  }
875	} else {
876	  if ('\0' == *s2) {
877	    back = 1;
878	  } else {
879	    if (*s1 > *s2) {
880	      back = 1;
881	    } else {
882	      if (*s1 < *s2) {
883	        back = -1;
884	      } else {
885	        s1++; s2++;
886	      }
887	    }
888	  }
889	}
890      }
891#else
892      back = strncmp(s1, s2, n);
893      if (-1 > back) back = -1;
894      if ( 1 < back) back =  1;
895#endif
896    } else {
897      back = 1;
898    }
899  } else {
900    if (NULL != s2) {
901      back = -1;
902    }
903  }
904  return back;
905}
906
907
908
909int
910
911dk4str8_casecmp(const char *s1, const char *s2)
912{
913  int		back = 0;
914  if (NULL != s1) {
915    if (NULL != s2) {
916#if DK4_ON_WINDOWS
917    /* +++ Windows */
918#if DK4_WIN_AVOID_CRT || DK4_WIN_DENY_CRT
919      back = lstrcmpiA(s1, s2);
920#else
921      back = _stricmp(s1, s2);
922#endif
923    /* --- non-Windows */
924#else
925    /* +++ non-Windows */
926#if DK4_HAVE_STRCASECMP
927      back = strcasecmp(s1, s2);
928#else
929#if DK4_HAVE__STRICMP
930      back = _stricmp(s1, s2);
931#else
932#if DK4_HAVE_STRICMP
933      back = stricmp(s1, s2);
934#else
935#error	No function for case-insensitive comparison available!
936#endif
937#endif
938#endif
939      /* --- non-Windows */
940#endif
941      if (0 > back) back = -1;
942      if (0 < back) back =  1;
943    } else {
944      back = 1;
945    }
946  } else {
947    if (NULL != s2) {
948      back = -1;
949    }
950  }
951  return back;
952}
953
954
955
956#if (DK4_ON_WINDOWS) || (DK4_HAVE_MALLOC && DK4_HAVE_FREE)
957
958char *
959
960dk4str8_dup(const char *src, dk4_er_t *erp)
961{
962  char		*back =	NULL;
963  size_t	 lgt;
964  if (NULL != src) {
965    lgt = dk4str8_len(src);
966    if (SIZE_MAX > lgt) {
967      back = (char *)dk4mem_malloc_bytes((1+lgt), erp);
968      if (NULL != back) {
969        (void)dk4str8_cpy_s(back, (1+lgt), src, erp);
970      }
971    } else {
972      dk4error_set_simple_error_code(erp, DK4_E_MATH_OVERFLOW);
973    }
974  } else {
975    dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
976  }
977  return back;
978}
979
980#endif
981
982
983
984char *
985
986dk4str8_start(const char *src, const char *delim)
987{
988  char *back = NULL;
989  if (NULL != src) {
990    if (NULL == delim) { delim = dk4str8_modkw[0]; }
991    while (('\0' != *src) && (NULL == back)) {
992      if (NULL != dk4str8_chr(delim, *src)) {
993        src++;
994      } else {
995        back = (char *)src;
996      }
997    }
998  }
999  return back;
1000}
1001
1002
1003
1004/**	Internal function for next token search.
1005
1006	CRT on Windows: Optional, disabling CRT degrades performance.
1007	@param	src	String containing tokens, must be set to
1008			the start of a token.
1009	@param	delim	Delimiter set.
1010	@return	Pointer to next token on success, NULL on error.
1011*/
1012static
1013char *
1014dk4str8_i_next(char *src, const char *delim)
1015{
1016  char *back	=	NULL;
1017  if (NULL != src) {
1018    if (NULL == delim) { delim = dk4str8_modkw[0]; }
1019    while (('\0' != *src) && (NULL == back)) {
1020      if (NULL != dk4str8_chr(delim, *src)) {
1021        back = src;
1022      } else {
1023        src++;
1024      }
1025    }
1026    if (NULL != back) {
1027      *(back++) = '\0';
1028      back = dk4str8_start(back, delim);
1029    }
1030  }
1031  return back;
1032}
1033
1034
1035
1036char *
1037
1038dk4str8_next(char *src, const char *delim)
1039{
1040  char	*back	=	NULL;
1041  if (NULL != src) {
1042    back = dk4str8_start(src, delim);
1043    if (NULL != back) {
1044      back = dk4str8_i_next(back, delim);
1045    }
1046  }
1047  return back;
1048}
1049
1050
1051
1052char *
1053
1054dk4str8_sep(char **stringp, const char *delim)
1055{
1056  char	*back	=	NULL;
1057  $? "+ dk4str8_sep \"%!8s\" \"%!8s\"", TR_8STR(*stringp), TR_8STR(delim)
1058#if	DK4_USE_ASSERT
1059  assert(NULL != stringp);
1060#endif
1061  if (NULL != stringp) {
1062    if (NULL != *stringp) {
1063      if (NULL == delim) { delim = dk4str8_modkw[0]; }
1064      back = dk4str8_start(*stringp, delim);
1065      if (NULL != back) {
1066	*stringp = dk4str8_next(back, delim);
1067      } else {
1068	*stringp = NULL;
1069      }
1070    }
1071  } $? "- dk4str8_sep \"%!8s\"", TR_8STR(back)
1072  return back;
1073}
1074
1075
1076
1077size_t
1078
1079dk4str8_tokenize(
1080  char **dpp, size_t szdpp, char *src, const char *delim, dk4_er_t *erp
1081)
1082{
1083  char		*context;
1084  char		*token;
1085  size_t	 back		=	0;
1086#if	DK4_USE_ASSERT
1087  assert(NULL != dpp);
1088  assert(0 < szdpp);
1089#endif
1090  if ((NULL != dpp) && (0 < szdpp) && (NULL != src)) {
1091    if (NULL == delim) { delim = dk4str8_modkw[0]; }
1092    context = src;
1093    while (NULL != (token = dk4str8_sep(&context, delim))) {
1094      if (back < szdpp) {
1095        dpp[back] = token;
1096      }
1097      back++;
1098    }
1099    if (back >= szdpp) {
1100      dk4error_set_elsize_nelem(
1101        erp, DK4_E_BUFFER_TOO_SMALL, sizeof(DK4_PCHAR), back
1102      );
1103      back = 0;
1104    }
1105  } else {
1106    dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
1107  }
1108  return back;
1109}
1110
1111
1112
1113void
1114
1115dk4str8_normalize(char *src, const char *delim)
1116{
1117  char		*p1;	/* Pointer to traverse text. */
1118  char		*p2;	/* Start of next token. */
1119  int		 cc;	/* Flag: Can continue. */
1120  if (NULL != src) {
1121    /*
1122	Set up delimiter set, correct if necessary.
1123    */
1124    if (NULL == delim) { delim = dk4str8_modkw[0]; }
1125    /*
1126	Remove leading delimiters.
1127    */
1128    p1 = src;
1129    cc = 0;
1130    if ('\0' != *p1) {
1131      if (NULL != dk4str8_chr(delim, *p1)) {
1132        p2 = dk4str8_start(p1, delim);
1133        if (NULL != p2) {
1134          dk4str8_cpy_to_left(p1, p2);
1135          cc = 1;
1136        } else {
1137          *p1 = '\0';
1138        }
1139      } else {
1140        cc = 1;
1141      }
1142    }
1143    /*
1144	Now traverse the string.
1145    */
1146    while (0 != cc) {
1147      if ('\0' == *p1) {
1148        cc = 0;
1149      } else {
1150        if (NULL != dk4str8_chr(delim, *p1)) {
1151	  *p1 = *delim;
1152	  if ('\0' == p1[1]) {
1153	    *p1 = '\0';
1154	    cc = 0;
1155	  } else {
1156	    if (NULL != dk4str8_chr(delim, p1[1])) {
1157	      p2 = dk4str8_start(p1, delim);
1158	      if (NULL != p2) {
1159	        dk4str8_cpy_to_left(++p1, p2);
1160	      } else {
1161	        *p1 = '\0';
1162		cc = 0;
1163	      }
1164	    } else {
1165	      p1++;
1166	    }
1167	  }
1168	} else {
1169	  p1++;
1170	}
1171      }
1172    }
1173  }
1174}
1175
1176
1177
1178int
1179
1180dk4str8_array_index(const char * const *arr, const char *str, int cs)
1181{
1182  int		back	=	-1;
1183  int		cand	=	 0;
1184#if	DK4_USE_ASSERT
1185  assert(NULL != arr);
1186#endif
1187  if ((NULL != arr) && (NULL != str)) {
1188    while((NULL != *arr) && (-1 == back)) {
1189      if (0 != cs) {
1190        if (0 == dk4str8_cmp(*arr, str)) { back = cand; }
1191      } else {
1192        if (0 == dk4str8_casecmp(*arr, str)) { back = cand; }
1193      }
1194      arr++; cand++;
1195    }
1196  }
1197  return back;
1198}
1199
1200
1201
1202int
1203
1204dk4str8_is_abbr(const char *str, const char *pattern, char spec, int cs)
1205{
1206  const char	*lptr	=	NULL;	/* Pointer to traverse string */
1207  const	char	*pptr	=	NULL;	/* Pointer to traverse pattern */
1208  int		 back	=	0;	/* Function result */
1209  int		 fspec	=	0;	/* Flag: spec was found */
1210  int		 cc	=	1;	/* Flag: Can continue */
1211  char		 cl;
1212  char		 cp;
1213  $? "+ dk4str8_is_abbr \"%!8s\" \"%!8s\" \"%!8c\"", pattern, str, spec
1214#if	DK4_USE_ASSERT
1215  assert(NULL != pattern);
1216#endif
1217  if ((NULL != str) && (NULL != pattern)) {
1218    lptr = str; pptr = pattern;
1219    while (0 != cc) {
1220      if ((0 == fspec) && (*pptr == spec)) {
1221        fspec = 1;
1222	pptr++;
1223      } else {
1224        if ('\0' != *lptr) {
1225	  cl = *lptr; cp = *pptr;
1226	  if (0 == cs) {
1227	    cl = dk4str8_toupper(cl);
1228	    cp = dk4str8_toupper(cp);
1229	  }
1230	  if (cl == cp) {
1231	    lptr++;
1232	    pptr++;
1233	  } else {
1234	    cc = 0;
1235	  }
1236	} else {
1237	  cc = 0;
1238	  if (('\0' == *pptr) || (0 != fspec)) { back = 1; }
1239	}
1240      }
1241    }
1242  } $? "- dk4str8_is_abbr %d", back
1243  return back;
1244}
1245
1246
1247
1248int
1249
1250dk4str8_abbr_index(const char * const *arr, char spec, const char *str, int cs)
1251{
1252  int		 back	=	-1;
1253  int		 cand	=	 0;
1254  $? "+ dk4str8_abbr_index"
1255#if	DK4_USE_ASSERT
1256  assert(NULL != arr);
1257#endif
1258  if ((NULL != arr) && (NULL != str)) {
1259    while ((NULL != *arr) && (-1 == back)) {
1260      if (0 != dk4str8_is_abbr(str, *arr, spec, cs)) {
1261        back = cand;
1262      }
1263      arr++; cand++;
1264    }
1265  }
1266  $? "- dk4str8_abbr_index %d", back
1267  return back;
1268}
1269
1270
1271
1272int
1273
1274dk4str8_is_bool(const char *str)
1275{
1276  int		 back	=	0;
1277  if (NULL != str) {
1278    if (-1 < dk4str8_abbr_index(dk4str8_boolkw, '$', str, 0)) {
1279      back = 1;
1280    }
1281  }
1282  return back;
1283}
1284
1285
1286
1287int
1288
1289dk4str8_is_on(const char *str)
1290{
1291  int		 back	=	0;
1292  $? "+ dk4str8_is_on"
1293  if (NULL != str) {
1294    back = dk4str8_abbr_index(dk4str8_boolkw, '$', str, 0);
1295    if (-1 < back) {
1296      if (DK4STR8_FIRST_TRUE <= back) {
1297        back = 1;
1298      } else {
1299        back = 0;
1300      }
1301    } else {
1302      back = 0;
1303    }
1304  }
1305  $? "- dk4str8_is_on %d", back
1306  return back;
1307}
1308
1309
1310
1311int
1312
1313dk4str8_pathcmp(const char *s1, const char *s2)
1314{
1315#if DK4_HAVE_CASE_INSENSITIVE_PATHNAMES
1316  return (dk4str8_casecmp(s1, s2));
1317#else
1318  return (dk4str8_cmp(s1, s2));
1319#endif
1320}
1321
1322
1323
1324void
1325
1326dk4str8_rtwh(char *str, const char *whsp)
1327{
1328  char *ptr = NULL;
1329  if (NULL == whsp) { whsp = dk4str8_modkw[0]; }
1330  if (NULL != str) {
1331    while ('\0' != *str) {
1332      if (NULL != dk4str8_chr(whsp, *str)) {
1333        if (NULL == ptr) { ptr = str; }
1334      } else {
1335        ptr = NULL;
1336      }
1337      str++;
1338    }
1339    if (NULL != ptr) { *ptr = '\0'; }
1340  }
1341}
1342
1343
1344
1345void
1346
1347dk4str8_delnl(char *lptr)
1348{
1349  if (NULL != lptr) {
1350    while ('\0' != *lptr) {
1351      if ('\n' == *lptr) {
1352        *lptr = '\0';
1353      } else {
1354        if ('\r' == *lptr) {
1355	  if ('\n' == lptr[1]) {
1356	    *lptr = '\0';
1357	  } else {
1358	    if ('\0' == lptr[1]) {
1359	      *lptr = '\0';
1360	    } else {
1361	      lptr++;
1362	    }
1363	  }
1364	} else {
1365	  lptr++;
1366	}
1367      }
1368    }
1369  }
1370}
1371
1372
1373
1374const char *
1375
1376dk4str8_skip(const char *line, size_t skip)
1377{
1378  const char	*back	= NULL;
1379  size_t	 words	= 0;	/* Number of words already found */
1380  int		 lwt	= 0;	/* Flag: Last char was text */
1381
1382  if (NULL != line) {
1383    if (0 < skip) {
1384      while((NULL == back) && ('\0' != *line)) {
1385        switch (*line) {
1386	  case ' ': case '\t': case '\n': case '\r': {
1387	    lwt = 0;
1388	  } break;
1389	  default: {
1390	    if (0 == lwt) {
1391	      if (++words > skip) {
1392	        back = line;
1393	      }
1394	    }
1395	    lwt = 1;
1396	  } break;
1397	}
1398        line++;
1399      }
1400    } else {
1401      back = dk4str8_start(line, NULL);
1402    }
1403  }
1404  return back;
1405}
1406
1407
1408
1409int
1410
1411dk4str8_buffer_sanitize(char *str, size_t sz)
1412{
1413  int		 back	= 0;
1414#if	DK4_USE_ASSERT
1415  assert(NULL != str);
1416  assert(0 < sz);
1417#endif
1418  if (NULL != str) {
1419    if (0 < sz) {
1420      back = dk4mem_reset_secure(str, sz, NULL);
1421    } else {
1422      back = 1;
1423    }
1424  }
1425  return back;
1426}
1427
1428
1429
1430int
1431
1432dk4str8_sanitize(char *str)
1433{
1434  int		 back	= 0;
1435  if (NULL != str) {
1436    back = dk4str8_buffer_sanitize(str, dk4str8_len(str));
1437  }
1438  return back;
1439}
1440
1441
1442
1443int
1444
1445dk4str8_free_sanitized(char *str)
1446{
1447  int		 back	= 0;
1448  if (NULL != str) {
1449    back = dk4str8_sanitize(str);
1450    dk4mem_free(str);
1451  }
1452  return back;
1453}
1454
1455