| /* | |
| * Helper functions for the Wine tools | |
| * | |
| * Copyright 2021 Alexandre Julliard | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library 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 | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #ifndef __WINE_TOOLS_H | |
| #define __WINE_TOOLS_H | |
| #ifndef __WINE_CONFIG_H | |
| # error You must include config.h to use this header | |
| #endif | |
| #include <limits.h> | |
| #include <stdarg.h> | |
| #include <stdbool.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <sys/types.h> | |
| #include <sys/stat.h> | |
| #include <signal.h> | |
| #include <fcntl.h> | |
| #include <time.h> | |
| #include <errno.h> | |
| #ifdef HAVE_SYS_SYSCTL_H | |
| # include <sys/sysctl.h> | |
| #endif | |
| #ifdef __APPLE__ | |
| # include <mach-o/dyld.h> | |
| #endif | |
| #ifdef _WIN32 | |
| # include <direct.h> | |
| # include <io.h> | |
| # include <process.h> | |
| # define mkdir(path,mode) mkdir(path) | |
| # ifndef S_ISREG | |
| # define S_ISREG(mod) (((mod) & _S_IFMT) == _S_IFREG) | |
| # endif | |
| # ifndef S_ISDIR | |
| # define S_ISDIR(mod) (((mod) & _S_IFMT) == _S_IFDIR) | |
| # endif | |
| # ifdef _MSC_VER | |
| # define popen _popen | |
| # define pclose _pclose | |
| # define strtoll _strtoi64 | |
| # define strtoull _strtoui64 | |
| # define strncasecmp _strnicmp | |
| # define strcasecmp _stricmp | |
| # endif | |
| # include <windef.h> | |
| # include <winbase.h> | |
| #else | |
| extern char **environ; | |
| # include <spawn.h> | |
| # include <sys/wait.h> | |
| # include <unistd.h> | |
| # ifndef O_BINARY | |
| # define O_BINARY 0 | |
| # endif | |
| # ifndef __int64 | |
| # if defined(__x86_64__) || defined(__aarch64__) || defined(__powerpc64__) | |
| # define __int64 long | |
| # else | |
| # define __int64 long long | |
| # endif | |
| # endif | |
| #endif | |
| #if !defined(__GNUC__) && !defined(__attribute__) | |
| #define __attribute__(x) | |
| #endif | |
| #ifndef max | |
| #define max(a,b) (((a) > (b)) ? (a) : (b)) | |
| #endif | |
| #ifndef min | |
| #define min(a,b) (((a) < (b)) ? (a) : (b)) | |
| #endif | |
| #ifndef ARRAY_SIZE | |
| #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) | |
| #endif | |
| struct target | |
| { | |
| enum { CPU_i386, CPU_x86_64, CPU_ARM, CPU_ARM64, CPU_ARM64EC } cpu; | |
| enum | |
| { | |
| PLATFORM_UNSPECIFIED, | |
| PLATFORM_APPLE, | |
| PLATFORM_ANDROID, | |
| PLATFORM_LINUX, | |
| PLATFORM_FREEBSD, | |
| PLATFORM_SOLARIS, | |
| PLATFORM_WINDOWS, | |
| PLATFORM_WINDOWS_GNU, | |
| PLATFORM_MINGW, | |
| } platform; | |
| }; | |
| static void fatal_perror( const char *msg, ... ) __attribute__ ((__format__ (__printf__, 1, 2), noreturn)); | |
| static inline void fatal_perror( const char *msg, ... ) | |
| { | |
| va_list valist; | |
| va_start( valist, msg ); | |
| fprintf( stderr, "error: " ); | |
| vfprintf( stderr, msg, valist ); | |
| perror( " " ); | |
| va_end( valist ); | |
| exit(1); | |
| } | |
| static inline void *xmalloc( size_t size ) | |
| { | |
| void *res = malloc( size ? size : 1 ); | |
| if (res == NULL) | |
| { | |
| fprintf( stderr, "Virtual memory exhausted.\n" ); | |
| exit(1); | |
| } | |
| return res; | |
| } | |
| static inline void *xrealloc (void *ptr, size_t size) | |
| { | |
| void *res = realloc( ptr, size ); | |
| if (size && res == NULL) | |
| { | |
| fprintf( stderr, "Virtual memory exhausted.\n" ); | |
| exit(1); | |
| } | |
| return res; | |
| } | |
| static inline char *xstrdup( const char *str ) | |
| { | |
| return strcpy( xmalloc( strlen(str)+1 ), str ); | |
| } | |
| static inline bool strendswith( const char *str, const char *end ) | |
| { | |
| int l = strlen( str ); | |
| int m = strlen( end ); | |
| return l >= m && !strcmp( str + l - m, end ); | |
| } | |
| static char *strmake( const char* fmt, ... ) __attribute__ ((__format__ (__printf__, 1, 2))); | |
| static inline char *strmake( const char* fmt, ... ) | |
| { | |
| int n; | |
| size_t size = 100; | |
| va_list ap; | |
| for (;;) | |
| { | |
| char *p = xmalloc( size ); | |
| va_start( ap, fmt ); | |
| n = vsnprintf( p, size, fmt, ap ); | |
| va_end( ap ); | |
| if (n == -1) size *= 2; | |
| else if ((size_t)n >= size) size = n + 1; | |
| else return p; | |
| free( p ); | |
| } | |
| } | |
| /* generic array functions */ | |
| struct array | |
| { | |
| size_t count; /* count of array entries */ | |
| size_t size; /* size of allocated array */ | |
| void *data; | |
| }; | |
| static const struct array empty_array; | |
| static inline void *array_grow( struct array *array, size_t n, size_t elem_size ) | |
| { | |
| if (array->count + n > array->size) | |
| { | |
| array->size = max( array->size * 2, 16 ); | |
| array->size = max( array->size, array->count + n ); | |
| array->data = xrealloc( array->data, array->size * elem_size ); | |
| } | |
| array->count += n; | |
| return (char *)array->data + (array->count - n) * elem_size; | |
| } | |
| #define ARRAY_ADD(array,type) ((type *)array_grow( array, 1, sizeof(type) )) | |
| #define ARRAY_SORT(array,type,cmp) qsort( (array)->data, (array)->count, sizeof(type), cmp ) | |
| #define ARRAY_ENTRY(array,idx,type) ((type *)(array)->data + (idx)) | |
| #define ARRAY_FOR_EACH(cursor,array,type) \ | |
| for (type *cursor = (array)->data; cursor - (type *)(array)->data < (array)->count; cursor++) | |
| /* string array functions */ | |
| struct strarray | |
| { | |
| unsigned int count; /* strings in use */ | |
| unsigned int size; /* total allocated size */ | |
| const char **str; | |
| }; | |
| static const struct strarray empty_strarray; | |
| #define STRARRAY_FOR_EACH(cursor,array) \ | |
| for (const char **__p = (array)->str, *cursor = NULL; \ | |
| __p - (array)->str < (array)->count && (cursor = *__p, 1); __p++) | |
| static inline void strarray_add( struct strarray *array, const char *str ) | |
| { | |
| if (array->count == array->size) | |
| { | |
| if (array->size) array->size *= 2; | |
| else array->size = 16; | |
| array->str = xrealloc( array->str, sizeof(array->str[0]) * array->size ); | |
| } | |
| array->str[array->count++] = str; | |
| } | |
| static inline void strarray_addall( struct strarray *array, struct strarray added ) | |
| { | |
| STRARRAY_FOR_EACH( ptr, &added ) strarray_add( array, ptr ); | |
| } | |
| static inline bool strarray_exists( struct strarray array, const char *str ) | |
| { | |
| STRARRAY_FOR_EACH( ptr, &array ) if (!strcmp( ptr, str )) return true; | |
| return false; | |
| } | |
| static inline void strarray_add_uniq( struct strarray *array, const char *str ) | |
| { | |
| if (!strarray_exists( *array, str )) strarray_add( array, str ); | |
| } | |
| static inline void strarray_addall_uniq( struct strarray *array, struct strarray added ) | |
| { | |
| STRARRAY_FOR_EACH( ptr, &added ) strarray_add_uniq( array, ptr ); | |
| } | |
| static inline struct strarray strarray_fromstring( const char *str, const char *delim ) | |
| { | |
| struct strarray array = empty_strarray; | |
| char *buf = xstrdup( str ); | |
| const char *tok; | |
| for (tok = strtok( buf, delim ); tok; tok = strtok( NULL, delim )) | |
| strarray_add( &array, xstrdup( tok )); | |
| free( buf ); | |
| return array; | |
| } | |
| static inline struct strarray strarray_frompath( const char *path ) | |
| { | |
| if (!path) return empty_strarray; | |
| #ifdef _WIN32 | |
| return strarray_fromstring( path, ";" ); | |
| #else | |
| return strarray_fromstring( path, ":" ); | |
| #endif | |
| } | |
| static inline char *strarray_tostring( struct strarray array, const char *sep ) | |
| { | |
| char *str; | |
| unsigned int i, len = 1 + (array.count - 1) * strlen(sep); | |
| if (!array.count) return xstrdup(""); | |
| STRARRAY_FOR_EACH( str, &array ) len += strlen( str ); | |
| str = xmalloc( len ); | |
| strcpy( str, array.str[0] ); | |
| for (i = 1; i < array.count; i++) | |
| { | |
| strcat( str, sep ); | |
| strcat( str, array.str[i] ); | |
| } | |
| return str; | |
| } | |
| static inline void strarray_qsort( struct strarray *array, int (*func)(const char **, const char **) ) | |
| { | |
| if (array->count) qsort( array->str, array->count, sizeof(*array->str), (void *)func ); | |
| } | |
| static inline const char *strarray_bsearch( struct strarray array, const char *str, | |
| int (*func)(const char **, const char **) ) | |
| { | |
| char **res = NULL; | |
| if (array.count) res = bsearch( &str, array.str, array.count, sizeof(*array.str), (void *)func ); | |
| return res ? *res : NULL; | |
| } | |
| static inline void strarray_trace( struct strarray args ) | |
| { | |
| unsigned int i; | |
| for (i = 0; i < args.count; i++) | |
| { | |
| if (strpbrk( args.str[i], " \t\n\r")) printf( "\"%s\"", args.str[i] ); | |
| else printf( "%s", args.str[i] ); | |
| if (i < args.count - 1) putchar( ' ' ); | |
| } | |
| putchar( '\n' ); | |
| } | |
| static inline int strarray_spawn( struct strarray args ) | |
| { | |
| #ifdef _WIN32 | |
| strarray_add( &args, NULL ); | |
| return _spawnvp( _P_WAIT, args.str[0], args.str ); | |
| #else | |
| pid_t pid, wret; | |
| int status; | |
| strarray_add( &args, NULL ); | |
| if (posix_spawnp( &pid, args.str[0], NULL, NULL, (char **)args.str, environ )) | |
| return -1; | |
| while (pid != (wret = waitpid( pid, &status, 0 ))) | |
| if (wret == -1 && errno != EINTR) break; | |
| if (pid == wret && WIFEXITED(status)) return WEXITSTATUS(status); | |
| return 255; /* abnormal exit with an abort or an interrupt */ | |
| #endif | |
| } | |
| static inline char *get_basename( const char *file ) | |
| { | |
| const char *ret = strrchr( file, '/' ); | |
| return xstrdup( ret ? ret + 1 : file ); | |
| } | |
| static inline char *get_basename_noext( const char *file ) | |
| { | |
| char *ext, *ret = get_basename( file ); | |
| if ((ext = strrchr( ret, '.' ))) *ext = 0; | |
| return ret; | |
| } | |
| static inline char *get_dirname( const char *file ) | |
| { | |
| const char *end = strrchr( file, '/' ); | |
| if (!end) return xstrdup( "." ); | |
| if (end == file) end++; | |
| return strmake( "%.*s", (int)(end - file), file ); | |
| } | |
| static inline char *replace_extension( const char *name, const char *old_ext, const char *new_ext ) | |
| { | |
| int name_len = strlen( name ); | |
| if (strendswith( name, old_ext )) name_len -= strlen( old_ext ); | |
| return strmake( "%.*s%s", name_len, name, new_ext ); | |
| } | |
| /* build a path with the relative dir from 'from' to 'dest' appended to base */ | |
| static inline char *build_relative_path( const char *base, const char *from, const char *dest ) | |
| { | |
| const char *start; | |
| char *ret; | |
| unsigned int dotdots = 0; | |
| for (;;) | |
| { | |
| while (*from == '/') from++; | |
| while (*dest == '/') dest++; | |
| start = dest; /* save start of next path element */ | |
| if (!*from) break; | |
| while (*from && *from != '/' && *from == *dest) { from++; dest++; } | |
| if ((!*from || *from == '/') && (!*dest || *dest == '/')) continue; | |
| do /* count remaining elements in 'from' */ | |
| { | |
| dotdots++; | |
| while (*from && *from != '/') from++; | |
| while (*from == '/') from++; | |
| } | |
| while (*from); | |
| break; | |
| } | |
| ret = xmalloc( strlen(base) + 3 * dotdots + strlen(start) + 2 ); | |
| strcpy( ret, base ); | |
| while (dotdots--) strcat( ret, "/.." ); | |
| if (!start[0]) return ret; | |
| strcat( ret, "/" ); | |
| strcat( ret, start ); | |
| return ret; | |
| } | |
| /* temp files management */ | |
| extern const char *temp_dir; | |
| extern struct strarray temp_files; | |
| static inline char *make_temp_dir( const char *tmpdir ) | |
| { | |
| unsigned int value = time(NULL) + getpid(); | |
| int count; | |
| char *name; | |
| for (count = 0; count < 0x8000; count++) | |
| { | |
| if (tmpdir) | |
| name = strmake( "%s/tmp%08x", tmpdir, value ); | |
| else | |
| name = strmake( "tmp%08x", value ); | |
| if (!mkdir( name, 0700 )) return name; | |
| value += 7777; | |
| if (errno == EACCES && !tmpdir) | |
| { | |
| if (!(tmpdir = getenv("TMPDIR"))) tmpdir = "/tmp"; | |
| } | |
| else if (errno != EEXIST) fatal_perror( "cannot create directory in %s", tmpdir ? tmpdir : "." ); | |
| free( name ); | |
| } | |
| fprintf( stderr, "failed to create directory for temp files\n" ); | |
| exit(1); | |
| } | |
| static inline char *make_temp_file( const char *prefix, const char *suffix ) | |
| { | |
| static unsigned int value; | |
| int fd, count; | |
| char *name; | |
| if (!temp_dir) temp_dir = make_temp_dir( NULL ); | |
| if (!suffix) suffix = ""; | |
| if (!prefix) prefix = "tmp"; | |
| else prefix = get_basename_noext( prefix ); | |
| for (count = 0; count < 0x8000; count++) | |
| { | |
| name = strmake( "%s/%s-%08x%s", temp_dir, prefix, value++, suffix ); | |
| fd = open( name, O_RDWR | O_CREAT | O_EXCL, 0600 ); | |
| if (fd >= 0) | |
| { | |
| #ifdef HAVE_SIGPROCMASK /* block signals while manipulating the temp files list */ | |
| sigset_t mask_set, old_set; | |
| sigemptyset( &mask_set ); | |
| sigaddset( &mask_set, SIGHUP ); | |
| sigaddset( &mask_set, SIGTERM ); | |
| sigaddset( &mask_set, SIGINT ); | |
| sigprocmask( SIG_BLOCK, &mask_set, &old_set ); | |
| strarray_add( &temp_files, name ); | |
| sigprocmask( SIG_SETMASK, &old_set, NULL ); | |
| #else | |
| strarray_add( &temp_files, name ); | |
| #endif | |
| close( fd ); | |
| return name; | |
| } | |
| free( name ); | |
| } | |
| fprintf( stderr, "failed to create temp file for %s%s in %s\n", prefix, suffix, temp_dir ); | |
| exit(1); | |
| } | |
| static inline void remove_temp_files(void) | |
| { | |
| STRARRAY_FOR_EACH( file, &temp_files ) if (file) unlink( file ); | |
| if (temp_dir) rmdir( temp_dir ); | |
| } | |
| static inline void init_signals( void (*cleanup)(int) ) | |
| { | |
| signal( SIGTERM, cleanup ); | |
| signal( SIGINT, cleanup ); | |
| #ifdef SIGHUP | |
| signal( SIGHUP, cleanup ); | |
| #endif | |
| } | |
| static inline void mkdir_p( const char *dir ) | |
| { | |
| char *p, *path; | |
| if (!dir[0]) return; | |
| path = xstrdup( dir ); | |
| for (p = path + 1; (p = strchr( p, '/' )); p++) | |
| { | |
| *p = 0; | |
| if (mkdir( path, 0755 ) == -1 && errno != EEXIST) fatal_perror( "mkdir %s", path ); | |
| *p = '/'; | |
| } | |
| if (mkdir( path, 0755 ) == -1) | |
| { | |
| struct stat st; | |
| if (errno != EEXIST || stat( path, &st ) || !S_ISDIR( st.st_mode )) | |
| fatal_perror( "cannot create %s", path ); | |
| } | |
| free( path ); | |
| } | |
| static inline void *read_file( const char *name, size_t *size ) | |
| { | |
| struct stat st; | |
| int res, fd; | |
| void *data; | |
| if ((fd = open( name, O_RDONLY | O_BINARY )) == -1) return NULL; | |
| fstat( fd, &st ); | |
| data = xmalloc( st.st_size ); | |
| res = read( fd, data, st.st_size ); | |
| if (res == -1) | |
| { | |
| free( data ); | |
| data = NULL; | |
| *size = 0; | |
| } | |
| else *size = res; | |
| close( fd ); | |
| return data; | |
| } | |
| static inline struct target get_default_target(void) | |
| { | |
| struct target target; | |
| #ifdef __i386__ | |
| target.cpu = CPU_i386; | |
| #elif defined(__x86_64__) | |
| target.cpu = CPU_x86_64; | |
| #elif defined(__arm__) | |
| target.cpu = CPU_ARM; | |
| #elif defined(__aarch64__) | |
| target.cpu = CPU_ARM64; | |
| #else | |
| #error Unsupported CPU | |
| #endif | |
| #ifdef __APPLE__ | |
| target.platform = PLATFORM_APPLE; | |
| #elif defined(__ANDROID__) | |
| target.platform = PLATFORM_ANDROID; | |
| #elif defined(__linux__) | |
| target.platform = PLATFORM_LINUX; | |
| #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) | |
| target.platform = PLATFORM_FREEBSD; | |
| #elif defined(__sun) | |
| target.platform = PLATFORM_SOLARIS; | |
| #elif defined(__CYGWIN__) | |
| target.platform = PLATFORM_MINGW; | |
| #elif defined(_WIN32) | |
| target.platform = PLATFORM_MINGW; | |
| #else | |
| target.platform = PLATFORM_UNSPECIFIED; | |
| #endif | |
| return target; | |
| } | |
| static inline unsigned int get_target_ptr_size( struct target target ) | |
| { | |
| static const unsigned int sizes[] = | |
| { | |
| [CPU_i386] = 4, | |
| [CPU_x86_64] = 8, | |
| [CPU_ARM] = 4, | |
| [CPU_ARM64] = 8, | |
| [CPU_ARM64EC] = 8, | |
| }; | |
| return sizes[target.cpu]; | |
| } | |
| static inline void set_target_ptr_size( struct target *target, unsigned int size ) | |
| { | |
| switch (target->cpu) | |
| { | |
| case CPU_i386: | |
| if (size == 8) target->cpu = CPU_x86_64; | |
| break; | |
| case CPU_x86_64: | |
| if (size == 4) target->cpu = CPU_i386; | |
| break; | |
| case CPU_ARM: | |
| if (size == 8) target->cpu = CPU_ARM64; | |
| break; | |
| case CPU_ARM64: | |
| case CPU_ARM64EC: | |
| if (size == 4) target->cpu = CPU_ARM; | |
| break; | |
| } | |
| } | |
| static inline bool is_llvm_pe_target( struct target target ) | |
| { | |
| return target.platform == PLATFORM_WINDOWS || | |
| target.platform == PLATFORM_WINDOWS_GNU; | |
| } | |
| static inline bool is_pe_target( struct target target ) | |
| { | |
| return (is_llvm_pe_target( target ) || target.platform == PLATFORM_MINGW); | |
| } | |
| static inline int get_cpu_from_name( const char *name ) | |
| { | |
| static const struct | |
| { | |
| const char *name; | |
| int cpu; | |
| } cpu_names[] = | |
| { | |
| { "i386", CPU_i386 }, | |
| { "i486", CPU_i386 }, | |
| { "i586", CPU_i386 }, | |
| { "i686", CPU_i386 }, | |
| { "i786", CPU_i386 }, | |
| { "x86_64", CPU_x86_64 }, | |
| { "amd64", CPU_x86_64 }, | |
| { "aarch64", CPU_ARM64 }, | |
| { "arm64ec", CPU_ARM64EC }, | |
| { "arm64", CPU_ARM64 }, | |
| { "arm", CPU_ARM }, | |
| }; | |
| unsigned int i; | |
| for (i = 0; i < ARRAY_SIZE(cpu_names); i++) | |
| if (!strncmp( cpu_names[i].name, name, strlen(cpu_names[i].name) )) return cpu_names[i].cpu; | |
| return -1; | |
| } | |
| static inline int get_platform_from_name( const char *name ) | |
| { | |
| static const struct | |
| { | |
| const char *name; | |
| int platform; | |
| } platform_names[] = | |
| { | |
| { "macos", PLATFORM_APPLE }, | |
| { "darwin", PLATFORM_APPLE }, | |
| { "android", PLATFORM_ANDROID }, | |
| { "linux", PLATFORM_LINUX }, | |
| { "freebsd", PLATFORM_FREEBSD }, | |
| { "solaris", PLATFORM_SOLARIS }, | |
| { "mingw32", PLATFORM_MINGW }, | |
| { "windows-gnu", PLATFORM_WINDOWS_GNU }, | |
| { "winnt", PLATFORM_MINGW }, | |
| { "windows", PLATFORM_WINDOWS }, | |
| { "cygwin", PLATFORM_MINGW }, | |
| }; | |
| unsigned int i; | |
| for (i = 0; i < ARRAY_SIZE(platform_names); i++) | |
| if (!strncmp( platform_names[i].name, name, strlen(platform_names[i].name) )) | |
| return platform_names[i].platform; | |
| return -1; | |
| }; | |
| static inline const char *get_arch_dir( struct target target ) | |
| { | |
| static const char *cpu_names[] = | |
| { | |
| [CPU_i386] = "i386", | |
| [CPU_x86_64] = "x86_64", | |
| [CPU_ARM] = "arm", | |
| [CPU_ARM64] = "aarch64", | |
| [CPU_ARM64EC] = "aarch64", | |
| }; | |
| if (!cpu_names[target.cpu]) return ""; | |
| return strmake( "/%s-%s", cpu_names[target.cpu], is_pe_target( target ) ? "windows" : "unix" ); | |
| } | |
| static inline bool parse_target( const char *name, struct target *target ) | |
| { | |
| int res; | |
| char *p, *spec = xstrdup( name ); | |
| /* target specification is in the form CPU-MANUFACTURER-OS or CPU-MANUFACTURER-KERNEL-OS */ | |
| /* get the CPU part */ | |
| if ((p = strchr( spec, '-' ))) | |
| { | |
| *p++ = 0; | |
| if ((res = get_cpu_from_name( spec )) == -1) | |
| { | |
| free( spec ); | |
| return false; | |
| } | |
| target->cpu = res; | |
| } | |
| else if (!strcmp( spec, "mingw32" )) | |
| { | |
| target->cpu = CPU_i386; | |
| p = spec; | |
| } | |
| else | |
| { | |
| free( spec ); | |
| return false; | |
| } | |
| /* get the OS part */ | |
| target->platform = PLATFORM_UNSPECIFIED; /* default value */ | |
| for (;;) | |
| { | |
| if ((res = get_platform_from_name( p )) != -1) | |
| { | |
| target->platform = res; | |
| break; | |
| } | |
| if (!(p = strchr( p, '-' ))) break; | |
| p++; | |
| } | |
| free( spec ); | |
| return true; | |
| } | |
| static inline struct target init_argv0_target( const char *argv0 ) | |
| { | |
| char *name = get_basename( argv0 ); | |
| struct target target; | |
| if (!strchr( name, '-' ) || !parse_target( name, &target )) | |
| target = get_default_target(); | |
| free( name ); | |
| return target; | |
| } | |
| static inline char *get_bindir( const char *argv0 ) | |
| { | |
| #ifndef _WIN32 | |
| char *dir = NULL; | |
| #if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) \ | |
| || defined(__CYGWIN__) || defined(__MSYS__) | |
| dir = realpath( "/proc/self/exe", NULL ); | |
| #elif defined (__FreeBSD__) || defined(__DragonFly__) | |
| static int pathname[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; | |
| size_t path_size = PATH_MAX; | |
| char *path = xmalloc( path_size ); | |
| if (!sysctl( pathname, ARRAY_SIZE(pathname), path, &path_size, NULL, 0 )) | |
| dir = realpath( path, NULL ); | |
| free( path ); | |
| #elif defined(__APPLE__) | |
| uint32_t path_size = PATH_MAX; | |
| char *path = xmalloc( path_size ); | |
| if (!_NSGetExecutablePath( path, &path_size )) | |
| dir = realpath( path, NULL ); | |
| free( path ); | |
| #endif | |
| if (!dir && !(dir = realpath( argv0, NULL ))) return NULL; | |
| return get_dirname( dir ); | |
| #else | |
| char path[MAX_PATH], *p; | |
| GetModuleFileNameA( NULL, path, ARRAYSIZE(path) ); | |
| for (p = path; *p; p++) if (*p == '\\') *p = '/'; | |
| return get_dirname( path ); | |
| #endif | |
| } | |
| #ifdef LIBDIR | |
| static inline const char *get_libdir( const char *bindir ) | |
| { | |
| #ifdef BINDIR | |
| if (bindir) return build_relative_path( bindir, BINDIR, LIBDIR ); | |
| #endif | |
| return LIBDIR; | |
| } | |
| #endif | |
| #ifdef DATADIR | |
| static inline const char *get_datadir( const char *bindir ) | |
| { | |
| #ifdef BINDIR | |
| if (bindir) return build_relative_path( bindir, BINDIR, DATADIR ); | |
| #endif | |
| return DATADIR; | |
| } | |
| #endif | |
| #ifdef INCLUDEDIR | |
| static inline const char *get_includedir( const char *bindir ) | |
| { | |
| #ifdef BINDIR | |
| if (bindir) return build_relative_path( bindir, BINDIR, INCLUDEDIR ); | |
| #endif | |
| return INCLUDEDIR; | |
| } | |
| #endif | |
| static inline const char *get_nlsdir( const char *bindir, const char *srcdir ) | |
| { | |
| if (bindir && strendswith( bindir, srcdir )) return strmake( "%s/../../nls", bindir ); | |
| #ifdef DATADIR | |
| else | |
| { | |
| const char *datadir = get_datadir( bindir ); | |
| if (datadir) return strmake( "%s/wine/nls", datadir ); | |
| } | |
| #endif | |
| return NULL; | |
| } | |
| /* output buffer management */ | |
| extern unsigned char *output_buffer; | |
| extern size_t output_buffer_pos; | |
| extern size_t output_buffer_size; | |
| static inline void check_output_buffer_space( size_t size ) | |
| { | |
| if (output_buffer_pos + size >= output_buffer_size) | |
| { | |
| output_buffer_size = max( output_buffer_size * 2, output_buffer_pos + size ); | |
| output_buffer = xrealloc( output_buffer, output_buffer_size ); | |
| } | |
| } | |
| static inline void init_output_buffer(void) | |
| { | |
| output_buffer_size = 1024; | |
| output_buffer_pos = 0; | |
| output_buffer = xmalloc( output_buffer_size ); | |
| } | |
| static inline void put_data( const void *data, size_t size ) | |
| { | |
| check_output_buffer_space( size ); | |
| memcpy( output_buffer + output_buffer_pos, data, size ); | |
| output_buffer_pos += size; | |
| } | |
| static inline void put_byte( unsigned char val ) | |
| { | |
| check_output_buffer_space( 1 ); | |
| output_buffer[output_buffer_pos++] = val; | |
| } | |
| static inline void put_word( unsigned short val ) | |
| { | |
| check_output_buffer_space( 2 ); | |
| output_buffer[output_buffer_pos++] = val; | |
| output_buffer[output_buffer_pos++] = val >> 8; | |
| } | |
| static inline void put_dword( unsigned int val ) | |
| { | |
| check_output_buffer_space( 4 ); | |
| output_buffer[output_buffer_pos++] = val; | |
| output_buffer[output_buffer_pos++] = val >> 8; | |
| output_buffer[output_buffer_pos++] = val >> 16; | |
| output_buffer[output_buffer_pos++] = val >> 24; | |
| } | |
| static inline void put_qword( unsigned int val ) | |
| { | |
| put_dword( val ); | |
| put_dword( 0 ); | |
| } | |
| static inline void align_output( unsigned int align ) | |
| { | |
| size_t size = align - (output_buffer_pos % align); | |
| if (size == align) return; | |
| check_output_buffer_space( size ); | |
| memset( output_buffer + output_buffer_pos, 0, size ); | |
| output_buffer_pos += size; | |
| } | |
| static inline void flush_output_buffer( const char *name ) | |
| { | |
| int fd = open( name, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666 ); | |
| if (fd == -1 || write( fd, output_buffer, output_buffer_pos ) != output_buffer_pos) | |
| { | |
| perror( name ); | |
| exit(1); | |
| } | |
| close( fd ); | |
| free( output_buffer ); | |
| } | |
| /* command-line option parsing */ | |
| /* partly based on the Glibc getopt() implementation */ | |
| struct long_option | |
| { | |
| const char *name; | |
| bool has_arg; | |
| int val; | |
| }; | |
| static inline struct strarray parse_options( int argc, char **argv, const char *short_opts, | |
| const struct long_option *long_opts, int long_only, | |
| void (*callback)( int, char* ) ) | |
| { | |
| struct strarray ret = empty_strarray; | |
| const char *flag; | |
| char *start, *end; | |
| int i; | |
| #define OPT_ERR(fmt) { callback( '?', strmake( fmt, argv[i] )); continue; } | |
| for (i = 1; i < argc; i++) | |
| { | |
| if (argv[i][0] != '-' || !argv[i][1]) /* not an option */ | |
| { | |
| strarray_add( &ret, argv[i] ); | |
| continue; | |
| } | |
| if (!strcmp( argv[i], "--" )) | |
| { | |
| /* add remaining args */ | |
| while (++i < argc) strarray_add( &ret, argv[i] ); | |
| break; | |
| } | |
| start = argv[i] + 1 + (argv[i][1] == '-'); | |
| if (argv[i][1] == '-' || (long_only && (argv[i][2] || !strchr( short_opts, argv[i][1] )))) | |
| { | |
| /* handle long option */ | |
| const struct long_option *opt, *found = NULL; | |
| int count = 0; | |
| if (!(end = strchr( start, '=' ))) end = start + strlen(start); | |
| for (opt = long_opts; opt && opt->name; opt++) | |
| { | |
| if (strncmp( opt->name, start, end - start )) continue; | |
| if (!opt->name[end - start]) /* exact match */ | |
| { | |
| found = opt; | |
| count = 1; | |
| break; | |
| } | |
| if (!found) | |
| { | |
| found = opt; | |
| count++; | |
| } | |
| else if (long_only || found->has_arg != opt->has_arg || found->val != opt->val) | |
| { | |
| count++; | |
| } | |
| } | |
| if (count > 1) OPT_ERR( "option '%s' is ambiguous" ); | |
| if (found) | |
| { | |
| if (*end) | |
| { | |
| if (!found->has_arg) OPT_ERR( "argument not allowed in '%s'" ); | |
| end++; /* skip '=' */ | |
| } | |
| else if (found->has_arg == 1) | |
| { | |
| if (i == argc - 1) OPT_ERR( "option '%s' requires an argument" ); | |
| end = argv[++i]; | |
| } | |
| else end = NULL; | |
| callback( found->val, end ); | |
| continue; | |
| } | |
| if (argv[i][1] == '-' || !long_only || !strchr( short_opts, argv[i][1] )) | |
| OPT_ERR( "unrecognized option '%s'" ); | |
| } | |
| /* handle short option */ | |
| for ( ; *start; start++) | |
| { | |
| if (!(flag = strchr( short_opts, *start ))) OPT_ERR( "invalid option '%s'" ); | |
| if (flag[1] == ':') | |
| { | |
| end = start + 1; | |
| if (!*end) end = NULL; | |
| if (flag[2] != ':' && !end) | |
| { | |
| if (i == argc - 1) OPT_ERR( "option '%s' requires an argument" ); | |
| end = argv[++i]; | |
| } | |
| callback( *start, end ); | |
| break; | |
| } | |
| callback( *start, NULL ); | |
| } | |
| } | |
| return ret; | |
| #undef OPT_ERR | |
| } | |
| #endif /* __WINE_TOOLS_H */ |