summaryrefslogblamecommitdiffstats
path: root/main/common/mprintf.c
blob: ffd73bc29566a284dd722586105f44c22cd84854 (plain) (tree)
1
2
3
4


                                                                           
  











































                                                                           




                              




                                                               

































                                                 







                                            
                                        








                                                               















                                   








                                                            
















































                                                              








                                                                     
















































                                                              








                                                          















                                              








                                                               
















                                          







                                                                














































                                                           







                                                                     














































                                                           








                                                                          













                                                     


                  


                                                                     



                                                                  


























































                                           


               



                                                          



                                                       


























































































































                                                                            










                                                               

                                                                 



                                              

                 
 



                                        




                                  

                 
 



                                    




                      

                 
 



                                  




                       





















                                               
 
/**************************************************************************
 *
 * Copyright (c) 2013 Alcatel-Lucent
 *
 * Alcatel Lucent licenses this file to You under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License.  A copy of the License is contained the
 * file LICENSE at the top level of this repository.
 * You may also obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 **************************************************************************
 *
 * vmprintf.c:
 *
 * Same as mprintf, but this uses stdarg.h.
 * Cleaner and simpler.  I have some targets using this and will probably
 * convert all targets to use this eventually.
 *
 * Note that this requires that the CFLAGS used in the building of this
 * file should omit the "-nostdinc" option, so that the standard header
 * (stdarg.h) can be pulled in.
 *
 * Public functions:
 * vsnprintf(), snprintf(), sprintf(), printf(), cprintf()
 *
 * Original author:     Ed Sutter (ed.sutter@alcatel-lucent.com)
 *
 */
#include <stdarg.h>
#include "stddefs.h"
#include "cli.h"

extern long abs(long);
extern void puts(char *);
extern int atoi(char *), putchar(char);
int printf(char *fmt, ...);

static char *hexdigits = "0123456789abcdef";
static char *Hexdigits = "0123456789ABCDEF";

#define MAX_NUMBER_SIZE     18
#define HDRSIZE             4
#define NEGATIVE            1
#define POSITIVE            2
#define SCREENWIDTH         80

/* PROCCHARP(), PROCCHAR(), BUFDEC() & BUFINC():
 * Macros used to conveniently distinguish between buffer-bound
 * and console-bound characters during the formatting process.
 */
#define PROCCHARP(bp,eob,cp)                    \
    if (bp) {                                   \
        if (eob && (bp >= eob)) {               \
            puts("s_printf buffer overflow");   \
            return(-1);                         \
        }                                       \
        *bp++ = *cp++;                          \
    }                                           \
    else {                                      \
        putchar(*cp++);                         \
    }

#define PROCCHAR(bp,eob,c)                      \
    if (bp) {                                   \
        if (eob && (bp >= eob)) {               \
            puts("s_printf buffer overflow");   \
            return(-1);                         \
        }                                       \
        *bp++ = c;                              \
    }                                           \
    else {                                      \
        if (c)                                  \
            putchar(c);                     \
    }

#define BUFDEC(bp,val)      \
    if (bp) {               \
        bp -= val;          \
    }

#define BUFINC(bp,val)      \
    if (bp) {               \
        bp += val;          \
    }


/* isdigit():
 * Copied here to avoid inclusion of ctype.h
 */
static int
isdigit(char ch)
{
    return ((ch >= '0') && (ch <= '9'));
}

/* proc_d_prefix():
 * Deal with the characters prior to the 'd' format designator.
 * For example, hdr would be '8' in the format %8d.
 */
static int
proc_d_prefix(char *hdr, char *lbp)
{
    char fill;
    int minsize, i;

    minsize = 0;
    if(hdr[0]) {
        if(hdr[0] == '0') {
            fill = '0';
        } else {
            fill = ' ';
        }
        minsize = (short)atoi(hdr);
        for(i=0; i<minsize; i++) {
            *lbp++ = fill;
        }
    }
    return(minsize);
}


/* long_to_dec():
 * Convert an incoming long value to ASCII decimal notation.
 */
