tools.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  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 struct target get_default_target(void)
  302. {
  303. struct target target;
  304. #ifdef __i386__
  305. target.cpu = CPU_i386;
  306. #elif defined(__x86_64__)
  307. target.cpu = CPU_x86_64;
  308. #elif defined(__arm__)
  309. target.cpu = CPU_ARM;
  310. #elif defined(__aarch64__)
  311. target.cpu = CPU_ARM64;
  312. #else
  313. #error Unsupported CPU
  314. #endif
  315. #ifdef __APPLE__
  316. target.platform = PLATFORM_APPLE;
  317. #elif defined(__ANDROID__)
  318. target.platform = PLATFORM_ANDROID;
  319. #elif defined(__linux__)
  320. target.platform = PLATFORM_LINUX;
  321. #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
  322. target.platform = PLATFORM_FREEBSD;
  323. #elif defined(__sun)
  324. target.platform = PLATFORM_SOLARIS;
  325. #elif defined(__CYGWIN__)
  326. target.platform = PLATFORM_CYGWIN;
  327. #elif defined(_WIN32)
  328. target.platform = PLATFORM_MINGW;
  329. #else
  330. target.platform = PLATFORM_UNSPECIFIED;
  331. #endif
  332. return target;
  333. }
  334. static inline unsigned int get_target_ptr_size( struct target target )
  335. {
  336. static const unsigned int sizes[] =
  337. {
  338. [CPU_i386] = 4,
  339. [CPU_x86_64] = 8,
  340. [CPU_ARM] = 4,
  341. [CPU_ARM64] = 8,
  342. };
  343. return sizes[target.cpu];
  344. }
  345. static inline void set_target_ptr_size( struct target *target, unsigned int size )
  346. {
  347. switch (target->cpu)
  348. {
  349. case CPU_i386:
  350. if (size == 8) target->cpu = CPU_x86_64;
  351. break;
  352. case CPU_x86_64:
  353. if (size == 4) target->cpu = CPU_i386;
  354. break;
  355. case CPU_ARM:
  356. if (size == 8) target->cpu = CPU_ARM64;
  357. break;
  358. case CPU_ARM64:
  359. if (size == 4) target->cpu = CPU_ARM;
  360. break;
  361. }
  362. }
  363. static inline int get_cpu_from_name( const char *name )
  364. {
  365. static const struct
  366. {
  367. const char *name;
  368. int cpu;
  369. } cpu_names[] =
  370. {
  371. { "i386", CPU_i386 },
  372. { "i486", CPU_i386 },
  373. { "i586", CPU_i386 },
  374. { "i686", CPU_i386 },
  375. { "i786", CPU_i386 },
  376. { "x86_64", CPU_x86_64 },
  377. { "amd64", CPU_x86_64 },
  378. { "aarch64", CPU_ARM64 },
  379. { "arm64", CPU_ARM64 },
  380. { "arm", CPU_ARM },
  381. };
  382. unsigned int i;
  383. for (i = 0; i < ARRAY_SIZE(cpu_names); i++)
  384. if (!strncmp( cpu_names[i].name, name, strlen(cpu_names[i].name) )) return cpu_names[i].cpu;
  385. return -1;
  386. }
  387. static inline int get_platform_from_name( const char *name )
  388. {
  389. static const struct
  390. {
  391. const char *name;
  392. int platform;
  393. } platform_names[] =
  394. {
  395. { "macos", PLATFORM_APPLE },
  396. { "darwin", PLATFORM_APPLE },
  397. { "android", PLATFORM_ANDROID },
  398. { "linux", PLATFORM_LINUX },
  399. { "freebsd", PLATFORM_FREEBSD },
  400. { "solaris", PLATFORM_SOLARIS },
  401. { "mingw32", PLATFORM_MINGW },
  402. { "windows-gnu", PLATFORM_MINGW },
  403. { "winnt", PLATFORM_MINGW },
  404. { "windows", PLATFORM_WINDOWS },
  405. { "cygwin", PLATFORM_CYGWIN },
  406. };
  407. unsigned int i;
  408. for (i = 0; i < ARRAY_SIZE(platform_names); i++)
  409. if (!strncmp( platform_names[i].name, name, strlen(platform_names[i].name) ))
  410. return platform_names[i].platform;
  411. return -1;
  412. };
  413. static inline const char *get_arch_dir( struct target target )
  414. {
  415. static const char *cpu_names[] =
  416. {
  417. [CPU_i386] = "i386",
  418. [CPU_x86_64] = "x86_64",
  419. [CPU_ARM] = "arm",
  420. [CPU_ARM64] = "aarch64"
  421. };
  422. if (!cpu_names[target.cpu]) return "";
  423. switch (target.platform)
  424. {
  425. case PLATFORM_WINDOWS:
  426. case PLATFORM_CYGWIN:
  427. case PLATFORM_MINGW:
  428. return strmake( "/%s-windows", cpu_names[target.cpu] );
  429. default:
  430. return strmake( "/%s-unix", cpu_names[target.cpu] );
  431. }
  432. }
  433. static inline int parse_target( const char *name, struct target *target )
  434. {
  435. int res;
  436. char *p, *spec = xstrdup( name );
  437. /* target specification is in the form CPU-MANUFACTURER-OS or CPU-MANUFACTURER-KERNEL-OS */
  438. /* get the CPU part */
  439. if ((p = strchr( spec, '-' )))
  440. {
  441. *p++ = 0;
  442. if ((res = get_cpu_from_name( spec )) == -1)
  443. {
  444. free( spec );
  445. return 0;
  446. }
  447. target->cpu = res;
  448. }
  449. else if (!strcmp( spec, "mingw32" ))
  450. {
  451. target->cpu = CPU_i386;
  452. p = spec;
  453. }
  454. else
  455. {
  456. free( spec );
  457. return 0;
  458. }
  459. /* get the OS part */
  460. target->platform = PLATFORM_UNSPECIFIED; /* default value */
  461. for (;;)
  462. {
  463. if ((res = get_platform_from_name( p )) != -1)
  464. {
  465. target->platform = res;
  466. break;
  467. }
  468. if (!(p = strchr( p, '-' ))) break;
  469. p++;
  470. }
  471. free( spec );
  472. return 1;
  473. }
  474. static inline struct target init_argv0_target( const char *argv0 )
  475. {
  476. char *name = get_basename( argv0 );
  477. struct target target;
  478. if (!strchr( name, '-' ) || !parse_target( name, &target ))
  479. target = get_default_target();
  480. free( name );
  481. return target;
  482. }
  483. /* output buffer management */
  484. extern unsigned char *output_buffer;
  485. extern size_t output_buffer_pos;
  486. extern size_t output_buffer_size;
  487. static inline void check_output_buffer_space( size_t size )
  488. {
  489. if (output_buffer_pos + size >= output_buffer_size)
  490. {
  491. output_buffer_size = max( output_buffer_size * 2, output_buffer_pos + size );
  492. output_buffer = xrealloc( output_buffer, output_buffer_size );
  493. }
  494. }
  495. static inline void init_output_buffer(void)
  496. {
  497. output_buffer_size = 1024;
  498. output_buffer_pos = 0;
  499. output_buffer = xmalloc( output_buffer_size );
  500. }
  501. static inline void put_data( const void *data, size_t size )
  502. {
  503. check_output_buffer_space( size );
  504. memcpy( output_buffer + output_buffer_pos, data, size );
  505. output_buffer_pos += size;
  506. }
  507. static inline void put_byte( unsigned char val )
  508. {
  509. check_output_buffer_space( 1 );
  510. output_buffer[output_buffer_pos++] = val;
  511. }
  512. static inline void put_word( unsigned short val )
  513. {
  514. check_output_buffer_space( 2 );
  515. output_buffer[output_buffer_pos++] = val;
  516. output_buffer[output_buffer_pos++] = val >> 8;
  517. }
  518. static inline void put_dword( unsigned int val )
  519. {
  520. check_output_buffer_space( 4 );
  521. output_buffer[output_buffer_pos++] = val;
  522. output_buffer[output_buffer_pos++] = val >> 8;
  523. output_buffer[output_buffer_pos++] = val >> 16;
  524. output_buffer[output_buffer_pos++] = val >> 24;
  525. }
  526. static inline void put_qword( unsigned int val )
  527. {
  528. put_dword( val );
  529. put_dword( 0 );
  530. }
  531. static inline void align_output( unsigned int align )
  532. {
  533. size_t size = align - (output_buffer_pos % align);
  534. if (size == align) return;
  535. check_output_buffer_space( size );
  536. memset( output_buffer + output_buffer_pos, 0, size );
  537. output_buffer_pos += size;
  538. }
  539. static inline void flush_output_buffer( const char *name )
  540. {
  541. int fd = open( name, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666 );
  542. if (fd == -1 || write( fd, output_buffer, output_buffer_pos ) != output_buffer_pos)
  543. {
  544. perror( name );
  545. exit(1);
  546. }
  547. close( fd );
  548. free( output_buffer );
  549. }
  550. /* command-line option parsing */
  551. /* partly based on the Glibc getopt() implementation */
  552. struct long_option
  553. {
  554. const char *name;
  555. int has_arg;
  556. int val;
  557. };
  558. static inline struct strarray parse_options( int argc, char **argv, const char *short_opts,
  559. const struct long_option *long_opts, int long_only,
  560. void (*callback)( int, char* ) )
  561. {
  562. struct strarray ret = empty_strarray;
  563. const char *flag;
  564. char *start, *end;
  565. int i;
  566. #define OPT_ERR(fmt) { callback( '?', strmake( fmt, argv[1] )); continue; }
  567. for (i = 1; i < argc; i++)
  568. {
  569. if (argv[i][0] != '-' || !argv[i][1]) /* not an option */
  570. {
  571. strarray_add( &ret, argv[i] );
  572. continue;
  573. }
  574. if (!strcmp( argv[i], "--" ))
  575. {
  576. /* add remaining args */
  577. while (++i < argc) strarray_add( &ret, argv[i] );
  578. break;
  579. }
  580. start = argv[i] + 1 + (argv[i][1] == '-');
  581. if (argv[i][1] == '-' || (long_only && (argv[i][2] || !strchr( short_opts, argv[i][1] ))))
  582. {
  583. /* handle long option */
  584. const struct long_option *opt, *found = NULL;
  585. int count = 0;
  586. if (!(end = strchr( start, '=' ))) end = start + strlen(start);
  587. for (opt = long_opts; opt && opt->name; opt++)
  588. {
  589. if (strncmp( opt->name, start, end - start )) continue;
  590. if (!opt->name[end - start]) /* exact match */
  591. {
  592. found = opt;
  593. count = 1;
  594. break;
  595. }
  596. if (!found)
  597. {
  598. found = opt;
  599. count++;
  600. }
  601. else if (long_only || found->has_arg != opt->has_arg || found->val != opt->val)
  602. {
  603. count++;
  604. }
  605. }
  606. if (count > 1) OPT_ERR( "option '%s' is ambiguous" );
  607. if (found)
  608. {
  609. if (*end)
  610. {
  611. if (!found->has_arg) OPT_ERR( "argument not allowed in '%s'" );
  612. end++; /* skip '=' */
  613. }
  614. else if (found->has_arg == 1)
  615. {
  616. if (i == argc - 1) OPT_ERR( "option '%s' requires an argument" );
  617. end = argv[++i];
  618. }
  619. else end = NULL;
  620. callback( found->val, end );
  621. continue;
  622. }
  623. if (argv[i][1] == '-' || !long_only || !strchr( short_opts, argv[i][1] ))
  624. OPT_ERR( "unrecognized option '%s'" );
  625. }
  626. /* handle short option */
  627. for ( ; *start; start++)
  628. {
  629. if (!(flag = strchr( short_opts, *start ))) OPT_ERR( "invalid option '%s'" );
  630. if (flag[1] == ':')
  631. {
  632. end = start + 1;
  633. if (!*end) end = NULL;
  634. if (flag[2] != ':' && !end)
  635. {
  636. if (i == argc - 1) OPT_ERR( "option '%s' requires an argument" );
  637. end = argv[++i];
  638. }
  639. callback( *start, end );
  640. break;
  641. }
  642. callback( *start, NULL );
  643. }
  644. }
  645. return ret;
  646. #undef OPT_ERR
  647. }
  648. #endif /* __WINE_TOOLS_H */