home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / 1988 / 04 / holub / holub.lst < prev   
File List  |  1979-12-31  |  16KB  |  383 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.             Listing One -- C:/SRC/TOOLS/IDOPRNT.C,  Printed 1/10/1988
  8.    ____________________________________________________________________________
  9.   1| #include <stdarg.h> /* ANSI variable-argument defines           */
  10.   2| #include <dos.h>    /* uSoft: defines for FP_OFF and FP_SEG     */
  11.   3| 
  12.   4| /* IDOPRNT - integer-only printf/fprintf/sprintf workhorse
  13.   5|  *           function. 
  14.   6|  *
  15.   7|  *           (C) 1988 Allen I. Holub. All rights reserved.
  16.   8|  *
  17.   9|  * The following conversions are supported:
  18.  10|  *
  19.  11|  *      %d %ld  decimal, long decimal
  20.  12|  *      %u      unsigned (int only, no longs)
  21.  13|  *      %s      string
  22.  14|  *      %x %lx  hex, long hex
  23.  15|  *      %o %lo  octal, long octal
  24.  16|  *      %b %lb  binary, long binary                     (nonstandard)
  25.  17|  *      %p      far pointer (in hex XXXX:XXXX)
  26.  18|  *
  27.  19|  *      Note that it's impossible to get zero fill in the offset
  28.  20|  *      part of a %p conversion. That is 1234:0001 is always
  29.  21|  *      printed as 1234:1. Sorry. The following modifiers are
  30.  22|  *      supported for all conversions:
  31.  23|  *
  32.  24|  *      %0x     zero left fill
  33.  25|  *      %-x     left justification in field
  34.  26|  *      %|x     centered in field               (nonstandard)
  35.  27|  *      %*x     field width from first argument
  36.  28|  *
  37.  29|  *      Precision is supported in strings:
  38.  30|  *
  39.  31|  *      %X.Ys   X-character wide field, print at most Y characters
  40.  32|  *              (even if the string is longer). If X or Y is a *,
  41.  33|  *              the width is taken from the next argument.
  42.  34|  *
  43.  35|  *      Potential portability problems:
  44.  36|  *
  45.  37|  *      %p is a FAR pointer. I've used the "far" keyword to
  46.  38|  *      declare it as such and I've used the FP_SEG and FP_OFF
  47.  39|  *      to extract the segment and offset parts of the pointer.
  48.  40|  *      The offset part of a near pointes can be printed by
  49.  41|  *      casting it to an int and using %x.
  50.  42|  */
  51.  43| 
  52.  44| extern char     *ltos(long, char*, int);
  53.  45| 
  54.  46| /*------------------------------------------------------------
  55.  47|  * Macros to save some code space below. If your compiler
  56.  48|  * doesn't accept multi-line macros, remove the backslashes
  57.  49|  * and merge the entire definition onto one line.
  58.  50|  * These both have horrible side effects. Be careful.
  59.  51|  *
  60.  52|  * PAD(fw,filchar,out,op)   outputs filchar, fw times. fw = 0.
  61.  53|  * TOINT(p,x)               works like "x = atoi(p);" except p
  62.  54|  *                              is advanced past the number.
  63.  55|  */
  64.  56| 
  65.  57| #define PAD(fw,fc,out,op)    while( --(fw) >= 0 )       \
  66.  58|                                 (*out)( fc, op )
  67.  59| 
  68.  60| #define TOINT(p,x)  while( '0' <= *p  &&  *p <= '9' )   \
  69.  61|                         x = (x * 10) + (*p++ - '0')
  70.  62| 
  71.  63| /*------------------------------------------------------------
  72.  64|  * INTMASK is a portable way to mask off the bottom N bits
  73.  65|  * of a long, were N is the width of an int.
  74.  66|  */
  75.  67| 
  76.  68| #define INTMASK         (long)( (unsigned)(~0) )
  77.  69| 
  78.  70| /*------------------------------------------------------------*/
  79.  71| 
  80.  72| void    idoprnt( out, o_param, format, args )
  81.  73| 
  82.  74| int     (*out)();       /* output subroutine               */
  83.  75| void    *o_param;       /* 2nd argument to pass to out()   */
  84.  76| char    *format;        /* pointer to format string        */
  85.  77| va_list args;           /* pointer to arguments            */
  86.  78| {
  87.  79|     char filchar ;      /* Fill character used to pad fields */
  88.  80|     char nbuf[34];      /* Buffer used to hold converted #s  */
  89.  81|     char *bp     ;      /* Pointer to current output buffer  */
  90.  82|     int slen     ;      /* length of string pointe to by bp  */
  91.  83|     int base     ;      /* Current base (%x=16, %d=10, etc.) */
  92.  84|     int fldwth   ;      /* Field width as in %10x            */
  93.  85|     int precision;      /* Precision as in %10.10s or %10.3f */
  94.  86|     int lftjust  ;      /* 1 = left justifying (ie. %-10d)   */
  95.  87|     int center   ;      /* 1 = centered (ie. %|10d)          */
  96.  88|     int longf    ;      /* doing long int (ie. %lx or %X)    */
  97.  89|     long lnum    ;      /* used to hold numeric arguments    */
  98.  90|     void far *pnum;     /* Pointer-sized number              */
  99.  91| 
  100.  92|     for(; *format ; format++ )
  101.  93|     {
  102.  94|         if( *format != '%')            /* No conversion, just  */
  103.  95|         {                              /* print the next char. */
  104.  96|             (*out)(*format, o_param);
  105.  97|         }
  106.  98|         else                            /* Process a % conversion */
  107.  99|         {
  108. 100|             bp          = nbuf  ;
  109. 101|             filchar     = ' '   ;
  110. 102|             fldwth      = 0     ;
  111. 103|             lftjust     = 0     ;
  112. 104|             center      = 0     ;
  113. 105|             longf       = 0     ;
  114. 106|             precision   = 0     ;
  115. 107|             slen        = 0     ;
  116. 108| 
  117. 109|             /*  Interpret any modifiers that can precede a
  118. 110|              *  conversion character. (ie %04x , %-10.6s... etc).
  119. 111|              *  if a * is present instead of a field width then
  120. 112|              *  the width is taken from the argument list.
  121. 113|              */
  122. 114| 
  123. 115|             if( *++format == '-') { ++format ;  ++lftjust;     }
  124. 116|             if( *format   == '|') { ++format ;  ++center;      }
  125. 117|             if( *format   == '0') { ++format ;  filchar = '0'; }
  126. 118| 
  127. 119|             if( *format != '*' )
  128. 120|                 TOINT( format, fldwth );
  129. 121|             else
  130. 122|             {
  131. 123|                 ++format;
  132. 124|                 fldwth = va_arg(args, int);
  133. 125|             }
  134. 126| 
  135. 127|             if( *format == '.' )
  136. 128|             {
  137. 129|                 if( *++format != '*' )
  138. 130|                     TOINT( format, precision );
  139. 131|                 else
  140. 132|                 {
  141. 133|                     ++format;
  142. 134|                     precision = va_arg(args, int);
  143. 135|                 }
  144. 136|             }
  145. 137| 
  146. 138|             if( *format == 'l' || *format == 'L' )
  147. 139|             {
  148. 140|                 ++format;
  149. 141|                 ++longf ;
  150. 142|             }
  151. 143| 
  152. 144| 
  153. 145|             /* By now we've picked off all the modifiers and
  154. 146|              * *format is looking at the actual conversion
  155. 147|              * character. Pick the appropriatly sized argument
  156. 148|              * off the stack and advanced the pointer (ap) to
  157. 149|              * point at the next argument.
  158. 150|              */
  159. 151| 
  160. 152|             switch( *format )
  161. 153|             {
  162. 154|             default:    *bp++ = *format ;               break;
  163. 155|             case 'c':   *bp++ = va_arg(args, int   );   break;
  164. 156|             case 's':   bp    = va_arg(args, char *);   break;
  165. 157| 
  166. 158|             case 'p':   
  167. 159|                 pnum  = va_arg(args, void far *);
  168. 160|                 bp    = ltos( (unsigned long)FP_SEG(pnum), bp, 16);
  169. 161|                 *bp++ = ':';
  170. 162|                 bp    = ltos( (unsigned long)FP_OFF(pnum), bp, 16 );
  171. 163|                 break;
  172. 164| 
  173. 165|             case 'u':   base = -10 ; goto pnum ;
  174. 166|             case 'd':   base = 10  ; goto pnum ;
  175. 167|             case 'x':   base = 16  ; goto pnum ;
  176. 168|             case 'b':   base =  2  ; goto pnum ;
  177. 169|             case 'o':   base =  8  ;
  178. 170| pnum:
  179. 171|                 /* Fetch a long or int sized argument off the
  180. 172|                  * stack as appropriate. If the fetched number
  181. 173|                  * is a base 10 int then mask off the top
  182. 174|                  * bits to prevent sign extension.
  183. 175|                  */
  184. 176| 
  185. 177|                 if( longf )
  186. 178|                     lnum = va_arg(args, long);
  187. 179|                 else
  188. 180|                 {
  189. 181|                     lnum = (long) va_arg(args, int);
  190. 182| 
  191. 183|                     if( base == -10 )       /* Unsigned int */
  192. 184|                     {
  193. 185|                         base  = 10;
  194. 186|                         lnum &= INTMASK;
  195. 187|                     }
  196. 188|                     else if( base != 10 )   /* Nondecimal int */
  197. 189|                     {
  198. 190|                         lnum &= INTMASK;
  199. 191|                     }
  200. 192|                 }
  201. 193| 
  202. 194|                 if( lnum < 0L  &&  base == 10  && filchar == '0' )
  203. 195|                 {
  204. 196|                         /* Again, print a - to avoid "000-123."
  205. 197|                          * Only decimal numbers get - signs.
  206. 198|                          */
  207. 199| 
  208. 200|                         (*out)( '-' ,o_param) ;
  209. 201|                         --fldwth ;
  210. 202|                         lnum = -lnum ;
  211. 203|                 }
  212. 204| 
  213. 205|                 bp   = ltos( lnum, bp, base );
  214. 206|                 break;
  215. 207|             }
  216. 208| 
  217. 209|             /*  Terminate the string if necessary and compute
  218. 210|              *  the string length (slen). Bp will point at the
  219. 211|              *  beginning of the output string.
  220. 212|              */
  221. 213| 
  222. 214|             if (*format != 's')
  223. 215|             {
  224. 216|                 *bp  = '\0';
  225. 217|                 slen = bp - nbuf;
  226. 218|                 bp   = nbuf;
  227. 219|             }
  228. 220|             else
  229. 221|             {
  230. 222|                 slen = strlen(bp);
  231. 223|                 if( precision  &&  slen > precision )
  232. 224|                         slen = precision;
  233. 225|             }
  234. 226| 
  235. 227| 
  236. 228|             /*  Adjust fldwth to be the amount of padding we need
  237. 229|              *  to fill the buffer out to the specified field
  238. 230|              *  width. Then print leading padding (if we aren't
  239. 231|              *  left justifying), the buffer itself, and any
  240. 232|              *  required trailing padding (if we are left
  241. 233|              *  justifying.
  242. 234|              */
  243. 235| 
  244. 236|             if( (fldwth -= slen) < 0 )
  245. 237|                 fldwth = 0;
  246. 238| 
  247. 239|             if( center )
  248. 240|             {
  249. 241|                                    /* Use longf as counter */
  250. 242|                 longf = fldwth/2;
  251. 243|                 PAD( longf, filchar, out, o_param );
  252. 244|             }
  253. 245|             else if( !lftjust )
  254. 246|                 PAD( fldwth,   filchar, out, o_param );
  255. 247| 
  256. 248|             while( --slen >= 0 )
  257. 249|                 (*out)(*bp++,o_param);
  258. 250| 
  259. 251|             if( center )
  260. 252|             {
  261. 253|                 longf = fldwth - fldwth/2;
  262. 254|                 PAD( longf, filchar, out, o_param );
  263. 255|             }
  264. 256|             else if( lftjust)
  265. 257|                 PAD( fldwth, filchar, out, o_param );
  266. 258| 
  267. 259|         }
  268. 260|     }
  269. 261| }
  270. 262| 
  271. 263| /*------------------------------------------------------------*/
  272. 264| 
  273. 265| #ifdef DEBUG
  274. 266| 
  275. 267| #include <stdio.h>
  276. 268| 
  277. 269| printm( fmt, ... )
  278. 270| char    *fmt;
  279. 271| {
  280. 272|         extern int  fputc();
  281. 273|         va_list args;
  282. 274|         va_start(args, fmt);
  283. 275|         idoprnt( fputc, stdout, fmt, args );
  284. 276| }
  285. 277| 
  286. 278| /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  287. 279| 
  288. 280| main()
  289. 281| {
  290. 282|     printm("should see <0 1 2 3 4 5 6 7>: ");
  291. 283|     printm("           %d %x %o %ld %lx %lo %c %s\n",
  292. 284|                              0, 1, 2, 3L, 4L, 5L, '6', "7" );
  293. 285| 
  294. 286|     printf("should see: hello world: "          );
  295. 287|     printm("%s %s %c", "hello", "world", '\n'   );
  296. 288| 
  297. 289|     printm("should see <string>  : <%6.6s>\n",  "string NO NO" );
  298. 290|     printm("should see <   str>  : <%*.*s>\n",  6, 3, "string" );
  299. 291|     printm("should see <70000>   : <%ld>\n", 70000L     );
  300. 292|     printm("should see <fffff>   : <%lx>\n",0xfffffL);
  301. 293|     printm("should see <ffff>    : <%x>\n",     -1      );
  302. 294|     printm("should see <-1>      : <%ld>\n",    -1L     );
  303. 295|     printm("should see <x>       : <%c>\n",     'x'     );
  304. 296|     printm("should see <  x  >   : <%|5c>\n",   'x'     );
  305. 297|     printm("should see <a5>      : <%x>\n",     0xa5    );
  306. 298|     printm("should see <765>     : <%o>\n",     0765    );
  307. 299|     printm("should see <1010>    : <%b>\n",     0xa     );
  308. 300|     printm("should see <   123>  : <%6d>\n",    123     );
  309. 301|     printm("should see <   456>  : <%*d>\n",  6,456     );
  310. 302|     printm("should see <  -123>  : <%6d>\n",   -123     );
  311. 303|     printm("should see <123   >  : <%-6d>\n",   123     );
  312. 304|     printm("should see <-123  >  : <%-6d>\n",  -123     );
  313. 305|     printm("should see <-00123>  : <%06d>\n",  -123     );
  314. 306|     printm("should see <abcd:123>: <%p>\n",
  315. 307|                    (char far *)( 0xabcd0123L) );
  316. 308| } 
  317. 309| #endif
  318.  
  319.                Listing Two -- /SRC/TOOLS/LTOS.C, Printed 1/10/1988
  320.    ____________________________________________________________________________
  321.   1| /* LTOS.C       Convert long to string in indicated base.
  322.   2|  *              (C) 1988 Allen I. Holub. All rights reserved.
  323.   3|  */
  324.   4| 
  325.   5| char    *ltos( n, buf, base )
  326.   6| unsigned long   n    ;
  327.   7| char            *buf ;
  328.   8| int             base ;
  329.   9| {
  330.  10|     /*  Convert long to string. Prints in hex, decimal, octal,
  331.  11|      *                                          or binary.
  332.  12|      *  "n"    is the number to be converted
  333.  13|      *  "buf"  is the output buffer.
  334.  14|      *  "base" is the base (16, 10, 8 or 2).
  335.  15|      *  
  336.  16|      *  The output string is null terminated and a pointer to the
  337.  17|      *  null terminator is returned.
  338.  18|      *
  339.  19|      *  The number is put into an array one digit at a time as
  340.  20|      *  it's translated. The array is filled with the digits
  341.  21|      *  reversed (ie. the \0 goes in first, then the rightmost
  342.  22|      *  digit, etc.) and then is reversed in place before
  343.  23|      *  returning.
  344.  24|      *
  345.  25|      *  This routine is much like the unix() ltoa except that
  346.  26|      *  it returns a pointer to the end of the string.
  347.  27|      */
  348.  28| 
  349.  29|     register char       *bp   = buf;
  350.  30|     register int        minus = 0;
  351.  31|     char                *endp;
  352.  32| 
  353.  33|     if( base < 2 || base > 16 )
  354.  34|             return 0;
  355.  35| 
  356.  36|     if( base == 10 && (long)n < 0 ) /* If the number is negative */
  357.  37|     {                               /* and we're in base 10, set */
  358.  38|         minus++ ;                   /* minus to true and make it */
  359.  39|         n = -( (long)n );           /* positive.                 */
  360.  40|     }
  361.  41| 
  362.  42|     *bp = '\0' ;            /* Have to put the null in now */
  363.  43|                             /* because the array is being  */
  364.  44|                             /* filled in reverse order.    */
  365.  45|     do {
  366.  46|             *++bp = "0123456789abcdef" [ n % base ];
  367.  47|             n /= base;
  368.  48| 
  369.  49|     } while( n );
  370.  50| 
  371.  51|     if( minus )
  372.  52|             *++bp = '-';
  373.  53| 
  374.  54|     for( endp = bp; bp > buf ;)      /* Reverse string in place */
  375.  55|     {
  376.  56|             minus  = *bp;            /* Use minus for temporary */
  377.  57|             *bp -- = *buf;           /* storage.                */
  378.  58|             *buf++ = minus;
  379.  59|     }
  380.  60| 
  381.  61|     return endp;        /* Return pointer to terminating null   */
  382.  62| }
  383.