123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776 |
- /*
- * 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
- #include <stdarg.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <time.h>
- #include <errno.h>
- #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
- # ifdef _MSC_VER
- # define popen _popen
- # define pclose _pclose
- # define strtoll _strtoi64
- # define strtoull _strtoui64
- # define strncasecmp _strnicmp
- # define strcasecmp _stricmp
- # endif
- #else
- # 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;
- enum
- {
- PLATFORM_UNSPECIFIED,
- PLATFORM_APPLE,
- PLATFORM_ANDROID,
- PLATFORM_LINUX,
- PLATFORM_FREEBSD,
- PLATFORM_SOLARIS,
- PLATFORM_WINDOWS,
- PLATFORM_MINGW,
- PLATFORM_CYGWIN
- } platform;
- };
- 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 int 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 );
- }
- }
- /* 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;
- 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 )
- {
- unsigned int i;
- for (i = 0; i < added.count; i++) strarray_add( array, added.str[i] );
- }
- static inline int strarray_exists( const struct strarray *array, const char *str )
- {
- unsigned int i;
- for (i = 0; i < array->count; i++) if (!strcmp( array->str[i], str )) return 1;
- return 0;
- }
- 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 )
- {
- unsigned int i;
- for (i = 0; i < added.count; i++) strarray_add_uniq( array, added.str[i] );
- }
- 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("");
- for (i = 0; i < array.count; i++) len += strlen( array.str[i] );
- 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( const 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] );
- putchar( i < args.count - 1 ? ' ' : '\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;
- if (!(pid = fork()))
- {
- strarray_add( &args, NULL );
- execvp( args.str[0], (char **)args.str );
- _exit(1);
- }
- if (pid == -1) 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 );
- }
- static inline int make_temp_file( const char *prefix, const char *suffix, char **name )
- {
- static unsigned int value;
- int fd, count;
- const char *tmpdir = NULL;
- if (!prefix) prefix = "tmp";
- if (!suffix) suffix = "";
- value += time(NULL) + getpid();
- for (count = 0; count < 0x8000; count++)
- {
- if (tmpdir)
- *name = strmake( "%s/%s-%08x%s", tmpdir, prefix, value, suffix );
- else
- *name = strmake( "%s-%08x%s", prefix, value, suffix );
- fd = open( *name, O_RDWR | O_CREAT | O_EXCL, 0600 );
- if (fd >= 0) return fd;
- value += 7777;
- if (errno == EACCES && !tmpdir && !strchr( prefix, '/' ))
- {
- if (!(tmpdir = getenv("TMPDIR"))) tmpdir = "/tmp";
- }
- free( *name );
- }
- fprintf( stderr, "failed to create temp file for %s%s\n", prefix, suffix );
- exit(1);
- }
- 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_CYGWIN;
- #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,
- };
- 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:
- if (size == 4) target->cpu = CPU_ARM;
- break;
- }
- }
- 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 },
- { "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_MINGW },
- { "winnt", PLATFORM_MINGW },
- { "windows", PLATFORM_WINDOWS },
- { "cygwin", PLATFORM_CYGWIN },
- };
- 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"
- };
- if (!cpu_names[target.cpu]) return "";
- switch (target.platform)
- {
- case PLATFORM_WINDOWS:
- case PLATFORM_CYGWIN:
- case PLATFORM_MINGW:
- return strmake( "/%s-windows", cpu_names[target.cpu] );
- default:
- return strmake( "/%s-unix", cpu_names[target.cpu] );
- }
- }
- static inline int 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 0;
- }
- target->cpu = res;
- }
- else if (!strcmp( spec, "mingw32" ))
- {
- target->cpu = CPU_i386;
- p = spec;
- }
- else
- {
- free( spec );
- return 0;
- }
- /* 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 1;
- }
- 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;
- }
- /* 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;
- int 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[1] )); 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 */
|