tools.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776
  1. /*
  2. * Helper functions for the Wine tools
  3. *
  4. * Copyright 2021 Alexandre Julliard
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2.1 of the License, or (at your option) any later version.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with this library; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  19. */
  20. #ifndef __WINE_TOOLS_H
  21. #define __WINE_TOOLS_H
  22. #include <stdarg.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <sys/types.h>
  27. #include <sys/stat.h>
  28. #include <fcntl.h>
  29. #include <time.h>
  30. #include <errno.h>
  31. #ifdef _WIN32
  32. # include <direct.h>
  33. # include <io.h>
  34. # include <process.h>
  35. # define mkdir(path,mode) mkdir(path)
  36. # ifndef S_ISREG
  37. # define S_ISREG(mod) (((mod) & _S_IFMT) == _S_IFREG)
  38. # endif
  39. # ifdef _MSC_VER
  40. # define popen _popen
  41. # define pclose _pclose
  42. # define strtoll _strtoi64
  43. # define strtoull _strtoui64
  44. # define strncasecmp _strnicmp
  45. # define strcasecmp _stricmp
  46. # endif
  47. #else
  48. # include <sys/wait.h>
  49. # include <unistd.h>
  50. # ifndef O_BINARY
  51. # define O_BINARY 0
  52. # endif
  53. # ifndef __int64
  54. # if defined(__x86_64__) || defined(__aarch64__) || defined(__powerpc64__)
  55. # define __int64 long
  56. # else
  57. # define __int64 long long
  58. # endif
  59. # endif
  60. #endif
  61. #if !defined(__GNUC__) && !defined(__attribute__)
  62. #define __attribute__(x)
  63. #endif
  64. #ifndef max
  65. #define max(a,b) (((a) > (b)) ? (a) : (b))
  66. #endif
  67. #ifndef min
  68. #define min(a,b) (((a) < (b)) ? (a) : (b))
  69. #endif
  70. #ifndef ARRAY_SIZE
  71. #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
  72. #endif
  73. struct target
  74. {
  75. enum { CPU_i386, CPU_x86_64, CPU_ARM, CPU_ARM64 } cpu;
  76. enum
  77. {
  78. PLATFORM_UNSPECIFIED,
  79. PLATFORM_APPLE,
  80. PLATFORM_ANDROID,
  81. PLATFORM_LINUX,
  82. PLATFORM_FREEBSD,
  83. PLATFORM_SOLARIS,
  84. PLATFORM_WINDOWS,
  85. PLATFORM_MINGW,
  86. PLATFORM_CYGWIN
  87. } platform;
  88. };
  89. static inline void *xmalloc( size_t size )
  90. {
  91. void *res = malloc( size ? size : 1 );
  92. if (res == NULL)
  93. {
  94. fprintf( stderr, "Virtual memory exhausted.\n" );
  95. exit(1);
  96. }
  97. return res;
  98. }
  99. static inline void *xrealloc (void *ptr, size_t size)
  100. {
  101. void *res = realloc( ptr, size );
  102. if (size && res == NULL)
  103. {
  104. fprintf( stderr, "Virtual memory exhausted.\n" );
  105. exit(1);
  106. }
  107. return res;
  108. }
  109. static inline char *xstrdup( const char *str )
  110. {
  111. return strcpy( xmalloc( strlen(str)+1 ), str );
  112. }
  113. static inline int strendswith( const char *str, const char *end )
  114. {
  115. int l = strlen( str );
  116. int m = strlen( end );
  117. return l >= m && !strcmp( str + l - m, end );
  118. }
  119. static char *strmake( const char* fmt, ... ) __attribute__ ((__format__ (__printf__, 1, 2)));
  120. static inline char *strmake( const char* fmt, ... )
  121. {
  122. int n;
  123. size_t size = 100;
  124. va_list ap;
  125. for (;;)
  126. {
  127. char *p = xmalloc( size );
  128. va_start( ap, fmt );
  129. n = vsnprintf( p, size, fmt, ap );
  130. va_end( ap );
  131. if (n == -1) size *= 2;
  132. else if ((size_t)n >= size) size = n + 1;
  133. else return p;
  134. free( p );
  135. }
  136. }
  137. /* string array functions */
  138. struct strarray
  139. {
  140. unsigned int count; /* strings in use */
  141. unsigned int size; /* total allocated size */
  142. const char **str;
  143. };
  144. static const struct strarray empty_strarray;
  145. static inline void strarray_add( struct strarray *array, const char *str )
  146. {
  147. if (array->count == array->size)
  148. {
  149. if (array->size) array->size *= 2;
  150. else array->size = 16;
  151. array->str = xrealloc( array->str, sizeof(array->str[0]) * array->size );
  152. }
  153. array->str[array->count++] = str;
  154. }
  155. static inline void strarray_addall( struct strarray *array, struct strarray added )
  156. {
  157. unsigned int i;
  158. for (i = 0; i < added.count; i++) strarray_add( array, added.str[i] );
  159. }
  160. static inline int strarray_exists( const struct strarray *array, const char *str )
  161. {
  162. unsigned int i;
  163. for (i = 0; i < array->count; i++) if (!strcmp( array->str[i], str )) return 1;
  164. return 0;
  165. }
  166. static inline void strarray_add_uniq( struct strarray *array, const char *str )
  167. {
  168. if (!strarray_exists( array, str )) strarray_add( array, str );
  169. }
  170. static inline void strarray_addall_uniq( struct strarray *array, struct strarray added )
  171. {
  172. unsigned int i;
  173. for (i = 0; i < added.count; i++) strarray_add_uniq( array, added.str[i] );
  174. }
  175. static inline struct strarray strarray_fromstring( const char *str, const char *delim )
  176. {
  177. struct strarray array = empty_strarray;
  178. char *buf = xstrdup( str );
  179. const char *tok;
  180. for (tok = strtok( buf, delim ); tok; tok = strtok( NULL, delim ))
  181. strarray_add( &array, xstrdup( tok ));
  182. free( buf );
  183. return array;
  184. }
  185. static inline struct strarray strarray_frompath( const char *path )
  186. {
  187. if (!path) return empty_strarray;
  188. #ifdef _WIN32
  189. return strarray_fromstring( path, ";" );
  190. #else
  191. return strarray_fromstring( path, ":" );
  192. #endif
  193. }
  194. static inline char *strarray_tostring( struct strarray array, const char *sep )
  195. {
  196. char *str;
  197. unsigned int i, len = 1 + (array.count - 1) * strlen(sep);
  198. if (!array.count) return xstrdup("");
  199. for (i = 0; i < array.count; i++) len += strlen( array.str[i] );
  200. str = xmalloc( len );
  201. strcpy( str, array.str[0] );
  202. for (i = 1; i < array.count; i++)
  203. {
  204. strcat( str, sep );
  205. strcat( str, array.str[i] );
  206. }
  207. return str;
  208. }
  209. static inline void strarray_qsort( struct strarray *array, int (*func)(const char **, const char **) )
  210. {
  211. if (array->count) qsort( array->str, array->count, sizeof(*array->str), (void *)func );
  212. }
  213. static inline const char *strarray_bsearch( const struct strarray *array, const char *str,
  214. int (*func)(const char **, const char **) )
  215. {
  216. char **res = NULL;
  217. if (array->count) res = bsearch( &str, array->str, array->count, sizeof(*array->str), (void *)func );
  218. return res ? *res : NULL;
  219. }
  220. static inline void strarray_trace( struct strarray args )
  221. {
  222. unsigned int i;
  223. for (i = 0; i < args.count; i++)
  224. {
  225. if (strpbrk( args.str[i], " \t\n\r")) printf( "\"%s\"", args.str[i] );
  226. else printf( "%s", args.str[i] );
  227. putchar( i < args.count - 1 ? ' ' : '\n' );
  228. }
  229. }
  230. static inline int strarray_spawn( struct strarray args )
  231. {
  232. #ifdef _WIN32
  233. strarray_add( &args, NULL );
  234. return _spawnvp( _P_WAIT, args.str[0], args.str );
  235. #else
  236. pid_t pid, wret;
  237. int status;
  238. if (!(pid = fork()))
  239. {
  240. strarray_add( &args, NULL );
  241. execvp( args.str[0], (char **)args.str );
  242. _exit(1);
  243. }
  244. if (pid == -1) return -1;
  245. while (pid != (wret = waitpid( pid, &status, 0 )))
  246. if (wret == -1 && errno != EINTR) break;
  247. if (pid == wret && WIFEXITED(status)) return WEXITSTATUS(status);
  248. return 255; /* abnormal exit with an abort or an interrupt */
  249. #endif
  250. }
  251. static inline char *get_basename( const char *file )
  252. {
  253. const char *ret = strrchr( file, '/' );
  254. return xstrdup( ret ? ret + 1 : file );
  255. }
  256. static inline char *get_basename_noext( const char *file )
  257. {
  258. char *ext, *ret = get_basename( file );
  259. if ((ext = strrchr( ret, '.' ))) *ext = 0;
  260. return ret;
  261. }
  262. static inline char *get_dirname( const char *file )
  263. {
  264. const char *end = strrchr( file, '/' );
  265. if (!end) return xstrdup( "." );
  266. if (end == file) end++;
  267. return strmake( "%.*s", (int)(end - file), file );
  268. }
  269. static inline char *replace_extension( const char *name, const char *old_ext, const char *new_ext )
  270. {
  271. int name_len = strlen( name );
  272. if (strendswith( name, old_ext )) name_len -= strlen( old_ext );
  273. return strmake( "%.*s%s", name_len, name, new_ext );
  274. }
  275. static inline int make_temp_file( const char *prefix, const char *suffix, char **name )
  276. {
  277. static unsigned int value;
  278. int fd, count;
  279. const char *tmpdir = NULL;
  280. if (!prefix) prefix = "tmp";
  281. if (!suffix) suffix = "";
  282. value += time(NULL) + getpid();
  283. for (count = 0; count < 0x8000; count++)
  284. {
  285. if (tmpdir)
  286. *name = strmake( "%s/%s-%08x%s", tmpdir, prefix, value, suffix );
  287. else
  288. *name = strmake( "%s-%08x%s", prefix, value, suffix );
  289. fd = open( *name, O_RDWR | O_CREAT | O_EXCL, 0600 );
  290. if (fd >= 0) return fd;
  291. value += 7777;
  292. if (errno == EACCES && !tmpdir && !strchr( prefix, '/' ))
  293. {
  294. if (!(tmpdir = getenv("TMPDIR"))) tmpdir = "/tmp";
  295. }
  296. free( *name );
  297. }
  298. fprintf( stderr, "failed to create temp file for %s%s\n", prefix, suffix );
  299. exit(1);
  300. }
  301. static inline void *read_file( const char *name, size_t *size )
  302. {
  303. struct stat st;
  304. int res, fd;
  305. void *data;
  306. if ((fd = open( name, O_RDONLY | O_BINARY )) == -1) return NULL;
  307. fstat( fd, &st );
  308. data = xmalloc( st.st_size );
  309. res = read( fd, data, st.st_size );
  310. if (res == -1)
  311. {
  312. free( data );
  313. data = NULL;
  314. *size = 0;
  315. }
  316. else *size = res;
  317. close( fd );
  318. return data;
  319. }
  320. static inline struct target get_default_target(void)
  321. {
  322. struct target target;
  323. #ifdef __i386__
  324. target.cpu = CPU_i386;
  325. #elif defined(__x86_64__)
  326. target.cpu = CPU_x86_64;
  327. #elif defined(__arm__)
  328. target.cpu = CPU_ARM;
  329. #elif defined(__aarch64__)
  330. target.cpu = CPU_ARM64;
  331. #else
  332. #error Unsupported CPU
  333. #endif
  334. #ifdef __APPLE__
  335. target.platform = PLATFORM_APPLE;
  336. #elif defined(__ANDROID__)
  337. target.platform = PLATFORM_ANDROID;
  338. #elif defined(__linux__)
  339. target.platform = PLATFORM_LINUX;
  340. #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
  341. target.platform = PLATFORM_FREEBSD;
  342. #elif defined(__sun)
  343. target.platform = PLATFORM_SOLARIS;
  344. #elif defined(__CYGWIN__)
  345. target.platform = PLATFORM_CYGWIN;
  346. #elif defined(_WIN32)
  347. target.platform = PLATFORM_MINGW;
  348. #else
  349. target.platform = PLATFORM_UNSPECIFIED;
  350. #endif
  351. return target;
  352. }
  353. static inline unsigned int get_target_ptr_size( struct target target )
  354. {
  355. static const unsigned int sizes[] =
  356. {
  357. [CPU_i386] = 4,
  358. [CPU_x86_64] = 8,
  359. [CPU_ARM] = 4,
  360. [CPU_ARM64] = 8,
  361. };
  362. return sizes[target.cpu];
  363. }
  364. static inline void set_target_ptr_size( struct target *target, unsigned int size )
  365. {
  366. switch (target->cpu)
  367. {
  368. case CPU_i386:
  369. if (size == 8) target->cpu = CPU_x86_64;
  370. break;
  371. case CPU_x86_64:
  372. if (size == 4) target->cpu = CPU_i386;
  373. break;
  374. case CPU_ARM:
  375. if (size == 8) target->cpu = CPU_ARM64;
  376. break;
  377. case CPU_ARM64:
  378. if (size == 4) target->cpu = CPU_ARM;
  379. break;
  380. }
  381. }
  382. static inline int get_cpu_from_name( const char *name )
  383. {
  384. static const struct
  385. {
  386. const char *name;
  387. int cpu;
  388. } cpu_names[] =
  389. {
  390. { "i386", CPU_i386 },
  391. { "i486", CPU_i386 },
  392. { "i586", CPU_i386 },
  393. { "i686", CPU_i386 },
  394. { "i786", CPU_i386 },
  395. { "x86_64", CPU_x86_64 },
  396. { "amd64", CPU_x86_64 },
  397. { "aarch64", CPU_ARM64 },
  398. { "arm64", CPU_ARM64 },
  399. { "arm", CPU_ARM },
  400. };
  401. unsigned int i;
  402. for (i = 0; i < ARRAY_SIZE(cpu_names); i++)
  403. if (!strncmp( cpu_names[i].name, name, strlen(cpu_names[i].name) )) return cpu_names[i].cpu;
  404. return -1;
  405. }
  406. static inline int get_platform_from_name( const char *name )
  407. {
  408. static const struct
  409. {
  410. const char *name;
  411. int platform;
  412. } platform_names[] =
  413. {
  414. { "macos", PLATFORM_APPLE },
  415. { "darwin", PLATFORM_APPLE },
  416. { "android", PLATFORM_ANDROID },
  417. { "linux", PLATFORM_LINUX },
  418. { "freebsd", PLATFORM_FREEBSD },
  419. { "solaris", PLATFORM_SOLARIS },
  420. { "mingw32", PLATFORM_MINGW },
  421. { "windows-gnu", PLATFORM_MINGW },
  422. { "winnt", PLATFORM_MINGW },
  423. { "windows", PLATFORM_WINDOWS },
  424. { "cygwin", PLATFORM_CYGWIN },
  425. };
  426. unsigned int i;
  427. for (i = 0; i < ARRAY_SIZE(platform_names); i++)
  428. if (!strncmp( platform_names[i].name, name, strlen(platform_names[i].name) ))
  429. return platform_names[i].platform;
  430. return -1;
  431. };
  432. static inline const char *get_arch_dir( struct target target )
  433. {
  434. static const char *cpu_names[] =
  435. {
  436. [CPU_i386] = "i386",
  437. [CPU_x86_64] = "x86_64",
  438. [CPU_ARM] = "arm",
  439. [CPU_ARM64] = "aarch64"
  440. };
  441. if (!cpu_names[target.cpu]) return "";
  442. switch (target.platform)
  443. {
  444. case PLATFORM_WINDOWS:
  445. case PLATFORM_CYGWIN:
  446. case PLATFORM_MINGW:
  447. return strmake( "/%s-windows", cpu_names[target.cpu] );
  448. default:
  449. return strmake( "/%s-unix", cpu_names[target.cpu] );
  450. }
  451. }
  452. static inline int parse_target( const char *name, struct target *target )
  453. {
  454. int res;
  455. char *p, *spec = xstrdup( name );
  456. /* target specification is in the form CPU-MANUFACTURER-OS or CPU-MANUFACTURER-KERNEL-OS */
  457. /* get the CPU part */
  458. if ((p = strchr( spec, '-' )))
  459. {
  460. *p++ = 0;
  461. if ((res = get_cpu_from_name( spec )) == -1)
  462. {
  463. free( spec );
  464. return 0;
  465. }
  466. target->cpu = res;
  467. }
  468. else if (!strcmp( spec, "mingw32" ))
  469. {
  470. target->cpu = CPU_i386;
  471. p = spec;
  472. }
  473. else
  474. {
  475. free( spec );
  476. return 0;
  477. }
  478. /* get the OS part */
  479. target->platform = PLATFORM_UNSPECIFIED; /* default value */
  480. for (;;)
  481. {
  482. if ((res = get_platform_from_name( p )) != -1)
  483. {
  484. target->platform = res;
  485. break;
  486. }
  487. if (!(p = strchr( p, '-' ))) break;
  488. p++;
  489. }
  490. free( spec );
  491. return 1;
  492. }
  493. static inline struct target init_argv0_target( const char *argv0 )
  494. {
  495. char *name = get_basename( argv0 );
  496. struct target target;
  497. if (!strchr( name, '-' ) || !parse_target( name, &target ))
  498. target = get_default_target();
  499. free( name );
  500. return target;
  501. }
  502. /* output buffer management */
  503. extern unsigned char *output_buffer;
  504. extern size_t output_buffer_pos;
  505. extern size_t output_buffer_size;
  506. static inline void check_output_buffer_space( size_t size )
  507. {
  508. if (output_buffer_pos + size >= output_buffer_size)
  509. {
  510. output_buffer_size = max( output_buffer_size * 2, output_buffer_pos + size );
  511. output_buffer = xrealloc( output_buffer, output_buffer_size );
  512. }
  513. }
  514. static inline void init_output_buffer(void)
  515. {
  516. output_buffer_size = 1024;
  517. output_buffer_pos = 0;
  518. output_buffer = xmalloc( output_buffer_size );
  519. }
  520. static inline void put_data( const void *data, size_t size )
  521. {
  522. check_output_buffer_space( size );
  523. memcpy( output_buffer + output_buffer_pos, data, size );
  524. output_buffer_pos += size;
  525. }
  526. static inline void put_byte( unsigned char val )
  527. {
  528. check_output_buffer_space( 1 );
  529. output_buffer[output_buffer_pos++] = val;
  530. }
  531. static inline void put_word( unsigned short val )
  532. {
  533. check_output_buffer_space( 2 );
  534. output_buffer[output_buffer_pos++] = val;
  535. output_buffer[output_buffer_pos++] = val >> 8;
  536. }
  537. static inline void put_dword( unsigned int val )
  538. {
  539. check_output_buffer_space( 4 );
  540. output_buffer[output_buffer_pos++] = val;
  541. output_buffer[output_buffer_pos++] = val >> 8;
  542. output_buffer[output_buffer_pos++] = val >> 16;
  543. output_buffer[output_buffer_pos++] = val >> 24;
  544. }
  545. static inline void put_qword( unsigned int val )
  546. {
  547. put_dword( val );
  548. put_dword( 0 );
  549. }
  550. static inline void align_output( unsigned int align )
  551. {
  552. size_t size = align - (output_buffer_pos % align);
  553. if (size == align) return;
  554. check_output_buffer_space( size );
  555. memset( output_buffer + output_buffer_pos, 0, size );
  556. output_buffer_pos += size;
  557. }
  558. static inline void flush_output_buffer( const char *name )
  559. {
  560. int fd = open( name, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666 );
  561. if (fd == -1 || write( fd, output_buffer, output_buffer_pos ) != output_buffer_pos)
  562. {
  563. perror( name );
  564. exit(1);
  565. }
  566. close( fd );
  567. free( output_buffer );
  568. }
  569. /* command-line option parsing */
  570. /* partly based on the Glibc getopt() implementation */
  571. struct long_option
  572. {
  573. const char *name;
  574. int has_arg;
  575. int val;
  576. };
  577. static inline struct strarray parse_options( int argc, char **argv, const char *short_opts,
  578. const struct long_option *long_opts, int long_only,
  579. void (*callback)( int, char* ) )
  580. {
  581. struct strarray ret = empty_strarray;
  582. const char *flag;
  583. char *start, *end;
  584. int i;
  585. #define OPT_ERR(fmt) { callback( '?', strmake( fmt, argv[1] )); continue; }
  586. for (i = 1; i < argc; i++)
  587. {
  588. if (argv[i][0] != '-' || !argv[i][1]) /* not an option */
  589. {
  590. strarray_add( &ret, argv[i] );
  591. continue;
  592. }
  593. if (!strcmp( argv[i], "--" ))
  594. {
  595. /* add remaining args */
  596. while (++i < argc) strarray_add( &ret, argv[i] );
  597. break;
  598. }
  599. start = argv[i] + 1 + (argv[i][1] == '-');
  600. if (argv[i][1] == '-' || (long_only && (argv[i][2] || !strchr( short_opts, argv[i][1] ))))
  601. {
  602. /* handle long option */
  603. const struct long_option *opt, *found = NULL;
  604. int count = 0;
  605. if (!(end = strchr( start, '=' ))) end = start + strlen(start);
  606. for (opt = long_opts; opt && opt->name; opt++)
  607. {
  608. if (strncmp( opt->name, start, end - start )) continue;
  609. if (!opt->name[end - start]) /* exact match */
  610. {
  611. found = opt;
  612. count = 1;
  613. break;
  614. }
  615. if (!found)
  616. {
  617. found = opt;
  618. count++;
  619. }
  620. else if (long_only || found->has_arg != opt->has_arg || found->val != opt->val)
  621. {
  622. count++;
  623. }
  624. }
  625. if (count > 1) OPT_ERR( "option '%s' is ambiguous" );
  626. if (found)
  627. {
  628. if (*end)
  629. {
  630. if (!found->has_arg) OPT_ERR( "argument not allowed in '%s'" );
  631. end++; /* skip '=' */
  632. }
  633. else if (found->has_arg == 1)
  634. {
  635. if (i == argc - 1) OPT_ERR( "option '%s' requires an argument" );
  636. end = argv[++i];
  637. }
  638. else end = NULL;
  639. callback( found->val, end );
  640. continue;
  641. }
  642. if (argv[i][1] == '-' || !long_only || !strchr( short_opts, argv[i][1] ))
  643. OPT_ERR( "unrecognized option '%s'" );
  644. }
  645. /* handle short option */
  646. for ( ; *start; start++)
  647. {
  648. if (!(flag = strchr( short_opts, *start ))) OPT_ERR( "invalid option '%s'" );
  649. if (flag[1] == ':')
  650. {
  651. end = start + 1;
  652. if (!*end) end = NULL;
  653. if (flag[2] != ':' && !end)
  654. {
  655. if (i == argc - 1) OPT_ERR( "option '%s' requires an argument" );
  656. end = argv[++i];
  657. }
  658. callback( *start, end );
  659. break;
  660. }
  661. callback( *start, NULL );
  662. }
  663. }
  664. return ret;
  665. #undef OPT_ERR
  666. }
  667. #endif /* __WINE_TOOLS_H */