int
long_to_dec(long lval,char *buf,char *bufend,char *hdr)
{
    unsigned long   value;
    short   size, i, minsize;
    char    lbuf[MAX_NUMBER_SIZE], *lbp;

    lbp = lbuf;
    minsize = proc_d_prefix(hdr,lbuf);

    /* First determine how many ascii digits are needed. */
    value = abs(lval);
    size = 0;
    while(value > 0) {
        size++;
        value /= 10;
    }
    if(lval < 0) {
        size++;
    }
    if(minsize > size) {
        size = minsize;
    }
    lbp += size;

    /* Now build the string. */
    value = abs(lval);
    if(value == 0) {
        if(minsize==0) {
            lbuf[0] = '0';
            size = 1;
        } else {
            *--lbp = '0';
        }
    } else {
        while(value > 0) {
            *--lbp = (char)((value % 10) + '0');
            value /= 10;
        }
    }
    if(lval < 0) {
        *--lbp = '-';
    }
    lbuf[size] = 0;

    /* At this point, lbuf[] contains the ascii decimal string
     * so we can now pass it through PROCCHAR...
     */
    for(i=0; i<size; i++) {
        PROCCHAR(buf,bufend,lbuf[i]);
    }
    return((int)size);
}

/* llong_to_dec():
 * Convert an incoming long long value to ASCII decimal notation.
 * This is essentially identical to long_to_dec, but we use longlong.
 */
static int
llong_to_dec(int64_t llval,char *buf,char *bufend,char *hdr)
{
    uint64_t value;
    short   size, i, minsize;
    char    lbuf[MAX_NUMBER_SIZE], *lbp;

    lbp = lbuf;
    minsize = proc_d_prefix(hdr, lbuf);

    /* First determine how many ascii digits are needed. */
    value = llval < 0 ? -llval : llval;
    size = 0;
    while(value > 0) {
        size++;
        value /= 10;
    }
    if(llval < 0) {
        size++;
    }
    if(minsize > size) {
        size = minsize;
    }
    lbp += size;

    /* Now build the string. */
    value = llval < 0 ? -llval : llval;
    if(value == 0) {
        if(minsize==0) {
            lbuf[0] = '0';
            size = 1;
        } else {
            *--lbp = '0';
        }
    } else {
        while(value > 0) {
            *--lbp = (char)((value % 10) + '0');
            value /= 10;
        }
    }
    if(llval < 0) {
        *--lbp = '-';
    }
    lbuf[size] = 0;

    /* At this point, lbuf[] contains the ascii decimal string
     * so we can now pass it through PROCCHAR...
     */
    for(i=0; i<size; i++) {
        PROCCHAR(buf,bufend,lbuf[i]);
    }
    return((int)size);
}

/* long_to_ip():
 * Convert an incoming long value to an ascii IP formatted
 * string (i.e.  0x01020304 is converted to "1.2.3.4")
 */
static int
long_to_ip(long lval,char *buf,char *bufend,char *hdr)
{
    int i, j, len;
    unsigned char *lp;

    len = 0;
    lp = (unsigned char *)&lval;
    for(j=0; j<4; j++) {
        i = long_to_dec(*lp++,buf,bufend,hdr);
        BUFINC(buf,i);
        if(j < 3) {
            PROCCHAR(buf,bufend,'.');
        }
        len += (i + 1);
    }
    BUFDEC(buf,1);
    len--;
    return(len);
}

/* proc_x_prefix():
 * Deal with the characters prior to the 'x' format designator.
 * For example, hdr would be '02' in the format %02x.
 */
static int
proc_x_prefix(char *hdr, char *lbp)
{
    int i, minsize;

    minsize = 0;
    if(hdr[0]) {
        if(hdr[1]) {
            minsize = (short)(hdr[1]&0xf);
            for(i=0; i<minsize; i++) {
                *lbp++ = hdr[0];
            }
        } else {
            minsize = (short)(hdr[0]&0xf);
            for(i=0; i<minsize; i++) {
                *lbp++ = ' ';
            }
        }
    }
    return(minsize);
}

/* long_to_hex():
 * Convert an incoming long value to ASCII hexadecimal notation.
 */
static int
long_to_hex(ulong lval,char *buf,char *bufend,char *hdr, char x)
{
    ulong   value;
    short   size, i, minsize;
    char    lbuf[MAX_NUMBER_SIZE], *lbp;

    lbp = lbuf;
    minsize = proc_x_prefix(hdr,lbuf);

    /* First determine how many ascii digits are needed. */
    value = lval;
    size = 0;
    while(value > 0) {
        size++;
        value /= 16;
    }
    if(minsize > size) {
        size = minsize;
    }
    lbp += size;

    /* Now build the string. */
    if(lval == 0) {
        if(size == 0) {
            size = 1;
        } else {
            lbp--;
        }
        *lbp = '0';
    } else {
        while(lval > 0) {
            if(x == 'X') {
                *--lbp = Hexdigits[(int)(lval % 16)];
            } else {
                *--lbp = hexdigits[(int)(lval % 16)];
            }
            lval /= 16;
        }
    }
    lbp[size] = 0;

    /* At this point, lbuf[] contains the ascii hex string
     * so we can now pass it through PROCCHAR...
     */
    for(i=0; i<size; i++) {
        PROCCHAR(buf,bufend,lbuf[i]);
    }

    return((int)size);
}

