home *** CD-ROM | disk | FTP | other *** search
/ Aminet 18 / aminetcdnumber181997.iso / Aminet / misc / emu / AROSdev.lha / AROS / rom / exec / rawdofmt.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-01-09  |  10.2 KB  |  441 lines

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