Category : Files from Magazines
Archive   : DDJ-9008.ZIP
Filename : MISCHEL.LST

 
Output of file : MISCHEL.LST contained in archive : DDJ-9008.ZIP
_EXTENDING PRINTF()_
by Jim Mischel

[LISTING ONE]

/* mfmt.h -- macros and function prototypes for mfmt routine. */

#define printf AltPrintf
#define fprintf AltFprintf
#define sprintf AltSprintf
#define vprintf AltVprintf
#define vfprintf AltVfprintf
#define vsprintf AltVsprintf

int AltPrintf (char *fmt, ...);
int AltFprintf (FILE *f, char *fmt, ...);
int AltSprintf (char *Dest, char *fmt, ...);
int AltVprintf (char *fmt, va_list Arg);
int AltVfprintf (FILE *f, char *fmt, va_list Arg);
int AltVsprintf (char *Dest, char *fmt, va_list Arg);



[LISTING TWO]

/* mfmt.c -- function to output comma-separated numbers with printf().
* Copyright 1990, Jim Mischel
* To compile for testing:
* tcc -ms -f mfmt
* To compile for use as a called module:
* tcc -ms -c -f mfmt
*/
/* #define TESTING /* remove comment for testing */
#include "stdio.h"
#include "stdlib.h"
#include "ctype.h"
#include "string.h"
#include "math.h"

#define AddBlockArg(ap, type) (*((type *)(NewArgPtr))++) = va_arg(ap, type)
#define RemoveBlockArg(type) ((type *)(NewArgPtr))--

static va_list OldArgPtr; /* Pointer to passed arguments */
static va_list NewArgPtr; /* Pointer to new argument block */
static va_list NewBlock = NULL; /* New argument block */
static char *Sptr; /* Pointer to passed format string */
static char *Num; /* Formatted number */
static char *NumPtr; /* Pointer into formatted number string */
static char *t = NULL; /* New format string */
unsigned tSize; /* Length of new format string */
static char *Tptr; /* Pointer to new format string */
static char *SavePtr; /* Saved Tptr used when %m format found */
static int Flags; /* Flags identified in format string */
static int Width; /* Width from format string */
static int WidthFlag; /* Identifies width type */
static int Precision; /* Width from format string */
static int PrecisionFlag; /* Identifies precision type */
static int Size; /* printf() size modifier */
static int Sign; /* Sign of number for formatting */

/* Increment the flags counter for each occurance of a flag character.
* Valid flag characters are blank, '-', '#', '+'. */
/* Definitions for flag characters */
#define Blank 1
#define Dash 2
#define Pound 4
#define Plus 8

static void ParseFlags (void) {
Flags = 0;
while (*Sptr == ' ' || *Sptr == '-' || *Sptr == '#' || *Sptr == '+')
switch (*Tptr++ = *Sptr++) {
case ' ' : Flags += Blank; break;
case '-' : Flags += Dash; break;
case '#' : Flags += Pound; break;
case '+' : Flags += Plus; break;
} /* switch */
} /* ParseFlags */

/* Width specification. Minimum field width is returned in the Width variable.
* WidthFlag specifies if '0' or '*' was given in the format string. */
static void ParseWidth (void) {
Width = WidthFlag = 0;

if (*Sptr == '0')
WidthFlag = (*Tptr++ = *Sptr++);
if (*Sptr == '*') {
WidthFlag |= 0x80;
*Tptr++ = *Sptr++;
Width = (AddBlockArg(OldArgPtr, int));
if (Width < 0) {
Width = abs (Width);
Flags |= Dash;
}
}
while (isdigit (*Sptr)) {
Width = Width * 10 + *Sptr - '0';
*Tptr++ = *Sptr++;
}
} /* ParseWidth */

/* Precision specification. Returns precision in the variables Precision and
* PrecisionFlag.
* PrecisionFlag = 0, no precision was specified
* PrecisionFlag = '0', ".0" specified
* PrecisionFlag = 'n', ".n" specified
* PrecisionFlag = '*', ".*" specified
*/
static void ParsePrecision (void) {
Precision = PrecisionFlag = 0;

if (*Sptr == '.') { /* precision specified */
*Tptr++ = *Sptr++;
if (*Sptr == '*') {
PrecisionFlag = (*Tptr++ = *Sptr++);
Precision = (AddBlockArg(OldArgPtr, int));
}
else if (*Sptr == '0')
PrecisionFlag = (*Tptr++ = *Sptr++);
else {
PrecisionFlag = 'n';
while (isdigit (*Sptr)) {
Precision = Precision * 10 + *Sptr - '0';
*Tptr++ = *Sptr++;
}
}
}
} /* ParsePrecision */

