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