/* llong_to_hex():
 * Convert an incoming long long value to ASCII hexadecimal notation.
 */
static int
llong_to_hex(uint64_t llval,char *buf,char *bufend,char *hdr, char x)
{
    uint64_t    value;
    short   size, i, minsize;
    char    lbuf[MAX_NUMBER_SIZE], *lbp;

    lbp = lbuf;
    minsize = proc_x_prefix(hdr,lbuf);

    /* First determine how many ascii digits are needed. */
    value = llval;
    size = 0;
    while(value > 0) {
        size++;
        value /= 16;
    }
    if(minsize > size) {
        size = minsize;
    }
    lbp += size;

    /* Now build the string. */
    if(llval == 0) {
        if(size == 0) {
            size = 1;
        } else {
            lbp--;
        }
        *lbp = '0';
    } else {
        while(llval > 0) {
            if(x == 'X') {
                *--lbp = Hexdigits[(int)(llval % 16)];
            } else {
                *--lbp = hexdigits[(int)(llval % 16)];
            }
            llval /= 16;
        }
    }
    lbp[size] = 0;

    /* At this point, lbuf[] contains the ascii hex string
     * so we can now pass it through PROCCHAR...
     */
    for(i=0; i<size; i++) {
        PROCCHAR(buf,bufend,lbuf[i]);
    }

    return((int)size);
}

/* bin_to_mac():
 * Convert an incoming buffer to an ascii MAC formatted
 * string (i.e.  buf[] = 010203040506 is converted to "01:02:03:04:05:06")
 */
static int
bin_to_mac(uchar *ibin,char *buf,char *bufend)
{
    int i, j, len;

    len = 0;
    for(j=0; j<6; j++) {
        i = long_to_hex(*ibin++,buf,bufend,"02",'x');
        BUFINC(buf,i);
        if(j < 5) {
            PROCCHAR(buf,bufend,':');
        }
        len += (i + 1);
    }
    BUFDEC(buf,1);
    len--;
    return(len);
}

/* build_string():
 *  Build a string from 'src' to 'dest' based on the hdr and sign
 *  values.  Return the size of the string (may include left or right
 *  justified padding).
 */
static int
build_string(char *src,char *dest,char *bufend,char *hdr,int sign)
{
    char    *cp1;
    short   minsize, i, j;

    if(!src) {
        cp1 = "NULL_POINTER";
        while(*cp1) {
            PROCCHARP(dest,bufend,cp1);
        }
        return(12);
    }
    if(!*src) {
        return(0);
    }
    if(!hdr[0]) {
        j = 0;
        while(*src) {
            PROCCHARP(dest,bufend,src);
            j++;
        }
        return(j);
    }
    minsize = (short)atoi(hdr);
    i = 0;
    cp1 = (char *)src;
    while(*cp1) {
        i++;
        cp1++;
    }
    cp1 = (char *)src;
    j = 0;
    if(minsize > i) {
        if(sign == POSITIVE) {
            while(minsize > i) {
                j++;
                PROCCHAR(dest,bufend,' ');
                minsize--;
            }
            while(*cp1) {
                j++;
                PROCCHARP(dest,bufend,cp1);
            }
        } else {
            while(*cp1) {
                j++;
                PROCCHARP(dest,bufend,cp1);
            }
            while(minsize > i) {
                j++;
                PROCCHAR(dest,bufend,' ');
                minsize--;
            }
        }
    } else {
        while(*cp1) {
            j++;
            PROCCHARP(dest,bufend,cp1);
        }
    }
    return(j);
}

/* vsnprintf():
 *  Backend to all the others below it.
 *  Formats incoming argument list based on format string.
 *  Terminates population of buffer if it is to exceed the
 *  specified buffer size.
 */
