/* Copyright (c) 1992, 1999, 2001, 2002, 2003 John E. Davis * This file is part of the S-Lang library. * * You may distribute under the terms of either the GNU General Public * License or the Perl Artistic License. */ #define _GNU_SOURCE #include "slinclud.h" #include #include "slang.h" #include "_slang.h" #define DEBUG_MALLOC 0 #if DEBUG_MALLOC # define SLREALLOC_FUN SLdebug_realloc # define SLMALLOC_FUN SLdebug_malloc # define SLFREE_FUN SLdebug_free #else # define SLREALLOC_FUN SLREALLOC # define SLMALLOC_FUN SLMALLOC # define SLFREE_FUN SLFREE #endif /* Version information goes here since this file is always needed. */ int SLang_Version = SLANG_VERSION; char *SLang_Version_String = SLANG_VERSION_STRING; char *SLmake_string(char *str) { return SLmake_nstring(str, strlen (str)); } char *SLmake_nstring (char *str, unsigned int n) { char *ptr; if (NULL == (ptr = SLmalloc(n + 1))) { return NULL; } SLMEMCPY (ptr, str, n); ptr[n] = 0; return(ptr); } void SLmake_lut (unsigned char *lut, unsigned char *range, unsigned char reverse) { /* register unsigned char *l = lut, *lmax = lut + 256; */ int i, r1, r2; memset ((char *)lut, reverse, 256); /* while (l < lmax) *l++ = reverse; */ reverse = !reverse; r1 = *range++; while (r1) { r2 = *range++; if ((r2 == '-') && (*range != 0)) { r2 = *range++; for (i = r1; i <= r2; i++) lut[i] = reverse; r1 = *range++; continue; } lut[r1] = reverse; r1 = r2; } } char *SLmalloc (unsigned int len) { char *p; p = (char *) SLMALLOC_FUN (len); if (p == NULL) SLang_Error = SL_MALLOC_ERROR; return p; } void SLfree (char *p) { if (p != NULL) SLFREE_FUN (p); } char *SLrealloc (char *p, unsigned int len) { if (len == 0) { SLfree (p); return NULL; } if (p == NULL) p = SLmalloc (len); else { p = (char *)SLREALLOC_FUN (p, len); if (p == NULL) SLang_Error = SL_MALLOC_ERROR; } return p; } char *SLcalloc (unsigned int nelems, unsigned int len) { char *p; len = nelems * len; p = SLmalloc (len); if (p != NULL) SLMEMSET (p, 0, len); return p; } char *_SLskip_whitespace (char *s) { while (isspace (*s)) s++; return s; } /* p and ch may point to the same buffer */ char *_SLexpand_escaped_char(char *p, char *ch) { int i = 0; int max = 0, num, base = 0; char ch1; ch1 = *p++; switch (ch1) { default: num = ch1; break; case 'n': num = '\n'; break; case 't': num = '\t'; break; case 'v': num = '\v'; break; case 'b': num = '\b'; break; case 'r': num = '\r'; break; case 'f': num = '\f'; break; case 'E': case 'e': num = 27; break; case 'a': num = 7; break; /* octal */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': max = '7'; base = 8; i = 2; num = ch1 - '0'; break; case 'd': /* decimal -- S-Lang extension */ base = 10; i = 3; max = '9'; num = 0; break; case 'x': /* hex */ base = 16; max = '9'; i = 2; num = 0; break; } while (i--) { ch1 = *p; if ((ch1 <= max) && (ch1 >= '0')) { num = base * num + (ch1 - '0'); } else if (base == 16) { ch1 |= 0x20; if ((ch1 < 'a') || ((ch1 > 'f'))) break; num = base * num + 10 + (ch1 - 'a'); } else break; p++; } *ch = (char) num; return p; } /* s and t could represent the same space */ void SLexpand_escaped_string (register char *s, register char *t, register char *tmax) { char ch; while (t < tmax) { ch = *t++; if (ch == '\\') { t = _SLexpand_escaped_char (t, &ch); } *s++ = ch; } *s = 0; } int SLextract_list_element (char *list, unsigned int nth, char delim, char *elem, unsigned int buflen) { char *el, *elmax; char ch; while (nth > 0) { while ((0 != (ch = *list)) && (ch != delim)) list++; if (ch == 0) return -1; list++; nth--; } el = elem; elmax = el + (buflen - 1); while ((0 != (ch = *list)) && (ch != delim) && (el < elmax)) *el++ = *list++; *el = 0; return 0; } #ifndef HAVE_VSNPRINTF int _SLvsnprintf (char *buf, unsigned int buflen, char *fmt, va_list ap) { #if 1 unsigned int len; /* On some systems vsprintf returns useless information. So, punt */ vsprintf (buf, fmt, ap); len = strlen (buf); if (len >= buflen) { SLang_exit_error ("\ Your system lacks the vsnprintf system call and vsprintf overflowed a buffer.\n\ The integrity of this program has been violated.\n"); return EOF; /* NOT reached */ } return (int)len; #else int status; status = vsprintf (buf, fmt, ap); if (status >= (int) buflen) { /* If we are lucky, we will get this far. The real solution is to * provide a working version of vsnprintf */ SLang_exit_error ("\ Your system lacks the vsnprintf system call and vsprintf overflowed a buffer.\n\ The integrity of this program has been violated.\n"); return EOF; /* NOT reached */ } return status; #endif } #endif #ifndef HAVE_SNPRINTF int _SLsnprintf (char *buf, unsigned int buflen, char *fmt, ...) { int status; va_list ap; va_start (ap, fmt); status = _SLvsnprintf (buf, buflen, fmt, ap); va_end (ap); return status; } #endif typedef struct _Cleanup_Function_Type { struct _Cleanup_Function_Type *next; void (*f)(void); } Cleanup_Function_Type; static Cleanup_Function_Type *Cleanup_Function_List; static void cleanup_slang (void) { while (Cleanup_Function_List != NULL) { Cleanup_Function_Type *next = Cleanup_Function_List->next; (*Cleanup_Function_List->f)(); SLFREE_FUN ((char *) Cleanup_Function_List); Cleanup_Function_List = next; } } #ifndef HAVE_ATEXIT # ifdef HAVE_ON_EXIT static void on_exit_cleanup_slang (int arg_unused) { (void) arg_unused; cleanup_slang (); } # endif #endif int SLang_add_cleanup_function (void (*f)(void)) { Cleanup_Function_Type *l; l = (Cleanup_Function_Type *) SLMALLOC_FUN (sizeof (Cleanup_Function_Type)); if (l == NULL) return -1; l->f = f; l->next = Cleanup_Function_List; if (Cleanup_Function_List == NULL) { #ifdef HAVE_ATEXIT (void) atexit (cleanup_slang); #else # ifdef HAVE_ON_EXIT (void) on_exit (on_exit_cleanup_slang, 0); # endif #endif } Cleanup_Function_List = l; return 0; } int SLang_guess_type (char *t) { char *p; register char ch; int modifier; if (*t == '-') t++; p = t; #if SLANG_HAS_FLOAT if (*p != '.') { #endif modifier = 0; while ((*p >= '0') && (*p <= '9')) p++; if (t == p) return (SLANG_STRING_TYPE); if ((*p == 'x') && (p == t + 1)) /* 0x?? */ { modifier |= 8; p++; while (ch = *p, ((ch >= '0') && (ch <= '9')) || (((ch | 0x20) >= 'a') && ((ch | 0x20) <= 'f'))) p++; } /* Now look for UL, LU, UH, HU, L, H modifiers */ while ((ch = *p) != 0) { ch |= 0x20; if (ch == 'h') modifier |= 1; else if (ch == 'l') modifier |= 2; else if (ch == 'u') modifier |= 4; else break; p++; } if ((1|2) == (modifier & (1|2))) /* hl present */ return SLANG_STRING_TYPE; if (ch == 0) { if ((modifier & 0x7) == 0) return SLANG_INT_TYPE; if (modifier & 4) { if (modifier & 1) return SLANG_USHORT_TYPE; if (modifier & 2) return SLANG_ULONG_TYPE; return SLANG_UINT_TYPE; } if (modifier & 1) return SLANG_SHORT_TYPE; if (modifier & 2) return SLANG_LONG_TYPE; return SLANG_INT_TYPE; } if (modifier) return SLANG_STRING_TYPE; #if SLANG_HAS_FLOAT } /* now down to double case */ if (*p == '.') { p++; while ((*p >= '0') && (*p <= '9')) p++; } if (*p == 0) return(SLANG_DOUBLE_TYPE); if ((*p != 'e') && (*p != 'E')) { # if SLANG_HAS_COMPLEX if (((*p == 'i') || (*p == 'j')) && (p[1] == 0)) return SLANG_COMPLEX_TYPE; # endif if (((*p | 0x20) == 'f') && (p[1] == 0)) return SLANG_FLOAT_TYPE; return SLANG_STRING_TYPE; } p++; if ((*p == '-') || (*p == '+')) p++; while ((*p >= '0') && (*p <= '9')) p++; if (*p != 0) { # if SLANG_HAS_COMPLEX if (((*p == 'i') || (*p == 'j')) && (p[1] == 0)) return SLANG_COMPLEX_TYPE; # endif if (((*p | 0x20) == 'f') && (p[1] == 0)) return SLANG_FLOAT_TYPE; return SLANG_STRING_TYPE; } return SLANG_DOUBLE_TYPE; #else return SLANG_STRING_TYPE; #endif /* SLANG_HAS_FLOAT */ } static int hex_atoul (unsigned char *s, unsigned long *ul) { register unsigned char ch; register unsigned long value; register int base; s++; /* skip the leading 0 */ /* look for 'x' which indicates hex */ if ((*s | 0x20) == 'x') { base = 16; s++; if (*s == 0) { SLang_Error = SL_SYNTAX_ERROR; return -1; } } else base = 8; value = 0; while ((ch = *s++) != 0) { char ch1 = ch | 0x20; switch (ch1) { default: SLang_Error = SL_SYNTAX_ERROR; break; case 'u': case 'l': case 'h': *ul = value; return 0; case '8': case '9': if (base != 16) SLang_Error = SL_SYNTAX_ERROR; /* drop */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': ch1 -= '0'; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': if (base != 16) SLang_Error = SL_SYNTAX_ERROR; ch1 = (ch1 - 'a') + 10; break; } value = value * base + ch1; } *ul = value; return 0; } /* Note: These routines do not check integer overflow. I would use the C * library functions atol and atoul but some implementations check overflow * and some do not. The following implementations provide a consistent * behavior. */ unsigned long SLatoul (unsigned char *s) { int sign; unsigned long value; if (*s == '-') sign = -1; else { sign = 1; if (*s == '+') s++; } if (*s == '0') { if (-1 == hex_atoul (s, &value)) return (unsigned long) -1; } else { s = (unsigned char *) _SLskip_whitespace ((char *)s); value = 0; while (isdigit (*s)) { value = value * 10 + (unsigned long) (*s - '0'); s++; } } if (sign == -1) value = (unsigned long)-1L * value; return value; } long SLatol (unsigned char *s) { s = (unsigned char *) _SLskip_whitespace ((char *)s); if (*s == '-') { long value = (long) SLatoul (s+1); return -value; } return (long) SLatoul (s); } int SLatoi (unsigned char *s) { return (int) SLatol (s); } #if !defined(HAVE_ISSETUGID) && defined(__GLIBC__) && (__GLIBC__ >= 2) extern int __libc_enable_secure; # define HAVE___LIBC_ENABLE_SECURE 1 #endif int _SLsecure_issetugid (void) { #ifdef HAVE_ISSETUGID return (1 == issetugid ()); #else # ifdef HAVE___LIBC_ENABLE_SECURE return __libc_enable_secure; # else # if defined(HAVE_GETUID) && defined(HAVE_GETEUID) && defined(HAVE_GETGID) && defined(HAVE_GETEUID) static int enable_secure; if (enable_secure == 0) { if ((getuid () != geteuid ()) || (getgid () != getegid ())) enable_secure = 1; else enable_secure = -1; } return (enable_secure == 1); # else return 0; # endif # endif #endif } /* Like getenv, except if running as setuid or setgid, returns NULL */ char *_SLsecure_getenv (char *s) { if (_SLsecure_issetugid ()) return NULL; return getenv (s); }