/* Input size modifier. (F|N|h|l|L) */
static void ParseSize (void) {
if (*Sptr == 'F' || *Sptr == 'l' || *Sptr == 'L' ||
*Sptr == 'N' || *Sptr == 'h')
Size = (*Tptr++ = *Sptr++);
else
Size = 0;
} /* ParseSize */

/* dtoa -- convert a double to comma-separated ASCII representation. */
static void dtoa (double Work, int P, int Digits) {
int c;

if (P >= 0 || Work != 0) {
if (P == 0) {
c = '.';
Digits = 0;
P--;
}
else if (Digits == 3) {
c = ',';
Digits = 0;
}
else {
c = (int)(fmod (Work, 10))+'0';
modf (Work/10, &Work);
if (P > 0)
P--;
else
Digits++;
}
dtoa (Work, P, Digits);
*NumPtr++ = c;
}
} /* dtoa */

/* Right- or left- justifies the formatted number in the string.
* If the number is too large for the specified width, the number
* field is filled with the asterisk character (*). */
static void Justify (void) {
if ((WidthFlag & 0x7f) == '0') /* check width */
if (strlen (Num) > Width) {
memset (Num, '*', Width);
NumPtr = Num + Width;
*NumPtr = '\0';
return;
}
if (strlen (NumPtr) < Width) {
if (Flags & Dash) /* left justify */
memset (NumPtr, ' ', Width - strlen (Num));
else { /* right justify */
int n = Width - strlen (Num);
char *p = Num;
memmove (p + n, p, strlen (Num));
memset (p, ' ', n);
}
NumPtr = Num + Width;
*NumPtr = '\0';
}
} /* Justify */

/* Reports an out of memory error and aborts the program. */
static void MemoryError (char *s) {
fprintf (stderr, "Out of memory in function %s\n", s);
exit (1);
} /* MemoryError */

/* Format 'm' type into the new format string t. All parameters (flags, width,
* precision, size, and type) are stored in the respective variables. */
static void mFormat (void) {
double Work;
/* Move the block pointer back where it belongs if there were width or
* precision specifications in the argument list. */
*SavePtr = '\0';
if (PrecisionFlag == '*')
RemoveBlockArg (int);
if (WidthFlag & 0x80)
RemoveBlockArg (int);

if ((Num = malloc (128)) == NULL)
MemoryError ("mFormat");
NumPtr = Num;
/* The next argument in the list is the number that is to be formatted.
* This number will either be a float, a double, or a long double. The
* input size modifier will tell us what type. Whatever type it is, it will
* be copied into the double variable Work for us to work with. */
if (Size == 'l') Work = va_arg (OldArgPtr, double);
else if (Size == 'L') Work = (double) va_arg (OldArgPtr, long double);
else Work = (double) va_arg (OldArgPtr, float);
Sign = (Work < 0) ? -1 : 1;
if (!PrecisionFlag)
Precision = 2;
dtoa (floor (fabs (Work) * pow10 (Precision)),
(Precision == 0) ? -1 : Precision, 0);
*NumPtr = '\0';
/* The number is formatted into Num. Precision was handled by the dtoa()
* function. Add the sign and perform padding/justifying as necessary.
* Determine the proper sign */
if (Sign == -1) Sign = '-';
else if (Flags & Plus) Sign = '+';
else if (Flags & Blank) Sign = ' ';
else Sign = '\0';

if (Sign != '\0') /* Place sign */
if (Flags & Pound) { /* trailing sign */
*NumPtr++ = Sign;
*NumPtr = '\0';
}
else { /* leading sign */
memmove (Num+1, Num, (NumPtr - Num));
*Num = Sign;
NumPtr++;
}
Justify ();
/* Now re-allocate the string to add more characters and then append the
* newly-formatted number to the string and release the memory taken by
* the formatted number string. */
if ((t = realloc (t, (tSize += strlen (Num)))) == NULL)
MemoryError ("mFormat");
strcat (t, Num);
Tptr = t + strlen (t);
free (Num);
} /* mFormat */

/* Determine the conversion type. For any type but 'm', simply copy the passed
* argument to the new argument list and return. */
static void ParseType (void) {
switch (*Tptr++ = *Sptr++) {
case 'd' :
case 'i' :
case 'o' :
case 'u' :
case 'x' :
case 'X' :
case 'c' : /* in Turbo C, char is treated as int */
if (Size == 'l') AddBlockArg(OldArgPtr, long);
else AddBlockArg(OldArgPtr, int);
break;
case 'f' :
case 'e' :
case 'g' :
case 'E' :
case 'G' :
if (Size == 'l') AddBlockArg(OldArgPtr, double);
else if (Size == 'L') AddBlockArg(OldArgPtr, long double);
else AddBlockArg(OldArgPtr, float);
break;
/* In Turbo C, pointers to all types are the same size */
case 's' :
case 'n' :
case 'p' :
if (Size == 'F') AddBlockArg(OldArgPtr, char far *);
else if (Size == 'N') AddBlockArg(OldArgPtr, char near *);
else AddBlockArg(OldArgPtr, char *);
break;
case 'm' :
mFormat (); /* format 'm' type */
break;
default : /* anything else isn't defined */
break;
} /* switch */
} /* ParseType */