int
vsnprintf(char *buf,int bsize, char *fmt, va_list argp)
{
    long    arg_l;
    long long arg_ll;
    int     i, sign, tot;
    char    *cp, hdr[HDRSIZE], *base, *bufend, arg_c, *arg_cp, ll;

    ll = 0;
    tot = 0;
    base = buf;

    if(bsize == 0) {
        bufend = 0;
    } else {
        bufend = base+(bsize-1);
    }

    cp = fmt;
    for(i=0; i<HDRSIZE; i++) {
        hdr[i] = 0;
    }
    while(*cp) {
        if(*cp != '%') {
            PROCCHARP(buf,bufend,cp);
            tot++;
            continue;
        }
        cp++;
        if(*cp == '%') {
            PROCCHARP(buf,bufend,cp);
            tot++;
            continue;
        }
        sign = POSITIVE;
        if(*cp == '-') {
            sign = NEGATIVE;
            cp++;
        }
        if(isdigit(*cp)) {
            for(i=0; i<(HDRSIZE-1); i++) {
                if(isdigit(*cp)) {
                    hdr[i] = *cp++;
                } else {
                    break;
                }
            }
        }

        ll = 0;
        if(*cp == 'l') {            /* Ignore the 'long' designator */
            cp++;
            if(*cp == 'l') {        /* unless its the longlong designator */
                cp++;
                ll = 1;
            }
        }

        switch(*cp) {
        case 'c':           /* Character conversion */
            arg_c = (char)va_arg(argp,int);
            PROCCHAR(buf,bufend,arg_c);
            tot++;
            break;
        case 's':           /* String conversion */
            arg_cp = (char *)va_arg(argp,int);
            i = build_string(arg_cp,buf,bufend,hdr,sign);
            BUFINC(buf,i);
            tot += i;
            break;
        case 'M':           /* MAC address conversion */
            arg_cp = (char *)va_arg(argp,int);
            i = bin_to_mac((uchar *)arg_cp,buf,bufend);
            BUFINC(buf,i);
            tot += i;
            break;
        case 'I':           /* IP address conversion */
            arg_l = (long)va_arg(argp,int);
            i = long_to_ip(arg_l,buf,bufend,hdr);
            BUFINC(buf,i);
            tot += i;
            break;
        case 'd':           /* Decimal conversion */
        case 'u':
            if(ll) {
                arg_ll = (long long)va_arg(argp,long long);
                i = llong_to_dec(arg_ll,buf,bufend,hdr);
            } else {
                arg_l = (long)va_arg(argp,int);
                i = long_to_dec(arg_l,buf,bufend,hdr);
            }
            BUFINC(buf,i);
            tot += i;
            break;
        case 'p':           /* Hex conversion */
        case 'x':
        case 'X':
            if(*cp == 'p') {
                PROCCHAR(buf,bufend,'0');
                PROCCHAR(buf,bufend,'x');
            }
            if(ll) {
                arg_ll = (long long)va_arg(argp,long long);
                i = llong_to_hex(arg_ll,buf,bufend,hdr,*cp);
            } else {
                arg_l = (long)va_arg(argp,int);
                i = long_to_hex((ulong)arg_l,buf,bufend,hdr,*cp);
            }
            BUFINC(buf,i);
            tot += i;
            break;
        default:
            PROCCHARP(buf,bufend,cp);
            tot++;
            break;
        }
        cp++;

        if(hdr[0]) {
            for(i=0; i<HDRSIZE; i++) {
                hdr[i] = 0;
            }
        }
    }
    PROCCHAR(buf,bufend,0);
    return(tot);
}

/* snprintf(), sprintf(), printf() & cprintf():
 * All functions use vsnprintf() to format a string.
 * The string is either transferred to a buffer or transferred
 * directly to this target's console through putchar (refer to
 * the macros at the top of this file).
 *
 * - sprintf() formats to a buffer.
 * - printf() formats to stdio.
 * - cprintf() formats to a buffer, then centers the content of
 *      the buffer based on its size and a console screen with of
 *      SCREENWIDTH characters.
 */
int
snprintf(char *buf, int bsize, char *fmt, ...)
{
    int tot;
    va_list argp;

    va_start(argp,fmt);
    tot = vsnprintf(buf,bsize,fmt,argp);
    va_end(argp);
    return(tot);
}

int
sprintf(char *buf, char *fmt, ...)
{
    int tot;
    va_list argp;

    va_start(argp,fmt);
    tot = vsnprintf(buf,0,fmt,argp);
    va_end(argp);
    return(tot);
}

int
printf(char *fmt, ...)
{
    int tot;
    va_list argp;

    va_start(argp,fmt);
    tot = vsnprintf(0,0,fmt,argp);
    va_end(argp);
    return(tot);
}

int
cprintf(char *fmt, ...)
{
    int i, tot, spaces;
    char    pbuf[CMDLINESIZE];
    va_list argp;

    va_start(argp,fmt);
    tot = vsnprintf(pbuf,CMDLINESIZE,fmt,argp);
    va_end(argp);

    if(tot < SCREENWIDTH) {
        spaces = (SCREENWIDTH-tot)/2;
        for(i=0; i<spaces; i++) {
            putchar(' ');
        }
    } else {
        spaces = 0;
    }

    for(i=0; i<tot; i++) {
        putchar(pbuf[i]);
    }

    return(tot+spaces);
}