Skip to content

Instantly share code, notes, and snippets.

@amuramatsu
Last active June 24, 2018 16:50
Show Gist options
  • Save amuramatsu/f2d79896dd4add7ba6b0be846ced3970 to your computer and use it in GitHub Desktop.
Save amuramatsu/f2d79896dd4add7ba6b0be846ced3970 to your computer and use it in GitHub Desktop.
/*
* Integer vsnprintf, et. al.
*
* Copyright (C) 2018 MURAMATSU Atsushi <amura@tomato.sakura.ne.jp>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* integer vsnprintf, et. al. based on BDS C v1.6 library (PDS)
*
* This code provides
* sprintf family:
* my_vsnprintf, my_snprintf, my_vsprintf, my_sprintf,
* and
* fprintf family (if defined WITH_FILEIO macro):
* my_vfprintf, my_fprintf, my_vprintf, my_printf
*
* Supported flags:
* '0': zero padding
* '-': left adgust
* '+': always show sign
* ' ': space presented when value is positive
*
* Supported modifies:
* 'l': long
*
* Supported conversions:
* 'd', 'i': decimal
* 'u': unsigned decimal
* 'o': octal
* 'x': hexadecimal (lower case)
* 'X': hexadecimal (upper case)
* 'b': binary decimal
* 'c': charactor
* 's': string
* '%' and others: non-converted
*/
/*
STDLIB2.C -- for BDS C v1.6 -- 1/86
Copyright (c) 1982, 1986 by BD Software, Inc.
The files STDLIB1.C, STDLIB2.C and STDLIB3.C contain the source
listings for all functions present in the DEFF.CRL library object
file. (Note: DEFF2.CRL contains the .CSM-coded portions of the
library.)
STDLIB2.C contains mainly formatted text I/O functions:
printf fprintf sprintf lprintf _spr
scanf fscanf sscanf _scn
getline puts
putdec
*/
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
/*** PROTOTYPES ****/
/* #ifndef MY_VSPRINTF_H */
/* #define MY_VSPRINTF_H */
/* #define WITH_FILEIO 1 */
#include <stdarg.h>
int my_vsnprintf(char *buffer, int n, const char *format, va_list va);
int my_snprintf(char *buffer, int n, const char *format, ...);
int my_vsprintf(char *buffer, const char *format, va_list va);
int my_sprintf(char *buffer, const char *format, ...);
#ifdef WITH_FILEIO
#include <stdio.h>
int my_vfprintf(FILE *out, const char *format, va_list va);
int my_fprintf(FILE *out, const char *format, ...);
int my_vprintf(const char *format, va_list va);
int my_printf(const char *format, ...);
#endif /* WITH_FILEIO */
/* #endif */ /* MY_VSPRINTF_H */
/*** END OF PROTOTYPES ****/
typedef enum { OK, ERROR } _result_t;
static int _spr(const char *format, va_list va,
_result_t (*putcf)(char, void *), void *arg);
struct bufdata {
char *bufptr;
char *endbufptr;
};
static _result_t
_mem_writer(char c, void *arg)
{
struct bufdata *data = (struct bufdata *)arg;
if (data->endbufptr && data->bufptr >= data->endbufptr)
return ERROR;
*(data->bufptr++) = c;
return OK;
}
int
my_vsnprintf(char *buffer, int n, const char *format, va_list va)
{
int s;
struct bufdata data;
if (n == 0) return 0;
data.bufptr = buffer;
data.endbufptr = buffer + n;
_spr(format, va, &_mem_writer, &data);
s = data.bufptr - buffer;
_mem_writer('\0', &data);
buffer[n-1] = '\0';
return s;
}
int
my_vsprintf(char *buffer, const char *format, va_list va)
{
int s;
struct bufdata data;
data.bufptr = buffer;
data.endbufptr = NULL;
_spr(format, va, &_mem_writer, &data);
s = data.bufptr - buffer;
_mem_writer('\0', &data);
return s;
}
int
my_snprintf(char *buffer, int n, const char *format, ...)
{
int s;
va_list va;
va_start(va, format);
s = my_vsnprintf(buffer, n, format, va);
va_end(va);
return s;
}
int
my_sprintf(char *buffer, const char *format, ...)
{
int s;
va_list va;
va_start(va, format);
s = my_vsprintf(buffer, format, va);
va_end(va);
return s;
}
#ifdef WITH_FILEIO
struct filedata {
FILE *outfile;
int outcount;
};
static _result_t
_file_writer(char c, void *arg)
{
struct filedata *data = (struct filedata *)arg;
if (fputc(c, data->outfile) == EOF)
return ERROR;
data->outcount++;
return OK;
}
int
my_vfprintf(FILE *out, const char *format, va_list va)
{
struct filedata data;
data.outfile = out;
data.outcount = 0;
_spr(format, va, &_file_writer, &data);
return data.outcount;
}
int
my_fprintf(FILE *out, const char *format, ...)
{
int s;
va_list va;
va_start(va, format);
s = my_vfprintf(out, format, va);
va_end(va);
return s;
}
int
my_vprintf(const char *format, va_list va)
{
return my_vfprintf(stdout, format, va);
}
int
my_printf(const char *format, ...)
{
int s;
va_list va;
va_start(va, format);
s = my_vprintf(format, va);
va_end(va);
return s;
}
#endif /* WITH_FILEIO */
/*
Internal routine used by "_spr" to perform ascii-
to-decimal conversion and update an associated pointer:
*/
static int
_gv2(const char **sptr)
{
int n;
n = 0;
while (isdigit(**sptr))
n = 10*n + *(*sptr)++ - '0';
return n;
}
static char
_uspr(char **string, unsigned long n, unsigned base, int upperflag)
{
int length;
if (n < base) {
*(*string)++ = (n < 10) ? n + '0'
: (upperflag ? (n + 'A' - 10) : (n + 'a' - 10));
return 1;
}
length = _uspr(string, n / base, base, upperflag);
_uspr(string, n % base, base, upperflag);
return length + 1;
}
static int
_spr(const char *format, va_list va, _result_t (*putcf)(char, void*), void *arg)
{
char c, prefill, *wptr, sign;
long value;
int ljflag, upperflag, longflag;
int base = 10;
char wbuf[128]; /* 128 is enough for all but %s */
int length, *args, width, precision;
while ((c = *format++)) {
if (c == '%') {
wptr = wbuf;
precision = INT_MAX;
width = ljflag = upperflag = longflag = 0;
prefill = ' ';
sign = '\0';
while (1) {
if (*format == '-')
ljflag = 1;
else if (*format == '0')
prefill = '0';
else if (*format == ' ')
sign = ' ';
else if (*format == '+')
sign = '+';
else
break;
format++;
}
if (*format == '*') {
format++;
width = va_arg(va, int);
}
else if (isdigit(*format))
width = _gv2(&format);
if (*format == '.') {
format++;
if (*format == '*') {
format++;
precision = va_arg(va, int);
}
else {
precision = _gv2(&format);
}
}
if (*format == 'l') {
format++;
longflag = 1;
}
switch (c = *format++) {
case 'd':
case 'i':
{
long value;
if (longflag)
value = va_arg(va, long);
else
value = va_arg(va, int);
if (value < 0) {
*wptr++ = '-';
value = -value;
width--;
}
else if (sign != '\0') {
*wptr++ = sign;
width--;
}
width -= _uspr(&wptr, value, base, 0);
}
goto pad;
case 'u':
base = 10; goto val;
case 'b':
base = 2; goto val;
case 'x':
base = 16; goto val;
case 'X':
base = 16; upperflag = 1; goto val;
case 'o':
base = 8;
val:
if (longflag)
width -= _uspr(&wptr, va_arg(va, unsigned long),
base, upperflag);
else
width -= _uspr(&wptr, va_arg(va, unsigned),
base, upperflag);
goto pad;
case 'c':
*wptr++ = va_arg(va, int) & 0xff;
width--;
goto pad;
pad:
*wptr = '\0';
length = strlen(wptr = wbuf);
pad7: /* don't modify the string at wptr */
if (!ljflag) {
while (width-- > 0)
if (putcf(prefill, arg) == ERROR)
return ERROR;
}
while (length--) {
if (putcf(*wptr++, arg) == ERROR)
return ERROR;
}
if (ljflag) {
while (width-- > 0)
if (putcf(' ', arg) == ERROR)
return ERROR;
}
break;
case 's':
wptr = va_arg(va, char *);
length = strlen(wptr);
if (precision < length)
length = precision;
width -= length;
goto pad7;
case '\0':
return OK;
default:
if (putcf(c, arg) == ERROR)
return ERROR;
}
}
else if (putcf(c, arg) == ERROR)
return ERROR;
}
return OK;
}
#ifdef DEBUG
#include <stdio.h>
void
test(const char *name, const char *format, ...)
{
char buf1[1024], buf2[1024];
int s1, s2;
va_list va1, va2;
va_start(va1, format);
va_copy(va2, va1);
s1 = vsprintf(buf1, format, va1);
s2 = my_vsprintf(buf2, format, va2);
va_end(va1);
va_end(va2);
if (strcmp(buf1, buf2) != 0)
printf("ERR!:: %s:%s:%s\n", name, buf1, buf2);
if (s1 != s2)
printf("ERR!:: %s: %d != %d\n", name, s1, s2);
/* printf("(%d)%s\n", s1, buf1); */
}
int
main()
{
test("test1", "testdata, %d, %10d", 10, 200);
test("test2", "testdata, %s, %10.10s", "dfadf,", "xxxxxxxxxxxxx");
test("test3", "testdata, %x, %10X", 10, 200);
test("test4", "testdata, %X, %10x", 10, 200);
test("test5", "testdata, %ld, %10ld", 1100000000L, 2000000000000L);
test("test6", "testdata, %+d, %+10d", -10, 200);
test("test7", "testdata, %+x, %+o", 10, 200);
test("test8", "testdata, %03x, %04o", 10, 200);
test("test9", "testdata, % 2d, % 3x", 10, 200);
return 0;
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment