Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1026 lines
35 KiB

  1. /***
  2. *output.c - printf style output to a struct w4io
  3. *
  4. * Copyright (c) 1989-1991, Microsoft Corporation. All rights reserved.
  5. *
  6. *Purpose:
  7. * This file contains the code that does all the work for the
  8. * printf family of functions. It should not be called directly, only
  9. * by the *printf functions. We don't make any assumtions about the
  10. * sizes of ints, longs, shorts, or long doubles, but if types do overlap, we
  11. * also try to be efficient. We do assume that pointers are the same size
  12. * as either ints or longs.
  13. *
  14. *Revision History:
  15. * 06-01-89 PHG Module created
  16. * 08-28-89 JCR Added cast to get rid of warning (no object changes)
  17. * 02-15-90 GJF Fixed copyright
  18. * 10-03-90 WHB Defined LOCAL(x) to "static x" for local procedures
  19. * 06-05-95 SVA Added support for printing GUIDs.
  20. *
  21. *******************************************************************************/
  22. #include <stdlib.h>
  23. #include <limits.h>
  24. #include <string.h>
  25. #include <stdarg.h>
  26. #include "wchar.h"
  27. #include "w4io.h"
  28. /* temporary work-around for compiler without 64-bit support */
  29. #ifndef _INTEGRAL_MAX_BITS
  30. #define _INTEGRAL_MAX_BITS 64
  31. #endif /* _INTEGRAL_MAX_BITS */
  32. /* this macro defines a function which is private and as fast as possible: */
  33. /* for example, in C 6.0, it might be static _fastcall <type>. */
  34. #define LOCAL(x) static x
  35. #define NOFLOATS // Win 4 doesn't need floating point
  36. /* int/long/short/pointer sizes */
  37. /* the following should be set depending on the sizes of various types */
  38. // FLAT or LARGE model is assumed
  39. #ifdef FLAT
  40. # define LONG_IS_INT 1 /* 1 means long is same size as int */
  41. # define SHORT_IS_INT 0 /* 1 means short is same size as int */
  42. # define PTR_IS_INT 1 /* 1 means ptr is same size as int */
  43. # define PTR_IS_LONG 0 /* 1 means ptr is same size as long */
  44. #else // LARGE model
  45. # define LONG_IS_INT 0 /* 1 means long is same size as int */
  46. # define SHORT_IS_INT 1 /* 1 means short is same size as int */
  47. # define PTR_IS_INT 0 /* 1 means ptr is same size as int */
  48. # define PTR_IS_LONG 1 /* 1 means ptr is same size as long */
  49. #endif
  50. #define LONGDOUBLE_IS_DOUBLE 0 /* 1 means long double is same as double */
  51. #if LONG_IS_INT
  52. #define get_long_arg(x) (long)get_int_arg(x)
  53. #endif
  54. #if defined(_WIN64)
  55. #define get_ptr_arg(x) (void *)get_int64_arg(x)
  56. #elif PTR_IS_INT
  57. #define get_ptr_arg(x) (void *)get_int_arg(x)
  58. #elif PTR_IS_LONG
  59. #define get_ptr_arg(x) (void *)get_long_arg(x)
  60. #else
  61. #error Size of pointer must be same as size of int or long
  62. #endif
  63. #ifndef NOFLOATS
  64. /* These are "fake" double and long doubles to fool the compiler,
  65. so we don't drag in floating point. */
  66. typedef struct {
  67. char x[sizeof(double)];
  68. } DOUBLE;
  69. typedef struct {
  70. char x[sizeof(long double)];
  71. } LONGDOUBLE;
  72. #endif
  73. /* CONSTANTS */
  74. //#define BUFFERSIZE CVTBUFSIZE /* buffer size for maximum double conv */
  75. #define BUFFERSIZE 20
  76. /* flag definitions */
  77. #define FL_SIGN 0x0001 /* put plus or minus in front */
  78. #define FL_SIGNSP 0x0002 /* put space or minus in front */
  79. #define FL_LEFT 0x0004 /* left justify */
  80. #define FL_LEADZERO 0x0008 /* pad with leading zeros */
  81. #define FL_LONG 0x0010 /* long value given */
  82. #define FL_SHORT 0x0020 /* short value given */
  83. #define FL_SIGNED 0x0040 /* signed data given */
  84. #define FL_ALTERNATE 0x0080 /* alternate form requested */
  85. #define FL_NEGATIVE 0x0100 /* value is negative */
  86. #define FL_FORCEOCTAL 0x0200 /* force leading '0' for octals */
  87. #define FL_LONGDOUBLE 0x0400 /* long double value given */
  88. #define FL_WIDE 0x0800 /* wide character/string given */
  89. #define FL_I64 0x08000 /* __int64 value given */
  90. /* state definitions */
  91. enum STATE {
  92. ST_NORMAL, /* normal state; outputting literal chars */
  93. ST_PERCENT, /* just read '%' */
  94. ST_FLAG, /* just read flag character */
  95. ST_WIDTH, /* just read width specifier */
  96. ST_DOT, /* just read '.' */
  97. ST_PRECIS, /* just read precision specifier */
  98. ST_SIZE, /* just read size specifier */
  99. ST_TYPE /* just read type specifier */
  100. };
  101. #define NUMSTATES (ST_TYPE + 1)
  102. /* character type values */
  103. enum CHARTYPE {
  104. CH_OTHER, /* character with no special meaning */
  105. CH_PERCENT, /* '%' */
  106. CH_DOT, /* '.' */
  107. CH_STAR, /* '*' */
  108. CH_ZERO, /* '0' */
  109. CH_DIGIT, /* '1'..'9' */
  110. CH_FLAG, /* ' ', '+', '-', '#' */
  111. CH_SIZE, /* 'h', 'l', 'L', 'N', 'F' */
  112. CH_TYPE /* type specifying character */
  113. };
  114. /* static data (read only, since we are re-entrant) */
  115. char *nullstring = "(null)"; /* string to print on null ptr */
  116. /* The state table. This table is actually two tables combined into one. */
  117. /* The lower nybble of each byte gives the character class of any */
  118. /* character; while the uper nybble of the byte gives the next state */
  119. /* to enter. See the macros below the table for details. */
  120. /* */
  121. /* The table is generated by maketab.c -- use the maketab program to make */
  122. /* changes. */
  123. /* Brief description of the table, since I can't find maketab.c - t-stevan */
  124. /* Each entry in form 0xYZ. Here Z is a character class used in the macro */
  125. /* find_char_class defined below. The character classes are defined in the */
  126. /* CHARTYPE enum. For example, 'I' maps to CH_TYPE. To find a particular entry */
  127. /* Subtract the ASCI value for the space char from the character, and that is */
  128. /* the index to look up. The Y value is holds state transition information. */
  129. /* It is used in the macro find_next_state. */
  130. static char lookuptable[] = {
  131. 0x06, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00,
  132. 0x10, 0x00, 0x03, 0x06, 0x00, 0x06, 0x02, 0x10,
  133. 0x04, 0x45, 0x45, 0x45, 0x05, 0x05, 0x05, 0x05,
  134. 0x05, 0x35, 0x30, 0x00, 0x50, 0x00, 0x00, 0x00,
  135. 0x00, 0x20, 0x28, 0x38, 0x50, 0x58, 0x07, 0x08,
  136. 0x00, 0x38, 0x30, 0x30, 0x57, 0x50, 0x07, 0x00,
  137. 0x00, 0x20, 0x20, 0x08, 0x00, 0x00, 0x00, 0x00,
  138. 0x08, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00,
  139. 0x00, 0x70, 0x70, 0x78, 0x78, 0x78, 0x78, 0x08,
  140. 0x07, 0x08, 0x00, 0x00, 0x07, 0x00, 0x08, 0x08,
  141. 0x08, 0x00, 0x00, 0x08, 0x07, 0x08, 0x00, 0x07,
  142. 0x08
  143. };
  144. #define find_char_class(c) \
  145. ((c) < ' ' || (c) > 'x' ? \
  146. CH_OTHER \
  147. : \
  148. lookuptable[(c)-' '] & 0xF)
  149. #define find_next_state(class, state) \
  150. (lookuptable[(class) * NUMSTATES + (state)] >> 4)
  151. #if !LONG_IS_INT
  152. LOCAL(long) get_long_arg(va_list *pargptr);
  153. #endif
  154. LOCAL(int) get_int_arg(va_list *pargptr);
  155. #if _INTEGRAL_MAX_BITS >= 64
  156. LOCAL(__int64) get_int64_arg(va_list *pargptr);
  157. #endif /* _INTEGRAL_MAX_BITS >= 64 */
  158. LOCAL(void) writestring(char *string,
  159. int len,
  160. struct w4io *f,
  161. int *pcchwritten,
  162. int fwide);
  163. #ifndef NOFLOATS
  164. /* extern float convert routines */
  165. typedef int (* PFI)();
  166. extern PFI _cfltcvt_tab[5];
  167. #define _cfltcvt(a,b,c,d,e) (*_cfltcvt_tab[0])(a,b,c,d,e)
  168. #define _cropzeros(a) (*_cfltcvt_tab[1])(a)
  169. #define _fassign(a,b,c) (*_cfltcvt_tab[2])(a,b,c)
  170. #define _forcdecpt(a) (*_cfltcvt_tab[3])(a)
  171. #define _positive(a) (*_cfltcvt_tab[4])(a)
  172. #define _cldcvt(a,b,c,d,e) (*_cfltcvt_tab[5])(a,b,c,d,e)
  173. #endif
  174. /* Defines for printing out GUIDs */
  175. #ifndef GUID_DEFINED
  176. #define GUID_DEFINED
  177. /* size is 16 */
  178. typedef struct _GUID
  179. {
  180. unsigned long Data1;
  181. unsigned short Data2;
  182. unsigned short Data3;
  183. unsigned char Data4[ 8 ];
  184. } GUID;
  185. #endif // !GUID_DEFINED
  186. #ifndef _REFGUID_DEFINED
  187. #define _REFGUID_DEFINED
  188. #define REFGUID const GUID * const
  189. #endif // !_REFGUID_DEFINED
  190. /* This is actually one less than the normal GUIDSTR_MAX */
  191. /* Because we don't tag on a NULL byte */
  192. #define OUTPUT_GUIDSTR_MAX (1+ 8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12 + 1 /* + 1 */)
  193. /* Make sure our buffer size is big enough to hold a GUID */
  194. #if BUFFERSIZE < OUTPUT_GUIDSTR_MAX
  195. #undef BUFFERSIZE
  196. #define BUFFERSIZE OUTPUT_GUIDSTR_MAX
  197. #endif
  198. /* Function used to write a GUID to a string */
  199. int StrFromGUID(REFGUID rguid, char * lpsz, int cbMax);
  200. /***
  201. *int w4iooutput(f, format, argptr)
  202. *
  203. *Purpose:
  204. * Output performs printf style output onto a stream. It is called by
  205. * printf/fprintf/sprintf/vprintf/vfprintf/vsprintf to so the dirty
  206. * work. In multi-thread situations, w4iooutput assumes that the given
  207. * stream is already locked.
  208. *
  209. * Algorithm:
  210. * The format string is parsed by using a finite state automaton
  211. * based on the current state and the current character read from
  212. * the format string. Thus, looping is on a per-character basis,
  213. * not a per conversion specifier basis. Once the format specififying
  214. * character is read, output is performed.
  215. *
  216. *Entry:
  217. * struct w4io *f - stream for output
  218. * char *format - printf style format string
  219. * va_list argptr - pointer to list of subsidiary arguments
  220. *
  221. *Exit:
  222. * Returns the number of characters written, or -1 if an output error
  223. * occurs.
  224. *
  225. *Exceptions:
  226. *
  227. *******************************************************************************/
  228. int _cdecl w4iooutput(struct w4io *f, const char *format, va_list argptr)
  229. {
  230. int hexadd; /* offset to add to number to get 'a'..'f' */
  231. char ch; /* character just read */
  232. wchar_t wc; /* wide character temp */
  233. wchar_t *pwc; /* wide character temp pointer */
  234. int flags; /* flag word -- see #defines above for flag values */
  235. enum STATE state; /* current state */
  236. enum CHARTYPE chclass; /* class of current character */
  237. int radix; /* current conversion radix */
  238. int charsout; /* characters currently written so far, -1 = IO error */
  239. int fldwidth; /* selected field with -- 0 means default */
  240. int fwide;
  241. int precision; /* selected precision -- -1 means default */
  242. char prefix[2]; /* numeric prefix -- up to two characters */
  243. int prefixlen; /* length of prefix -- 0 means no prefix */
  244. int capexp; /* non-zero = 'E' exponent signifiet, zero = 'e' */
  245. int no_output; /* non-zero = prodcue no output for this specifier */
  246. char *text; /* pointer text to be printed, not zero terminated */
  247. int textlen; /* length of the text to be printed */
  248. char buffer[BUFFERSIZE]; /* buffer for conversions */
  249. charsout = 0; /* no characters written yet */
  250. state = ST_NORMAL; /* starting state */
  251. /* main loop -- loop while format character exist and no I/O errors */
  252. while ((ch = *format++) != '\0' && charsout >= 0) {
  253. chclass = find_char_class(ch); /* find character class */
  254. state = find_next_state(chclass, state); /* find next state */
  255. /* execute code for each state */
  256. switch (state) {
  257. case ST_NORMAL:
  258. NORMAL_STATE:
  259. /* normal state -- just write character */
  260. f->writechar(ch, 1, f, &charsout);
  261. break;
  262. case ST_PERCENT:
  263. /* set default value of conversion parameters */
  264. prefixlen = fldwidth = no_output = capexp = 0;
  265. flags = 0;
  266. precision = -1;
  267. fwide = 0;
  268. break;
  269. case ST_FLAG:
  270. /* set flag based on which flag character */
  271. switch (ch) {
  272. case '-':
  273. flags |= FL_LEFT; /* '-' => left justify */
  274. break;
  275. case '+':
  276. flags |= FL_SIGN; /* '+' => force sign indicator */
  277. break;
  278. case ' ':
  279. flags |= FL_SIGNSP; /* ' ' => force sign or space */
  280. break;
  281. case '#':
  282. flags |= FL_ALTERNATE; /* '#' => alternate form */
  283. break;
  284. case '0':
  285. flags |= FL_LEADZERO; /* '0' => pad with leading zeros */
  286. break;
  287. }
  288. break;
  289. case ST_WIDTH:
  290. /* update width value */
  291. if (ch == '*') {
  292. /* get width from arg list */
  293. fldwidth = get_int_arg(&argptr);
  294. if (fldwidth < 0) {
  295. /* ANSI says neg fld width means '-' flag and pos width */
  296. flags |= FL_LEFT;
  297. fldwidth = -fldwidth;
  298. }
  299. }
  300. else {
  301. /* add digit to current field width */
  302. fldwidth = fldwidth * 10 + (ch - '0');
  303. }
  304. break;
  305. case ST_DOT:
  306. /* zero the precision, since dot with no number means 0
  307. not default, according to ANSI */
  308. precision = 0;
  309. break;
  310. case ST_PRECIS:
  311. /* update precison value */
  312. if (ch == '*') {
  313. /* get precision from arg list */
  314. precision = get_int_arg(&argptr);
  315. if (precision < 0)
  316. precision = -1; /* neg precision means default */
  317. }
  318. else {
  319. /* add digit to current precision */
  320. precision = precision * 10 + (ch - '0');
  321. }
  322. break;
  323. case ST_SIZE:
  324. /* just read a size specifier, set the flags based on it */
  325. switch (ch) {
  326. #if !LONG_IS_INT
  327. case 'l':
  328. flags |= FL_LONG; /* 'l' => long int */
  329. break;
  330. #endif
  331. case 'L':
  332. #if !LONGDOUBLE_IS_DOUBLE
  333. flags |= FL_LONGDOUBLE; /* 'L' => long double */
  334. #endif
  335. flags |= FL_I64; /* 'L' => __int64 */
  336. break;
  337. case 'I':
  338. /*
  339. * In order to handle the I64 size modifier, we depart from
  340. * the simple deterministic state machine. The code below
  341. * scans
  342. */
  343. if ( (*format == '6') && (*(format + 1) == '4') ) {
  344. format += 2;
  345. flags |= FL_I64; /* I64 => __int64 */
  346. }
  347. else {
  348. state = ST_NORMAL;
  349. goto NORMAL_STATE;
  350. }
  351. break;
  352. #if !SHORT_IS_INT
  353. case 'h':
  354. flags |= FL_SHORT; /* 'h' => short int */
  355. break;
  356. #endif
  357. case 'w':
  358. flags |= FL_WIDE; /* 'w' => wide character */
  359. break;
  360. case 't':
  361. #ifdef _UNICODE
  362. flags |= FL_WIDE;
  363. #endif
  364. break;
  365. }
  366. break;
  367. case ST_TYPE:
  368. /* we have finally read the actual type character, so we */
  369. /* now format and "print" the output. We use a big switch */
  370. /* statement that sets 'text' to point to the text that should */
  371. /* be printed, and 'textlen' to the length of this text. */
  372. /* Common code later on takes care of justifying it and */
  373. /* other miscellaneous chores. Note that cases share code, */
  374. /* in particular, all integer formatting is doen in one place. */
  375. /* Look at those funky goto statements! */
  376. switch (ch) {
  377. case 'c': {
  378. /* print a single character specified by int argument */
  379. wc = (wchar_t) get_int_arg(&argptr); /* get char to print */
  380. * (wchar_t *) buffer = wc;
  381. text = buffer;
  382. textlen = 1; /* print just a single character */
  383. }
  384. break;
  385. case 'S': {
  386. /* print a Counted String */
  387. struct string {
  388. short Length;
  389. short MaximumLength;
  390. char *Buffer;
  391. } *pstr;
  392. pstr = get_ptr_arg(&argptr);
  393. if (pstr == NULL || pstr->Buffer == NULL) {
  394. /* null ptr passed, use special string */
  395. text = nullstring;
  396. textlen = strlen(text);
  397. flags &= ~FL_WIDE;
  398. } else {
  399. text = pstr->Buffer;
  400. /* The length field is a count of bytes, not characters. */
  401. if (flags & FL_WIDE)
  402. textlen = pstr->Length / sizeof( wchar_t );
  403. else
  404. textlen = pstr->Length;
  405. if (precision != -1)
  406. textlen = min( textlen, precision );
  407. }
  408. }
  409. break;
  410. case 's': {
  411. /* print a string -- */
  412. /* ANSI rules on how much of string to print: */
  413. /* all if precision is default, */
  414. /* min(precision, length) if precision given. */
  415. /* prints '(null)' if a null string is passed */
  416. int i;
  417. char *p; /* temps */
  418. text = get_ptr_arg(&argptr);
  419. if (text == NULL) {
  420. /* null ptr passed, use special string */
  421. text = nullstring;
  422. flags &= ~FL_WIDE;
  423. }
  424. /* At this point it is tempting to use strlen(), but */
  425. /* if a precision is specified, we're not allowed to */
  426. /* scan past there, because there might be no null */
  427. /* at all. Thus, we must do our own scan. */
  428. i = (precision == -1) ? INT_MAX : precision;
  429. /* scan for null upto i characters */
  430. if (flags & FL_WIDE) {
  431. pwc = (wchar_t *) text;
  432. while (i-- && (wc = *pwc) && (wc & 0x00ff)) {
  433. ++pwc;
  434. if (wc & 0xff00) { // if high byte set,
  435. break; // error will be indicated
  436. }
  437. }
  438. textlen = (int)(pwc - (wchar_t *) text); /*string length*/
  439. } else {
  440. p = text;
  441. while (i-- && *p) {
  442. ++p;
  443. }
  444. textlen = (int)(p - text); /* length of the string */
  445. }
  446. }
  447. break;
  448. /* print a GUID */
  449. case 'I':
  450. {
  451. void *p; /* temp */
  452. p = get_ptr_arg(&argptr);
  453. if (p == NULL)
  454. {
  455. /* null ptr passed, use special string */
  456. text = nullstring;
  457. textlen = strlen(nullstring);
  458. }
  459. else
  460. {
  461. textlen = StrFromGUID(p, buffer, BUFFERSIZE);
  462. text = buffer;
  463. }
  464. }
  465. break;
  466. case 'n': {
  467. /* write count of characters seen so far into */
  468. /* short/int/long thru ptr read from args */
  469. void *p; /* temp */
  470. p = get_ptr_arg(&argptr);
  471. /* store chars out into short/long/int depending on flags */
  472. #if !LONG_IS_INT
  473. if (flags & FL_LONG)
  474. *(long *)p = charsout;
  475. else
  476. #endif
  477. #if !SHORT_IS_INT
  478. if (flags & FL_SHORT)
  479. *(short *)p = (short) charsout;
  480. else
  481. #endif
  482. *(int *)p = charsout;
  483. no_output = 1; /* force no output */
  484. }
  485. break;
  486. #ifndef NOFLOATS
  487. case 'E':
  488. case 'G':
  489. capexp = 1; /* capitalize exponent */
  490. ch += 'a' - 'A'; /* convert format char to lower */
  491. /* DROP THROUGH */
  492. case 'e':
  493. case 'f':
  494. case 'g': {
  495. /* floating point conversion -- we call cfltcvt routines */
  496. /* to do the work for us. */
  497. flags |= FL_SIGNED; /* floating point is signed conversion */
  498. text = buffer; /* put result in buffer */
  499. flags &= ~FL_WIDE; /* 8 bit string */
  500. /* compute the precision value */
  501. if (precision < 0)
  502. precision = 6; /* default precision: 6 */
  503. else if (precision == 0 && ch == 'g')
  504. precision = 1; /* ANSI specified */
  505. #if !LONGDOUBLE_IS_DOUBLE
  506. /* do the conversion */
  507. if (flags & FL_LONGDOUBLE) {
  508. _cldcvt(argptr, text, ch, precision, capexp);
  509. va_arg(argptr, LONGDOUBLE);
  510. }
  511. else
  512. #endif
  513. {
  514. _cfltcvt(argptr, text, ch, precision, capexp);
  515. va_arg(argptr, DOUBLE);
  516. }
  517. /* '#' and precision == 0 means force a decimal point */
  518. if ((flags & FL_ALTERNATE) && precision == 0)
  519. _forcdecpt(text);
  520. /* 'g' format means crop zero unless '#' given */
  521. if (ch == 'g' && !(flags & FL_ALTERNATE))
  522. _cropzeros(text);
  523. /* check if result was negative, save '-' for later */
  524. /* and point to positive part (this is for '0' padding) */
  525. if (*text == '-') {
  526. flags |= FL_NEGATIVE;
  527. ++text;
  528. }
  529. textlen = strlen(text); /* compute length of text */
  530. }
  531. break;
  532. #endif // NOFLOATS
  533. case 'd':
  534. case 'i':
  535. /* signed decimal output */
  536. flags |= FL_SIGNED;
  537. radix = 10;
  538. goto COMMON_INT;
  539. case 'u':
  540. radix = 10;
  541. goto COMMON_INT;
  542. case 'p':
  543. /* write a pointer -- this is like an integer or long */
  544. /* except we force precision to pad with zeros and */
  545. /* output in big hex. */
  546. precision = 2 * sizeof(void *); /* number of hex digits needed */
  547. #if !PTR_IS_INT
  548. flags |= FL_LONG; /* assume we're converting a long */
  549. #endif
  550. /* DROP THROUGH to hex formatting */
  551. case 'C':
  552. case 'X':
  553. /* unsigned upper hex output */
  554. hexadd = 'A' - '9' - 1; /* set hexadd for uppercase hex */
  555. goto COMMON_HEX;
  556. case 'x':
  557. /* unsigned lower hex output */
  558. hexadd = 'a' - '9' - 1; /* set hexadd for lowercase hex */
  559. /* DROP THROUGH TO COMMON_HEX */
  560. COMMON_HEX:
  561. radix = 16;
  562. if (flags & FL_ALTERNATE) {
  563. /* alternate form means '0x' prefix */
  564. prefix[0] = '0';
  565. prefix[1] = (char)('x' - 'a' + '9' + 1 + hexadd); /* 'x' or 'X' */
  566. prefixlen = 2;
  567. }
  568. goto COMMON_INT;
  569. case 'o':
  570. /* unsigned octal output */
  571. radix = 8;
  572. if (flags & FL_ALTERNATE) {
  573. /* alternate form means force a leading 0 */
  574. flags |= FL_FORCEOCTAL;
  575. }
  576. /* DROP THROUGH to COMMON_INT */
  577. COMMON_INT: {
  578. /* This is the general integer formatting routine. */
  579. /* Basically, we get an argument, make it positive */
  580. /* if necessary, and convert it according to the */
  581. /* correct radix, setting text and textlen */
  582. /* appropriately. */
  583. #if _INTEGRAL_MAX_BITS >= 64
  584. unsigned __int64 number; /* number to convert */
  585. int digit; /* ascii value of digit */
  586. __int64 l; /* temp long value */
  587. #else /* _INTEGRAL_MAX_BITS >= 64 */
  588. unsigned long number; /* number to convert */
  589. int digit; /* ascii value of digit */
  590. long l; /* temp long value */
  591. #endif /* _INTEGRAL_MAX_BITS >= 64 */
  592. /* 1. read argument into l, sign extend as needed */
  593. #if _INTEGRAL_MAX_BITS >= 64
  594. if (flags & FL_I64)
  595. l = get_int64_arg(&argptr);
  596. else
  597. #endif /* _INTEGRAL_MAX_BITS >= 64 */
  598. #if !LONG_IS_INT
  599. if (flags & FL_LONG)
  600. l = get_long_arg(&argptr);
  601. else
  602. #endif
  603. #if !SHORT_IS_INT
  604. if (flags & FL_SHORT) {
  605. if (flags & FL_SIGNED)
  606. l = (short) get_int_arg(&argptr); /* sign extend */
  607. else
  608. l = (unsigned short) get_int_arg(&argptr); /* zero-extend*/
  609. }
  610. else
  611. #endif
  612. {
  613. if (flags & FL_SIGNED)
  614. l = get_int_arg(&argptr); /* sign extend */
  615. else
  616. l = (unsigned int) get_int_arg(&argptr); /* zero-extend*/
  617. }
  618. /* 2. check for negative; copy into number */
  619. if ( (flags & FL_SIGNED) && l < 0) {
  620. number = -l;
  621. flags |= FL_NEGATIVE; /* remember negative sign */
  622. }
  623. else {
  624. number = l;
  625. }
  626. #if _INTEGRAL_MAX_BITS >= 64
  627. if ( (flags & FL_I64) == 0 ) {
  628. /*
  629. * Unless printing a full 64-bit value, insure values
  630. * here are not in cananical longword format to prevent
  631. * the sign extended upper 32-bits from being printed.
  632. */
  633. number &= 0xffffffff;
  634. }
  635. #endif /* _INTEGRAL_MAX_BITS >= 64 */
  636. /* 3. check precision value for default; non-default */
  637. /* turns off 0 flag, according to ANSI. */
  638. if (precision < 0)
  639. precision = 1; /* default precision */
  640. else
  641. flags &= ~FL_LEADZERO;
  642. /* 4. Check if data is 0; if so, turn off hex prefix */
  643. if (number == 0)
  644. prefixlen = 0;
  645. /* 5. Convert data to ASCII -- note if precision is zero */
  646. /* and number is zero, we get no digits at all. */
  647. text = &buffer[BUFFERSIZE-1]; // last digit at end of buffer
  648. flags &= ~FL_WIDE; // 8 bit characters
  649. while (precision-- > 0 || number != 0) {
  650. digit = (int)(number % radix) + '0';
  651. number /= radix; /* reduce number */
  652. if (digit > '9') {
  653. /* a hex digit, make it a letter */
  654. digit += hexadd;
  655. }
  656. *text-- = (char)digit; /* store the digit */
  657. }
  658. textlen = (int)((char *)&buffer[BUFFERSIZE-1] - text); /* compute length of number */
  659. ++text; /* text points to first digit now */
  660. /* 6. Force a leading zero if FORCEOCTAL flag set */
  661. if ((flags & FL_FORCEOCTAL) && (text[0] != '0' || textlen == 0)) {
  662. *--text = '0';
  663. ++textlen; /* add a zero */
  664. }
  665. }
  666. break;
  667. }
  668. /* At this point, we have done the specific conversion, and */
  669. /* 'text' points to text to print; 'textlen' is length. Now we */
  670. /* justify it, put on prefixes, leading zeros, and then */
  671. /* print it. */
  672. if (!no_output) {
  673. int padding; /* amount of padding, negative means zero */
  674. if (flags & FL_SIGNED) {
  675. if (flags & FL_NEGATIVE) {
  676. /* prefix is a '-' */
  677. prefix[0] = '-';
  678. prefixlen = 1;
  679. }
  680. else if (flags & FL_SIGN) {
  681. /* prefix is '+' */
  682. prefix[0] = '+';
  683. prefixlen = 1;
  684. }
  685. else if (flags & FL_SIGNSP) {
  686. /* prefix is ' ' */
  687. prefix[0] = ' ';
  688. prefixlen = 1;
  689. }
  690. }
  691. /* calculate amount of padding -- might be negative, */
  692. /* but this will just mean zero */
  693. padding = fldwidth - textlen - prefixlen;
  694. /* put out the padding, prefix, and text, in the correct order */
  695. if (!(flags & (FL_LEFT | FL_LEADZERO))) {
  696. /* pad on left with blanks */
  697. f->writechar(' ', padding, f, &charsout);
  698. }
  699. /* write prefix */
  700. writestring(prefix, prefixlen, f, &charsout, 0);
  701. if ((flags & FL_LEADZERO) && !(flags & FL_LEFT)) {
  702. /* write leading zeros */
  703. f->writechar('0', padding, f, &charsout);
  704. }
  705. /* write text */
  706. writestring(text, textlen, f, &charsout, flags & FL_WIDE);
  707. if (flags & FL_LEFT) {
  708. /* pad on right with blanks */
  709. f->writechar(' ', padding, f, &charsout);
  710. }
  711. /* we're done! */
  712. }
  713. break;
  714. }
  715. }
  716. return charsout; /* return value = number of characters written */
  717. }
  718. /***
  719. *int get_int_arg(va_list pargptr)
  720. *
  721. *Purpose:
  722. * Gets an int argument off the given argument list and updates *pargptr.
  723. *
  724. *Entry:
  725. * va_list pargptr - pointer to argument list; updated by function
  726. *
  727. *Exit:
  728. * Returns the integer argument read from the argument list.
  729. *
  730. *Exceptions:
  731. *
  732. *******************************************************************************/
  733. LOCAL(int) get_int_arg(va_list *pargptr)
  734. {
  735. return va_arg(*pargptr, int);
  736. }
  737. /***
  738. *long get_long_arg(va_list pargptr)
  739. *
  740. *Purpose:
  741. * Gets an long argument off the given argument list and updates pargptr.
  742. *
  743. *Entry:
  744. * va_list pargptr - pointer to argument list; updated by function
  745. *
  746. *Exit:
  747. * Returns the long argument read from the argument list.
  748. *
  749. *Exceptions:
  750. *
  751. *******************************************************************************/
  752. #if !LONG_IS_INT
  753. LOCAL(long) get_long_arg(va_list *pargptr)
  754. {
  755. return va_arg(*pargptr, long);
  756. }
  757. #endif
  758. #if _INTEGRAL_MAX_BITS >= 64
  759. LOCAL(__int64) get_int64_arg (
  760. va_list *pargptr
  761. )
  762. {
  763. return va_arg(*pargptr, __int64);
  764. }
  765. #endif /* _INTEGRAL_MAX_BITS >= 64 */
  766. /***
  767. *void writestring(char *string, int len, struct w4io *f, int *pcchwritten, int fwide)
  768. *
  769. *Purpose:
  770. * Writes a string of the given length to the given file. If no error occurs,
  771. * then *pcchwritten is incremented by len; otherwise, *pcchwritten is set
  772. * to -1. If len is negative, it is treated as zero.
  773. *
  774. *Entry:
  775. * char *string - string to write (NOT null-terminated)
  776. * int len - length of string
  777. * struct w4io *f - file to write to
  778. * int *pcchwritten - pointer to integer to update with total chars written
  779. * int fwide - wide character flag
  780. *
  781. *Exit:
  782. * No return value.
  783. *
  784. *Exceptions:
  785. *
  786. *******************************************************************************/
  787. LOCAL(void) writestring(
  788. char *string,
  789. int len,
  790. struct w4io *f,
  791. int *pcchwritten,
  792. int fwide)
  793. {
  794. wchar_t *pwc;
  795. //printf("string: str=%.*s, len=%d, cch=%d, f=%d\n", len, string, len, *pcchwritten, fwide);
  796. if (fwide) {
  797. pwc = (wchar_t *) string;
  798. while (len-- > 0) {
  799. if (*pwc & 0xff00) {
  800. f->writechar('^', 1, f, pcchwritten);
  801. }
  802. f->writechar((char) *pwc++, 1, f, pcchwritten);
  803. }
  804. } else {
  805. while (len-- > 0) {
  806. f->writechar(*string++, 1, f, pcchwritten);
  807. }
  808. }
  809. }
  810. const wchar_t a_wcDigits[] = L"0123456789ABCDEF";
  811. //+---------------------------------------------------------------------------
  812. //
  813. // Function: FormatHexNum
  814. //
  815. // Synopsis: Given a value, and a count of characters, translate
  816. // the value into a hex string. This is the ANSI version
  817. //
  818. // Arguments: [ulValue] -- Value to convert
  819. // [chChars] -- Number of characters to format
  820. // [pchStr] -- Pointer to output buffer
  821. //
  822. // Requires: pwcStr must be valid for chChars
  823. //
  824. // History: 5-31-95 t-stevan Copied and Modified for use in debug output function
  825. //
  826. // Notes:
  827. //
  828. //----------------------------------------------------------------------------
  829. void FormatHexNum( unsigned long ulValue, unsigned long chChars, char *pchStr)
  830. {
  831. while(chChars--)
  832. {
  833. pchStr[chChars] = (char) a_wcDigits[ulValue & 0xF];
  834. ulValue = ulValue >> 4;
  835. }
  836. }
  837. //+-------------------------------------------------------------------------
  838. //
  839. // Function: StrFromGUID (private)
  840. //
  841. // Synopsis: Converts a GUID into a string (duh!)
  842. //
  843. // Arguments: [rguid] - the guid to convert
  844. // [lpszy] - buffer to hold the results
  845. // [cbMax] - sizeof the buffer
  846. //
  847. // Returns: amount of data copied to lpsz if successful
  848. // 0 if buffer too small.
  849. //
  850. //--------------------------------------------------------------------------
  851. int StrFromGUID(REFGUID rguid, char * lpsz, int cbMax) // internal
  852. {
  853. if (cbMax < OUTPUT_GUIDSTR_MAX)
  854. return 0;
  855. // Make the GUID into"{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
  856. *lpsz++ = '{';
  857. FormatHexNum( rguid->Data1, 8 , lpsz);
  858. lpsz += 8;
  859. *lpsz++ = '-';
  860. FormatHexNum( rguid->Data2, 4 , lpsz);
  861. lpsz += 4;
  862. *lpsz++ = '-';
  863. FormatHexNum( rguid->Data3, 4 , lpsz);
  864. lpsz += 4;
  865. *lpsz++ = '-';
  866. FormatHexNum( rguid->Data4[0], 2 , lpsz);
  867. lpsz += 2;
  868. FormatHexNum( rguid->Data4[1], 2 , lpsz);
  869. lpsz += 2;
  870. *lpsz++ = '-';
  871. FormatHexNum( rguid->Data4[2], 2 , lpsz);
  872. lpsz += 2;
  873. FormatHexNum( rguid->Data4[3], 2 , lpsz);
  874. lpsz += 2;
  875. FormatHexNum( rguid->Data4[4], 2 , lpsz);
  876. lpsz += 2;
  877. FormatHexNum( rguid->Data4[5], 2 , lpsz);
  878. lpsz += 2;
  879. FormatHexNum( rguid->Data4[6], 2 , lpsz);
  880. lpsz += 2;
  881. FormatHexNum( rguid->Data4[7], 2 , lpsz);
  882. lpsz += 2;
  883. *lpsz++ = '}';
  884. /* We don't want to tag on a NULL char because we don't need to print one out *\
  885. /* *lpsz = 0; */
  886. return OUTPUT_GUIDSTR_MAX;
  887. }