home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 5 / Amiga Tools 5.iso / tools / developer-tools / aros / source / exec / kernel / src / rawdofmt.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-16  |  9.3 KB  |  402 lines

  1. /*
  2.     (C) 1995 AROS - The Amiga Replacement OS
  3.     $Id: rawdofmt.c 1.2 1995/12/17 21:48:47 digulla Exp digulla $
  4.     $Log: rawdofmt.c $
  5.  * Revision 1.2  1995/12/17  21:48:47  digulla
  6.  * Adjusted names of parameters in description
  7.  *
  8.  * Revision 1.1  1995/11/14  22:12:08  digulla
  9.  * Initial revision
  10.  *
  11.     Desc:
  12.     Lang: english
  13. */
  14. #include "exec_intern.h"
  15. #include "machine.h"
  16.  
  17. /*****************************************************************************
  18.  
  19.     NAME */
  20.     #include <dos/dos.h>
  21.     #include <clib/exec_protos.h>
  22.  
  23.     __AROS_LH4I(APTR, RawDoFmt,
  24.  
  25. /*  SYNOPSIS */
  26.     __AROS_LA(UBYTE   *, formatString, A0),
  27.     __AROS_LA(APTR     , dataStream, A1),
  28.     __AROS_LA(VOID_FUNC, putChProc, A2),
  29.     __AROS_LA(APTR     , putChData, A3),
  30.  
  31. /*  LOCATION */
  32.     struct ExecBase *, SysBase, 87, Exec)
  33.  
  34. /*  FUNCTION
  35.     printf-style formatting function with callback hook.
  36.  
  37.     INPUTS
  38.     formatString - Pointer to the format string with any of the following
  39.                stream formatting options allowed:
  40.  
  41.                %[leftalign][minwidth.][maxwidth][size][type]
  42.  
  43.                leftalign - '-' means align left. Default: align right.
  44.                minwidth  - minimum width of field. Defaults to 0.
  45.                maxwidth  - maximum width of field (for strings only).
  46.                    Defaults to no limit.
  47.                size     - 'l' means longword. Defaults to word.
  48.                type     - 'b' BCPL string. A BPTR to a one byte
  49.                        byte count followed by the characters.
  50.                    'c' single character.
  51.                    'd' signed decimal number.
  52.                    's' C string. NUL terminated.
  53.                    'u' unsigned decimal number.
  54.                    'x' unsigned sedecimal number.
  55.  
  56.     dataStream   - Array of the data items.
  57.     putChProc    - Callback function. Called for each character, including
  58.                the NUL terminator.
  59.     putChData    - Data propagated to each call of the callback hook.
  60.  
  61.     RESULT
  62.     Pointer to the rest of the dataStream.
  63.  
  64.     NOTES
  65.     The field size defaults to words which may be different from the
  66.     default integer size of the compiler.
  67.  
  68.     EXAMPLE
  69.     build a sprintf style function
  70.  
  71.     static void callback (UBYTE chr, UBYTE **data)
  72.     {
  73.         *(*data)++ = chr;
  74.     }
  75.  
  76.     void my_sprintf (UBYTE *buffer, UBYTE *format, ...)
  77.     {
  78.         RawDoFmt (format, &format+1, callback, &buffer);
  79.     }
  80.  
  81.     BUGS
  82.     putChData cannot be modified from the callback hook.
  83.  
  84.     SEE ALSO
  85.  
  86.     INTERNALS
  87.  
  88.     HISTORY
  89.     22-10-95    created by m. fleischer
  90.  
  91. ******************************************************************************/
  92. {
  93.     __AROS_FUNC_INIT
  94.     /* Cast for easier access */
  95.     typedef void (* __AROS_LIBCALL putch_t)(D0 UBYTE chr, A3 APTR data);
  96.     putch_t putch = (putch_t)putChProc;
  97.     ULONG stream = (ULONG)dataStream;
  98.  
  99.     /* As long as there is something to format left */
  100.     while(*formatString)
  101.     {
  102.     /* Check for '%' sign */
  103.     if(*formatString=='%')
  104.     {
  105.         /*
  106.         left     - left align flag
  107.         fill     - pad character
  108.         minus     - 1: number is negative
  109.         minwidth - minimum width
  110.         maxwidth - maximum width
  111.         larg     - long argument flag
  112.         width     - width of printable string
  113.         buf     - pointer to printable string
  114.         */
  115.         int left=0;
  116.         int fill=' ';
  117.         int minus=0;
  118.         ULONG minwidth=0;
  119.         ULONG maxwidth=~0;
  120.         int larg=0;
  121.         ULONG width=0;
  122.         UBYTE *buf;
  123.  
  124.         /*
  125.         Number of decimal places required to convert a unsigned long to
  126.         ascii. The formula is: ceil(number_of_bits*log10(2)).
  127.         Since I can't do this here I use .302 instead of log10(2) and
  128.         +1 instead of ceil() which most often leads to exactly the
  129.         same result (and never becomes smaller).
  130.  
  131.         Note that when the buffer is large enough for decimal it's
  132.         large enough for sedecimal as well.
  133.         */
  134. #define CBUFSIZE (sizeof(ULONG)*8*302/1000+1)
  135.         /* The buffer for converting long to ascii */
  136.         UBYTE cbuf[CBUFSIZE];
  137.         ULONG i;
  138.  
  139.         /* Skip over '%' character */
  140.         formatString++;
  141.  
  142.         /* '-' modifier? (left align) */
  143.         if(*formatString=='-')
  144.         left=*formatString++;
  145.  
  146.         /* '0' modifer? (pad with zeros) */
  147.         if(*formatString=='0')
  148.         fill=*formatString++;
  149.  
  150.         /* Get minimal width */
  151.         while(*formatString>='0'&&*formatString<='9')
  152.         minwidth=minwidth*10+(*formatString++-'0');
  153.  
  154.         /* Dot following width modifier? */
  155.         if(*formatString=='.')
  156.         formatString++;
  157.         else
  158.         {
  159.         /* No. It was in fact a maxwidth modifier */
  160.         maxwidth=minwidth;
  161.         minwidth=0;
  162.         /*
  163.             I could skip the next step here - but it doesn't really
  164.             matter because the next character can't be a number.
  165.         */
  166.         }
  167.  
  168.         /* Get maximum width */
  169.         if(*formatString>='0'&&*formatString<='9')
  170.         {
  171.         maxwidth=0;
  172.         do
  173.             maxwidth=maxwidth*10+(*formatString++-'0');
  174.         while(*formatString>='0'&&*formatString<='9');
  175.         }
  176.  
  177.         /* 'l' modifier? (long argument) */
  178.         if(*formatString=='l')
  179.         larg=*formatString++;
  180.  
  181.         /* Switch over possible format characters. Sets minus, width and buf. */
  182.         switch(*formatString)
  183.         {
  184.         /* BCPL string */
  185.         case 'b':
  186.             /* Get address, but align datastream first */
  187.             if(LONGALIGN>WORDALIGN)
  188.             stream=(stream+LONGALIGN-1)&~(LONGALIGN-1);
  189.             buf=(UBYTE *)BADDR(*(*(BPTR **)&stream)++);
  190.  
  191.             /* Set width */
  192.             width=*buf++;
  193.  
  194.             /* Strings may be modified with the maxwidth modifier */
  195.             if(width>maxwidth)
  196.             width=maxwidth;
  197.             break;
  198.  
  199.         /* signed decimal value */
  200.         case 'd':
  201.         /* unsigned decimal value */
  202.         case 'u':
  203.             {
  204.             ULONG n;
  205.  
  206.             /* Get value */
  207.             if(larg)
  208.             {
  209.                 /* Align datastream */
  210.                 if(LONGALIGN>WORDALIGN)
  211.                 stream=(stream+LONGALIGN-1)&~(LONGALIGN-1);
  212.                 /*
  213.                 For longs reading signed and unsigned
  214.                 doesn't make a difference.
  215.                 */
  216.                 n=*(*(ULONG **)&stream)++;
  217.  
  218.                 /* But for words it may do. */
  219.             }else
  220.                 /*
  221.                 Sorry - it may not: Stupid exec always treats
  222.                 UWORD as WORD even when 'u' is used.
  223.                 */
  224. #ifdef FIX_EXEC_BUGS
  225.                 if(*formatString=='d')
  226. #else
  227.                 if(1)
  228. #endif
  229.                 n=*(*(WORD **)&stream)++;
  230.                 else
  231.                 n=*(*(UWORD **)&stream)++;
  232.  
  233.             /* Negative number? */
  234.             if(*formatString=='d'&&(LONG)n<0)
  235.             {
  236.                 minus=1;
  237.                 n=-n;
  238.             }
  239.  
  240.             /* Convert to ASCII */
  241.             buf=&cbuf[CBUFSIZE];
  242.             do
  243.             {
  244.                 ULONG r;
  245.  
  246.                 /*
  247.                 divide 'n' by 10 and get quotient 'n'
  248.                 and remainder 'r'
  249.                 */
  250.                 UDIVMOD10(n,n,r);
  251.                 *--buf=r+'0';
  252.                 width++;
  253.             }while(n);
  254.             }
  255.             break;
  256.  
  257.         /* unsigned sedecimal value */
  258.         case 'x':
  259.             {
  260.             ULONG n;
  261.  
  262.             /* Get value */
  263.             if(larg)
  264.             {
  265.                 /* Align datastream */
  266.                 if(LONGALIGN>WORDALIGN)
  267.                 stream=(stream+LONGALIGN-1)&~(LONGALIGN-1);
  268.                 n=*(*(ULONG **)&stream)++;
  269.             }else
  270.                 n=*(*(UWORD **)&stream)++;
  271.  
  272.             /* Convert to ASCII */
  273.             buf=&cbuf[CBUFSIZE];
  274.             do
  275.             {
  276.                 /*
  277.                 Uppercase characters for lowercase 'x'?
  278.                 Stupid exec original!
  279.                 */
  280.                 *--buf="0123456789ABCDEF"[n&15];
  281.                 n>>=4;
  282.                 width++;
  283.             }while(n);
  284.             }
  285.             break;
  286.  
  287.         /* C string */
  288.         case 's':
  289.             {
  290.             UBYTE *buffer;
  291.  
  292.             /* Get address, but align datastream first */
  293.             if(PTRALIGN>WORDALIGN)
  294.                 stream=(stream+PTRALIGN-1)&~(PTRALIGN-1);
  295.             buf=*(*(UBYTE ***)&stream)++;
  296.  
  297.             /* width=strlen(buf) */
  298.             buffer=buf;
  299.             while(*buffer++)
  300.                 ;
  301.             width=~(buf-buffer);
  302.  
  303.             /* Strings may be modified with the maxwidth modifier */
  304.             if(width>maxwidth)
  305.                 width=maxwidth;
  306.             }
  307.             break;
  308.  
  309.         /* single character */
  310.         case 'c':
  311.             /* Some space for the result */
  312.             buf=cbuf;
  313.             width=1;
  314.  
  315.             /* Get value */
  316.             if(larg)
  317.             {
  318.             /* Align datastream */
  319.             if(LONGALIGN>WORDALIGN)
  320.                 stream=(stream+LONGALIGN-1)&~(LONGALIGN-1);
  321.             *buf=*(*(ULONG **)&stream)++;
  322.             }else
  323.             *buf=*(*(UWORD **)&stream)++;
  324.             break;
  325.  
  326.         /* '%' before '\0'? */
  327.         case '\0':
  328.             /*
  329.             This is nonsense - but do something useful:
  330.             Instead of reading over the '\0' reuse the '\0'.
  331.             */
  332.             formatString--;
  333.             /* Get compiler happy */
  334.             buf=NULL;
  335.             break;
  336.  
  337.         /* Convert '%unknown' to 'unknown'. This includes '%%' to '%'. */
  338.         default:
  339.             buf=formatString;
  340.             width=1;
  341.             break;
  342.         }
  343.         /* Skip the format character */
  344.         formatString++;
  345.  
  346.         /*
  347.         Now everything I need is known:
  348.         buf     - contains the string to be printed
  349.         width     - the size of the string
  350.         minus     - is 1 if there is a '-' to print
  351.         fill     - is the pad character
  352.         left     - is 1 if the string should be left aligned
  353.         minwidth - is the minimal width of the field
  354.         (maxwidth is already part of width)
  355.  
  356.         So just print it.
  357.         */
  358.  
  359.         /*
  360.         Stupid exec always prints the '-' sign directly before
  361.         the decimals. Even if the pad character is a '0'.
  362.         */
  363. #ifdef FIX_EXEC_BUGS
  364.         /* Print '-' (if there is one and the pad character is no space) */
  365.         if(minus&&fill!=' ')
  366.         (*putch)('-',putChData);
  367. #endif
  368.         /* Pad left if not left aligned */
  369.         if(!left)
  370.         for(i=width+minus;i<minwidth;i++)
  371.             (*putch)(fill,putChData);
  372.  
  373.         /* Print '-' (if there is one and the pad character is a space) */
  374. #ifdef FIX_EXEC_BUGS
  375.         if(minus&&fill==' ')
  376. #else
  377.         if(minus)
  378. #endif
  379.         (*putch)('-',putChData);
  380.  
  381.         /* Print body upto width */
  382.         for(i=0;i<width;i++)
  383.         (*putch)(*buf++,putChData);
  384.  
  385.         /* Pad right if left aligned */
  386.         if(left)
  387.         for(i=width+minus;i<minwidth;i++)
  388.             /* Pad right with '0'? Sigh - if the user wants to! */
  389.             (*putch)(fill,putChData);
  390.     }else
  391.         /* No '%' sign? Put the formatstring out */
  392.         (*putch)(*formatString++,putChData);
  393.     }
  394.     /* All done. Put the terminator out. */
  395.     (*putch)('\0',putChData);
  396.  
  397.     /* Return the rest of the datastream. */
  398.     return (APTR)stream;
  399.     __AROS_FUNC_EXIT
  400. } /* RawDoFmt */
  401.  
  402.