/* *$Id$ * * - various general purpose functions * * Copyright (C) 2001-2003 FhG Fokus * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * History * ------ * 2003-01-18 un_escape function introduced for convenience of code needing * the complex&slow feature of unescaping * 2003-01-28 scratchpad removed (jiri) * 2003-01-29 pathmax added (jiri) * 2003-02-13 strlower added (janakj) * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-03-30 str2int and str2float added (janakj) * 2003-04-26 ZSW (jiri) * 2004-03-08 updated int2str (64 bits, INT2STR_MAX_LEN used) (andrei) * 2005-11-29 reverse_hex2int/int2reverse_hex switched to unsigned int (andrei) * 2005-12-09 added msgid_var (andrei) * 2007-05-14 added get_sys_ver() (andrei) * 2007-06-05 added MAX_UVAR_VALUE(), MAX_int(a,b) MIN_int(a,b) (andrei) * 2008-05-21 added ushort2sbuf(), ushort2str() (andrei) * 2009-03-16 added sint2strbuf() and incremented INST2STR_MAX_LEN to account * for sign (andrei) */ /** various general purpose/helper functions. * @file */ #ifndef ut_h #define ut_h #include "comp_defs.h" #include #include #include #include #include #include #include #include #include #include "compiler_opt.h" #include "config.h" #include "dprint.h" #include "str.h" #include "mem/mem.h" #include "mem/shm_mem.h" /* zero-string wrapper */ #define ZSW(_c) ((_c)?(_c):"") /* returns string beginning and length without insignificant chars */ #define trim_len( _len, _begin, _mystr ) \ do{ static char _c; \ (_len)=(_mystr).len; \ while ((_len) && ((_c=(_mystr).s[(_len)-1])==0 || _c=='\r' || \ _c=='\n' || _c==' ' || _c=='\t' )) \ (_len)--; \ (_begin)=(_mystr).s; \ while ((_len) && ((_c=*(_begin))==' ' || _c=='\t')) { \ (_len)--;\ (_begin)++; \ } \ }while(0) #define trim_r( _mystr ) \ do{ static char _c; \ while( ((_mystr).len) && ( ((_c=(_mystr).s[(_mystr).len-1]))==0 ||\ _c=='\r' || _c=='\n' ) \ ) \ (_mystr).len--; \ }while(0) #define translate_pointer( _new_buf , _org_buf , _p) \ ( (_p)?(_new_buf + (_p-_org_buf)):(0) ) #define via_len(_via) \ ((_via)->bsize-((_via)->name.s-\ ((_via)->hdr.s+(_via)->hdr.len))) /* rounds to sizeof(type), but type must have a 2^k size (e.g. short, int, * long, void*) */ #define ROUND2TYPE(s, type) \ (((s)+(sizeof(type)-1))&(~(sizeof(type)-1))) /* rounds to sizeof(char*) - the first 4 byte multiple on 32 bit archs * and the first 8 byte multiple on 64 bit archs */ #define ROUND_POINTER(s) ROUND2TYPE(s, char*) /* rounds to sizeof(long) - the first 4 byte multiple on 32 bit archs * and the first 8 byte multiple on 64 bit archs (equiv. to ROUND_POINTER)*/ #define ROUND_LONG(s) ROUND2TYPE(s, long) /* rounds to sizeof(int) - the first t byte multiple on 32 and 64 bit archs */ #define ROUND_INT(s) ROUND2TYPE(s, int) /* rounds to sizeof(short) - the first 2 byte multiple */ #define ROUND_SHORT(s) ROUND2TYPE(s, short) /* params: v - either a variable name, structure member or a type * returns an unsigned long containing the maximum possible value that will * fit in v, if v is unsigned or converted to an unsigned version * example: MAX_UVAR_VALUE(unsigned short); MAX_UVAR_VALUE(i); * MAX_UVAR_VALUE(((struct foo*)0)->bar) */ #define MAX_UVAR_VALUE(v) \ (((unsigned long)(-1))>>((sizeof(unsigned long)-sizeof(v))*8UL)) #define MIN_int(a, b) (((a)<(b))?(a):(b)) #define MAX_int(a, b) (((a)>(b))?(a):(b)) #define MIN_unsigned(a, b) (unsigned)(((unsigned)(a)<(unsigned)(b))?(a):(b)) #define MAX_unsigned(a, b) (unsigned)(((unsigned)(a)>(unsigned)(b))?(a):(b)) #if 0 #define MIN_int(a, b) ((b)+(((a)-(b))& -((a)<(b)))) #define MAX_int(a, b) ((a)-(((a)-(b))& -((b)>(a)))) /* depend on signed right shift result which depends on the compiler */ #define MIN_int(a, b) ((b)+(((a)-(b))&(((a)-(b))>>(sizeof(int)*8-1)))) #define MAX_int(a, b) ((a)-(((a)-(b))&(((a)-(b))>>(sizeof(int)*8-1)))) #endif #define append_str(_dest,_src,_len) \ do{ \ memcpy( (_dest) , (_src) , (_len) ); \ (_dest) += (_len) ; \ }while(0); \ /*! append _c char to _dest string */ #define append_chr(_dest,_c) \ *((_dest)++) = _c; /* links a value to a msgid */ struct msgid_var{ union{ char char_val; int int_val; long long_val; }u; unsigned int msgid; }; /* return the value or 0 if the msg_id doesn't match */ #define get_msgid_val(var, id, type)\ ((type)((type)((var).msgid!=(id))-1)&((var).u.type##_val)) #define set_msgid_val(var, id, type, value)\ do{\ (var).msgid=(id); \ (var).u.type##_val=(value); \ }while(0) /* char to hex conversion table */ static char fourbits2char[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /* converts a str to an u. short, returns the u. short and sets *err on * error and if err!=null */ static inline unsigned short str2s(const char* s, unsigned int len, int *err) { unsigned short ret; int i; unsigned char *limit; unsigned char *init; unsigned char* str; /*init*/ str=(unsigned char*)s; ret=i=0; limit=str+len; init=str; for(;str= '0') ){ ret=ret*10+*str-'0'; i++; if (i>5) goto error_digits; }else{ /* error unknown char */ goto error_char; } } if (err) *err=0; return ret; error_digits: /*DBG("str2s: ERROR: too many letters in [%.*s]\n", (int)len, init); */ if (err) *err=1; return 0; error_char: /*DBG("str2s: ERROR: unexpected char %c in %.*s\n", *str, (int)len, init); * */ if (err) *err=1; return 0; } static inline int btostr( char *p, unsigned char val) { unsigned int a,b,i =0; if ( (a=val/100)!=0 ) *(p+(i++)) = a+'0'; /*first digit*/ if ( (b=val%100/10)!=0 || a) *(p+(i++)) = b+'0'; /*second digit*/ *(p+(i++)) = '0'+val%10; /*third digit*/ return i; } #define INT2STR_MAX_LEN (19+1+1+1) /* 2^64~= 16*10^18 => 19+1 digits + sign + \0 */ /* * returns a pointer to a static buffer containing l in asciiz (with base "base") & sets len * left padded with 0 to "size" */ static inline char* int2str_base_0pad(unsigned int l, int* len, int base, int size) { static char r[INT2STR_MAX_LEN]; int i, j; if (base < 2) { BUG("base underflow\n"); return NULL; } if (base > 36) { BUG("base overflow\n"); return NULL; } i=INT2STR_MAX_LEN-2; j=i-size; r[INT2STR_MAX_LEN-1]=0; /* null terminate */ do{ r[i]=l%base; if (r[i]<10) r[i]+='0'; else r[i]+='a'-10; i--; l/=base; }while((l || i>j) && (i>=0)); if (l && (i<0)){ BUG("result buffer overflow\n"); } if (len) *len=(INT2STR_MAX_LEN-2)-i; return &r[i+1]; } /* returns a pointer to a static buffer containing l in asciiz (with base "base") & sets len */ static inline char* int2str_base(unsigned int l, int* len, int base) { return int2str_base_0pad(l, len, base, 0); } /** unsigned long to str conversion using a provided buffer. * Converts/prints an unsigned long to a string. The result buffer must be * provided and its length must be at least INT2STR_MAX_LEN. * @param l - unsigned long to be converted * @param r - pointer to result buffer * @param r_size - result buffer size, must be at least INT2STR_MAX_LEN. * @param *len - length of the written string, _without_ the terminating 0. * @return pointer _inside_ r, to the converted string (note: the string * is written from the end of the buffer and not from the start and hence * the returned pointer will most likely not be equal to r). In case of error * it returns 0 (the only error being insufficient provided buffer size). */ static inline char* int2strbuf(unsigned long l, char *r, int r_size, int* len) { int i; if(unlikely(r_size if someone misuses it => crash (feature no. 1) */ } i=INT2STR_MAX_LEN-2; r[INT2STR_MAX_LEN-1]=0; /* null terminate */ do{ r[i]=l%10+'0'; i--; l/=10; }while(l && (i>=0)); if (l && (i<0)){ LOG(L_CRIT, "BUG: int2str: overflow\n"); } if (len) *len=(INT2STR_MAX_LEN-2)-i; return &r[i+1]; } extern char ut_buf_int2str[INT2STR_MAX_LEN]; /** interger(long) to string conversion. * This version uses a static buffer (shared with sint2str()). * WARNING: other function calls might overwrite the static buffer, so * either always save the result immediately or use int2strbuf(...). * @param l - unsigned long to be converted/printed. * @param *len - will be filled with the final length (without the terminating * 0). * @return a pointer to a static buffer containing l in asciiz & sets len. */ static inline char* int2str(unsigned long l, int* len) { return int2strbuf(l, ut_buf_int2str, INT2STR_MAX_LEN, len); } /** signed long to str conversion using a provided buffer. * Converts a long to a signed string. The result buffer must be provided * and its length must be at least INT2STR_MAX_LEN. * @param l - long to be converted * @param r - pointer to result buffer * @param r_size - result buffer size, must be at least INT2STR_MAX_LEN. * @param *len - length of the written string, _without_ the terminating 0. * @return pointer _inside_ r, to the converted string (note: the string * is written from the end of the buffer and not from the start and hence * the returned pointer will most likely not be equal to r). In case of error * it returns 0 (the only error being insufficient provided buffer size). */ static inline char* sint2strbuf(long l, char* r, int r_size, int* len) { int sign; char *p; int p_len; sign = 0; if(l<0) { sign = 1; l = -l; } p = int2strbuf((unsigned long)l, r, r_size, &p_len); if(sign && p_len<(r_size-1)) { *(--p) = '-'; p_len++;; } if (likely(len)) *len = p_len; return p; } /** Signed INTeger-TO-STRing: converts a long to a string. * This version uses a static buffer, shared with int2str(). * WARNING: other function calls might overwrite the static buffer, so * either always save the result immediately or use sint2strbuf(...). * @param l - long to be converted/printed. * @param *len - will be filled with the final length (without the terminating * 0). * @return a pointer to a static buffer containing l in asciiz & sets len. */ static inline char* sint2str(long l, int* len) { return sint2strbuf(l, ut_buf_int2str, INT2STR_MAX_LEN, len); } #define USHORT2SBUF_MAX_LEN 5 /* 65535*/ /* converts an unsigned short (16 bits) to asciiz * returns bytes written or 0 on error * the passed len must be at least USHORT2SBUF_MAX chars or error * would be returned. * (optimized for port conversion (4 or 5 digits most of the time)*/ static inline int ushort2sbuf(unsigned short u, char* buf, int len) { int offs; unsigned char a, b, c, d; if (unlikely(len0; pc--, len--) { *res <<= 4 ; mychar=*pc; if ( mychar >='0' && mychar <='9') *res+=mychar -'0'; else if (mychar >='a' && mychar <='f') *res+=mychar -'a'+10; else if (mychar >='A' && mychar <='F') *res+=mychar -'A'+10; else return -1; } return 1; } inline static int int2reverse_hex( char **c, int *size, unsigned int nr ) { unsigned short digit; if (*size && nr==0) { **c = '0'; (*c)++; (*size)--; return 1; } while (*size && nr ) { digit = nr & 0xf ; **c= digit >= 10 ? digit + 'a' - 10 : digit + '0'; nr >>= 4; (*c)++; (*size)--; } return nr ? -1 /* number not processed; too little space */ : 1; } /* double output length assumed ; does NOT zero-terminate */ inline static int string2hex( /* input */ unsigned char *str, int len, /* output */ char *hex ) { int orig_len; if (len==0) { *hex='0'; return 1; } orig_len=len; while ( len ) { *hex=fourbits2char[(*str) >> 4]; hex++; *hex=fourbits2char[(*str) & 0xf]; hex++; len--; str++; } return orig_len-len; } /* portable sleep in microseconds (no interrupt handling now) */ inline static void sleep_us( unsigned int nusecs ) { struct timeval tval; tval.tv_sec =nusecs/1000000; tval.tv_usec=nusecs%1000000; select(0, NULL, NULL, NULL, &tval ); } /* portable determination of max_path */ inline static int pathmax() { #ifdef PATH_MAX static int pathmax=PATH_MAX; #else static int pathmax=0; #endif if (pathmax==0) { /* init */ pathmax=pathconf("/", _PC_PATH_MAX); pathmax=(pathmax<=0)?PATH_MAX_GUESS:pathmax+1; } return pathmax; } inline static int hex2int(char hex_digit) { if (hex_digit>='0' && hex_digit<='9') return hex_digit-'0'; if (hex_digit>='a' && hex_digit<='f') return hex_digit-'a'+10; if (hex_digit>='A' && hex_digit<='F') return hex_digit-'A'+10; /* no valid hex digit ... */ LOG(L_ERR, "ERROR: hex2int: '%c' is no hex char\n", hex_digit ); return -1; } /* Un-escape URI user -- it takes a pointer to original user str, as well as the new, unescaped one, which MUST have an allocated buffer linked to the 'str' structure ; (the buffer can be allocated with the same length as the original string -- the output string is always shorter (if escaped characters occur) or same-long as the original one). only printable characters are permitted <0 is returned on an unescaping error, length of the unescaped string otherwise */ inline static int un_escape(str *user, str *new_user ) { int i, j, value; int hi, lo; if( new_user==0 || new_user->s==0) { LOG(L_CRIT, "BUG: un_escape: called with invalid param\n"); return -1; } new_user->len = 0; j = 0; for (i = 0; i < user->len; i++) { if (user->s[i] == '%') { if (i + 2 >= user->len) { LOG(L_ERR, "ERROR: un_escape: escape sequence too short in" " '%.*s' @ %d\n", user->len, user->s, i ); goto error; } hi=hex2int(user->s[i + 1]); if (hi<0) { LOG(L_ERR, "ERROR: un_escape: non-hex high digit in an escape sequence in" " '%.*s' @ %d\n", user->len, user->s, i+1 ); goto error; } lo=hex2int(user->s[i + 2]); if (lo<0) { LOG(L_ERR, "ERROR: non-hex low digit in an escape sequence in " "'%.*s' @ %d\n", user->len, user->s, i+2 ); goto error; } value=(hi<<4)+lo; if (value < 32 || value > 126) { LOG(L_ERR, "ERROR: non-ASCII escaped character in '%.*s' @ %d\n", user->len, user->s, i ); goto error; } new_user->s[j] = value; i+=2; /* consume the two hex digits, for cycle will move to the next char */ } else { new_user->s[j] = user->s[i]; } j++; /* good -- we translated another character */ } new_user->len = j; return j; error: new_user->len = j; return -1; } /* * Convert a string to lower case */ static inline void strlower(str* _s) { int i; for(i = 0; i < _s->len; i++) { _s->s[i] = tolower(_s->s[i]); } } /* * Convert a str into integer */ static inline int str2int(str* _s, unsigned int* _r) { int i; *_r = 0; for(i = 0; i < _s->len; i++) { if ((_s->s[i] >= '0') && (_s->s[i] <= '9')) { *_r *= 10; *_r += _s->s[i] - '0'; } else { return -1; } } return 0; } /* * Convert an str to signed integer */ static inline int str2sint(str* _s, int* _r) { int i; int sign; if (_s->len == 0) return -1; *_r = 0; sign = 1; i = 0; if (_s->s[0] == '+') { i++; } else if (_s->s[0] == '-') { sign = -1; i++; } for(; i < _s->len; i++) { if ((_s->s[i] >= '0') && (_s->s[i] <= '9')) { *_r *= 10; *_r += _s->s[i] - '0'; } else { return -1; } } *_r *= sign; return 0; } #ifdef SHM_MEM /** * \brief Make a copy of a str structure using shm_malloc * \param dst destination * \param src source * \return 0 on success, -1 on failure */ static inline int shm_str_dup(str* dst, const str* src) { dst->s = shm_malloc(src->len); if (!dst->s) { SHM_MEM_ERROR; return -1; } memcpy(dst->s, src->s, src->len); dst->len = src->len; return 0; } #endif /* SHM_MEM */ /** * \brief Make a copy of a str structure using pkg_malloc * \param dst destination * \param src source * \return 0 on success, -1 on failure */ static inline int pkg_str_dup(str* dst, const str* src) { dst->s = pkg_malloc(src->len); if (dst->s==NULL) { PKG_MEM_ERROR; return -1; } memcpy(dst->s, src->s, src->len); dst->len = src->len; return 0; } /** * \brief Compare two str's case sensitive * \param str1 first str * \param str2 second str * \return 0 if both are equal, positive if str1 is greater, negative if str2 is greater, -2 on errors */ static inline int str_strcmp(const str *str1, const str *str2) { if(str1==NULL || str2==NULL || str1->s ==NULL || str2->s==NULL || str1->len<0 || str2->len<0) { LM_ERR("bad parameters\n"); return -2; } if (str1->len < str2->len) return -1; else if (str1->len > str2->len) return 1; else return strncmp(str1->s, str2->s, str1->len); } /** * \brief Compare two str's case insensitive * \param str1 first str * \param str2 second str * \return 0 if both are equal, positive if str1 is greater, negative if str2 is greater, -2 on errors */ static inline int str_strcasecmp(const str *str1, const str *str2) { if(str1==NULL || str2==NULL || str1->s ==NULL || str2->s==NULL || str1->len<0 || str2->len<0) { LM_ERR("bad parameters\n"); return -2; } if (str1->len < str2->len) return -1; else if (str1->len > str2->len) return 1; else return strncasecmp(str1->s, str2->s, str1->len); } /* converts a username into uid:gid, * returns -1 on error & 0 on success */ int user2uid(int* uid, int* gid, char* user); /* converts a group name into a gid * returns -1 on error, 0 on success */ int group2gid(int* gid, char* group); /* * Replacement of timegm (does not exists on all platforms * Taken from * http://lists.samba.org/archive/samba-technical/2002-November/025737.html */ time_t _timegm(struct tm* t); /* Convert time_t value that is relative to local timezone to UTC */ time_t local2utc(time_t in); /* Convert time_t value in UTC to to value relative to local time zone */ time_t utc2local(time_t in); /* * Return str as zero terminated string allocated * using pkg_malloc */ char* as_asciiz(str* s); /* return system version (major.minor.minor2) as * (major<<16)|(minor)<<8|(minor2) * (if some of them are missing, they are set to 0) * if the parameters are not null they are set to the coresp. part */ unsigned int get_sys_version(int* major, int* minor, int* minor2); /** Converts relative pathnames to absolute pathnames. This function returns * the full pathname of a file in parameter. If the file pathname does not * start with / then it will be converted into an absolute pathname. The * function gets the absolute directory pathname from \c base and appends \c * file to it. The first parameter can be NULL, in this case the function will * use the location of the main SER configuration file as reference. * @param base filename to be used as reference when \c file is relative. It * must be absolute. The location of the SER configuration file * will be used as reference if you set the value of this * parameter to NULL. * @param file A pathname to be converted to absolute. * @return A string containing absolute pathname, the string must be freed * with free. NULL on error. */ char* get_abs_pathname(str* base, str* file); /** * search for needle in text */ char *str_search(str *text, str *needle); #endif