/** \file wutil.c Wide character equivalents of various standard unix functions. Also contains fallback implementations of a large number of wide character unix functions. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "util.h" #include "common.h" #include "wutil.h" #define TMP_LEN_MIN 256 /** Buffer for converting wide arguments to narrow arguments, used by the \c wutil_wcs2str() function. */ static char *tmp=0; /** Length of the \c tmp buffer. */ static size_t tmp_len=0; /** Counts the number of calls to the wutil wrapper functions */ static int wutil_calls = 0; void wutil_init() { } void wutil_destroy() { free( tmp ); tmp=0; tmp_len=0; debug( 3, L"wutil functions called %d times", wutil_calls ); } /** Convert the specified wide aharacter string to a narrow character string. This function uses an internal temporary buffer for storing the result so subsequent results will overwrite previous results. */ static char *wutil_wcs2str( const wchar_t *in ) { wutil_calls++; size_t new_sz =MAX_UTF8_BYTES*wcslen(in)+1; if( tmp_len < new_sz ) { new_sz = maxi( new_sz, TMP_LEN_MIN ); tmp = realloc( tmp, new_sz ); if( !tmp ) { die_mem(); } tmp_len = new_sz; } wcstombs( tmp, in, tmp_len ); return tmp; } wchar_t *wgetcwd( wchar_t *buff, size_t sz ) { char buffc[sz*MAX_UTF8_BYTES]; char *res = getcwd( buffc, sz*MAX_UTF8_BYTES ); if( !res ) return 0; if( (size_t)-1 == mbstowcs( buff, buffc, sizeof( wchar_t ) * sz ) ) { return 0; } return buff; } int wchdir( const wchar_t * dir ) { char *tmp = wutil_wcs2str(dir); return chdir( tmp ); } FILE *wfopen(const wchar_t *path, const char *mode) { char *tmp =wutil_wcs2str(path); FILE *res=0; if( tmp ) { res = fopen(tmp, mode); } return res; } FILE *wfreopen(const wchar_t *path, const char *mode, FILE *stream) { char *tmp =wutil_wcs2str(path); FILE *res=0; if( tmp ) { res = freopen(tmp, mode, stream); } return res; } int wopen(const wchar_t *pathname, int flags, ...) { char *tmp =wutil_wcs2str(pathname); int res=-1; va_list argp; if( tmp ) { if( ! (flags & O_CREAT) ) { res = open(tmp, flags); } else { va_start( argp, flags ); res = open(tmp, flags, va_arg(argp, int) ); va_end( argp ); } } return res; } int wcreat(const wchar_t *pathname, mode_t mode) { char *tmp =wutil_wcs2str(pathname); int res = -1; if( tmp ) { res= creat(tmp, mode); } return res; } DIR *wopendir(const wchar_t *name) { char *tmp =wutil_wcs2str(name); DIR *res = 0; if( tmp ) { res = opendir(tmp); } return res; } int wstat(const wchar_t *file_name, struct stat *buf) { char *tmp =wutil_wcs2str(file_name); int res = -1; if( tmp ) { res = stat(tmp, buf); } return res; } int lwstat(const wchar_t *file_name, struct stat *buf) { char *tmp =wutil_wcs2str(file_name); int res = -1; if( tmp ) { res = lstat(tmp, buf); } return res; } int waccess(const wchar_t *file_name, int mode) { char *tmp =wutil_wcs2str(file_name); int res = -1; if( tmp ) { res= access(tmp, mode); } return res; } void wperror(const wchar_t *s) { if( s != 0 ) { fwprintf( stderr, L"%ls: ", s ); } fwprintf( stderr, L"%s\n", strerror( errno ) ); } #if !HAVE_FWPRINTF void pad( void (*writer)(wchar_t), int count) { int i; if( count < 0 ) return; for( i=0; i= L'0') && (*filter <= L'9')) { width=10*width+(*filter++ - L'0'); } } while( loop ) { switch(*filter) { case L'l': /* Long variable */ is_long++; filter++; break; case L'*': /* Set minimum field width */ width = va_arg( va, int ); filter++; break; case L'-': filter++; pad_left=0; break; case L'.': /* Set precision. */ filter++; if( *filter == L'*' ) { precision = va_arg( va, int ); } else { precision=0; while( (*filter >= L'0') && (*filter <= L'9')) { precision=10*precision+(*filter++ - L'0'); } } break; default: loop=0; break; } } switch( *filter ) { case L'c': { wchar_t c; if( (width >= 0) && pad_left ) { pad( writer, width-1 ); count += maxi( width-1, 0 ); } c = is_long?va_arg(va, wchar_t):btowc(va_arg(va, int)); if( precision != 0 ) writer( c ); if( (width >= 0) && !pad_left ) { pad( writer, width-1 ); count += maxi( width-1, 0 ); } count++; break; } case L's': { wchar_t *ss=0; if( is_long ) { ss = va_arg(va, wchar_t *); } else { char *ns = va_arg(va, char*); if( ns ) { ss = str2wcs( ns ); } } if( !ss ) { return -1; } if( (width >= 0) && pad_left ) { pad( writer, width-wcslen(ss) ); count += maxi(width-wcslen(ss), 0); } wchar_t *s=ss; int precount = count; while( *s ) { if( (precision > 0) && (precision <= (count-precount) ) ) break; writer( *(s++) ); count++; } if( (width >= 0) && !pad_left ) { pad( writer, width-wcslen(ss) ); count += maxi( width-wcslen(ss), 0 ); } if( !is_long ) free( ss ); break; } case L'd': case L'i': { char str[32]; char *pos; switch( is_long ) { case 0: { int d = va_arg( va, int ); if( precision >= 0 ) snprintf( str, 32, "%.*d", precision, d ); else snprintf( str, 32, "%d", d ); break; } case 1: { long d = va_arg( va, long ); if( precision >= 0 ) snprintf( str, 32, "%.*ld", precision, d ); else snprintf( str, 32, "%ld", d ); break; } case 2: { long long d = va_arg( va, long long ); if( precision >= 0 ) snprintf( str, 32, "%.*lld", precision, d ); else snprintf( str, 32, "%lld", d ); break; } default: return -1; } if( (width >= 0) && pad_left ) { pad( writer, width-strlen(str) ); count +=maxi(width-strlen(str), 0 ); } pos = str; while( *pos ) { writer( *(pos++) ); count++; } if( (width >= 0) && !pad_left ) { pad( writer, width-strlen(str) ); count += maxi(width-strlen(str), 0 ); } break; } case L'u': { char str[32]; char *pos; switch( is_long ) { case 0: { unsigned d = va_arg( va, unsigned ); if( precision >= 0 ) snprintf( str, 32, "%.*u", precision, d ); else snprintf( str, 32, "%u", d ); break; } case 1: { unsigned long d = va_arg( va, unsigned long ); if( precision >= 0 ) snprintf( str, 32, "%.*lu", precision, d ); else snprintf( str, 32, "%lu", d ); break; } case 2: { unsigned long long d = va_arg( va, unsigned long long ); if( precision >= 0 ) snprintf( str, 32, "%.*llu", precision, d ); else snprintf( str, 32, "%llu", d ); break; } default: return -1; } if( (width >= 0) && pad_left ) { pad( writer, width-strlen(str) ); count += maxi( width-strlen(str), 0 ); } pos = str; while( *pos ) { writer( *(pos++) ); count++; } if( (width >= 0) && !pad_left ) { pad( writer, width-strlen(str) ); count += maxi( width-strlen(str), 0 ); } break; } case L'f': { char str[32]; char *pos; double val = va_arg( va, double ); if( precision>= 0 ) { if( width>= 0 ) { snprintf( str, 32, "%*.*f", width, precision, val ); } else { snprintf( str, 32, "%.*f", precision, val ); } } else { if( width>= 0 ) { snprintf( str, 32, "%*f", width, val ); } else { snprintf( str, 32, "%f", val ); } } pos = str; while( *pos ) { writer( *(pos++) ); count++; } break; } case L'n': { int *n = va_arg( va, int *); *n = count; break; } case L'%': { writer('%'); count++; break; } default: debug( 0, L"Unknown switch %lc in string %ls\n", *filter, filter_org ); // exit(1); } } else { writer( *filter ); count++; } } return count; } /** Holds data for swprintf writer */ static struct { int count; int max; wchar_t *pos; } sw_data; /** Writers for string output */ static void sw_writer( wchar_t c ) { if( sw_data.count < sw_data.max ) *(sw_data.pos++)=c; sw_data.count++; } int vswprintf( wchar_t *out, size_t n, const wchar_t *filter, va_list va ) { int written; sw_data.pos=out; sw_data.max=n; sw_data.count=0; written=vgwprintf( &sw_writer, filter, va ); if( written < n ) { *sw_data.pos = 0; } else { written=-1; } return written; } int swprintf( wchar_t *out, size_t n, const wchar_t *filter, ... ) { va_list va; int written; va_start( va, filter ); written = vswprintf( out, n, filter, va ); va_end( va ); return written; } /** Holds auxiliary data for fwprintf and wprintf writer */ static FILE *fw_data; static void fw_writer( wchar_t c ) { putwc( c, fw_data ); } /* Writers for file output */ int vfwprintf( FILE *f, const wchar_t *filter, va_list va ) { fw_data = f; return vgwprintf( &fw_writer, filter, va ); } int fwprintf( FILE *f, const wchar_t *filter, ... ) { va_list va; int written; va_start( va, filter ); written = vfwprintf( f, filter, va ); va_end( va ); return written; } int vwprintf( const wchar_t *filter, va_list va ) { return vfwprintf( stdout, filter, va ); } int wprintf( const wchar_t *filter, ... ) { va_list va; int written; va_start( va, filter ); written=vwprintf( filter, va ); va_end( va ); return written; } #endif #ifndef HAVE_FGETWC wint_t fgetwc(FILE *stream) { wchar_t res=0; mbstate_t state=0; memset (&state, '\0', sizeof (state)); while(1) { int b = fgetc( stream ); char bb; int sz; if( b == EOF ) return WEOF; bb=b; sz = mbrtowc( &res, &bb, 1, &state ); switch( sz ) { case -1: memset (&state, '\0', sizeof (state)); return WEOF; case -2: break; case 0: return 0; default: return res; } } } wint_t getwc(FILE *stream) { return fgetwc( stream ); } #endif #ifndef HAVE_FPUTWC wint_t fputwc(wchar_t wc, FILE *stream) { int res; char *s[MB_CUR_MAX+1]; memset( s, 0, MB_CUR_MAX+1 ); wctomb( s, wc ); res = fputs( sm stream ); return res==EOF?WEOF:wc; } wint_t putwc(wchar_t wc, FILE *stream) { return fputwc( wc, stream ); } #endif #ifndef HAVE_WCSTOK /* Used by fallback wcstok. Borrowed from glibc */ static size_t wcsspn (const wchar_t *wcs, const wchar_t *accept ) { register const wchar_t *p; register const wchar_t *a; register size_t count = 0; for (p = wcs; *p != L'\0'; ++p) { for (a = accept; *a != L'\0'; ++a) if (*p == *a) break; if (*a == L'\0') return count; else ++count; } return count; } /* Used by fallback wcstok. Borrowed from glibc */ static wchar_t *wcspbrk (const wchar_t *wcs, const wchar_t *accept) { while (*wcs != L'\0') if (wcschr (accept, *wcs) == NULL) ++wcs; else return (wchar_t *) wcs; return NULL; } /* Fallback wcstok implementation. Borrowed from glibc. */ wchar_t *wcstok(wchar_t *wcs, const wchar_t *delim, wchar_t **ptr) { wchar_t *result; if (wcs == NULL) { if (*save_ptr == NULL) { errno = EINVAL; return NULL; } else wcs = *save_ptr; } /* Scan leading delimiters. */ wcs += wcsspn (wcs, delim); if (*wcs == L'\0') { *save_ptr = NULL; return NULL; } /* Find the end of the token. */ result = wcs; wcs = wcspbrk (result, delim); if (wcs == NULL) { /* This token finishes the string. */ *save_ptr = NULL; } else { /* Terminate the token and make *SAVE_PTR point past it. */ *wcs = L'\0'; *save_ptr = wcs + 1; } return result; } #endif #ifndef HAVE_WCSDUP wchar_t *wcsdup( const wchar_t *in ) { size_t len=wcslen(in); wchar_t *out = malloc( sizeof( wchar_t)*(len+1)); if( out == 0 ) { die_mem(); } memcpy( out, in, sizeof( wchar_t)*(len+1)); return out; } #endif #ifndef HAVE_WCSLEN size_t wcslen(const wchar_t *in) { const wchar_t *end=in; while( *end ) end++; return end-in; } #endif #ifndef HAVE_WCSCASECMP int wcscasecmp( const wchar_t *a, const wchar_t *b ) { if( *a == 0 ) { return (*b==0)?0:-1; } else if( *b == 0 ) { return 1; } int diff = towlower(*a)-towlower(*b); if( diff != 0 ) return diff; else return wcscasecmp( a+1,b+1); } #endif #ifndef HAVE_WCSNCASECMP int wcsncasecmp( const wchar_t *a, const wchar_t *b, int count ) { if( count == 0 ) return 0; if( *a == 0 ) { return (*b==0)?0:-1; } else if( *b == 0 ) { return 1; } int diff = towlower(*a)-towlower(*b); if( diff != 0 ) return diff; else return wcsncasecmp( a+1,b+1, count-1); } #endif #ifndef HAVE_WCWIDTH int wcwidth( wchar_t c ) { if( c < 32 ) return 0; if ( c == 127 ) return 0; return 1; } #endif #ifndef HAVE_WCSNDUP wchar_t *wcsndup( const wchar_t *in, int c ) { wchar_t *res = malloc( sizeof(wchar_t)*(c+1) ); if( res == 0 ) { die_mem(); } wcsncpy( res, in, c ); res[c] = L'\0'; return res; } #endif long convert_digit( wchar_t d, int base ) { long res=-1; if( (d <= L'9') && (d >= L'0') ) { res = d - L'0'; } else if( (d <= L'z') && (d >= L'a') ) { res = d + 10 - L'a'; } else if( (d <= L'Z') && (d >= L'A') ) { res = d + 10 - L'A'; } if( res >= base ) { res = -1; } return res; } #ifndef HAVE_WCSTOL long wcstol(const wchar_t *nptr, wchar_t **endptr, int base) { long long res=0; int is_set=0; if( base > 36 ) { errno = EINVAL; return 0; } while( 1 ) { long nxt = convert_digit( *nptr, base ); if( endptr != 0 ) *endptr = (wchar_t *)nptr; if( nxt < 0 ) { if( !is_set ) { errno = EINVAL; } return res; } res = (res*base)+nxt; is_set = 1; if( res > LONG_MAX ) { errno = ERANGE; return LONG_MAX; } if( res < LONG_MIN ) { errno = ERANGE; return LONG_MIN; } nptr++; } } #endif