ut.h
7fa84b41
 /*
  *$Id$
  *
  * - various general purpose functions
7dd0b342
  *
53c7e0f1
  * Copyright (C) 2001-2003 FhG Fokus
7dd0b342
  *
  * This file is part of ser, a free SIP server.
  *
  * ser is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version
  *
  * For a license to use the ser software under conditions
  * other than those described here, or to purchase support for this
  * software, please contact iptel.org by e-mail at the following addresses:
  *    info@iptel.org
  *
  * ser is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License 
  * along with this program; if not, write to the Free Software 
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
b6e94c57
  *
  * History
  * ------
  * 2003-01-18 un_escape function introduced for convenience of code needing
  *            the complex&slow feature of unescaping
6b6f226f
  * 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)
f261769d
  * 2004-03-08 updated int2str (64 bits, INT2STR_MAX_LEN used) (andrei)
879a3367
  * 2005-11-29 reverse_hex2int/int2reverse_hex switched to unsigned int (andrei)
  * 2005-12-09 added msgid_var (andrei)
8b8fc486
  * 2007-05-14 added get_sys_ver() (andrei)
57088737
  * 2007-06-05 added MAX_UVAR_VALUE(), MAX_int(a,b) MIN_int(a,b) (andrei)
91473bc8
  * 2008-05-21 added ushort2sbuf(), ushort2str() (andrei)
7fa84b41
  */
 
7dd0b342
 
7fa84b41
 #ifndef ut_h
 #define ut_h
 
049f64c2
 #include "comp_defs.h"
fb9d2755
 
 #include <sys/types.h>
 #include <sys/time.h>
049f64c2
 #include <limits.h>
74a8dcdf
 #include <time.h>
fb9d2755
 #include <unistd.h>
5c4113af
 #include <ctype.h>
fb9d2755
 
91473bc8
 #include "compiler_opt.h"
049f64c2
 #include "config.h"
7fa84b41
 #include "dprint.h"
b6e94c57
 #include "str.h"
7fa84b41
 
caf80ae6
 
 
6b6f226f
 /* zero-string wrapper */
 #define ZSW(_c) ((_c)?(_c):"")
 
dda9dab1
 /* returns string beginning and length without insignificant chars */
 #define trim_len( _len, _begin, _mystr ) \
51eadd0c
 	do{ 	static char _c; \
dda9dab1
 		(_len)=(_mystr).len; \
e3fc93f4
 		while ((_len) && ((_c=(_mystr).s[(_len)-1])==0 || _c=='\r' || \
 					_c=='\n' || _c==' ' || _c=='\t' )) \
dda9dab1
 			(_len)--; \
 		(_begin)=(_mystr).s; \
 		while ((_len) && ((_c=*(_begin))==' ' || _c=='\t')) { \
 			(_len)--;\
 			(_begin)++; \
 		} \
51eadd0c
 	}while(0)
dda9dab1
 
 #define trim_r( _mystr ) \
9c61e9f9
 	do{	static char _c; \
e22bbdb8
 		while( ((_mystr).len) && ( ((_c=(_mystr).s[(_mystr).len-1]))==0 ||\
 									_c=='\r' || _c=='\n' ) \
 				) \
dda9dab1
 			(_mystr).len--; \
51eadd0c
 	}while(0)
dda9dab1
 
327a9bd5
 
 #define  translate_pointer( _new_buf , _org_buf , _p) \
 	( (_p)?(_new_buf + (_p-_org_buf)):(0) )
 
4457a3c2
 #define via_len(_via) \
 	((_via)->bsize-((_via)->name.s-\
 		((_via)->hdr.s+(_via)->hdr.len)))
 
 
dcb59e67
 
 /* 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)
 
 
57088737
 /* 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))
 
72ad8673
 #define MIN_unsigned(a, b) (unsigned)(((unsigned)(a)<(unsigned)(b))?(a):(b))
 #define MAX_unsigned(a, b) (unsigned)(((unsigned)(a)>(unsigned)(b))?(a):(b))
 
57088737
 #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
 
dcb59e67
 
879a3367
 /* 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)
 
0aa3fcd6
 #define set_msgid_val(var, id, type, value)\
 	do{\
 		(var).msgid=(id); \
 		(var).u.type##_val=(value); \
 	}while(0)
 
caf80ae6
 /* 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' };
327a9bd5
 
 
3d2e8729
 /* converts a str to an u. short, returns the u. short and sets *err on
7fa84b41
  * error and if err!=null
51eadd0c
   */
f7760ec9
 static inline unsigned short str2s(const char* s, unsigned int len,
7fa84b41
 									int *err)
 {
 	unsigned short ret;
 	int i;
 	unsigned char *limit;
 	unsigned char *init;
6419a43f
 	unsigned char* str;
3d2e8729
 
7fa84b41
 	/*init*/
6419a43f
 	str=(unsigned char*)s;
7fa84b41
 	ret=i=0;
 	limit=str+len;
 	init=str;
3d2e8729
 
7fa84b41
 	for(;str<limit ;str++){
 		if ( (*str <= '9' ) && (*str >= '0') ){
 				ret=ret*10+*str-'0';
 				i++;
 				if (i>5) goto error_digits;
 		}else{
8f0e1af3
 				/* error unknown char */
7fa84b41
 				goto error_char;
 		}
 	}
 	if (err) *err=0;
 	return ret;
3d2e8729
 
7fa84b41
 error_digits:
545c0c82
 	/*DBG("str2s: ERROR: too many letters in [%.*s]\n", (int)len, init); */
7fa84b41
 	if (err) *err=1;
 	return 0;
 error_char:
545c0c82
 	/*DBG("str2s: ERROR: unexpected char %c in %.*s\n", *str, (int)len, init);
 	 * */
7fa84b41
 	if (err) *err=1;
 	return 0;
 }
 
 
 
