Removed #include of wingdi.h and windef.h from winuser.h (and resolved
[wine] / misc / wsprintf.c
1 /*
2  * wsprintf functions
3  *
4  * Copyright 1996 Alexandre Julliard
5  */
6
7 #include <stdarg.h>
8 #include <string.h>
9 #include <stdio.h>
10 #include "wine/winbase16.h"
11 #include "windef.h"
12 #include "wingdi.h"
13 #include "winuser.h"
14 #include "ldt.h"
15 #include "stackframe.h"
16 #include "module.h"
17 #include "global.h"
18 #include "debugtools.h"
19
20 DEFAULT_DEBUG_CHANNEL(string)
21
22
23 #define WPRINTF_LEFTALIGN   0x0001  /* Align output on the left ('-' prefix) */
24 #define WPRINTF_PREFIX_HEX  0x0002  /* Prefix hex with 0x ('#' prefix) */
25 #define WPRINTF_ZEROPAD     0x0004  /* Pad with zeros ('0' prefix) */
26 #define WPRINTF_LONG        0x0008  /* Long arg ('l' prefix) */
27 #define WPRINTF_SHORT       0x0010  /* Short arg ('h' prefix) */
28 #define WPRINTF_UPPER_HEX   0x0020  /* Upper-case hex ('X' specifier) */
29 #define WPRINTF_WIDE        0x0040  /* Wide arg ('w' prefix) */
30
31 typedef enum
32 {
33     WPR_UNKNOWN,
34     WPR_CHAR,
35     WPR_WCHAR,
36     WPR_STRING,
37     WPR_WSTRING,
38     WPR_SIGNED,
39     WPR_UNSIGNED,
40     WPR_HEXA
41 } WPRINTF_TYPE;
42
43 typedef struct
44 {
45     UINT         flags;
46     UINT         width;
47     UINT         precision;
48     WPRINTF_TYPE   type;
49 } WPRINTF_FORMAT;
50
51 typedef union {
52     WCHAR   wchar_view;
53     CHAR    char_view;
54     LPCSTR  lpcstr_view;
55     LPCWSTR lpcwstr_view;
56     INT     int_view;
57 } WPRINTF_DATA;
58
59 static const CHAR null_stringA[] = "(null)";
60 static const WCHAR null_stringW[] = { '(', 'n', 'u', 'l', 'l', ')', 0 };
61
62 /***********************************************************************
63  *           WPRINTF_ParseFormatA
64  *
65  * Parse a format specification. A format specification has the form:
66  *
67  * [-][#][0][width][.precision]type
68  *
69  * Return value is the length of the format specification in characters.
70  */
71 static INT WPRINTF_ParseFormatA( LPCSTR format, WPRINTF_FORMAT *res )
72 {
73     LPCSTR p = format;
74
75     res->flags = 0;
76     res->width = 0;
77     res->precision = 0;
78     if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; }
79     if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; }
80     if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; }
81     while ((*p >= '0') && (*p <= '9'))  /* width field */
82     {
83         res->width = res->width * 10 + *p - '0';
84         p++;
85     }
86     if (*p == '.')  /* precision field */
87     {
88         p++;
89         while ((*p >= '0') && (*p <= '9'))
90         {
91             res->precision = res->precision * 10 + *p - '0';
92             p++;
93         }
94     }
95     if (*p == 'l') { res->flags |= WPRINTF_LONG; p++; }
96     else if (*p == 'h') { res->flags |= WPRINTF_SHORT; p++; }
97     else if (*p == 'w') { res->flags |= WPRINTF_WIDE; p++; }
98     switch(*p)
99     {
100     case 'c':
101         res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR;
102         break;
103     case 'C':
104         res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR;
105         break;
106     case 'd':
107     case 'i':
108         res->type = WPR_SIGNED;
109         break;
110     case 's':
111         res->type = (res->flags & (WPRINTF_LONG |WPRINTF_WIDE)) 
112                     ? WPR_WSTRING : WPR_STRING;
113         break;
114     case 'S':
115         res->type = (res->flags & (WPRINTF_SHORT|WPRINTF_WIDE))
116                     ? WPR_STRING : WPR_WSTRING;
117         break;
118     case 'u':
119         res->type = WPR_UNSIGNED;
120         break;
121     case 'X':
122         res->flags |= WPRINTF_UPPER_HEX;
123         /* fall through */
124     case 'x':
125         res->type = WPR_HEXA;
126         break;
127     default: /* unknown format char */
128         res->type = WPR_UNKNOWN;
129         p--;  /* print format as normal char */
130         break;
131     }
132     return (INT)(p - format) + 1;
133 }
134
135
136 /***********************************************************************
137  *           WPRINTF_ParseFormatW
138  *
139  * Parse a format specification. A format specification has the form:
140  *
141  * [-][#][0][width][.precision]type
142  *
143  * Return value is the length of the format specification in characters.
144  */
145 static INT WPRINTF_ParseFormatW( LPCWSTR format, WPRINTF_FORMAT *res )
146 {
147     LPCWSTR p = format;
148
149     res->flags = 0;
150     res->width = 0;
151     res->precision = 0;
152     if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; }
153     if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; }
154     if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; }
155     while ((*p >= '0') && (*p <= '9'))  /* width field */
156     {
157         res->width = res->width * 10 + *p - '0';
158         p++;
159     }
160     if (*p == '.')  /* precision field */
161     {
162         p++;
163         while ((*p >= '0') && (*p <= '9'))
164         {
165             res->precision = res->precision * 10 + *p - '0';
166             p++;
167         }
168     }
169     if (*p == 'l') { res->flags |= WPRINTF_LONG; p++; }
170     else if (*p == 'h') { res->flags |= WPRINTF_SHORT; p++; }
171     else if (*p == 'w') { res->flags |= WPRINTF_WIDE; p++; }
172     switch((CHAR)*p)
173     {
174     case 'c':
175         res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR;
176         break;
177     case 'C':
178         res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR;
179         break;
180     case 'd':
181     case 'i':
182         res->type = WPR_SIGNED;
183         break;
184     case 's':
185         res->type = ((res->flags & WPRINTF_SHORT) && !(res->flags & WPRINTF_WIDE)) ? WPR_STRING : WPR_WSTRING;
186         break;
187     case 'S':
188         res->type = (res->flags & (WPRINTF_LONG|WPRINTF_WIDE)) ? WPR_WSTRING : WPR_STRING;
189         break;
190     case 'u':
191         res->type = WPR_UNSIGNED;
192         break;
193     case 'X':
194         res->flags |= WPRINTF_UPPER_HEX;
195         /* fall through */
196     case 'x':
197         res->type = WPR_HEXA;
198         break;
199     default:
200         res->type = WPR_UNKNOWN;
201         p--;  /* print format as normal char */
202         break;
203     }
204     return (INT)(p - format) + 1;
205 }
206
207
208 /***********************************************************************
209  *           WPRINTF_GetLen
210  */
211 static UINT WPRINTF_GetLen( WPRINTF_FORMAT *format, WPRINTF_DATA *arg,
212                               LPSTR number, UINT maxlen )
213 {
214     UINT len;
215
216     if (format->flags & WPRINTF_LEFTALIGN) format->flags &= ~WPRINTF_ZEROPAD;
217     if (format->width > maxlen) format->width = maxlen;
218     switch(format->type)
219     {
220     case WPR_CHAR:
221     case WPR_WCHAR:
222         return (format->precision = 1);
223     case WPR_STRING:
224         if (!arg->lpcstr_view) arg->lpcstr_view = null_stringA;
225         for (len = 0; !format->precision || (len < format->precision); len++)
226             if (!*(arg->lpcstr_view + len)) break;
227         if (len > maxlen) len = maxlen;
228         return (format->precision = len);
229     case WPR_WSTRING:
230         if (!arg->lpcwstr_view) arg->lpcwstr_view = null_stringW;
231         for (len = 0; !format->precision || (len < format->precision); len++)
232             if (!*(arg->lpcwstr_view + len)) break;
233         if (len > maxlen) len = maxlen;
234         return (format->precision = len);
235     case WPR_SIGNED:
236         len = sprintf( number, "%d", arg->int_view );
237         break;
238     case WPR_UNSIGNED:
239         len = sprintf( number, "%u", (UINT)arg->int_view );
240         break;
241     case WPR_HEXA:
242         len = sprintf( number,
243                        (format->flags & WPRINTF_UPPER_HEX) ? "%X" : "%x",
244                        (UINT)arg->int_view);
245         break;
246     default:
247         return 0;
248     }
249     if (len > maxlen) len = maxlen;
250     if (format->precision < len) format->precision = len;
251     if (format->precision > maxlen) format->precision = maxlen;
252     if ((format->flags & WPRINTF_ZEROPAD) && (format->width > format->precision))
253         format->precision = format->width;
254     if (format->flags & WPRINTF_PREFIX_HEX) len += 2;
255     return len;
256 }
257
258 /***********************************************************************
259  *           WPRINTF_ExtractVAPtr (Not a Windows API)
260  */
261 static WPRINTF_DATA WPRINTF_ExtractVAPtr( WPRINTF_FORMAT *format, va_list* args )
262 {
263     WPRINTF_DATA result;
264     switch(format->type)
265     {
266         case WPR_WCHAR:
267             result.wchar_view = va_arg( *args, WCHAR );     break;
268         case WPR_CHAR:
269             result.char_view = va_arg( *args, CHAR );       break;
270         case WPR_STRING:
271             result.lpcstr_view = va_arg( *args, LPCSTR);    break;
272         case WPR_WSTRING:
273             result.lpcwstr_view = va_arg( *args, LPCWSTR);  break;
274         case WPR_HEXA:
275         case WPR_SIGNED:
276         case WPR_UNSIGNED:
277             result.int_view = va_arg( *args, INT );         break;
278         default:
279             result.wchar_view = 0;                          break;
280     }
281     return result;
282 }
283
284 /***********************************************************************
285  *           wvsnprintf16   (Not a Windows API)
286  */
287 INT16 WINAPI wvsnprintf16( LPSTR buffer, UINT16 maxlen, LPCSTR spec,
288                            LPCVOID args )
289 {
290     WPRINTF_FORMAT format;
291     LPSTR p = buffer;
292     UINT i, len;
293     CHAR number[20];
294     WPRINTF_DATA cur_arg;
295     SEGPTR seg_str;
296
297     while (*spec && (maxlen > 1))
298     {
299         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
300         spec++;
301         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
302         spec += WPRINTF_ParseFormatA( spec, &format );
303         switch(format.type)
304         {
305         case WPR_WCHAR:  /* No Unicode in Win16 */
306         case WPR_CHAR:
307             cur_arg.char_view = VA_ARG16( args, CHAR );
308             break;
309         case WPR_WSTRING:  /* No Unicode in Win16 */
310         case WPR_STRING:
311             seg_str = VA_ARG16( args, SEGPTR );
312             if (IsBadReadPtr16(seg_str, 1 )) cur_arg.lpcstr_view = "";
313             else cur_arg.lpcstr_view = PTR_SEG_TO_LIN( seg_str );
314             break;
315         case WPR_SIGNED:
316             if (!(format.flags & WPRINTF_LONG))
317             {
318                 cur_arg.int_view = VA_ARG16( args, INT16 );
319                 break;
320             }
321             /* fall through */
322         case WPR_HEXA:
323         case WPR_UNSIGNED:
324             if (format.flags & WPRINTF_LONG)
325                 cur_arg.int_view = VA_ARG16( args, UINT );
326             else
327                 cur_arg.int_view = VA_ARG16( args, UINT16 );
328             break;
329         case WPR_UNKNOWN:
330             continue;
331         }
332         len = WPRINTF_GetLen( &format, &cur_arg, number, maxlen - 1 );
333         if (!(format.flags & WPRINTF_LEFTALIGN))
334             for (i = format.precision; i < format.width; i++, maxlen--)
335                 *p++ = ' ';
336         switch(format.type)
337         {
338         case WPR_WCHAR:  /* No Unicode in Win16 */
339         case WPR_CHAR:
340             *p= cur_arg.char_view;
341             if (*p != '\0') p++;
342             else if (format.width > 1) *p++ = ' ';
343             else len = 0;
344             break;
345         case WPR_WSTRING:  /* No Unicode in Win16 */
346         case WPR_STRING:
347             if (len) memcpy( p, cur_arg.lpcstr_view, len );
348             p += len;
349             break;
350         case WPR_HEXA:
351             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
352             {
353                 *p++ = '0';
354                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
355                 maxlen -= 2;
356                 len -= 2;
357             }
358             /* fall through */
359         case WPR_SIGNED:
360         case WPR_UNSIGNED:
361             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
362             if (len) memcpy( p, number, len );
363             p += len;
364             break;
365         case WPR_UNKNOWN:
366             continue;
367         }
368         if (format.flags & WPRINTF_LEFTALIGN)
369             for (i = format.precision; i < format.width; i++, maxlen--)
370                 *p++ = ' ';
371         maxlen -= len;
372     }
373     *p = 0;
374     return (maxlen > 1) ? (INT)(p - buffer) : -1;
375 }
376
377
378 /***********************************************************************
379  *           wvsnprintfA   (Not a Windows API)
380  */
381 INT WINAPI wvsnprintfA( LPSTR buffer, UINT maxlen, LPCSTR spec,
382                             va_list args )
383 {
384     WPRINTF_FORMAT format;
385     LPSTR p = buffer;
386     UINT i, len;
387     CHAR number[20];
388     WPRINTF_DATA argData;
389
390     while (*spec && (maxlen > 1))
391     {
392         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
393         spec++;
394         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
395         spec += WPRINTF_ParseFormatA( spec, &format );
396         argData = WPRINTF_ExtractVAPtr( &format, &args );
397         len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 );
398         if (!(format.flags & WPRINTF_LEFTALIGN))
399             for (i = format.precision; i < format.width; i++, maxlen--)
400                 *p++ = ' ';
401         switch(format.type)
402         {
403         case WPR_WCHAR:
404             *p = argData.wchar_view;
405             if (*p != '\0') p++;
406             else if (format.width > 1) *p++ = ' ';
407             else len = 0;
408             break;
409         case WPR_CHAR:
410             *p = argData.char_view;
411             if (*p != '\0') p++;
412             else if (format.width > 1) *p++ = ' ';
413             else len = 0;
414             break;
415         case WPR_STRING:
416             memcpy( p, argData.lpcstr_view, len );
417             p += len;
418             break;
419         case WPR_WSTRING:
420             {
421                 LPCWSTR ptr = argData.lpcwstr_view;
422                 for (i = 0; i < len; i++) *p++ = (CHAR)*ptr++;
423             }
424             break;
425         case WPR_HEXA:
426             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
427             {
428                 *p++ = '0';
429                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
430                 maxlen -= 2;
431                 len -= 2;
432             }
433             /* fall through */
434         case WPR_SIGNED:
435         case WPR_UNSIGNED:
436             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
437             memcpy( p, number, len );
438             p += len;
439             /* Go to the next arg */
440             break;
441         case WPR_UNKNOWN:
442             continue;
443         }
444         if (format.flags & WPRINTF_LEFTALIGN)
445             for (i = format.precision; i < format.width; i++, maxlen--)
446                 *p++ = ' ';
447         maxlen -= len;
448     }
449     *p = 0;
450     TRACE("%s\n",buffer);
451     return (maxlen > 1) ? (INT)(p - buffer) : -1;
452 }
453
454
455 /***********************************************************************
456  *           wvsnprintfW   (Not a Windows API)
457  */
458 INT WINAPI wvsnprintfW( LPWSTR buffer, UINT maxlen, LPCWSTR spec,
459                             va_list args )
460 {
461     WPRINTF_FORMAT format;
462     LPWSTR p = buffer;
463     UINT i, len;
464     CHAR number[20];
465
466     while (*spec && (maxlen > 1))
467     {
468         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
469         spec++;
470         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
471         spec += WPRINTF_ParseFormatW( spec, &format );
472         len = WPRINTF_GetLen( &format, args, number, maxlen - 1 );
473         if (!(format.flags & WPRINTF_LEFTALIGN))
474             for (i = format.precision; i < format.width; i++, maxlen--)
475                 *p++ = ' ';
476         switch(format.type)
477         {
478         case WPR_WCHAR:
479             *p = va_arg( args, WCHAR );
480             if (*p != '\0') p++;
481             else if (format.width > 1) *p++ = ' ';
482             else len = 0;
483             break;
484         case WPR_CHAR:
485             *p = (WCHAR)va_arg( args, CHAR );
486             if (*p != '\0') p++;
487             else if (format.width > 1) *p++ = ' ';
488             else len = 0;
489             break;
490         case WPR_STRING:
491             {
492                 LPCSTR ptr = va_arg( args, LPCSTR );
493                 for (i = 0; i < len; i++) *p++ = (WCHAR)*ptr++;
494             }
495             break;
496         case WPR_WSTRING:
497             if (len) memcpy( p, va_arg( args, LPCWSTR ), len * sizeof(WCHAR) );
498             p += len;
499             break;
500         case WPR_HEXA:
501             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
502             {
503                 *p++ = '0';
504                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
505                 maxlen -= 2;
506                 len -= 2;
507             }
508             /* fall through */
509         case WPR_SIGNED:
510         case WPR_UNSIGNED:
511             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
512             for (i = 0; i < len; i++) *p++ = (WCHAR)number[i];
513             (void)va_arg( args, INT ); /* Go to the next arg */
514             break;
515         case WPR_UNKNOWN:
516             continue;
517         }
518         if (format.flags & WPRINTF_LEFTALIGN)
519             for (i = format.precision; i < format.width; i++, maxlen--)
520                 *p++ = ' ';
521         maxlen -= len;
522     }
523     *p = 0;
524     return (maxlen > 1) ? (INT)(p - buffer) : -1;
525 }
526
527
528 /***********************************************************************
529  *           wvsprintf16   (USER.421)
530  */
531 INT16 WINAPI wvsprintf16( LPSTR buffer, LPCSTR spec, LPCVOID args )
532 {
533     TRACE("for %p got:\n",buffer);
534     return wvsnprintf16( buffer, 0xffff, spec, args );
535 }
536
537
538 /***********************************************************************
539  *           wvsprintfA   (USER32.587)
540  */
541 INT WINAPI wvsprintfA( LPSTR buffer, LPCSTR spec, va_list args )
542 {
543     TRACE("for %p got:\n",buffer);
544     return wvsnprintfA( buffer, 0xffffffff, spec, args );
545 }
546
547
548 /***********************************************************************
549  *           wvsprintfW   (USER32.588)
550  */
551 INT WINAPI wvsprintfW( LPWSTR buffer, LPCWSTR spec, va_list args )
552 {
553     TRACE("for %p got:\n",buffer);
554     return wvsnprintfW( buffer, 0xffffffff, spec, args );
555 }
556
557
558 /***********************************************************************
559  *           wsprintf16   (USER.420)
560  */
561 /* Winelib version */
562 INT16 WINAPIV wsprintf16( LPSTR buffer, LPCSTR spec, ... )
563 {
564     va_list valist;
565     INT16 res;
566
567     TRACE("for %p got:\n",buffer);
568     va_start( valist, spec );
569     /* Note: we call the 32-bit version, because the args are 32-bit */
570     res = (INT16)wvsnprintfA( buffer, 0xffffffff, spec, valist );
571     va_end( valist );
572     return res;
573 }
574
575 /* Emulator version */
576 INT16 WINAPIV WIN16_wsprintf16(void)
577 {
578     VA_LIST16 valist;
579     INT16 res;
580     SEGPTR buffer, spec;
581
582     VA_START16( valist );
583     buffer = VA_ARG16( valist, SEGPTR );
584     spec   = VA_ARG16( valist, SEGPTR );
585     TRACE("got:\n");
586     res = wvsnprintf16( (LPSTR)PTR_SEG_TO_LIN(buffer), 0xffff,
587                         (LPCSTR)PTR_SEG_TO_LIN(spec), valist );
588     VA_END16( valist );
589     return res;
590 }
591
592
593 /***********************************************************************
594  *           wsprintfA   (USER32.585)
595  */
596 INT WINAPIV wsprintfA( LPSTR buffer, LPCSTR spec, ... )
597 {
598     va_list valist;
599     INT res;
600
601     TRACE("for %p got:\n",buffer);
602     va_start( valist, spec );
603     res = wvsnprintfA( buffer, 0xffffffff, spec, valist );
604     va_end( valist );
605     return res;
606 }
607
608
609 /***********************************************************************
610  *           wsprintfW   (USER32.586)
611  */
612 INT WINAPIV wsprintfW( LPWSTR buffer, LPCWSTR spec, ... )
613 {
614     va_list valist;
615     INT res;
616
617     TRACE("wsprintfW for %p\n",buffer);
618     va_start( valist, spec );
619     res = wvsnprintfW( buffer, 0xffffffff, spec, valist );
620     va_end( valist );
621     return res;
622 }
623
624
625 /***********************************************************************
626  *           wsnprintfA   (Not a Windows API)
627  */
628 INT WINAPIV wsnprintfA( LPSTR buffer, UINT maxlen, LPCSTR spec, ... )
629 {
630     va_list valist;
631     INT res;
632
633     va_start( valist, spec );
634     res = wvsnprintfA( buffer, maxlen, spec, valist );
635     va_end( valist );
636     return res;
637 }
638
639
640 /***********************************************************************
641  *           wsnprintfW   (Not a Windows API)
642  */
643 INT WINAPIV wsnprintfW( LPWSTR buffer, UINT maxlen, LPCWSTR spec, ... )
644 {
645     va_list valist;
646     INT res;
647
648     va_start( valist, spec );
649     res = wvsnprintfW( buffer, maxlen, spec, valist );
650     va_end( valist );
651     return res;
652 }
653
654 /***********************************************************************
655  *           _DebugOutput                    (KERNEL.328)
656  */
657 void WINAPIV _DebugOutput( void )
658 {
659     VA_LIST16 valist;
660     WORD flags;
661     SEGPTR spec;
662     int i, nSeg = 0;
663     NE_MODULE *pModule;
664     char caller[101], temp[512];
665
666     /* Decode caller address */
667     pModule = NE_GetPtr( CURRENT_STACK16->cs );
668     if ( pModule )
669     {
670         SEGTABLEENTRY *pSeg = NE_SEG_TABLE( pModule );
671         for ( i = 0; i < pModule->seg_count; i++, pSeg++ )
672             if ( GlobalHandleToSel16( pSeg->hSeg ) == CURRENT_STACK16->cs )
673             {
674                 nSeg = i+1;
675                 break;
676             }
677     }
678     if ( nSeg )
679         sprintf( caller, "%s %02X:%04X", NE_MODULE_NAME( pModule ), 
680                                          nSeg, CURRENT_STACK16->ip );
681     else
682         sprintf( caller, "%04X:%04X", CURRENT_STACK16->cs, CURRENT_STACK16->ip );
683
684     /* Build debug message string */
685     VA_START16( valist );
686     flags = VA_ARG16( valist, WORD );
687     spec  = VA_ARG16( valist, SEGPTR );
688     wvsnprintf16( temp, sizeof(temp), (LPCSTR)PTR_SEG_TO_LIN(spec), valist );
689
690     /* Output */
691     DPRINTF( "_DebugOutput: %s %04X %s\n", 
692              caller, flags, debugstr_an(temp, sizeof(temp)) );
693 }
694