home *** CD-ROM | disk | FTP | other *** search
/ BCI NET 2 / BCI NET 2.iso / archives / programming / languages / fpl-v115.lha / FPL / src / sprintf.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-12-02  |  13.4 KB  |  523 lines

  1. /******************************************************************************
  2.  *                   FREXX PROGRAMMING LANGUAGE                  *
  3.  ******************************************************************************
  4.  
  5.  sprintf.c
  6.  
  7.  Sprintf() function source code.
  8.  
  9.  *****************************************************************************/
  10.  
  11. /************************************************************************
  12.  *                                                                      *
  13.  * fpl.library - A shared library interpreting script langauge.         *
  14.  * Copyright (C) 1992-1994 FrexxWare                                    *
  15.  * Author: Daniel Stenberg                                              *
  16.  *                                                                      *
  17.  * This program is free software; you may redistribute for non          *
  18.  * commercial purposes only. Commercial programs must have a written    *
  19.  * permission from the author to use FPL. FPL is *NOT* public domain!   *
  20.  * Any provided source code is only for reference and for assurance     *
  21.  * that users should be able to compile FPL on any operating system     *
  22.  * he/she wants to use it in!                                           *
  23.  *                                                                      *
  24.  * You may not change, resource, patch files or in any way reverse      *
  25.  * engineer anything in the FPL package.                                *
  26.  *                                                                      *
  27.  * This program is distributed in the hope that it will be useful,      *
  28.  * but WITHOUT ANY WARRANTY; without even the implied warranty of       *
  29.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                 *
  30.  *                                                                      *
  31.  * Daniel Stenberg                                                      *
  32.  * Ankdammsgatan 36, 4tr                                                *
  33.  * S-171 43 Solna                                                       *
  34.  * Sweden                                                               *
  35.  *                                                                      *
  36.  * FidoNet 2:201/328    email:dast@sth.frontec.se                       *
  37.  *                                                                      *
  38.  ************************************************************************/
  39.  
  40. #ifdef AMIGA
  41. #include <exec/types.h>
  42. #include <dos.h>
  43.  
  44. #elif defined(UNIX)
  45. #include <sys/types.h>
  46. #include <stdio.h>
  47. #endif
  48.  
  49. #include "script.h"
  50. #include "debug.h"
  51.  
  52. /* Lower-case digits.  */
  53. const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
  54.  
  55. /* Upper-case digits.  */
  56. const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  57.  
  58. #define    OUTCHAR(x) do { CALL(AddCharBuffer(scr, string, (x))); done++; } while(0)
  59.  
  60. #define BUFFSIZE 40 /* 40 bytes buffer for ltostr() calcs */
  61.  
  62. #define ADD_STRING_LIMIT 10 /* strings longer than this get appended right
  63.                                away instead of added char by char */
  64.  
  65. #define ADDBUFFER_SIZE 32 /* buffered size; before appending string */
  66.  
  67. #define ADD_RESET -1 /* reset buffer */
  68. #define ADD_FLUSH -2 /* flush buffer */
  69.  
  70. ReturnCode REGARGS
  71. AddCharBuffer(struct Data *scr,
  72.           struct fplStr **string,
  73.           int add)
  74. {
  75.     static char buffer[ADDBUFFER_SIZE];
  76.     static long len=0;
  77.     ReturnCode ret;
  78.     switch(add) {
  79.       default:
  80.     if(len<ADDBUFFER_SIZE) {
  81.             buffer[len++] = add;
  82.             break;
  83.     }
  84.       case ADD_FLUSH:
  85.     if(len) {
  86.         CALL(AppendStringToString(scr, string, buffer, len));
  87.     }
  88.       case ADD_RESET:
  89.     buffer[0]=(add>=0?add:'\0');
  90.         len=(add>=0?1:0);
  91.     break;
  92.     }
  93.     return FPL_OK;
  94. }
  95.  
  96. ReturnCode REGARGS
  97. Sprintf(struct Data *scr,
  98.     struct fplStr **string,
  99.     char *format,
  100.     void **param_array,
  101.     char *param_types)
  102. {
  103.   /* Base-36 digits for numbers.  */
  104.   const char *digits = lower_digits;
  105.  
  106.   /* Pointer into the format string.  */
  107.   register char *f;
  108.  
  109.   /* Number of characters written.  */
  110.   register size_t done = 0;
  111.  
  112.   long param_num=1; /* start with the first parameter */
  113.   ReturnCode ret;
  114.  
  115.   long param; /* current parameter to read */
  116.  
  117.   void *value; /* general purpose value holder */
  118.  
  119.   CALL(AddCharBuffer(scr, string, ADD_RESET));
  120.  
  121.   f = format;
  122.   while (*f != '\0') {
  123.       /* Type modifiers.  */
  124.       char is_short, is_long, is_long_double;
  125.  
  126.       /* Format spec modifiers.  */
  127.       char space, showsign, left, alt;
  128.  
  129.       /* Padding character: ' ' or '0'.  */
  130.       char pad;
  131.       /* Width of a field.  */
  132.       register long width;
  133.       /* Precision of a field.  */
  134.       long prec;
  135.  
  136.       /* Decimal integer is negative.  */
  137.       char is_neg;
  138.  
  139.       /* Current character of the format.  */
  140.       char fc;
  141.  
  142.       /* Base of a number to be written.  */
  143.       long base;
  144.       /* Integral values to be written.  */
  145.       unsigned long num;
  146.       long signed_num;
  147.  
  148.       param = 0;
  149.  
  150.       if (*f != '%') {
  151.       /* This isn't a format spec, so write
  152.          everything out until the next one.  */
  153.       char *next = strchr(f + 1, '%');
  154.       if (next == NULL)
  155.         next = strchr(f + 1, '\0');
  156.       if (next - f > ADD_STRING_LIMIT) {
  157.           /*
  158.            * bonka pa hela klabbet pa en gang!
  159.            */
  160.               CALL(AddCharBuffer(scr, string, ADD_FLUSH));
  161.           CALL(AppendStringToString(scr, string, f, next - f));
  162.           done += next - f;
  163.           f = next;
  164.       }
  165.       else
  166.         while (f < next)
  167.           OUTCHAR(*f++);
  168.       continue;
  169.       }
  170.  
  171.       ++f;
  172.  
  173.       /* Check for "%%".  Note that although the ANSI standard lists
  174.      '%' as a conversion specifier, it says "The complete format
  175.      specification shall be `%%'," so we can avoid all the width
  176.      and precision processing.  */
  177.       if (*f == '%') {
  178.       ++f;
  179.       OUTCHAR('%');
  180.       continue;
  181.       }
  182.  
  183.       /* Check for spec modifiers.  */
  184.       space = showsign = left = alt = 0;
  185.       pad = ' ';
  186.       while (*f == ' ' || *f == '+' || *f == '-' || *f == '#' || *f == '0') {
  187.     switch (*f++) {
  188.       case ' ':
  189.         /* Output a space in place of a sign, when there is no sign.  */
  190.         space = 1;
  191.         break;
  192.       case '+':
  193.         /* Always output + or - for numbers.  */
  194.         showsign = 1;
  195.         break;
  196.       case '-':
  197.         /* Left-justify things.  */
  198.         left = 1;
  199.         break;
  200.       case '#':
  201.         /* Use the "alternate form":
  202.            Hex has 0x or 0X, FP always has a decimal point.  */
  203.         alt = 1;
  204.         break;
  205.       case '0':
  206.         /* Pad with 0s.  */
  207.         pad = '0';
  208.         break;
  209.     }
  210.       }
  211.       if (left)
  212.     pad = ' ';
  213.  
  214.       /* Get the field width.  */
  215.       width = 0;
  216.       if (*f == '*') {
  217.       /* The field width is given in an argument.
  218.          A negative field width indicates left justification.  */
  219.         if(isdigit(f[1]) && '$' == f[2]) {
  220.           width = f[1] - '0';
  221.           if(width<1)
  222.             width=1;
  223.           width = (long) param_array[ width ];
  224.         }
  225.         else
  226.           width = (long) param_array[param_num++];
  227.         if (width < 0) {
  228.           width = - width;
  229.           left = 1;
  230.         }
  231.         ++f;
  232.       }
  233.       else {
  234.         if(isdigit(f[1]) && '$' == f[2]) {
  235.           param = f[1]-'0';
  236.           if(param<1)
  237.             param=1; /* use no less than 1! */
  238.         }
  239.     while (isdigit(*f)) {
  240.           width *= 10;
  241.           width += *f++ - '0';
  242.     }
  243.       }
  244.       /* Get the precision.  */
  245.       /* -1 means none given; 0 means explicit 0.  */
  246.       prec = -1;
  247.       if (*f == '.') {
  248.       ++f;
  249.       if (*f == '*') {
  250.             if(isdigit(f[1]) && '$' == f[2]) {
  251.               prec = f[1] - '0';
  252.               if(prec<1)
  253.                 prec=1;
  254.               prec = (long) param_array[ prec ];
  255.             }
  256.             else
  257.               /* The precision is given in an argument.  */
  258.               prec = (long) param_array[param_num++];;
  259.             /* Avoid idiocy.  */
  260.             if (prec < 0)
  261.               prec = -1;
  262.             ++f;
  263.           }
  264.       else if (isdigit(*f)) {
  265.           prec = 0;
  266.           while (*f != '\0' && isdigit(*f))
  267.         {
  268.           prec *= 10;
  269.           prec += *f++ - '0';
  270.         }
  271.         }
  272.       }
  273.  
  274.       /* Check for type modifiers.  */
  275.       is_short = is_long = is_long_double = 0;
  276.       while (*f == 'h' || *f == 'l' || *f == 'L') {
  277.     switch (*f++) {
  278.         case 'h':
  279.           /* int's are short int's.  */
  280. #if 0
  281.           /* ignored in FPL since there are no true shorts! */
  282.           is_short = 1;
  283. #endif
  284.           break;
  285.         case 'l':
  286.           /* int's are long int's.  */
  287.           is_long = 1;
  288.           break;
  289.         case 'L':
  290.           /* double's are long double's, and int's are long long int's.  */
  291.           is_long_double = 1;
  292.           break;
  293.         }
  294.       }
  295.       /* Format specification.  */
  296.       fc = *f++;
  297.  
  298.       value = (void *)(param?param_array[param]:param_array[param_num++]);
  299.  
  300.  
  301.       switch (fc) {
  302.       case 'i':
  303.       case 'd':
  304.         /* Decimal integer.  */
  305.         base = 10;
  306.             signed_num = (long) value;
  307.  
  308.         is_neg = signed_num < 0;
  309.         num = is_neg ? (- signed_num) : signed_num;
  310.         goto number;
  311.  
  312.       case 'u':
  313.         /* Decimal unsigned integer.  */
  314.         base = 10;
  315.         goto unsigned_number;
  316.  
  317.           case 'b':
  318.             /* Binary unsigned integer.  */
  319.             base = 2;
  320.             goto unsigned_number;
  321.  
  322.       case 'o':
  323.         /* Octal unsigned integer.  */
  324.         base = 8;
  325.         goto unsigned_number;
  326.  
  327.       case 'X':
  328.         /* Hexadecimal unsigned integer.  */
  329.       case 'x':
  330.         /* Hex with lower-case digits.  */
  331.  
  332.         digits = fc == 'X' ? upper_digits : lower_digits;
  333.  
  334.         base = 16;
  335.  
  336.       unsigned_number:;
  337.         /* Unsigned number of base BASE.  */
  338.             num = (unsigned long) value;
  339.  
  340.         is_neg = 0;
  341.  
  342.       number:;
  343.         /* Number of base BASE.  */
  344.         {
  345.           char work[BUFFSIZE];
  346.           char *workend = &work[sizeof(work) - 1];
  347.           register char *w;
  348.  
  349.           /* Supply a default precision if none was given.  */
  350.           if (prec == -1)
  351.         prec = 1;
  352.  
  353.           /* Put the number in WORK.  */
  354.           w = workend;
  355.           while (num > 0) {
  356.                 *w-- = digits[num % base];
  357.                 num /= base;
  358.               }
  359.           width -= workend - w;
  360.           prec -= workend - w;
  361.  
  362.           if (alt && base == 8 && prec <= 0) {
  363.                 *w-- = '0';
  364.                 --width;
  365.               }
  366.  
  367.           if (prec > 0) {
  368.                 width -= prec;
  369.                 while (prec-- > 0)
  370.                   *w-- = '0';
  371.               }
  372.  
  373.           if (alt && base == 16)
  374.         width -= 2;
  375.  
  376.           if (is_neg || showsign || space)
  377.         --width;
  378.  
  379.           if (!left && pad == ' ')
  380.         while (width-- > 0)
  381.           OUTCHAR(' ');
  382.  
  383.           if (is_neg)
  384.         OUTCHAR('-');
  385.           else if (showsign)
  386.         OUTCHAR('+');
  387.           else if (space)
  388.         OUTCHAR(' ');
  389.  
  390.           if (!left && pad == '0')
  391.         while (width-- > 0)
  392.           OUTCHAR('0');
  393.  
  394.           if (alt && base == 16) {
  395.           OUTCHAR('0');
  396.           OUTCHAR(fc);
  397.               }
  398.  
  399.           /* Write the number.  */
  400.           while (++w <= workend) {
  401.         OUTCHAR(*w);
  402.               }
  403.  
  404.           if (left)
  405.         while (width-- > 0)
  406.           OUTCHAR(' ');
  407.         }
  408.         break;
  409.  
  410.       case 'c':
  411.         /* Character.  */
  412.         num = (long) value;
  413.         if (!left)
  414.           while (--width > 0)
  415.         OUTCHAR(' ');
  416.         OUTCHAR((unsigned char) num);
  417.         if (left)
  418.           while (--width > 0)
  419.         OUTCHAR(' ');
  420.         break;
  421.  
  422.       case 's':
  423.         /* String.  */
  424.         {
  425.           static char null[] = "[ERR]";
  426.               char *str;
  427.           size_t len;
  428.  
  429.           str = (char *) value;
  430.           if ( str == NULL ||
  431.                   (param?param_types[param]:param_types[param_num-1]) !=
  432.                   FPL_STRARG )
  433.         /* Write "(err)" if there's space.  */
  434.         if (prec == -1 || prec >= (long) sizeof(null) - 1)
  435.           {
  436.             str = null;
  437.             len = sizeof(null) - 1;
  438.           }
  439.         else
  440.           {
  441.             str = "";
  442.             len = 0;
  443.           }
  444.           else
  445.         len = strlen(str);
  446.  
  447.           if (prec != -1 && (size_t) prec < len)
  448.         len = prec;
  449.           width -= len;
  450.  
  451.           if (!left)
  452.         while (width-- > 0)
  453.           OUTCHAR(' ');
  454.  
  455.           if (len < ADD_STRING_LIMIT)
  456.                   while (len-- > 0)
  457.                       OUTCHAR(*str++);
  458.           else {
  459.                   CALL(AddCharBuffer(scr, string, ADD_FLUSH));
  460.                   CALL(AppendStringToString(scr, string, str, len));
  461.                   done += len;
  462.               }
  463.           if (left)
  464.         while (width-- > 0)
  465.           OUTCHAR(' ');
  466.         }
  467.         break;
  468.  
  469.       case 'p':
  470.           case 'P': /* support this too, not ANSI though! */
  471.         /* Generic pointer.  */
  472.         {
  473.               void *ptr;
  474.           ptr = (void *) value;
  475.           if (ptr != NULL) {
  476.                 /* If the pointer is not NULL, write it as a %#x spec.  */
  477.                 base = 16;
  478.                 digits = fc == 'P' ? upper_digits : lower_digits;
  479.                 fc = fc == 'P'?'X':'x';
  480.                 alt = 1;
  481.                 num = (unsigned long) ptr;
  482.                 is_neg = 0;
  483.                 goto number;
  484.               }
  485.           else {
  486.                 /* Write "(nil)" for a nil pointer.  */
  487.                 static char nil[] = "(nil)";
  488.                 register char *p;
  489.  
  490.                 width -= sizeof(nil) - 1;
  491.                 if (left)
  492.                   while (width-- > 0)
  493.                     OUTCHAR(' ');
  494.                 for (p = nil; *p != '\0'; ++p)
  495.                   OUTCHAR(*p);
  496.                 if (!left)
  497.                   while (width-- > 0)
  498.                     OUTCHAR(' ');
  499.               }
  500.         }
  501.         break;
  502.  
  503.       case 'n':
  504.         /* Answer the count of characters written.  */
  505.             {
  506.               struct Identifier *ident;
  507.               if((param?param_types[param]:param_types[param_num]) ==
  508.                  FPL_INTVARARG) {
  509.                 ident = (struct Identifier *) value;
  510.                 ident->data.variable.var.val32[0] = done;
  511.               }
  512.             }
  513.         break;
  514.     }
  515.  
  516.   }
  517.  
  518.   CALL(AddCharBuffer(scr, string, ADD_FLUSH)); /* flush temp buffer */
  519.  
  520.   return FPL_OK;
  521. }
  522.  
  523.