3d2e8729
 static inline int btostr( char *p,  unsigned char val)
 {
 	unsigned int a,b,i =0;
7fa84b41
 
3d2e8729
 	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;
 }
 
 
f261769d
 #define INT2STR_MAX_LEN  (19+1+1) /* 2^64~= 16*10^18 => 19+1 digits + \0 */
3d2e8729
 
145e3fb3
 /* 
  * returns a pointer to a static buffer containing l in asciiz (with base "base") & sets len 
  * left padded with 0 to "size"
  */
91473bc8
 static inline char* int2str_base_0pad(unsigned int l, int* len, int base, 
 											int size)
09f7cd2c
 {
91473bc8
 	static char r[INT2STR_MAX_LEN];
 	int i, j;
145e3fb3
 
91473bc8
 	if (base < 2) {
 		BUG("base underflow\n");
145e3fb3
 		return NULL;
91473bc8
 	}
 	if (base > 36) {
 		BUG("base overflow\n");
145e3fb3
 		return NULL;
91473bc8
 	}
 	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];
09f7cd2c
 }
 
145e3fb3
 /* 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);
 }
09f7cd2c
 
93349b4e
 
 
145e3fb3
 /* returns a pointer to a static buffer containing l in asciiz & sets len */
 static inline char* int2str(unsigned int l, int* len)
 {
93349b4e
 	static char r[INT2STR_MAX_LEN];
 	int i;
 	
 	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];
145e3fb3
 }
09f7cd2c
 
93349b4e
 
 
91473bc8
 #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(len<USHORT2SBUF_MAX_LEN))
 		return 0;
 	offs=0;
 	a=u/10000; u%=10000;
 	buf[offs]=a+'0'; offs+=(a!=0);
 	b=u/1000;  u%=1000;
 	buf[offs]=b+'0'; offs+=((offs|b)!=0);
 	c=u/100;   u%=100;
 	buf[offs]=c+'0'; offs+=((offs|c)!=0);
 	d=u/10;    u%=10;
 	buf[offs]=d+'0'; offs+=((offs|d)!=0);
 	buf[offs]=(unsigned char)u+'0';
 	return offs+1;
 }
 
 
 
 #define USHORT2STR_MAX_LEN  (USHORT2SBUF_MAX_LEN+1) /* 65535\0*/
 /* converts an unsigned short (16 bits) to asciiz
  * (optimized for port conversiob (4 or 5 digits most of the time)*/
 static inline char* ushort2str(unsigned short u)
 {
 	static char buf[USHORT2STR_MAX_LEN];
 	int len;
 
 	len=ushort2sbuf(u, buf, sizeof(buf)-1);
 	buf[len]=0;
 	return buf;
 }
 
 
 
3d2e8729
 /* faster memchr version */
7fa84b41
 static inline char* q_memchr(char* p, int c, unsigned int size)
 {
 	char* end;
 
 	end=p+size;
 	for(;p<end;p++){
 		if (*p==(unsigned char)c) return p;
 	}
 	return 0;
 }
 	
 
33d814e0
 /* returns -1 on error, 1! on success (consistent with int2reverse_hex) */
 inline static int reverse_hex2int( char *c, int len, unsigned int* res)
fa377d6b
 {
 	char *pc;
 	char mychar;
 
33d814e0
 	*res=0;
fa377d6b
 	for (pc=c+len-1; len>0; pc--, len--) {
33d814e0
 		*res <<= 4 ;
fa377d6b
 		mychar=*pc;
33d814e0
 		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;
fa377d6b
 		else return -1;
 	}
33d814e0
 	return 1;
fa377d6b
 }
 
33d814e0
 inline static int int2reverse_hex( char **c, int *size, unsigned int nr )
fa377d6b
 {
 	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;
 }
 
caf80ae6
 /* 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;
 }
d17d750b
 
f51155cf
 /* portable sleep in microseconds (no interrupt handling now) */
 
fb9d2755
 inline static void sleep_us( unsigned int nusecs )
 {
 	struct timeval tval;
3ab8d9e9
 	tval.tv_sec =nusecs/1000000;
fb9d2755
 	tval.tv_usec=nusecs%1000000;
 	select(0, NULL, NULL, NULL, &tval );
 }
 
049f64c2
 
 /* 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;
 }
 
b6e94c57
 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).
 
53c7e0f1
    only printable characters are permitted
b6e94c57
 
53c7e0f1
 	<0 is returned on an unescaping error, length of the
b6e94c57
 	unescaped string otherwise
 */
 inline static int un_escape(str *user, str *new_user ) 
 {
  	int i, j, value;
 	int hi, lo;
 
5ddda324
 	if( new_user==0 || new_user->s==0) {
 		LOG(L_CRIT, "BUG: un_escape: called with invalid param\n");
 		return -1;
 	}
 
b6e94c57
 	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;
 } 
 
5c4113af
 
 /*
  * 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]);
 	}
 }
 
 
b6420530
 /*
  * 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;
83b99119
 }
 
 /*
  * 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;
b6420530
 }
 
2ac451f2
 /* 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);
 
74a8dcdf
 /*
  * 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);
 
b0afad34
 /* 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);
7936dce8
 
 /*
  * Return str as zero terminated string allocated
  * using pkg_malloc
  */
 char* as_asciiz(str* s);
 
8b8fc486
 
 /* 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);
 
b2471cfc
 /** 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);
 
7fa84b41
 #endif