1 /* Various functions for people who are lacking/have a poor libc
2 Copyright (C) 1999 Evin Robertson. The last part Copyright (C) FSF
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
18 The author can be reached at nitfol@deja.com
22 #include <stdlib.h> /* For malloc */
23 #include <limits.h> /* For LONG_MAX, LONG_MIN, ... */
24 #include <ctype.h> /* For isspace, isdigit, isalpha */
27 /* Nitfol malloc/realloc wrappers - never return NULL */
28 void *n_malloc(int size)
31 void *m = malloc(size);
39 n_show_fatal(E_MEMORY, "not enough memory for malloc", size);
41 n_show_fatal(E_MEMORY, "malloc(0)", size);
48 void *n_calloc(int nmemb, int size)
50 int totalsize = nmemb * size;
51 void *m = n_malloc(totalsize);
52 n_memset(m, 0, totalsize);
56 void *n_realloc(void *ptr, int size)
63 m = realloc(ptr, size);
64 if(m != NULL || size == 0)
67 m = realloc(ptr, size);
77 void n_free(void *ptr)
82 typedef struct rmmalloc_entry rmmalloc_entry;
84 struct rmmalloc_entry {
89 static rmmalloc_entry *rmmalloc_list = NULL;
91 /* This malloc maintains a list of malloced data, which can all be freed at
93 void *n_rmmalloc(int size)
95 rmmalloc_entry newentry;
96 newentry.data = n_malloc(size);
97 LEadd(rmmalloc_list, newentry);
104 for(p=rmmalloc_list; p; p=p->next)
106 LEdestroy(rmmalloc_list);
109 void n_rmfreeone(void *m)
111 rmmalloc_entry *p, *t;
112 LEsearchremove(rmmalloc_list, p, t, p->data == m, n_free(p->data));
116 /* Returns true if target is a null-terminated string identical to the
117 first len characters of starting */
118 BOOL n_strmatch(const char *target, const char *starting, unsigned len)
121 n_strlen(target) == len &&
122 n_strncasecmp(target, starting, len) == 0)
127 /* Write 'n' in decimal to 'dest' - assume there is enough space in buffer */
128 int n_to_decimal(char *buffer, unsigned n)
137 buffer[i++] = '0' + c;
145 const char *n_static_number(const char *preface, glui32 n)
147 static char *buffer = NULL;
149 int preflen = n_strlen(preface);
153 buffer = (char *) n_realloc(buffer, preflen + 12 + 2);
154 n_strcpy(buffer, preface);
155 numlen = n_to_decimal(number, n);
156 for(i = 0; i < numlen; i++)
157 buffer[preflen + i] = number[numlen - i - 1];
158 buffer[preflen + i] = 0;
162 /* n_strdup(NULL) works, unlike strdup(NULL) which segfaults */
163 char *n_strdup(const char *s)
168 n = (char *) n_malloc(n_strlen(s) + 1);
173 /* Swap n bytes between a and b */
174 void n_memswap(void *a, void *b, int n)
177 unsigned char *c = (unsigned char *) a;
178 unsigned char *d = (unsigned char *) b;
181 for(i = 0; i < n; i++) {
188 /* Wrappers to hide ugliness of Glk file opening functions */
189 strid_t n_file_prompt(glui32 usage, glui32 fmode)
191 frefid_t r = glk_fileref_create_by_prompt(usage, fmode, 0);
194 if((fmode & filemode_Read) && !glk_fileref_does_file_exist(r))
196 s = glk_stream_open_file(r, fmode, 0);
197 glk_fileref_destroy(r);
203 strid_t n_file_name(glui32 usage, glui32 fmode, const char *name)
205 frefid_t r = glk_fileref_create_by_name(usage, (char *) name, 0);
208 if((fmode & filemode_Read) && !glk_fileref_does_file_exist(r))
210 s = glk_stream_open_file(r, fmode, 0);
211 glk_fileref_destroy(r);
217 /* If given name is more than whitespace, open by name; else by prompt */
218 strid_t n_file_name_or_prompt(glui32 usage, glui32 fmode, const char *name)
221 for(c = name; *c; c++) {
223 return n_file_name(usage, fmode, name);
225 return n_file_prompt(usage, fmode);
230 /* Trivial wrappers to fix implicit (const char *) to (char *) cast warnings */
231 void w_glk_put_string(const char *s)
233 glk_put_string((char *) s);
236 void w_glk_put_string_stream(strid_t str, const char *s)
238 glk_put_string_stream(str, (char *) s);
241 void w_glk_put_buffer(const char *buf, glui32 len)
243 glk_put_buffer((char *) buf, len);
246 void w_glk_put_buffer_stream(strid_t str, const char *buf, glui32 len)
248 glk_put_buffer_stream(str, (char *) buf, len);
251 void w_glk_put_char(int ch)
258 /* The rest of the functions in this conform to ANSI/BSD/POSIX/whatever and
259 can be replaced with standard functions with appropriate #defines
260 They are included here only for systems lacking a proper libc. No effort
261 has been made to tune their speeds. */
269 /* FIXME: use autoconf for these someday */
272 /* ISO 9899 functions */
274 #define n_strlen(s) strlen(s)
275 #define n_strcpy(d, s) strcpy(d, s)
276 #define n_strncpy(d, s, n) strncpy(d, s, n)
277 #define n_memcpy(d, s, n) memcpy(d, s, n)
278 #define n_memmove(d, s, n) memmove(d, s, n)
279 #define n_memset(s, c, n) memset(s, c, n)
280 #define n_strcmp(a, b) strcmp(a, b)
281 #define n_strncmp(a, b, n) strncmp(a, b, n)
282 #define n_memcmp(a, b, n) memcmp(a, b, n)
283 #define n_strchr(a, c) strchr(a, c)
284 #define n_strcat(d, s) strcat(d, s)
285 #define n_strpbrk(s, a) strpbrk(s, a)
286 #define n_strspn(s, a) strspn(s, a)
287 #define n_strtok(s, d) strtok(s, d)
288 #define n_strstr(h, n) strstr(h, n)
289 #define n_strtol(n, e, b) strtol(n, e, b)
290 #define n_strrchr(s, c) strrchr(s, c)
293 #define n_qsort(b, n, s, c) qsort(b, n, s, c)
294 #define n_bsearch(k, b, n, s, c) bsearch(k, b, n, s, c)
297 #if defined(__USE_BSD) || defined(__USE_GNU)
298 #define n_strcasecmp(a, b) strcasecmp(a, b)
299 #define n_strncasecmp(a, b, n) strncasecmp(a, b, n)
303 #define n_lfind(k, b, n, s, c) lfind(k, b, n, s, c)
310 unsigned n_strlen(const char *s)
320 char *n_strcpy(char *dest, const char *src)
331 char *n_strncpy(char *dest, const char *src, int len)
344 void *n_memcpy(void *dest, const void *src, int n)
347 unsigned char *a = (unsigned char *) dest;
348 unsigned const char *b = (const unsigned char *) src;
349 for(i = 0; i < n; i++)
356 void *n_memmove(void *dest, const void *src, int n)
359 unsigned char *a = (unsigned char *) dest;
360 unsigned char *b = (unsigned char *) src;
362 for(i = 0; i < n; i++)
365 for(i = n-1; i >= 0; i--)
372 void *n_memset(void *s, int c, int n)
375 unsigned char *a = (unsigned char *) s;
376 for(i = 0; i < n; i++)
383 int n_strcmp(const char *a, const char *b)
397 int n_strncmp(const char *a, const char *b, int n)
411 int n_memcmp(const void *s1, const void *s2, int n)
413 const unsigned char *a = (unsigned char *) s1;
414 const unsigned char *b = (unsigned char *) s2;
425 int n_strcasecmp(const char *a, const char *b)
430 char c1 = glk_char_to_lower(*a);
431 char c2 = glk_char_to_lower(*b);
443 #ifndef n_strncasecmp
444 int n_strncasecmp(const char *a, const char *b, int n)
449 char c1 = glk_char_to_lower(*a);
450 char c2 = glk_char_to_lower(*b);
463 char *n_strlower(char *s)
467 *b = glk_char_to_lower(*b);
475 char *n_strupper(char *s)
479 *b = glk_char_to_upper(*b);
487 char *n_strchr(const char *s, int c)
489 const unsigned char *a = (const unsigned char *) s;
500 void *n_lfind(const void *key, const void *base, int *nmemb, int size,
501 int (*compar)(const void *, const void *))
504 char *t = (char *) base;
505 for(i = 0; i < *nmemb; i++) {
506 if((*compar)(t, key) == 0)
517 /* Modified by Evin Robertson for nitfolness Aug 4 1999 */
519 /******************************************************************/
520 /* qsort.c -- Non-Recursive ANSI Quicksort function */
522 /* Public domain by Raymond Gardner, Englewood CO February 1991 */
525 /* qsort(base, nbr_elements, width_bytes, compare_function); */
527 /* size_t nbr_elements, width_bytes; */
528 /* int (*compare_function)(const void *, const void *); */
530 /* Sorts an array starting at base, of length nbr_elements, each */
531 /* element of size width_bytes, ordered via compare_function, */
532 /* which is called as (*compare_function)(ptr_to_element1, */
533 /* ptr_to_element2) and returns < 0 if element1 < element2, */
534 /* 0 if element1 = element2, > 0 if element1 > element2. */
535 /* Most refinements are due to R. Sedgewick. See "Implementing */
536 /* Quicksort Programs", Comm. ACM, Oct. 1978, and Corrigendum, */
537 /* Comm. ACM, June 1979. */
538 /******************************************************************/
541 static void swap_chars(char *, char *, int);
543 #define SWAP(a, b) (swap_chars((char *)(a), (char *)(b), size))
546 #define COMP(a, b) ((*comp)((void *)(a), (void *)(b)))
548 #define T 7 /* subfiles of T or fewer elements will */
549 /* be sorted by a simple insertion sort */
550 /* Note! T must be at least 3 */
552 void n_qsort(void *basep, int nelems, int size,
553 int (*comp)(const void *, const void *))
555 char *stack[40], **sp; /* stack and stack pointer */
556 char *i, *j, *limit; /* scan and limit pointers */
557 int thresh; /* size of T elements in bytes */
558 char *base; /* base pointer as char * */
560 base = (char *)basep; /* set up char * base pointer */
561 thresh = T * size; /* init threshold */
562 sp = stack; /* init stack pointer */
563 limit = base + nelems * size;/* pointer past end of array */
564 for ( ;; ) { /* repeat until break... */
565 if ( limit - base > thresh ) { /* if more than T elements */
566 /* swap base with middle */
567 SWAP((((limit-base)/size)/2)*size+base, base);
568 i = base + size; /* i scans left to right */
569 j = limit - size; /* j scans right to left */
570 if ( COMP(i, j) > 0 ) /* Sedgewick's */
571 SWAP(i, j); /* three-element sort */
572 if ( COMP(base, j) > 0 ) /* sets things up */
573 SWAP(base, j); /* so that */
574 if ( COMP(i, base) > 0 ) /* *i <= *base <= *j */
575 SWAP(i, base); /* *base is pivot element */
576 for ( ;; ) { /* loop until break */
577 do /* move i right */
578 i += size; /* until *i >= pivot */
579 while ( COMP(i, base) < 0 );
581 j -= size; /* until *j <= pivot */
582 while ( COMP(j, base) > 0 );
583 if ( i > j ) /* if pointers crossed */
584 break; /* break loop */
585 SWAP(i, j); /* else swap elements, keep scanning*/
587 SWAP(base, j); /* move pivot into correct place */
588 if ( j - base > limit - i ) { /* if left subfile larger */
589 sp[0] = base; /* stack left subfile base */
590 sp[1] = j; /* and limit */
591 base = i; /* sort the right subfile */
592 } else { /* else right subfile larger*/
593 sp[0] = i; /* stack right subfile base */
594 sp[1] = limit; /* and limit */
595 limit = j; /* sort the left subfile */
597 sp += 2; /* increment stack pointer */
598 } else { /* else subfile is small, use insertion sort */
599 for ( j = base, i = j+size; i < limit; j = i, i += size )
600 for ( ; COMP(j, j+size) > 0; j -= size ) {
605 if ( sp != stack ) { /* if any entries on stack */
606 sp -= 2; /* pop the base and limit */
609 } else /* else stack empty, done */
616 ** swap nbytes between a and b
619 static void swap_chars(char *a, char *b, int nbytes)
623 tmp = *a; *a++ = *b; *b++ = tmp;
624 } while ( --nbytes );
632 /* These last several were adapted from glibc, GNU LGPLed, copyright FSF */
633 /* For nitfol, these are licensed under the GPL per section 3 of the LGPL */
636 char *n_strcat(char *dest, const char *src)
639 const char *s2 = src;
642 /* Find the end of the string. */
647 /* Make S1 point before the next character, so we can increment
648 it while memory is read (wins on pipelined cpus). */
663 char *n_strpbrk(const char *s, const char *accept)
666 if (n_strchr(accept, *s) == NULL)
676 int n_strspn(const char *s, const char *accept)
682 for (p = s; *p != '\0'; ++p)
684 for (a = accept; *a != '\0'; ++a)
698 char *n_strtok(char *s, const char *delim)
700 static char *olds = NULL;
707 /* errno = EINVAL; */
714 /* Scan leading delimiters. */
715 s += n_strspn(s, delim);
722 /* Find the end of the token. */
724 s = n_strpbrk(token, delim);
726 /* This token finishes the string. */
730 /* Terminate the token and make OLDS point past it. */
739 char *n_strstr(const char *haystack, const char *needle)
741 const char *needle_end = n_strchr(needle, '\0');
742 const char *haystack_end = n_strchr(haystack, '\0');
743 const unsigned needle_len = needle_end - needle;
744 const unsigned needle_last = needle_len - 1;
748 return (char *) haystack; /* ANSI 4.11.5.7, line 25. */
749 if ((size_t) (haystack_end - haystack) < needle_len)
752 for (begin = &haystack[needle_last]; begin < haystack_end; ++begin)
754 const char *n = &needle[needle_last];
755 const char *h = begin;
759 goto loop; /* continue for loop */
760 while (--n >= needle && --h >= haystack);
772 long int n_strtol (const char *nptr, char **endptr, int base)
775 unsigned long int cutoff;
783 if (base < 0 || base == 1 || base > 36)
788 /* Skip white space. */
794 /* Check for a sign. */
808 if (base == 16 && s[0] == '0' && glk_char_to_upper (s[1]) == 'X')
811 /* If BASE is zero, figure it out ourselves. */
815 if (glk_char_to_upper (s[1]) == 'X')
826 /* Save the pointer so we can check later if anything happened. */
829 cutoff = ULONG_MAX / (unsigned long int) base;
830 cutlim = ULONG_MAX % (unsigned long int) base;
834 for (c = *s; c != '\0'; c = *++s)
838 else if (isalpha (c))
839 c = glk_char_to_upper (c) - 'A' + 10;
844 /* Check for overflow. */
845 if (i > cutoff || (i == cutoff && c > cutlim))
849 i *= (unsigned long int) base;
854 /* Check if anything actually happened. */
858 /* Store in ENDPTR the address of one character
859 past the last character we converted. */
861 *endptr = (char *) s;
863 /* Check for a value that is within the range of
864 `unsigned long int', but outside the range of `long int'. */
866 -(unsigned long int) LONG_MIN : (unsigned long int) LONG_MAX))
871 /* errno = ERANGE; */
872 return negative ? LONG_MIN : LONG_MAX;
875 /* Return the result of the appropriate sign. */
876 return (negative ? -i : i);
879 /* There was no number to convert. */
881 *endptr = (char *) nptr;
887 char *n_strrchr(const char *s, int c)
889 const char *found, *p;
891 c = (unsigned char) c;
893 /* Since strchr is fast, we use it rather than the obvious loop. */
896 return n_strchr(s, '\0');
899 while ((p = n_strchr(s, c)) != NULL)
905 return (char *) found;
910 void *n_bsearch(const void *key, const void *base, int nmemb, int size,
911 int (*compar)(const void *, const void *))
922 p = (void *) (((const char *) base) + (idx * size));
923 comparison = (*compar)(key, p);
926 else if (comparison > 0)