Release 940518
[wine] / windows / utility.c
1 /*      utility.c       Utility functions for Wine
2  *                      Author:         acb
3  *                      Commenced:      10-9-1993
4  *
5  *                      This unit contains the implementations of
6  *                      various Windows API functions that perform
7  *                      utility tasks; i.e., that do not fit into
8  *                      any major category but perform useful tasks.
9  */
10
11 #include <stdio.h>
12 #include <stdarg.h>
13 #include <ctype.h>
14 #include <stdlib.h>
15 #include "windows.h"
16
17 static char Copyright[] = "Copyright Andrew C. Bulhak, 1993";
18
19 /*#define debug_utility*/
20
21 /*      MulDiv is a simple function that may as well have been
22  *      implemented as a macro; however Microsoft, in their infinite
23  *      wisdom, have implemented it as a DLL function and therefore
24  *      so should we. 
25  *      Basically, it takes two 16-bit integers, multiplies them
26  *      and divides by a third integer.
27  */
28
29 int MulDiv(int foo, int bar, int baz)
30 {
31         return (long)(((int)foo*bar)/baz);
32 };
33
34 /*      UTILITY_strip015() removes \015 (^M, CR) from a string;
35  *      this is done to convert a MS-DOS-style string to a more
36  *      UNIX-friendly format. Replacement is done in-place.
37  */
38
39 void UTILITY_strip015(char *dest) {
40         char *src = dest;
41
42         while(*src) {
43                 while(*src == '\015') src++;    /* Skip \015s */
44                 while((*src) && (*src != '\015')) *(dest++) = *(src++);
45         };
46         *dest = '\0';   /* Add null terminator */
47 };
48
49 /**********************************************************************
50  *                                      DebugPrintString
51  */
52 int
53 DebugPrintString(char *str)
54 {
55     fprintf(stderr, "%s", str);
56     return 0;
57 }
58
59 /*
60  *      OutputDebugString strips CRs from its (string) parameter and
61  *      calls DebugPrintString(), which was written by someone else. 
62  *      Since this is part of the standard Windows API, it needs no 
63  *      references to nonstandard DLLs.
64  */
65
66 void OutputDebugString(LPSTR foo)
67 {
68         UTILITY_strip015(foo);
69         DebugPrintString(foo);
70 };
71
72 /*      UTILITY_qualify(source, dest) takes the format string source and
73  *      changes all the parameters to correspond to Linux integer sizes
74  *      rather than Windows sizes. For example, it converts %i to %hi
75  *      and %lx to %x. No array size checking is done at present.
76  */
77
78 static void UTILITY_qualify(const char *source, char *dest)
79 {
80 #ifdef debug_utility
81         fprintf(stderr, "UTILITY_qualify(\"%s\", \"%s\");\n", source, dest);
82 #endif
83         if(!source) return;     /* Dumbass attack! */
84         while(*source) {
85                 /* Find next format code. */
86                 while((*source != '%') && (*source)) {
87                         *(dest++) = *(source++);
88                 }
89                 /* Yeah, I know I shouldn't use gotos.... */
90                 if (!(*source)) goto loop_end;
91                 /* skip the '%' */
92                 *(dest++) = *(source++);
93                 /* Now insert a size qualifier, if needed. */
94                 switch(*source) {
95                         case 'i':
96                         case 'd':
97                         case 'x':
98                         case 'X':
99                         case 'u':
100                         case 'o':
101                                 /* We have a 16-bit value here. */
102                                 *(dest++) = 'h';
103                                 break;
104                 };
105                 /* Here we go 'round the mulberry bush... */
106 loop_end:
107         };
108         *dest = '\0';
109 };
110
111 /*      UTILITY_argsize() evaluates the size of the argument list that
112  *      accompanies a vsprintf() or wvsprintf() call.
113  *      Arguments:
114  *              char *format;   printf-style format string.
115  *              BOOL windows;   if this is TRUE, we assume that ints are
116  *                              16 bits in size; otherwise we deal with
117  *                              32-bit variables.
118  *      Returns:
119  *              size (in bytes) of the arguments that follow the call.
120  */
121
122 size_t UTILITY_argsize(const char *format, BOOL windows)
123 {
124         size_t size = 0;
125
126 #define INT_SIZE (windows ? 2 : 4)
127
128         while(*format) {
129                 while((*format) && (*format != '%')) format++;  /* skip ahead */
130                 if(*format) {
131                         char modifier = ' ';
132 #ifdef debug_utility
133                         fprintf(stderr, "found:\t\"%%");
134 #endif
135                         format++;               /* skip past '%' */
136                         /* First skip the flags, field width, etc. */
137                         /* First the flags */
138                         if ((*format == '#') || (*format == '-') || (*format == '+')
139                                 || (*format == ' ')) {
140 #ifdef debug_utility
141                                 fprintf(stderr, "%c", *format);
142 #endif
143                                 format++;
144                         }
145                         /* Now the field width, etc. */
146                         while(isdigit(*format)) {
147 #ifdef debug_utility
148                                 fprintf(stderr, "%c", *format);
149 #endif
150                                 format++;
151                         }
152                         if(*format == '.') {
153 #ifdef debug_utility
154                                 fprintf(stderr, "%c", *format);
155 #endif
156                                 format++;
157                         }
158                         while(isdigit(*format)) {
159 #ifdef debug_utility
160                                 fprintf(stderr, "%c", *format);
161 #endif
162                                 format++;
163                         }
164                         /* Now we handle the rest */
165                         if((*format == 'h') || (*format == 'l') || (*format == 'L')) {
166 #ifdef debug_utility
167                                 fprintf(stderr, "%c", modifier);
168 #endif
169                                 modifier = *(format++);
170                         }
171                         /* Handle the actual type. */
172 #ifdef debug_utility
173                                 fprintf(stderr, "%c\"\n", *format);
174 #endif
175                         switch(*format) {
176                                 case 'd':
177                                 case 'i':
178                                 case 'o':
179                                 case 'x':
180                                 case 'X':
181                                 case 'u':
182                                 case 'c':
183                                         size += ((modifier == 'l') ? 4 : INT_SIZE);
184                                         break;
185                                 case 's': size += sizeof(char *); break;
186                                 case 'e':
187                                 case 'E':
188                                 case 'f':
189                                 case 'g':
190                                 case 'G':
191                                         /* It doesn't look as if Windows' wvsprintf()
192                                            supports floating-point arguments. However,
193                                            I'll leave this code here just in case. */
194                                         size += (modifier == 'L') ? sizeof(long double) : sizeof(double);
195                                         break;
196                                 case 'p': size += sizeof(void *); break;
197                                 case 'n': size += sizeof(int *); break;
198                         };
199                 };
200         };
201 #undef INT_SIZE
202 #ifdef debug_utility
203         fprintf(stderr, "UTILITY_argsize: returning %i\n", size);
204 #endif
205         return size;
206 };
207
208 /*      UTILITY_convertArgs() creates a 32-bit argument list from a 16-bit list.
209  *      This is used to allow wvsprintf() arguments to be fed through 
210  *      vsprintf().
211  *
212  *      Arguments:
213  *              char *fmt;      format string
214  *              char *winarg;   Windows-style arguments
215  *      
216  *      Returns:
217  *              malloc()ed pointer to new argument list. This should
218  *              be free()d as soon as it is finished with.
219  */
220
221 char *UTILITY_convertArgs(char *format, char *winarg)
222 {
223         char *result = (char *)malloc(UTILITY_argsize(format, 0));
224         char *rptr = result;
225
226         while(*format) {
227                 while((*format) && (*format != '%')) format++;  /* skip ahead */
228                 if(*format) {
229                         char modifier = ' ';
230 #ifdef debug_utility
231                         fprintf(stderr, "found:\t\"%%");
232 #endif
233                         format++;               /* skip past '%' */
234                         /* First skip the flags, field width, etc. */
235                         /* First the flags */
236                         if ((*format == '#') || (*format == '-') || (*format == '+')
237                                 || (*format == ' ')) format++;
238                         /* Now the field width, etc. */
239                         while(isdigit(*format)) format++;
240                         if(*format == '.') format++;
241                         while(isdigit(*format)) format++;
242                         /* Now we handle the rest */
243                         if((*format == 'h') || (*format == 'l') || (*format == 'L'))
244                                 modifier = *(format++);
245                         /* Handle the actual type. */
246 #ifdef debug_utility
247                                 fprintf(stderr, "%c\"\n", *format);
248 #endif
249                         switch(*format) {
250                                 case 'd':
251                                 case 'i':
252                                         *(((int *)rptr)++) = (modifier=='l') ? *(((int *)winarg)++) : *(((short *)winarg)++);
253                                         break;
254                                 case 'o':
255                                 case 'x':
256                                 case 'X':
257                                 case 'u':
258                                 case 'c':
259                                         *(((unsigned int *)rptr)++) = (modifier=='l') ? *(((unsigned int *)winarg)++) 
260                                                 : *(((unsigned short *)winarg)++);
261                                         break;
262                                 case 's':
263                                 case 'p':
264                                 case 'n':       /* A pointer, is a pointer, is a pointer... */
265                                         *(((char **)rptr)++) = *(((char **)winarg)++);
266                                         break;
267                                 case 'e':
268                                 case 'E':
269                                 case 'f':
270                                 case 'g':
271                                 case 'G':
272                                         /* It doesn't look as if Windows' wvsprintf()
273                                            supports floating-point arguments. However,
274                                            I'll leave this code here just in case. */
275                                         if(modifier=='L')
276                                                 *(((long double *)rptr)++) = *(((long double *)winarg)++);
277                                         else *(((double *)rptr)++) = *(((double *)winarg)++);
278                                         break;
279                         }
280                 }
281         }
282         return result;
283 };
284
285 #ifndef WINELIB
286 INT windows_wsprintf(BYTE *win_stack)
287 {
288         LPSTR lpOutput, lpFormat;
289         BYTE *new_stack, *stack_ptr, *ptr;
290         int stacklength, result;
291
292         lpOutput = (LPSTR) *(DWORD*)win_stack;
293         win_stack += 4;
294         lpFormat = (LPSTR) *(DWORD*)win_stack;
295         win_stack += 4;
296
297         /* determine # of bytes pushed on 16-bit stack by checking printf's
298            format string */
299         
300         ptr = lpFormat;
301         stacklength = 0;
302         do {
303                 if (*ptr++ != '%')
304                         continue;
305
306                 /* skip width/precision */
307                 while ( *ptr == '-' || *ptr == '+' || *ptr == '.' ||
308                         *ptr == ' ' || isdigit(*ptr))
309                         ptr++;
310
311                 switch(*ptr++) {
312                         case 'l': ptr++; /* skip next type character */
313                                 stacklength += 4;
314                                 continue;
315                         case 's':
316                                 stacklength += 4;
317                                 continue;
318                         case 'c':
319                         case 'd':
320                         case 'i':
321                         case 'u':
322                         case 'x':
323                         case 'X':
324                                 stacklength += 2;
325                                 continue;
326                         default:
327                                 fprintf(stderr, "wsprintf: oops, unknown formattype `%c' used!\n", *ptr);
328                 }
329         } while (*ptr);
330
331         /* create 32-bit stack for libc's vsprintf() */
332
333         new_stack = malloc(2 * stacklength); 
334         stack_ptr = new_stack + 2 * stacklength;
335         win_stack += stacklength;
336         ptr  = lpFormat;
337         do {
338                 if (*ptr++ != '%')
339                         continue;
340
341                 /* skip width/precision */
342                 while ( *ptr == '-' || *ptr == '+' || *ptr == '.' ||
343                         *ptr == ' ' || isdigit(*ptr))
344                         ptr++;
345                         
346                 switch(*ptr++) {
347                         case 's':
348                                 stack_ptr -= 4;
349                                 win_stack -= 4;
350                                 *(DWORD*)stack_ptr = *(DWORD*)win_stack;
351                                 continue;
352                         case 'l':
353                                 stack_ptr -= 4;
354                                 win_stack -= 4;
355                                 *(DWORD*)stack_ptr = *(DWORD*)win_stack;
356                                 ptr++; /* skip next type character */
357                                 continue;
358                         case 'c':
359                                 stack_ptr -= 4;
360                                 win_stack -= 2;
361         
362 /* windows' wsprintf() %c ignores 0's, we replace 0 with 1 to make sure
363    that the remaining part of the string isn't ignored by the winapp */
364                                 
365                                 if (*(WORD*)win_stack)
366                                         *(DWORD*)stack_ptr = *(WORD*)win_stack;
367                                 else
368                                         *(DWORD*)stack_ptr = 1;
369                                 continue;
370                         case 'd':
371                         case 'i':
372                                 stack_ptr -= 4;
373                                 win_stack -= 2;
374                                 *(int*)stack_ptr = *(INT*)win_stack;
375                                 continue;
376                         case 'u':
377                         case 'x':
378                         case 'X':
379                                 stack_ptr -= 4;
380                                 win_stack -= 2;
381                                 *(DWORD*)stack_ptr = *(WORD*)win_stack;
382                                 continue;
383                         default:
384                                 stack_ptr -= 4;
385                                 win_stack -= 4;
386                                 *(DWORD*)stack_ptr = 0;
387                                 fprintf(stderr, "wsprintf: oops, unknown formattype %c used!\n", *ptr);
388                 }
389         } while (*ptr);
390
391         result = vsprintf(lpOutput, lpFormat, stack_ptr);
392         free(new_stack);
393
394         return result;
395 }
396 #endif
397
398 /**************************************************************************
399  *                wsprintf        [USER.420] (not used by relay)
400  */
401 int wsprintf(LPSTR lpOutput, LPSTR lpFormat, ...)
402 {
403         va_list valist;
404         int ArgCnt;
405
406         va_start(valist, lpFormat);
407         ArgCnt = vsprintf(lpOutput, lpFormat, valist);
408         va_end(valist);
409
410         return ArgCnt;
411 }
412
413
414 /*      wvsprintf() is an implementation of vsprintf(). This
415  *      implementation converts the arguments to 32-bit integers and
416  *      calls the standard library function vsprintf().
417  *
418  *      Known shortcomings:
419  *              wvsprintf() doesn't yet convert the arguments back after
420  *              calling vsprintf(), so if Windows implements %n and a
421  *              program depends on it, we're in trouble.
422  */
423
424 int wvsprintf(LPSTR buf, LPSTR format, LPSTR args)
425 {
426         char qualified_fmt[1536];
427         char *newargs;
428         int result;
429
430         /* 1.5K is a safe value as wvsprintf can only handle buffers up to
431         1K and in a worst case such a buffer would look like "%i%i%i..." */
432
433         if(!buf || !format) return 0;
434
435         /* Change the format string so that ints are handled as short by
436            default */
437
438         /* Convert agruments to 32-bit values */
439         newargs = UTILITY_convertArgs(format, args);
440         result = vsprintf(buf, qualified_fmt, newargs);
441
442         free(newargs);
443         return result;
444 };