Bug fixes
[rodin/chimara.git] / interpreters / nitfol / portfunc.c
1 /*  Various functions for people who are lacking/have a poor libc
2     Copyright (C) 1999  Evin Robertson.  The last part Copyright (C) FSF
3
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.
8
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.
13
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.
17
18     The author can be reached at nitfol@deja.com
19 */
20
21
22 #include <stdlib.h>            /* For malloc */
23 #include <limits.h>            /* For LONG_MAX, LONG_MIN, ... */
24 #include <ctype.h>             /* For isspace, isdigit, isalpha */
25 #include "nitfol.h"
26
27 /* Nitfol malloc/realloc wrappers - never return NULL */
28 void *n_malloc(int size)
29 {
30   if(size != 0) {
31     void *m = malloc(size);
32     if(m != NULL)
33       return m;
34     while(free_undo()) {
35       m = malloc(size);
36       if(m)
37         return m;
38     }
39     n_show_fatal(E_MEMORY, "not enough memory for malloc", size);
40   } else {
41     n_show_fatal(E_MEMORY, "malloc(0)", size);
42   }
43
44   glk_exit();
45   return NULL;
46 }
47
48 void *n_calloc(int nmemb, int size)
49 {
50   int totalsize = nmemb * size;
51   void *m = n_malloc(totalsize);
52   n_memset(m, 0, totalsize);
53   return m;
54 }
55
56 void *n_realloc(void *ptr, int size)
57 {
58   void *m;
59   if(size == 0) {
60     n_free(ptr);
61     return NULL;
62   }
63   m = realloc(ptr, size);
64   if(m != NULL || size == 0)
65     return m;
66   while(free_undo()) {
67     m = realloc(ptr, size);
68     if(m)
69       return m;
70   }
71   n_free(ptr);
72
73   glk_exit();
74   return NULL;
75 }
76
77 void n_free(void *ptr)
78 {
79   free(ptr);
80 }
81
82 typedef struct rmmalloc_entry rmmalloc_entry;
83
84 struct rmmalloc_entry {
85   rmmalloc_entry *next;
86   void *data;
87 };
88
89 static rmmalloc_entry *rmmalloc_list = NULL;
90
91 /* This malloc maintains a list of malloced data, which can all be freed at
92    once */
93 void *n_rmmalloc(int size)
94 {
95   rmmalloc_entry newentry;
96   newentry.data = n_malloc(size);
97   LEadd(rmmalloc_list, newentry);
98   return newentry.data;
99 }
100
101 void n_rmfree(void)
102 {
103   rmmalloc_entry *p;
104   for(p=rmmalloc_list; p; p=p->next)
105     n_free(p->data);
106   LEdestroy(rmmalloc_list);  
107 }
108
109 void n_rmfreeone(void *m)
110 {
111   rmmalloc_entry *p, *t;
112   LEsearchremove(rmmalloc_list, p, t, p->data == m, n_free(p->data));
113 }
114
115
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)
119 {
120   if(target &&
121      n_strlen(target) == len &&
122      n_strncasecmp(target, starting, len) == 0)
123     return TRUE;
124   return FALSE;
125 }
126
127 /* Write 'n' in decimal to 'dest' - assume there is enough space in buffer */
128 int n_to_decimal(char *buffer, unsigned n)
129 {
130   int i = 0;
131   if(n == 0) {
132     buffer[0] = '0';
133     return 1;
134   } 
135   while(n) {
136     unsigned c = n % 10;
137     buffer[i++] = '0' + c;
138     n = (n - c) / 10;
139     if(i >= 12)
140       return i;
141   }
142   return i;
143 }
144
145 const char *n_static_number(const char *preface, glui32 n)
146 {
147   static char *buffer = NULL;
148   char number[12];
149   int preflen = n_strlen(preface);
150   int numlen;
151   int i;
152
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;
159   return buffer;
160 }
161
162 /* n_strdup(NULL) works, unlike strdup(NULL) which segfaults */
163 char *n_strdup(const char *s)
164 {
165   char *n;
166   if(s == NULL)
167     return NULL;
168   n = (char *) n_malloc(n_strlen(s) + 1);
169   n_strcpy(n, s);
170   return n;
171 }
172
173 /* Swap n bytes between a and b */
174 void n_memswap(void *a, void *b, int n)
175 {
176   int i;
177   unsigned char *c = (unsigned char *) a;
178   unsigned char *d = (unsigned char *) b;
179   unsigned char t;
180
181   for(i = 0; i < n; i++) {
182     t = d[i];
183     d[i] = c[i];
184     c[i] = t;
185   }
186 }
187
188 /* Wrappers to hide ugliness of Glk file opening functions */
189 strid_t n_file_prompt(glui32 usage, glui32 fmode)
190 {
191   frefid_t r = glk_fileref_create_by_prompt(usage, fmode, 0);
192   if(r) {
193     strid_t s;
194     if((fmode & filemode_Read) && !glk_fileref_does_file_exist(r))
195       return NULL;
196     s = glk_stream_open_file(r, fmode, 0);
197     glk_fileref_destroy(r);
198     return s;
199   }
200   return NULL;
201 }
202
203 strid_t n_file_name(glui32 usage, glui32 fmode, const char *name)
204 {
205   frefid_t r = glk_fileref_create_by_name(usage, (char *) name, 0);
206   if(r) {
207     strid_t s;
208     if((fmode & filemode_Read) && !glk_fileref_does_file_exist(r))
209         return NULL;
210     s = glk_stream_open_file(r, fmode, 0);
211     glk_fileref_destroy(r);
212     return s;
213   }
214   return NULL;
215 }
216
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)
219 {
220   const char *c;
221   for(c = name; *c; c++) {
222     if(*c != ' ')
223       return n_file_name(usage, fmode, name);
224   }
225   return n_file_prompt(usage, fmode);
226 }
227
228
229
230 /* Trivial wrappers to fix implicit (const char *) to (char *) cast warnings */
231 void w_glk_put_string(const char *s)
232 {
233   glk_put_string((char *) s);
234 }
235
236 void w_glk_put_string_stream(strid_t str, const char *s)
237 {
238   glk_put_string_stream(str, (char *) s);
239 }
240
241 void w_glk_put_buffer(const char *buf, glui32 len)
242 {
243   glk_put_buffer((char *) buf, len);
244 }
245
246 void w_glk_put_buffer_stream(strid_t str, const char *buf, glui32 len)
247 {
248   glk_put_buffer_stream(str, (char *) buf, len);
249 }
250
251 void w_glk_put_char(int ch)
252 {
253   glk_put_char(ch);
254 }
255
256
257
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. */
262
263 #ifdef HEADER
264
265 #ifndef NULL
266 #define NULL 0
267 #endif
268
269 /* FIXME: use autoconf for these someday */
270
271 #ifndef NO_LIBC
272 /* ISO 9899 functions */
273 #include <string.h>
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)
291
292 #include <stdlib.h>
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)
295 #endif
296
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)
300 #endif
301
302 #ifdef __USE_GNU
303 #define n_lfind(k, b, n, s, c) lfind(k, b, n, s, c)
304 #endif
305
306 #endif /* HEADER */
307
308
309 #ifndef n_strlen
310 unsigned n_strlen(const char *s)
311 {
312   int i = 0;
313   while(*s++)
314     i++;
315   return i;
316 }
317 #endif
318
319 #ifndef n_strcpy
320 char *n_strcpy(char *dest, const char *src)
321 {
322   while(*src) {
323     *dest++ = *src++;
324   }
325   *dest = 0;
326   return dest;
327 }
328 #endif
329
330 #ifndef n_strncpy
331 char *n_strncpy(char *dest, const char *src, int len)
332 {
333   while(*src && len) {
334     *dest++ = *src++;
335     len--;
336   }
337   while(len--)
338     *dest++ = 0;
339   return dest;
340 }
341 #endif
342
343 #ifndef n_memcpy
344 void *n_memcpy(void *dest, const void *src, int n)
345 {
346   int i;
347   unsigned char *a = (unsigned char *) dest;
348   unsigned const char *b = (const unsigned char *) src;
349   for(i = 0; i < n; i++)
350     a[i] = b[i];
351   return dest;
352 }
353 #endif
354
355 #ifndef n_memmove
356 void *n_memmove(void *dest, const void *src, int n)
357 {
358   int i;
359   unsigned char *a = (unsigned char *) dest;
360   unsigned char *b = (unsigned char *) src;
361   if(a < b)
362     for(i = 0; i < n; i++)
363       a[i] = b[i];
364   else
365     for(i = n-1; i >= 0; i--)
366       a[i] = b[i];
367   return a;
368 }
369 #endif
370
371 #ifndef n_memset
372 void *n_memset(void *s, int c, int n)
373 {
374   int i;
375   unsigned char *a = (unsigned char *) s;
376   for(i = 0; i < n; i++)
377     a[i] = c;
378   return s;
379 }
380 #endif
381
382 #ifndef n_strcmp
383 int n_strcmp(const char *a, const char *b)
384 {
385   for(;;) {
386     if(*a != *b)
387       return *a - *b;
388     if(*a == 0)
389       break;
390     a++; b++;
391   }
392   return 0;
393 }
394 #endif
395
396 #ifndef n_strncmp
397 int n_strncmp(const char *a, const char *b, int n)
398 {
399   for(; n; n--) {
400     if(*a != *b)
401       return *a - *b;
402     if(*a == 0)
403       break;
404     a++; b++;
405   }
406   return 0;
407 }
408 #endif
409
410 #ifndef n_memcmp
411 int n_memcmp(const void *s1, const void *s2, int n)
412 {
413   const unsigned char *a = (unsigned char *) s1;
414   const unsigned char *b = (unsigned char *) s2;
415   for(; n; n--) {
416     if(*a != *b)
417       return *a - *b;
418     a++; b++;
419   }
420   return 0;
421 }
422 #endif
423
424 #ifndef n_strcasecmp
425 int n_strcasecmp(const char *a, const char *b)
426 {
427   for(;;)
428   {
429     if(*a != *b) {
430       char c1 = glk_char_to_lower(*a);
431       char c2 = glk_char_to_lower(*b);
432       if(c1 != c2)
433         return c1 - c2;
434     }
435     if(*a == 0)
436       break;
437     a++; b++;
438   }
439   return 0;
440 }
441 #endif
442
443 #ifndef n_strncasecmp
444 int n_strncasecmp(const char *a, const char *b, int n)
445 {
446   for(; n; n--)
447   {
448     if(*a != *b) {
449       char c1 = glk_char_to_lower(*a);
450       char c2 = glk_char_to_lower(*b);
451       if(c1 != c2)
452         return c1 - c2;
453     }
454     if(*a == 0)
455       break;
456     a++; b++;
457   }
458   return 0;
459 }
460 #endif
461
462 #ifndef n_strlower
463 char *n_strlower(char *s)
464 {
465   char *b = s;
466   while(*b) {
467     *b = glk_char_to_lower(*b);
468     b++;
469   }
470   return s;
471 }
472 #endif
473
474 #ifndef n_strupper
475 char *n_strupper(char *s)
476 {
477   char *b = s;
478   while(*b) {
479     *b = glk_char_to_upper(*b);
480     b++;
481   }
482   return s;
483 }
484 #endif
485
486 #ifndef n_strchr
487 char *n_strchr(const char *s, int c)
488 {
489   const unsigned char *a = (const unsigned char *) s;
490   while(*a != c) {
491     if(*a == 0)
492       return NULL;
493     a++;
494   }
495   return (char *) a;
496 }
497 #endif
498
499 #ifndef n_lfind
500 void *n_lfind(const void *key, const void *base, int *nmemb, int size,
501               int (*compar)(const void *, const void *))
502 {
503   int i;
504   char *t = (char *) base;
505   for(i = 0; i < *nmemb; i++) {
506     if((*compar)(t, key) == 0)
507       return (void *) t;
508     t += size;
509   }
510   return NULL;
511 }
512 #endif
513
514
515 #ifndef n_qsort
516
517 /* Modified by Evin Robertson for nitfolness Aug 4 1999 */
518
519 /******************************************************************/
520 /* qsort.c  --  Non-Recursive ANSI Quicksort function             */
521 /*                                                                */
522 /* Public domain by Raymond Gardner, Englewood CO  February 1991  */
523 /*                                                                */
524 /* Usage:                                                         */
525 /*     qsort(base, nbr_elements, width_bytes, compare_function);  */
526 /*        void *base;                                             */
527 /*        size_t nbr_elements, width_bytes;                       */
528 /*        int (*compare_function)(const void *, const void *);    */
529 /*                                                                */
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 /******************************************************************/
539
540 /* prototypes */
541 static void swap_chars(char *, char *, int);
542
543 #define  SWAP(a, b)  (swap_chars((char *)(a), (char *)(b), size))
544
545
546 #define  COMP(a, b)  ((*comp)((void *)(a), (void *)(b)))
547
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          */
551
552 void n_qsort(void *basep, int nelems, int size,
553                             int (*comp)(const void *, const void *))
554 {
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 *         */
559
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 );
580             do                        /* move j left              */
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*/
586          }
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    */
596          }
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 ) {
601                SWAP(j, j+size);
602                if ( j == base )
603                   break;
604             }
605          if ( sp != stack ) {         /* if any entries on stack  */
606             sp -= 2;                  /* pop the base and limit   */
607             base = sp[0];
608             limit = sp[1];
609          } else                       /* else stack empty, done   */
610             break;
611       }
612    }
613 }
614
615 /*
616 **  swap nbytes between a and b
617 */
618
619 static void swap_chars(char *a, char *b, int nbytes)
620 {
621    char tmp;
622    do {
623       tmp = *a; *a++ = *b; *b++ = tmp;
624    } while ( --nbytes );
625 }
626
627 #endif
628
629
630
631
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 */
634
635 #ifndef n_strcat
636 char *n_strcat(char *dest, const char *src)
637 {
638   char *s1 = dest;
639   const char *s2 = src;
640   char c;
641
642   /* Find the end of the string.  */
643   do
644     c = *s1++;
645   while (c != '\0');
646
647   /* Make S1 point before the next character, so we can increment
648      it while memory is read (wins on pipelined cpus).  */
649   s1 -= 2;
650
651   do
652     {
653       c = *s2++;
654       *++s1 = c;
655     }
656   while (c != '\0');
657
658   return dest;
659 }
660 #endif
661
662 #ifndef n_strpbrk
663 char *n_strpbrk(const char *s, const char *accept)
664 {
665   while (*s != '\0')
666     if (n_strchr(accept, *s) == NULL)
667       ++s;
668     else
669       return (char *) s;
670
671   return NULL;
672 }
673 #endif
674
675 #ifndef n_strspn
676 int n_strspn(const char *s, const char *accept)
677 {
678   const char *p;
679   const char *a;
680   int count = 0;
681
682   for (p = s; *p != '\0'; ++p)
683     {
684       for (a = accept; *a != '\0'; ++a)
685         if (*p == *a)
686           break;
687       if (*a == '\0')
688         return count;
689       else
690         ++count;
691     }
692
693   return count;
694 }
695 #endif
696
697 #ifndef n_strtok
698 char *n_strtok(char *s, const char *delim)
699 {
700   static char *olds = NULL;
701   char *token;
702
703   if (s == NULL)
704     {
705       if (olds == NULL)
706         {
707           /* errno = EINVAL; */
708           return NULL;
709         }
710       else
711         s = olds;
712     }
713
714   /* Scan leading delimiters.  */
715   s += n_strspn(s, delim);
716   if (*s == '\0')
717     {
718       olds = NULL;
719       return NULL;
720     }
721
722   /* Find the end of the token.  */
723   token = s;
724   s = n_strpbrk(token, delim);
725   if (s == NULL)
726     /* This token finishes the string.  */
727     olds = NULL;
728   else
729     {
730       /* Terminate the token and make OLDS point past it.  */
731       *s = '\0';
732       olds = s + 1;
733     }
734   return token;
735 }
736 #endif
737
738 #ifndef n_strstr
739 char *n_strstr(const char *haystack, const char *needle)
740 {
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;
745   const char *begin;
746
747   if (needle_len == 0)
748     return (char *) haystack;   /* ANSI 4.11.5.7, line 25.  */
749   if ((size_t) (haystack_end - haystack) < needle_len)
750     return NULL;
751
752   for (begin = &haystack[needle_last]; begin < haystack_end; ++begin)
753     {
754       const char *n = &needle[needle_last];
755       const char *h = begin;
756
757       do
758         if (*h != *n)
759           goto loop;            /* continue for loop */
760       while (--n >= needle && --h >= haystack);
761
762       return (char *) h;
763
764     loop:;
765     }
766
767   return NULL;
768 }
769 #endif
770
771 #ifndef n_strtol
772 long int n_strtol (const char *nptr, char **endptr, int base)
773 {
774   int negative;
775   unsigned long int cutoff;
776   unsigned int cutlim;
777   unsigned long int i;
778   const char *s;
779   unsigned char c;
780   const char *save;
781   int overflow;
782
783   if (base < 0 || base == 1 || base > 36)
784     base = 10;
785
786   s = nptr;
787
788   /* Skip white space.  */
789   while (isspace (*s))
790     ++s;
791   if (*s == '\0')
792     goto noconv;
793
794   /* Check for a sign.  */
795   if (*s == '-')
796     {
797       negative = 1;
798       ++s;
799     }
800   else if (*s == '+')
801     {
802       negative = 0;
803       ++s;
804     }
805   else
806     negative = 0;
807
808   if (base == 16 && s[0] == '0' && glk_char_to_upper (s[1]) == 'X')
809     s += 2;
810
811   /* If BASE is zero, figure it out ourselves.  */
812   if (base == 0)
813     if (*s == '0')
814       {
815         if (glk_char_to_upper (s[1]) == 'X')
816           {
817             s += 2;
818             base = 16;
819           }
820         else
821           base = 8;
822       }
823     else
824       base = 10;
825
826   /* Save the pointer so we can check later if anything happened.  */
827   save = s;
828
829   cutoff = ULONG_MAX / (unsigned long int) base;
830   cutlim = ULONG_MAX % (unsigned long int) base;
831
832   overflow = 0;
833   i = 0;
834   for (c = *s; c != '\0'; c = *++s)
835     {
836       if (isdigit (c))
837         c -= '0';
838       else if (isalpha (c))
839         c = glk_char_to_upper (c) - 'A' + 10;
840       else
841         break;
842       if (c >= base)
843         break;
844       /* Check for overflow.  */
845       if (i > cutoff || (i == cutoff && c > cutlim))
846         overflow = 1;
847       else
848         {
849           i *= (unsigned long int) base;
850           i += c;
851         }
852     }
853
854   /* Check if anything actually happened.  */
855   if (s == save)
856     goto noconv;
857
858   /* Store in ENDPTR the address of one character
859      past the last character we converted.  */
860   if (endptr != NULL)
861     *endptr = (char *) s;
862
863   /* Check for a value that is within the range of
864      `unsigned long int', but outside the range of `long int'.  */
865   if (i > (negative ?
866            -(unsigned long int) LONG_MIN : (unsigned long int) LONG_MAX))
867     overflow = 1;
868
869   if (overflow)
870     {
871       /* errno = ERANGE; */
872       return negative ? LONG_MIN : LONG_MAX;
873     }
874
875   /* Return the result of the appropriate sign.  */
876   return (negative ? -i : i);
877
878 noconv:
879   /* There was no number to convert.  */
880   if (endptr != NULL)
881     *endptr = (char *) nptr;
882   return 0L;
883 }
884 #endif
885
886 #ifndef n_strrchr
887 char *n_strrchr(const char *s, int c)
888 {
889   const char *found, *p;
890
891   c = (unsigned char) c;
892
893   /* Since strchr is fast, we use it rather than the obvious loop.  */
894   
895   if (c == '\0')
896     return n_strchr(s, '\0');
897
898   found = NULL;
899   while ((p = n_strchr(s, c)) != NULL)
900     {
901       found = p;
902       s = p + 1;
903     }
904
905   return (char *) found;
906 }
907 #endif
908
909 #ifndef n_bsearch
910 void *n_bsearch(const void *key, const void *base, int nmemb, int size,
911                 int (*compar)(const void *, const void *))
912 {
913   int l, u, idx;
914   void *p;
915   int comparison;
916
917   l = 0;
918   u = nmemb;
919   while (l < u)
920     {
921       idx = (l + u) / 2;
922       p = (void *) (((const char *) base) + (idx * size));
923       comparison = (*compar)(key, p);
924       if (comparison < 0)
925         u = idx;
926       else if (comparison > 0)
927         l = idx + 1;
928       else
929         return (void *) p;
930     }
931
932   return NULL;
933 }
934 #endif
935