Friday, April 26, 2013

C source for printf (a minimal one without floating point)

While I was working with an 8K flash MCU, I found that the default printf provided by 'C' library occupies close to 1.8K (too much eh?). I decided to create a function of my own which generates approximately 850 Bytes of code. Posting it for my own reference and for others to use it

WARNING: I'm not responsible for the code if it cause night blindness to your pet DOG!
/**
 * @file     Minimal printf source file
 * @author   Sundarapandian A
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdarg.h>

#define F_UNS        1
#define F_BIG        2
#define F_ZERO       4

#define IBUFSZ       16

/* pputc function */
void pputc(int ch);

/* Print an integer in given format */
static int iprint(unsigned long int val, int flag, int prec, int base)
{
    static const char *lt = "0123456789ABCDEF";
    char buf[IBUFSZ];
    int i, j, neg = 0;

    if (!(flag & F_UNS) && (int) val < 0) {
        neg = 1;
        val = -val;
    }
    for (i = IBUFSZ - 1; i >= 0; i--) {
        buf[i] = lt[val - ((val / base) * base)];
        val = val / base;
        if (!val)
            break;
    }
    for (j = 0; j < prec - (IBUFSZ - i + neg); j++) {
        pputc((flag & F_ZERO) ? '0' : ' ');
    }
    if (neg) {
        pputc('-');
    }
    j = IBUFSZ - i + neg;
    for (; i < IBUFSZ; i++)
        pputc((flag & F_BIG) ? buf[i] : (buf[i] | 0x20));
    return prec > j ? prec : j;
}

/**
 * Minimal printf (Without floating point support)
 */
int min_printf(const char *fmt, ...)
{
    int apr = 0, pflag = 0, prec = 0, cnt = 0;
    va_list ap;
    const char *str;
    va_start(ap, fmt);
    for (; *fmt; fmt++) {
        if (!apr && *fmt == '%') {
            apr = 1;
            prec = pflag = 0;
            continue;
        }

        if (!apr) {
            pputc(*fmt);
            cnt++;
            continue;
        }
        switch (*fmt) {
        case '%':
            pputc('%');
            cnt++;
            apr = 0;
            break;
        case '0':
            if (!prec)
                pflag |= F_ZERO;
            else
                prec *= 10;
            break;
        case 's':
            str = va_arg(ap, const char *);
            while (*str) {
                pputc(*str++);
                cnt++;
            }
            apr = 0;
            break;
        case '#':
        case 'l':
            break;
        case 'u':
            cnt += iprint(va_arg(ap, unsigned int),
                          pflag | F_UNS, prec, 10);
            apr = 0;
            break;
        case 'c':
            pputc(va_arg(ap, int));
            cnt++;
            apr = 0;
            break;
        case 'd':
            cnt += iprint(va_arg(ap, long int), pflag, prec, 10);
            apr = 0;
            break;
        case 'X':
            pflag |= F_BIG;
        case 'x':
            cnt += iprint(va_arg(ap, unsigned int),
                          pflag | F_UNS, prec, 16);
            apr = 0;
            break;
        case '*':
            prec = va_arg(ap, int);
            break;
        default:
            if (*fmt >= '1' && *fmt <= '9') {
                prec = (prec * 10) + (*fmt - '0');
            } else {
                /* Something wrong */
                apr = 0;
            }
        }                       /* End of switch */
    }                           /* End of for loop */
    va_end(ap);
    return cnt;
}

#define TESTING

#ifdef TESTING
#include <stdio.h>
/**
 * Function to print a char on terminal/file
 */
void pputc(int ch)
{
    putchar(ch);
}

int main()
{
    int val;
    val = min_printf("0x%0*X %8d %lu %s %c\r\n",
                     8, 10, -567, (unsigned long int) -4,
                     "Test Str", 0x41);
    min_printf("Num chars printed on screen: %d\r\n", val);
    return 0;
}
#endif

No comments: