msi: Set or override a user environment string when there is no prefix.
[wine] / tools / winebuild / utils.c
1 /*
2  * Small utility functions for winebuild
3  *
4  * Copyright 2000 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
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <assert.h>
25 #include <ctype.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #ifdef HAVE_UNISTD_H
31 # include <unistd.h>
32 #endif
33 #ifdef HAVE_SYS_STAT_H
34 # include <sys/stat.h>
35 #endif
36 #ifdef HAVE_SYS_MMAN_H
37 #include <sys/mman.h>
38 #endif
39
40 #include "build.h"
41
42 #define MAX_TMP_FILES 8
43 static const char *tmp_files[MAX_TMP_FILES];
44 static unsigned int nb_tmp_files;
45
46 static const struct
47 {
48     const char *name;
49     enum target_cpu cpu;
50 } cpu_names[] =
51 {
52     { "i386",    CPU_x86 },
53     { "i486",    CPU_x86 },
54     { "i586",    CPU_x86 },
55     { "i686",    CPU_x86 },
56     { "i786",    CPU_x86 },
57     { "x86_64",  CPU_x86_64 },
58     { "sparc",   CPU_SPARC },
59     { "alpha",   CPU_ALPHA },
60     { "powerpc", CPU_POWERPC }
61 };
62
63 /* atexit handler to clean tmp files */
64 static void cleanup_tmp_files(void)
65 {
66     unsigned int i;
67     for (i = 0; i < MAX_TMP_FILES; i++) if (tmp_files[i]) unlink( tmp_files[i] );
68 }
69
70
71 void *xmalloc (size_t size)
72 {
73     void *res;
74
75     res = malloc (size ? size : 1);
76     if (res == NULL)
77     {
78         fprintf (stderr, "Virtual memory exhausted.\n");
79         exit (1);
80     }
81     return res;
82 }
83
84 void *xrealloc (void *ptr, size_t size)
85 {
86     void *res = realloc (ptr, size);
87     if (size && res == NULL)
88     {
89         fprintf (stderr, "Virtual memory exhausted.\n");
90         exit (1);
91     }
92     return res;
93 }
94
95 char *xstrdup( const char *str )
96 {
97     char *res = strdup( str );
98     if (!res)
99     {
100         fprintf (stderr, "Virtual memory exhausted.\n");
101         exit (1);
102     }
103     return res;
104 }
105
106 char *strupper(char *s)
107 {
108     char *p;
109     for (p = s; *p; p++) *p = toupper(*p);
110     return s;
111 }
112
113 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) == 0;
118 }
119
120 void fatal_error( const char *msg, ... )
121 {
122     va_list valist;
123     va_start( valist, msg );
124     if (input_file_name)
125     {
126         fprintf( stderr, "%s:", input_file_name );
127         if (current_line)
128             fprintf( stderr, "%d:", current_line );
129         fputc( ' ', stderr );
130     }
131     else fprintf( stderr, "winebuild: " );
132     vfprintf( stderr, msg, valist );
133     va_end( valist );
134     exit(1);
135 }
136
137 void fatal_perror( const char *msg, ... )
138 {
139     va_list valist;
140     va_start( valist, msg );
141     if (input_file_name)
142     {
143         fprintf( stderr, "%s:", input_file_name );
144         if (current_line)
145             fprintf( stderr, "%d:", current_line );
146         fputc( ' ', stderr );
147     }
148     vfprintf( stderr, msg, valist );
149     perror( " " );
150     va_end( valist );
151     exit(1);
152 }
153
154 void error( const char *msg, ... )
155 {
156     va_list valist;
157     va_start( valist, msg );
158     if (input_file_name)
159     {
160         fprintf( stderr, "%s:", input_file_name );
161         if (current_line)
162             fprintf( stderr, "%d:", current_line );
163         fputc( ' ', stderr );
164     }
165     vfprintf( stderr, msg, valist );
166     va_end( valist );
167     nb_errors++;
168 }
169
170 void warning( const char *msg, ... )
171 {
172     va_list valist;
173
174     if (!display_warnings) return;
175     va_start( valist, msg );
176     if (input_file_name)
177     {
178         fprintf( stderr, "%s:", input_file_name );
179         if (current_line)
180             fprintf( stderr, "%d:", current_line );
181         fputc( ' ', stderr );
182     }
183     fprintf( stderr, "warning: " );
184     vfprintf( stderr, msg, valist );
185     va_end( valist );
186 }
187
188 int output( const char *format, ... )
189 {
190     int ret;
191     va_list valist;
192
193     va_start( valist, format );
194     ret = vfprintf( output_file, format, valist );
195     va_end( valist );
196     if (ret < 0) fatal_perror( "Output error" );
197     return ret;
198 }
199
200 /* find a build tool in the path, trying the various names */
201 static char *find_tool( const char * const *names )
202 {
203     static char **dirs;
204     static unsigned int count, maxlen;
205
206     char *p, *file;
207     unsigned int i, len;
208     struct stat st;
209
210     if (!dirs)
211     {
212         char *path;
213
214         /* split the path in directories */
215
216         if (!getenv( "PATH" )) return NULL;
217         path = xstrdup( getenv( "PATH" ));
218         for (p = path, count = 2; *p; p++) if (*p == ':') count++;
219         dirs = xmalloc( count * sizeof(*dirs) );
220         count = 0;
221         dirs[count++] = p = path;
222         while (*p)
223         {
224             while (*p && *p != ':') p++;
225             if (!*p) break;
226             *p++ = 0;
227             dirs[count++] = p;
228         }
229         for (i = 0; i < count; i++) maxlen = max( maxlen, strlen(dirs[i])+2 );
230     }
231
232     while (*names)
233     {
234         len = strlen(*names) + sizeof(EXEEXT) + 1;
235         file = xmalloc( maxlen + len );
236
237         for (i = 0; i < count; i++)
238         {
239             strcpy( file, dirs[i] );
240             p = file + strlen(file);
241             if (p == file) *p++ = '.';
242             if (p[-1] != '/') *p++ = '/';
243             strcpy( p, *names );
244             strcat( p, EXEEXT );
245
246             if (!stat( file, &st ) && S_ISREG(st.st_mode) && (st.st_mode & 0111)) return file;
247         }
248         free( file );
249         names++;
250     }
251     return NULL;
252 }
253
254 const char *get_as_command(void)
255 {
256     if (!as_command)
257     {
258         if (target_alias)
259         {
260             as_command = xmalloc( strlen(target_alias) + sizeof("-as") );
261             strcpy( as_command, target_alias );
262             strcat( as_command, "-as" );
263         }
264         else
265         {
266             static const char * const commands[] = { "gas", "as", NULL };
267             if (!(as_command = find_tool( commands ))) as_command = xstrdup("as");
268         }
269
270         if (force_pointer_size)
271         {
272             const char *args = (target_platform == PLATFORM_APPLE) ?
273                 ((force_pointer_size == 8) ? " -arch x86_64" : " -arch i386") :
274                 ((force_pointer_size == 8) ? " --64" : " --32");
275             as_command = xrealloc( as_command, strlen(as_command) + strlen(args) + 1 );
276             strcat( as_command, args );
277         }
278     }
279     return as_command;
280 }
281
282 const char *get_ld_command(void)
283 {
284     if (!ld_command)
285     {
286         if (target_alias)
287         {
288             ld_command = xmalloc( strlen(target_alias) + sizeof("-ld") );
289             strcpy( ld_command, target_alias );
290             strcat( ld_command, "-ld" );
291         }
292         else
293         {
294             static const char * const commands[] = { "ld", "gld", NULL };
295             if (!(ld_command = find_tool( commands ))) ld_command = xstrdup("ld");
296         }
297
298         if (force_pointer_size)
299         {
300             const char *args = (target_platform == PLATFORM_APPLE) ?
301                 ((force_pointer_size == 8) ? " -arch x86_64" : " -arch i386") :
302                 ((force_pointer_size == 8) ? " -m elf_x86_64" : " -m elf_i386");
303             ld_command = xrealloc( ld_command, strlen(ld_command) + strlen(args) + 1 );
304             strcat( ld_command, args );
305         }
306     }
307     return ld_command;
308 }
309
310 const char *get_nm_command(void)
311 {
312     if (!nm_command)
313     {
314         if (target_alias)
315         {
316             nm_command = xmalloc( strlen(target_alias) + sizeof("-nm") );
317             strcpy( nm_command, target_alias );
318             strcat( nm_command, "-nm" );
319         }
320         else
321         {
322             static const char * const commands[] = { "nm", "gnm", NULL };
323             if (!(nm_command = find_tool( commands ))) nm_command = xstrdup("nm");
324         }
325     }
326     return nm_command;
327 }
328
329 const char *get_windres_command(void)
330 {
331     static char *windres_command;
332
333     if (!windres_command)
334     {
335         if (target_alias)
336         {
337             windres_command = xmalloc( strlen(target_alias) + sizeof("-windres") );
338             strcpy( windres_command, target_alias );
339             strcat( windres_command, "-windres" );
340         }
341         else
342         {
343             static const char * const commands[] = { "windres", NULL };
344             if (!(windres_command = find_tool( commands ))) windres_command = xstrdup("windres");
345         }
346     }
347     return windres_command;
348 }
349
350 /* get a name for a temp file, automatically cleaned up on exit */
351 char *get_temp_file_name( const char *prefix, const char *suffix )
352 {
353     char *name;
354     const char *ext;
355     int fd;
356
357     assert( nb_tmp_files < MAX_TMP_FILES );
358     if (!nb_tmp_files && !save_temps) atexit( cleanup_tmp_files );
359
360     if (!prefix || !prefix[0]) prefix = "winebuild";
361     if (!suffix) suffix = "";
362     if (!(ext = strchr( prefix, '.' ))) ext = prefix + strlen(prefix);
363     name = xmalloc( sizeof("/tmp/") + (ext - prefix) + sizeof(".XXXXXX") + strlen(suffix) );
364     strcpy( name, "/tmp/" );
365     memcpy( name + 5, prefix, ext - prefix );
366     strcpy( name + 5 + (ext - prefix), ".XXXXXX" );
367     strcat( name, suffix );
368
369     /* first try without the /tmp/ prefix */
370     if ((fd = mkstemps( name + 5, strlen(suffix) )) != -1)
371         name += 5;
372     else if ((fd = mkstemps( name, strlen(suffix) )) == -1)
373         fatal_error( "could not generate a temp file\n" );
374
375     close( fd );
376     tmp_files[nb_tmp_files++] = name;
377     return name;
378 }
379
380 /*******************************************************************
381  *         buffer management
382  *
383  * Function for reading from/writing to a memory buffer.
384  */
385
386 int byte_swapped = 0;
387 const char *input_buffer_filename;
388 const unsigned char *input_buffer;
389 size_t input_buffer_pos;
390 size_t input_buffer_size;
391 unsigned char *output_buffer;
392 size_t output_buffer_pos;
393 size_t output_buffer_size;
394
395 static void check_output_buffer_space( size_t size )
396 {
397     if (output_buffer_pos + size >= output_buffer_size)
398     {
399         output_buffer_size = max( output_buffer_size * 2, output_buffer_pos + size );
400         output_buffer = xrealloc( output_buffer, output_buffer_size );
401     }
402 }
403
404 void init_input_buffer( const char *file )
405 {
406     int fd;
407     struct stat st;
408
409     if ((fd = open( file, O_RDONLY | O_BINARY )) == -1) fatal_perror( "Cannot open %s", file );
410     if ((fstat( fd, &st ) == -1)) fatal_perror( "Cannot stat %s", file );
411     if (!st.st_size) fatal_error( "%s is an empty file\n", file );
412 #ifdef  HAVE_MMAP
413     if ((input_buffer = mmap( NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0 )) == (void*)-1)
414 #endif
415     {
416         unsigned char *buffer = xmalloc( st.st_size );
417         if (read( fd, buffer, st.st_size ) != st.st_size) fatal_error( "Cannot read %s\n", file );
418         input_buffer = buffer;
419     }
420     close( fd );
421     input_buffer_filename = xstrdup( file );
422     input_buffer_size = st.st_size;
423     input_buffer_pos = 0;
424     byte_swapped = 0;
425 }
426
427 void init_output_buffer(void)
428 {
429     output_buffer_size = 1024;
430     output_buffer_pos = 0;
431     output_buffer = xmalloc( output_buffer_size );
432 }
433
434 void flush_output_buffer(void)
435 {
436     if (fwrite( output_buffer, 1, output_buffer_pos, output_file ) != output_buffer_pos)
437         fatal_error( "Error writing to %s\n", output_file_name );
438     free( output_buffer );
439 }
440
441 unsigned char get_byte(void)
442 {
443     if (input_buffer_pos >= input_buffer_size)
444         fatal_error( "%s is a truncated file\n", input_buffer_filename );
445     return input_buffer[input_buffer_pos++];
446 }
447
448 unsigned short get_word(void)
449 {
450     unsigned short ret;
451
452     if (input_buffer_pos + sizeof(ret) > input_buffer_size)
453         fatal_error( "%s is a truncated file\n", input_buffer_filename );
454     memcpy( &ret, input_buffer + input_buffer_pos, sizeof(ret) );
455     if (byte_swapped) ret = (ret << 8) | (ret >> 8);
456     input_buffer_pos += sizeof(ret);
457     return ret;
458 }
459
460 unsigned int get_dword(void)
461 {
462     unsigned int ret;
463
464     if (input_buffer_pos + sizeof(ret) > input_buffer_size)
465         fatal_error( "%s is a truncated file\n", input_buffer_filename );
466     memcpy( &ret, input_buffer + input_buffer_pos, sizeof(ret) );
467     if (byte_swapped)
468         ret = ((ret << 24) | ((ret << 8) & 0x00ff0000) | ((ret >> 8) & 0x0000ff00) | (ret >> 24));
469     input_buffer_pos += sizeof(ret);
470     return ret;
471 }
472
473 void put_data( const void *data, size_t size )
474 {
475     check_output_buffer_space( size );
476     memcpy( output_buffer + output_buffer_pos, data, size );
477     output_buffer_pos += size;
478 }
479
480 void put_byte( unsigned char val )
481 {
482     check_output_buffer_space( 1 );
483     output_buffer[output_buffer_pos++] = val;
484 }
485
486 void put_word( unsigned short val )
487 {
488     if (byte_swapped) val = (val << 8) | (val >> 8);
489     put_data( &val, sizeof(val) );
490 }
491
492 void put_dword( unsigned int val )
493 {
494     if (byte_swapped)
495         val = ((val << 24) | ((val << 8) & 0x00ff0000) | ((val >> 8) & 0x0000ff00) | (val >> 24));
496     put_data( &val, sizeof(val) );
497 }
498
499 void put_qword( unsigned int val )
500 {
501     if (byte_swapped)
502     {
503         put_dword( 0 );
504         put_dword( val );
505     }
506     else
507     {
508         put_dword( val );
509         put_dword( 0 );
510     }
511 }
512
513 /* pointer-sized word */
514 void put_pword( unsigned int val )
515 {
516     if (get_ptr_size() == 8) put_qword( val );
517     else put_dword( val );
518 }
519
520 void align_output( unsigned int align )
521 {
522     size_t size = align - (output_buffer_pos % align);
523
524     if (size == align) return;
525     check_output_buffer_space( size );
526     memset( output_buffer + output_buffer_pos, 0, size );
527     output_buffer_pos += size;
528 }
529
530 /* output a standard header for generated files */
531 void output_standard_file_header(void)
532 {
533     if (spec_file_name)
534         output( "/* File generated automatically from %s; do not edit! */\n", spec_file_name );
535     else
536         output( "/* File generated automatically; do not edit! */\n" );
537     output( "/* This file can be copied, modified and distributed without restriction. */\n\n" );
538 }
539
540 /* dump a byte stream into the assembly code */
541 void dump_bytes( const void *buffer, unsigned int size )
542 {
543     unsigned int i;
544     const unsigned char *ptr = buffer;
545
546     if (!size) return;
547     output( "\t.byte " );
548     for (i = 0; i < size - 1; i++, ptr++)
549     {
550         if ((i % 16) == 15) output( "0x%02x\n\t.byte ", *ptr );
551         else output( "0x%02x,", *ptr );
552     }
553     output( "0x%02x\n", *ptr );
554 }
555
556
557 /*******************************************************************
558  *         open_input_file
559  *
560  * Open a file in the given srcdir and set the input_file_name global variable.
561  */
562 FILE *open_input_file( const char *srcdir, const char *name )
563 {
564     char *fullname;
565     FILE *file = fopen( name, "r" );
566
567     if (!file && srcdir)
568     {
569         fullname = xmalloc( strlen(srcdir) + strlen(name) + 2 );
570         strcpy( fullname, srcdir );
571         strcat( fullname, "/" );
572         strcat( fullname, name );
573         file = fopen( fullname, "r" );
574     }
575     else fullname = xstrdup( name );
576
577     if (!file) fatal_error( "Cannot open file '%s'\n", fullname );
578     input_file_name = fullname;
579     current_line = 1;
580     return file;
581 }
582
583
584 /*******************************************************************
585  *         close_input_file
586  *
587  * Close the current input file (must have been opened with open_input_file).
588  */
589 void close_input_file( FILE *file )
590 {
591     fclose( file );
592     free( input_file_name );
593     input_file_name = NULL;
594     current_line = 0;
595 }
596
597
598 /*******************************************************************
599  *         remove_stdcall_decoration
600  *
601  * Remove a possible @xx suffix from a function name.
602  * Return the numerical value of the suffix, or -1 if none.
603  */
604 int remove_stdcall_decoration( char *name )
605 {
606     char *p, *end = strrchr( name, '@' );
607     if (!end || !end[1] || end == name) return -1;
608     /* make sure all the rest is digits */
609     for (p = end + 1; *p; p++) if (!isdigit(*p)) return -1;
610     *end = 0;
611     return atoi( end + 1 );
612 }
613
614
615 /*******************************************************************
616  *         assemble_file
617  *
618  * Run a file through the assembler.
619  */
620 void assemble_file( const char *src_file, const char *obj_file )
621 {
622     const char *prog = get_as_command();
623     char *cmd;
624     int err;
625
626     cmd = xmalloc( strlen(prog) + strlen(obj_file) + strlen(src_file) + 6 );
627     sprintf( cmd, "%s -o %s %s", prog, obj_file, src_file );
628     if (verbose) fprintf( stderr, "%s\n", cmd );
629     err = system( cmd );
630     if (err) fatal_error( "%s failed with status %d\n", prog, err );
631     free( cmd );
632 }
633
634
635 /*******************************************************************
636  *         alloc_dll_spec
637  *
638  * Create a new dll spec file descriptor
639  */
640 DLLSPEC *alloc_dll_spec(void)
641 {
642     DLLSPEC *spec;
643
644     spec = xmalloc( sizeof(*spec) );
645     spec->file_name          = NULL;
646     spec->dll_name           = NULL;
647     spec->init_func          = NULL;
648     spec->main_module        = NULL;
649     spec->type               = SPEC_WIN32;
650     spec->base               = MAX_ORDINALS;
651     spec->limit              = 0;
652     spec->stack_size         = 0;
653     spec->heap_size          = 0;
654     spec->nb_entry_points    = 0;
655     spec->alloc_entry_points = 0;
656     spec->nb_names           = 0;
657     spec->nb_resources       = 0;
658     spec->characteristics    = IMAGE_FILE_EXECUTABLE_IMAGE;
659     if (get_ptr_size() > 4)
660         spec->characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE;
661     else
662         spec->characteristics |= IMAGE_FILE_32BIT_MACHINE;
663     spec->dll_characteristics = IMAGE_DLLCHARACTERISTICS_NX_COMPAT;
664     spec->subsystem          = 0;
665     spec->subsystem_major    = 4;
666     spec->subsystem_minor    = 0;
667     spec->entry_points       = NULL;
668     spec->names              = NULL;
669     spec->ordinals           = NULL;
670     spec->resources          = NULL;
671     return spec;
672 }
673
674
675 /*******************************************************************
676  *         free_dll_spec
677  *
678  * Free dll spec file descriptor
679  */
680 void free_dll_spec( DLLSPEC *spec )
681 {
682     int i;
683
684     for (i = 0; i < spec->nb_entry_points; i++)
685     {
686         ORDDEF *odp = &spec->entry_points[i];
687         free( odp->name );
688         free( odp->export_name );
689         free( odp->link_name );
690     }
691     free( spec->file_name );
692     free( spec->dll_name );
693     free( spec->init_func );
694     free( spec->entry_points );
695     free( spec->names );
696     free( spec->ordinals );
697     free( spec->resources );
698     free( spec );
699 }
700
701
702 /*******************************************************************
703  *         make_c_identifier
704  *
705  * Map a string to a valid C identifier.
706  */
707 const char *make_c_identifier( const char *str )
708 {
709     static char buffer[256];
710     char *p;
711
712     for (p = buffer; *str && p < buffer+sizeof(buffer)-1; p++, str++)
713     {
714         if (isalnum(*str)) *p = *str;
715         else *p = '_';
716     }
717     *p = 0;
718     return buffer;
719 }
720
721
722 /*******************************************************************
723  *         get_stub_name
724  *
725  * Generate an internal name for a stub entry point.
726  */
727 const char *get_stub_name( const ORDDEF *odp, const DLLSPEC *spec )
728 {
729     static char buffer[256];
730     if (odp->name || odp->export_name)
731     {
732         char *p;
733         sprintf( buffer, "__wine_stub_%s", odp->name ? odp->name : odp->export_name );
734         /* make sure name is a legal C identifier */
735         for (p = buffer; *p; p++) if (!isalnum(*p) && *p != '_') break;
736         if (!*p) return buffer;
737     }
738     sprintf( buffer, "__wine_stub_%s_%d", make_c_identifier(spec->file_name), odp->ordinal );
739     return buffer;
740 }
741
742 /* parse a cpu name and return the corresponding value */
743 enum target_cpu get_cpu_from_name( const char *name )
744 {
745     unsigned int i;
746
747     for (i = 0; i < sizeof(cpu_names)/sizeof(cpu_names[0]); i++)
748         if (!strcmp( cpu_names[i].name, name )) return cpu_names[i].cpu;
749     return -1;
750 }
751
752 /*****************************************************************
753  *  Function:    get_alignment
754  *
755  *  Description:
756  *    According to the info page for gas, the .align directive behaves
757  * differently on different systems.  On some architectures, the
758  * argument of a .align directive is the number of bytes to pad to, so
759  * to align on an 8-byte boundary you'd say
760  *     .align 8
761  * On other systems, the argument is "the number of low-order zero bits
762  * that the location counter must have after advancement."  So to
763  * align on an 8-byte boundary you'd say
764  *     .align 3
765  *
766  * The reason gas is written this way is that it's trying to mimick
767  * native assemblers for the various architectures it runs on.  gas
768  * provides other directives that work consistently across
769  * architectures, but of course we want to work on all arches with or
770  * without gas.  Hence this function.
771  *
772  *
773  *  Parameters:
774  *    align  --  the number of bytes to align to. Must be a power of 2.
775  */
776 unsigned int get_alignment(unsigned int align)
777 {
778     unsigned int n;
779
780     assert( !(align & (align - 1)) );
781
782     switch(target_cpu)
783     {
784     case CPU_x86:
785     case CPU_x86_64:
786     case CPU_SPARC:
787         if (target_platform != PLATFORM_APPLE) return align;
788         /* fall through */
789     case CPU_POWERPC:
790     case CPU_ALPHA:
791         n = 0;
792         while ((1u << n) != align) n++;
793         return n;
794     }
795     /* unreached */
796     assert(0);
797     return 0;
798 }
799
800 /* return the page size for the target CPU */
801 unsigned int get_page_size(void)
802 {
803     switch(target_cpu)
804     {
805     case CPU_x86:     return 4096;
806     case CPU_x86_64:  return 4096;
807     case CPU_POWERPC: return 4096;
808     case CPU_SPARC:   return 8192;
809     case CPU_ALPHA:   return 8192;
810     }
811     /* unreached */
812     assert(0);
813     return 0;
814 }
815
816 /* return the size of a pointer on the target CPU */
817 unsigned int get_ptr_size(void)
818 {
819     switch(target_cpu)
820     {
821     case CPU_x86:
822     case CPU_POWERPC:
823     case CPU_SPARC:
824     case CPU_ALPHA:
825         return 4;
826     case CPU_x86_64:
827         return 8;
828     }
829     /* unreached */
830     assert(0);
831     return 0;
832 }
833
834 /* return the assembly name for a C symbol */
835 const char *asm_name( const char *sym )
836 {
837     static char buffer[256];
838
839     switch (target_platform)
840     {
841     case PLATFORM_APPLE:
842     case PLATFORM_WINDOWS:
843         if (sym[0] == '.' && sym[1] == 'L') return sym;
844         buffer[0] = '_';
845         strcpy( buffer + 1, sym );
846         return buffer;
847     default:
848         return sym;
849     }
850 }
851
852 /* return an assembly function declaration for a C function name */
853 const char *func_declaration( const char *func )
854 {
855     static char buffer[256];
856
857     switch (target_platform)
858     {
859     case PLATFORM_APPLE:
860         return "";
861     case PLATFORM_WINDOWS:
862         sprintf( buffer, ".def _%s; .scl 2; .type 32; .endef", func );
863         break;
864     default:
865         sprintf( buffer, ".type %s,@function", func );
866         break;
867     }
868     return buffer;
869 }
870
871 /* output a size declaration for an assembly function */
872 void output_function_size( const char *name )
873 {
874     switch (target_platform)
875     {
876     case PLATFORM_APPLE:
877     case PLATFORM_WINDOWS:
878         break;
879     default:
880         output( "\t.size %s, .-%s\n", name, name );
881         break;
882     }
883 }
884
885 /* output the GNU note for non-exec stack */
886 void output_gnu_stack_note(void)
887 {
888     switch (target_platform)
889     {
890     case PLATFORM_WINDOWS:
891     case PLATFORM_APPLE:
892         break;
893     default:
894         output( "\t.section .note.GNU-stack,\"\",@progbits\n" );
895         break;
896     }
897 }
898
899 /* return a global symbol declaration for an assembly symbol */
900 const char *asm_globl( const char *func )
901 {
902     static char buffer[256];
903
904     switch (target_platform)
905     {
906     case PLATFORM_APPLE:
907         sprintf( buffer, "\t.globl _%s\n\t.private_extern _%s\n_%s:", func, func, func );
908         return buffer;
909     case PLATFORM_WINDOWS:
910         sprintf( buffer, "\t.globl _%s\n_%s:", func, func );
911         return buffer;
912     default:
913         sprintf( buffer, "\t.globl %s\n\t.hidden %s\n%s:", func, func, func );
914         return buffer;
915     }
916 }
917
918 const char *get_asm_ptr_keyword(void)
919 {
920     switch(get_ptr_size())
921     {
922     case 4: return ".long";
923     case 8: return ".quad";
924     }
925     assert(0);
926     return NULL;
927 }
928
929 const char *get_asm_string_keyword(void)
930 {
931     switch (target_platform)
932     {
933     case PLATFORM_APPLE:
934         return ".asciz";
935     default:
936         return ".string";
937     }
938 }
939
940 const char *get_asm_short_keyword(void)
941 {
942     switch (target_platform)
943     {
944     default:            return ".short";
945     }
946 }
947
948 const char *get_asm_rodata_section(void)
949 {
950     switch (target_platform)
951     {
952     case PLATFORM_APPLE: return ".const";
953     default:             return ".section .rodata";
954     }
955 }
956
957 const char *get_asm_string_section(void)
958 {
959     switch (target_platform)
960     {
961     case PLATFORM_APPLE: return ".cstring";
962     default:             return ".section .rodata";
963     }
964 }