home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 329_01 / dprintf.doc < prev    next >
Text File  |  1990-02-13  |  12KB  |  290 lines

  1. dprintf Operating Instructions
  2. (C) August 30  1989  Arkin Asaf
  3. All rights reserved
  4.  
  5.  
  6. About dprintf
  7.  
  8. A respectable number of C libraries nowadays offer, usually at extra
  9. cost, their source code. You can put your hands on listings of just
  10. about any type and usage: windowing, graphics and data-base, to name but
  11. few. That is, to name but one -- the library that comes bundled with
  12. your compiler. 
  13.  
  14. It may happen that you will want to change features, not of a 3D-plot
  15. function, but of the more casual printf. Unless highly dedicated, want
  16. is all you can do about it. Correction: all you could do about it. 
  17.  
  18. Presented in source code, dprintf is a clone of the printf function. 
  19. With minute effort you can modify and adapt it to suit your needs; You
  20. will find dprintf is mostly portable and expandable: it easily extends
  21. to accommodate newly devised formats; it can print to almost all output
  22. destinations; and, not less important, it follows the ANSI standard to
  23. the dot. 
  24.  
  25.  
  26. Define printf
  27.  
  28. dprintf comes close in calling convention and operation to its
  29. originator, less one distinction: dprintf has a pointer-to-function as
  30. its first parameter. This pointer designates the function, resemblance
  31. to putchar(), which performs all output. Having th e function not
  32. preselected, dprintf enjoys unlimited choice of output destinations. 
  33. The pointer's definition and function prototypes follow:
  34.  
  35.  
  36.  
  37. typedef  int (*dprintf_fp)(int);
  38.  
  39. int   dprintf(dprintf_fp Func, const char *Format, ...);
  40.  
  41. int  vdprintf(dprintf_fp Func, const char *Format, va_list Args);
  42.  
  43.  
  44.  
  45. For those with amnesia and the unfamiliar with Standard C, a summarized
  46. description of all output formats, as defined in the ANSI standard and
  47. carried out by dprintf, appears in separate (see file "SYNOPSIS.DOC"). 
  48.  
  49.  
  50. Variations On dprintf
  51.  
  52. Similar to printf, dprintf accepts a variable arguments list. It then
  53. passes vdprintf a pointer to this list, along with pointers to the
  54. output function and format string. Having so little to do, dprintf
  55. tends to be rather small, as short as four statemen ts long. Such
  56. shortness makes it an easy target for changes. 
  57.  
  58. Intricate output destinations require a list of arguments to handle. 
  59. Since vdprintf accepts fixed arguments and is too long a function to
  60. exercise adaptability on, I advise that dprintf absorb all non-standard
  61. arguments, setting them for vdprintf's conven ience. To better clarify
  62. the point consider aprintf. This function allocates a memory block to
  63. store output in:
  64.  
  65.  
  66.  
  67. char  *aprintf_base;
  68.  
  69. char   aprintf_ofst;
  70.  
  71.  
  72.  
  73. const char  *aprintf(const char *Format, ...)
  74.  
  75. {
  76.  
  77.   va_list  Args;
  78.  
  79.  
  80.  
  81.   va_start(Args,Format);
  82.  
  83.   aprintf_ofst=0;
  84.  
  85.   aprintf_base=NULL;
  86.  
  87.   vdprintf(aprintf_out,Format,Args);
  88.  
  89.   aprintf_out('\0');
  90.  
  91.   va_end(Args);
  92.  
  93.   return  aprintf_base;
  94.  
  95. }
  96.  
  97.  
  98.  
  99. int  aprintf_out(int Char)
  100.  
  101. {
  102.  
  103.   aprintf_base=realloc(aprintf_base,aprintf_ofst+1);
  104.  
  105.   if (aprintf_base==NULL)
  106.  
  107.     return  EOF;
  108.  
  109.   aprintf_base[aprintf_ofst++]=Char;
  110.  
  111.   return  Char;
  112.  
  113. }
  114.  
  115.  
  116.  
  117. Still, vdprintf does not manage unaltered: Adding new formats is a
  118. recommended practice and you should attempt it often. Few suggestions
  119. being: binary and Roman numerals, file and path names, and printer
  120. control codes. In fact, dprintf insists you modify it prior to use: The
  121. pointer format is not covered by the ANSI standard, varying considerably
  122. between system architectures. It has been left for you to adapt %p to
  123. your system's taste. 
  124.  
  125.  
  126. The Workings Of vdprintf
  127.  
  128. You may find this section and the following two somewhat hard to follow:
  129. They describe the internal working of vdprintf in a stepwise manner. It
  130. is best that you refer to the listing while reading. 
  131.  
  132. As said earlier, vdprintf outputs characters by means of a function the
  133. programmer provides. Designated by a pointer, this function returns EOF
  134. upon output error. Rather than passing a pointer and return value
  135. through three levels of functions, a static p ointer (OutFunc) and
  136. longjmp buffer (dputc_buf -- for quick return) are defined. 
  137. Consequently, vdprintf's first actions involve assigning OutFunc a
  138. pointer and initializing dputc_buf. 
  139.  
  140. That done, the printing process begins: vdprintf scans the format string
  141. a character at a time, further processing %'s and echoing all other
  142. characters to the output. 
  143.  
  144. Following the % format sign come the flags -- zero or more from a set of
  145. five: -, +, space, 0 and #. Successive flags are parsed from the format
  146. string one by one: strchr() (see file "STRCHR.C") matches each potential
  147. flag against FlagsList, returning eit her NULL (not a flag) or a pointer
  148. to the flag in FlagsList. Simple pointer substraction and bit shifting
  149. then produce a bit mask, which ORs onto Flags. Later on vdprintf will
  150. AND Flags with Mask macros to establish whether certain flags have been
  151. mentioned or not. 
  152.  
  153. All flags read, vdprintf proceeds to gather the width and precision
  154. parameters. The width reads first (zero assumed, if absent): either as
  155. a numeric, deduced from digits in the format string, or an int, consumed
  156. from the variable arguments list, if an * r eplaces the numerals. You
  157. must be careful not to start the width with zero, for it will be
  158. considered a flag. Differently, the precision may begin with zero,
  159. being separated from the width by a period and read much the same way. 
  160. Do take note that not spec ifying the precision value (zero assumed by
  161. default), and omitting the precision altogether, including period (minus
  162. one assumed), hold dissimilar meanings, e.g. "%5.s" implies a
  163. zero-length string, whereas "%5s" implies a string of five or more
  164. character s. 
  165.  
  166. Last before the format letter comes the argument size: default, short
  167. (type h), long (l), or long double (L). The long size is applicable
  168. only for integers, the long double size for floating points. Default
  169. may be int, double or any specialized type, such as char for the %c
  170. format. The short type serves only to maintain some compatibility with
  171. scanf(), short arguments being automatically promoted to int and float
  172. arguments to double by the compiler. In effect, it is meaningless. 
  173.  
  174. Finally, the format letter gets read. It determines what course of
  175. action be taken to generate the right output. Most formats execute by
  176. auxiliary functions (on which you will read in the next section),
  177. keeping vdprintf short, or else it may fail to compi le. An output
  178. error or incorrect format specification at any point and vdprintf
  179. returns EOF; if all goes well, vdprintf returns the number of characters
  180. successfully printed. 
  181.  
  182.  
  183. vdprintf's Auxiliary Functions
  184.  
  185. Assisting vdprintf are five auxiliary functions: PrintDecimal,
  186. PrintRadix, PrintFloat, ToInteger and Print. The first three transform
  187. long ints, long unsigneds, and long doubles, respectively, into
  188. printable strings of digits: ToInteger transforms and Pri nt does the
  189. printing. This section discusses them all, except for PrintFloat. 
  190.  
  191. PrintDecimal (%d or %i formats) produces signed decimals: The long int
  192. it receives dissociates into prefix and value, the prefix holding the
  193. sign. Once ToInteger stringizes the value, Print outputs both the
  194. prefix and it. 
  195.  
  196. PrintRadix yields long unsigned decimals (%u format), octals (%o),
  197. hexadecimals (%x or %X) and pointers (%p). Their value is forever
  198. positive and so the prefix, obtained in the varient format (# flag
  199. present), denotes the value's type: nothing for decimal s, 0 for octals
  200. and 0x for hexadecimals. (Note that hexadecimal letters are in the same
  201. case as is the format letter.)
  202.  
  203. As delivered to you, vdprintf's PrintRadix utters 8-digit hexadecimal
  204. (upper case letters) pointers, which @ prefixes in the varient format. 
  205. Various system architectures impose different pointer representation,
  206. both in memory and in writing. It may be ess ential that you modify not
  207. only PrintRadix, but also vdprintf's switch construct: It assumes
  208. pointers to remain intact, cast to long unsigneds. Not all systems
  209. guarantee this to hold true. 
  210.  
  211. So numeric values can be printed, they must first convert into
  212. characters. ToInteger does just that. It turns long unsigneds into
  213. NULL-terminated strings of digits in a given radix. A numeral must have
  214. no less than precision number of digits; if necessary , zeros precede
  215. the value. ToInteger stores the string in a malloced memory block. 
  216. It's address returns by reference -- through formal parameter char
  217. **Buffer; by value ToInteger returns the string's length (terminating
  218. NULL excluded.)
  219.  
  220. Print completes the auxiliary functions, printing the prefix and value
  221. in accord with Flags: normally, spaces are inserted before the prefix,
  222. right justifying it and the value within their field (remember the width
  223. parameter -- it sets the field); the 0 f lag states that zeros come
  224. between the prefix and value to fill the field whole; the - flag appends
  225. spaces at the end, left justifying the prefix and value. Regardless of
  226. the style used, no more than Maximum number of characters are printed
  227. (ignoring nega tive maximums). Finally, OutCnt rises by the total
  228. number of characters printed. 
  229.  
  230.  
  231. Floating Around
  232.  
  233. A complex function like PrintFloat deserves a section all of its own:
  234. This one describes PrintFloat and the problem associated with it. 
  235.  
  236. PrintFloat starts with the prefix: negative values have a - prefix;
  237. positive values have either nothing (default), a space (space flag
  238. present) or a + prefix (plus flag present, space flag present or not). 
  239.  
  240. At distance from the prefix resides the exponent. All formats but %f
  241. require it. The exponent results from dividing and multiplying the
  242. floating point number by ten, until its value rests between zero and one
  243. -- it totals all divisions minus all multiplic ations. The %g format
  244. forces vdprintf to choose between standard (%f) and engineering (%e)
  245. formats, whichever is shorter: If the exponent lies between -3 and
  246. precision (inclusive), standard format governs -- the exponent is
  247. cleared after it has been deduc ted from the precision; Otherwise, it's
  248. engineering. Either way, the precision loses one digit off its end. 
  249.  
  250. Here comes the tricky part. vdprintf must split a floating point number
  251. into integer and fraction parts. The integer rounding takes place by
  252. casting the floating point to int and back to float. Twice an integer
  253. is created. The second time, the part of the fraction to be printed is
  254. moved left of the decimal point, and again cast to int and back. We now
  255. have the integer and fraction parts stringized. 
  256.  
  257. The integer part prints first. If the %g but not varient format,
  258. trailing zeros are removed from the fraction. It may happen that no
  259. fraction remains, as if a zero precision. Only if the fraction follows
  260. or the # flag appears, does a period come after the integer. Unrelated,
  261. the exponent follows. An e, in the same case as is the format letter,
  262. precedes the exponent, which always contains a sign and at least two
  263. digits. 
  264.  
  265. Having printed a floating point number, we look back and notice that to
  266. do it a long double was cast to long int and back. On some systems, too
  267. long floating numbers may fail to convert properly, if at all. They may
  268. even raise an exception. The solution t o this problem is not
  269. complicated, neither perfect: Use the floor function to obtain the
  270. integer part of a floating point. This requires the floor math function
  271. (Standard C library), parts of PrintFloat changed, and a new function --
  272. ToFloat (see file "DP RINTF-F.C"), the floats' equivalent of ToInteger. 
  273. There is a catch, though: floor acts only on doubles, long doubles are
  274. not catered for. 
  275.  
  276. Depending on the system you use and your demands of it, either version
  277. of PrintFloat or both should work. If none complies, feel free to
  278. explore your math library, seeking functions that floor long doubles. 
  279. Hopefully, most readers will encounter no such d ifficulties. 
  280.  
  281.  
  282. Conclusion
  283.  
  284. I have a tendency to accompany the code I write with ample examples. 
  285. With your excuse, I will deviate from my inclination just this one time. 
  286. I will present no examples to prove dprintf works -- any program that
  287. finds use for printf can do that. And CUJ is full of such programs. 
  288.  
  289.  
  290.