/* Parse a standard printf() format. */
static void ParseFormat (void) {

SavePtr = Tptr - 1;

ParseFlags ();
ParseWidth ();
ParsePrecision ();
ParseSize ();
ParseType ();
} /* ParseFormat */

/* Copy the input format string to the new format string t, replacing all %m
* formats with the formatted digit string. Arguments will be placed in the
* ParamBlock structure, with the %m parameters removed. */
static void Formatter (char *s, va_list Arg) {
Sptr = s;
/* Allocate memory for new format string. */
if ((t = malloc (tSize = strlen (s))) == NULL)
MemoryError ("Formatter");
Tptr = t;
OldArgPtr = Arg;
while (*Sptr != '\0')
if ((*Tptr++ = *Sptr++) == '%')
ParseFormat ();
va_end (OldArgPtr);
*Tptr = '\0';
} /* Formatter */

/* Allocate a block of memory for the modified argument list. */
static void InitBlock (char *s) {
int BlockSize = 0;

while (*s != '\0') /* count the '%' characters */
if (*s == '%') /* in the format string */
BlockSize++;
/* Multiply the number of '%' by the size of a long double, giving some
* idea of the maximum required size of the new parameter block. It's
* crude and implementation dependent, but it works. */
BlockSize *= sizeof (long double);
if ((NewBlock = malloc (BlockSize)) == NULL)
MemoryError ("InitBlock");
NewArgPtr = NewBlock;
} /* InitBlock */

/* Free the memory used by the modified argument list (if any) and the
* modified format string. */
static void FreeBlock (void) {
free (t);
free (NewBlock);
NewBlock = t = NULL;
} /* FreeBlock */

/* These 6 routines are the only functions visible to the application program.
* They are accessed though the printf, fprintf, sprintf, vprintf, vfprintf,
* and vsprintf macros, respectively. */
int AltPrintf (char *fmt, ...) {
va_list Arg;
int r;
va_start (Arg, fmt);
Formatter (fmt, NewArgPtr = Arg);
r = vprintf (t, Arg);
FreeBlock ();
return r;
} /* AltPrintf */

int AltFprintf (FILE *f, char *fmt, ...) {
va_list Arg;
int r;
va_start (Arg, fmt);
Formatter (fmt, NewArgPtr = Arg);
r = vfprintf (f, t, Arg);
FreeBlock ();
return r;
} /* AltFprintf */

int AltSprintf (char *Dest, char *fmt, ...) {
va_list Arg;
int r;
va_start (Arg, fmt);
Formatter (fmt, NewArgPtr = Arg);
r = vsprintf (Dest, t, Arg);
FreeBlock ();
return r;
} /* AltSprintf */

int AltVprintf (char *fmt, va_list Arg) {
int r;
InitBlock (fmt);
Formatter (fmt, Arg);
r = vprintf (t, NewBlock);
FreeBlock ();
return r;
} /* AltVprintf */

int AltVfprintf (FILE *f, char *fmt, va_list Arg) {
int r;
InitBlock (fmt);
Formatter (fmt, Arg);
r = vfprintf (f, t, NewBlock);
FreeBlock ();
return r;
} /* AltVfprintf */

int AltVsprintf (char *Dest, char *fmt, va_list Arg) {
int r;
InitBlock (fmt);
Formatter (fmt, Arg);
r = vsprintf (Dest, t, NewBlock);
FreeBlock ();
return r;
} /* AltVsprintf */

#ifdef TESTING
#include "mfmt.h"
void main (void) {
printf ("The national debt exceeds $%.0lm\n", (double)1000000000000.0);
}
#endif




[Figure 1: Stack contents for printf() and vprintf() calls that output the
information contained in the Person structure.]

struct Person {
char *Name;
int Age;
} Jim = {"Jim Mischel", 29};

(a) printf ("%s is %d years old.\n", Jim.Name, Jim.Age);

/-------------------\
| Jim.Age |
|-------------------|
| Jim.Name |
|-------------------|
| &(format string) |
|-------------------|
| return address |
\-------------------/

(b) vprintf ("%s is %d years old.\n", &Jim);

/-------------------\
| &Jim |
|-------------------|
| &(format string) |
|-------------------|
| return address |
\-------------------/





  3 Responses to “Category : Files from Magazines
Archive   : DDJ-9008.ZIP
Filename : MISCHEL.LST

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/