printf()系の実装

正確にはsprintf()の変形のasprintf()が欲しいんだけど事情があって自作したい。
けど情報が無いので取り敢えず作ったものを書いてみる。


で調べたんだけど、可変引数のみの使用方法だったらいくらでも情報が出てくる。
というか、その程度であれば自分で作れる。


まず、苦労したのは%sとかの%を使った方法。
man 2 stdargなんかには以下のようなサンプルがある。

#include <stdio.h>
#include <stdarg.h>

void
foo(char *fmt, ...)
{
    va_list ap;
    int d;
    char c, *s;

    va_start(ap, fmt);
    while (*fmt)
        switch (*fmt++) {
        case 's':              /* string */
            s = va_arg(ap, char *);
            printf("string %s\n", s);
            break;
        case 'd':              /* int */
            d = va_arg(ap, int);
            printf("int %d\n", d);
            break;
        case 'c':              /* char */
            /* need a cast here since va_arg only
               takes fully promoted types */
            c = (char) va_arg(ap, int);
            printf("char %c\n", c);
            break;
        }
    va_end(ap);
}


けどこれって、

foo("s s", "foo", "var")

と使う。
自分が知りたいのは%sのような記法。


さらに、苦労したのは

foo("%s hoge %s", "foo", "var")

のようになっているパターン。
上記をmanのまま使う(%の問題は解決したとして)と"foo"と"var"しか取り出せない。


この辺りのサンプルは見付からなかった。
ということで前置きはここまでで、汚いソースだけどメモしておく。
めんどくさいので解説は無し。

//サンプルなので、メモリーリークがあるけど気にしない(実際は Boehm GCつかっているし)。
char *c2str(char c){
	char *ret;

	ret = malloc(sizeof(char)*2);

	ret[0] = c;
	ret[1] = '\0';

	return ret;
}

//関数名は考えるのが苦手なのでとりあえず、asprintf()にしている。
int asprintf(char **strp, char *fmt, ...){
	int max, step;
	int i, len, buf_len, fmt_len;
	char *buf, *str, c;
	char *format;
	va_list argp;

	fmt_len = strlen(fmt);
	format = malloc(fmt_len);
	strncpy(format, fmt, fmt_len);

	len = 0;
	step = 1024;
	max = step;
	str = malloc(max);

	i = 0;
	va_start(argp, fmt);
	while(i<fmt_len){
		if(*format != '%'){
			buf = c2str(*format);
			buf_len = 1;
			i++;
			format++;
			goto CONNECT;
		}

		switch(*fmt++){
			case 's':
				buf = va_arg(argp, char *);
				buf_len = strlen(buf);
				break;
			default:
				continue;
		}
		i+=2;
		format+=2;

CONNECT:
		if(len + buf_len >= max){
			max += step;
			str = realloc(str, step);
		}
		strncat(str, buf, buf_len);
		len += buf_len;
	}
	va_end(argp);

	*strp = str;

	return len;
}


printf()系を使用通りに作るのはかなり大変ですね。