/* 日本語文字対応printf()種類関数 (v1.7) * * 著作権:(c) 1999-2006 Andrew Church * * 他のソフトでの使用は(営利目的か否かに関わらず)完全に自由です。但し、 * このソースコードの再配布、または他のソフトのソースコードと一緒での配 * 布の場合は、必ず変更せずに元のままで配布して下さい。変更されたものの * 配布は禁じます。 * また、万一不具合が見つかった場合、上記メールアドレスまでご連絡下さい。 * * Copyright (c) 1999-2006 Andrew Church * * These routines may be used freely in any software, commercial or * otherwise. However, any distribution of this source code, whether * independently or as part of another program, must be of the original, * unmodified source code; distribution of modified versions of the source * code is prohibited. * Please report any bugs to the above address. */ /* 注意:以下の関数はjcodeモジュールを利用しています。*/ /* Notice: These functions require the "jcode" module. */ #include #include #include #include #include #include "jcode.h" #undef JPRINTF_DEFAULT #include "jprintf.h" typedef int (*_pfmt_writefunc_t) (const char *buf, size_t len, void *arg1, void *arg2); /*************************************************************************/ /* Basic format routine for *printf() interfaces. Takes a writing function * and two (optional) parameters, one pointer and one integer, for that * function which are passed on unmodified. The function should return the * number of bytes written, and 0 (not -1!) on write failure. */ static int _pfmt(const char *format_, va_list args, _pfmt_writefunc_t writefunc, void *arg1, void *arg2) { int total = 0; /* Total bytes written */ char *format; /* 文字コード変換後のフォーマット文字列 */ const char *startptr;/* Beginning of non-token text in format string. * Used for writing in bulk instead of * character-at-a-time. */ int n; /* Bytes written in last writefunc() call */ int valid; /* Was this a valid %-token? */ int alt_form; /* "Alternate form"? (# flag) */ int zero_pad; /* Zero-pad value? */ int left_justify; /* Left-justify? (0 means right-justify) */ int always_sign; /* Always add sign value? */ int width; /* Field width */ int precision; /* Precision */ int argsize; /* Size of argument: 0=normal, 1=short, 2=long, * 3=long long */ int what; /* What are we working on? 0=flags, 1=width, * 2=precision, 3=argsize, 4=argtype */ long intval; /* Integer value */ char *strval; /* String value */ void *ptrval; /* Pointer value */ char numbuf[64]; /* Temporary buffer for printing numbers */ char *numptr; /* Pointer to start of printed number in numbuf */ intval = 0; strval = NULL; ptrval = NULL; format = jcode_local(format_); if (!format) return -1; format = strdup(format); if (!format) return -1; startptr = format; while (*format) { if (*format != '%') { format++; continue; } if (startptr != format) { /* Write out accumulated text */ n = writefunc(startptr, format-startptr, arg1, arg2); total += n; /* Abort on short write */ if (n != format-startptr) break; /* Point to this token, in case it's a bad one */ startptr = format; } valid = 0; /* 1 if valid, -1 if known not valid (syntax error) */ alt_form = 0; left_justify = 0; always_sign = 0; zero_pad = 0; width = -1; precision = -1; argsize = 0; what = 0; while (!valid && *++format) { /* Broken out of by terminal chars */ switch (*format) { /* Flags */ case '#': if (what != 0) { valid = -1; break; } alt_form = 1; break; case '-': if (what != 0) { valid = -1; break; } left_justify = 1; break; case '+': if (what != 0) { valid = -1; break; } always_sign = 1; break; case '0': if (what == 0) { zero_pad = 1; break; } /* else fall through */ /* Field widths */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (what == 0) what = 1; else if (what > 2) { valid = -1; break; } if (what == 1) { if (width < 0) width = 0; width = width*10 + (*format)-'0'; } else { if (precision < 0) precision = 0; precision = precision*10 + (*format)-'0'; } break; case '*': if (what == 0) what = 1; else if (what >= 2) { valid = -1; break; } if (what == 1) { width = va_arg(args, int); if (width < 0) { width = -width; left_justify = 1; } } else { precision = va_arg(args, int); } break; case '.': if (what >= 2) { valid = -1; break; } what = 2; break; /* Argument sizes */ case 'h': if (what > 3) { valid = -1; break; } argsize = 1; what = 4; break; case 'l': if (what > 3) { valid = -1; break; } argsize = 2; what = 4; break; case 'L': if (what > 3) { valid = -1; break; } argsize = 3; what = 4; break; /* Argument types */ case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': if (argsize == 1) intval = va_arg(args, int); else if (argsize == 2) intval = va_arg(args, long); else if (argsize == 3) /* XXX we don't handle long longs yet */ intval = va_arg(args, long); else intval = va_arg(args, int); valid = 1; break; case 'c': intval = va_arg(args, int); valid = 1; break; case 's': strval = va_arg(args, char *); valid = 1; break; case 'p': ptrval = va_arg(args, void *); valid = 1; break; case 'n': *((int *)va_arg(args, int *)) = total; valid = 1; break; /* All other characters--this neatly catches "%%" too */ default: valid = -1; break; } /* switch (*format) */ } if (valid != 1) { /* Not a valid %-token; start loop over (token will get printed * out next time through). */ continue; } /* Don't zero-pad if a precision was given or left-justifying */ if (precision != -1 || left_justify) zero_pad = 0; /* For numbers, limit precision to the size of the print buffer */ if ((*format=='d' || *format=='i' || *format=='o' || *format=='u' || *format=='x' || *format=='X') && precision > (signed) sizeof(numbuf)) { precision = sizeof(numbuf); } switch (*format++) { /* Do something with this token */ case 'p': /* Print the NULL value specially */ if (ptrval == NULL) { total += writefunc("(null)", 6, arg1, arg2); break; } /* For all other values, pretend it's really %#.8x */ alt_form = 1; zero_pad = 0; precision = 8; intval = (long) ptrval; /* Fall through */ case 'x': case 'X': { static const char x_chars[] = "0123456789abcdef0x"; static const char X_chars[] = "0123456789ABCDEF0X"; const char *chars = (format[-1]=='X') ? X_chars : x_chars; const char *padstr = zero_pad ? "0" : " "; unsigned long uintval; int len; uintval = (unsigned long) intval; if (alt_form && uintval != 0) { n = writefunc(chars+16, 2, arg1, arg2); total += n; if (n != 2) break; width -= 2; } if (precision < 1) precision = 1; numptr = numbuf + sizeof(numbuf); for (len = 0; len < precision || uintval != 0; len++) { *--numptr = chars[uintval%16]; uintval /= 16; } if (left_justify) { n = writefunc(numptr, len, arg1, arg2); total += n; if (n != len) break; } while (len < width) { if (1 != writefunc(padstr, 1, arg1, arg2)) break; total++; width--; } if (!left_justify) total += writefunc(numptr, len, arg1, arg2); break; } /* case 'x', 'X' */ case 'o': { const char *padstr = zero_pad ? "0" : " "; unsigned long uintval; int len; uintval = (unsigned long) intval; if (precision < 1) precision = 1; numptr = numbuf + sizeof(numbuf); for (len = 0; len < precision || uintval != 0; len++) { *--numptr = (char)('0' + uintval%8); uintval /= 8; } if (alt_form && *numptr != '0') { *--numptr = '0'; len++; } if (left_justify) { n = writefunc(numptr, len, arg1, arg2); total += n; if (n != len) break; } while (len < width) { if (1 != writefunc(padstr, 1, arg1, arg2)) break; total++; width--; } if (!left_justify) total += writefunc(numptr, len, arg1, arg2); break; } /* case 'o' */ if (alt_form && *numptr != '0') *--numptr = '0'; case 'u': { const char *padstr = zero_pad ? "0" : " "; unsigned long uintval; int len; uintval = (unsigned long) intval; if (precision < 1) precision = 1; numptr = numbuf + sizeof(numbuf); for (len = 0; len < precision || uintval != 0; len++) { *--numptr = (char)('0' + uintval%10); uintval /= 10; } if (left_justify) { n = writefunc(numptr, len, arg1, arg2); total += n; if (n != len) break; } while (len < width) { if (1 != writefunc(padstr, 1, arg1, arg2)) break; total++; width--; } if (!left_justify) total += writefunc(numptr, len, arg1, arg2); break; } /* case 'u' */ case 'd': case 'i': { const char *padstr = zero_pad ? "0" : " "; int len; numptr = numbuf + sizeof(numbuf); len = 0; if (intval < 0) { if (1 != writefunc("-", 1, arg1, arg2)) break; total++; width--; intval = -intval; if (intval < 0) { /* true for 0x800...0 */ *numptr-- = '0' - intval%10; len++; intval /= 10; intval = -intval; } } else if (intval >= 0 && always_sign) { if (1 != writefunc("+", 1, arg1, arg2)) break; total++; width--; } if (precision < 1) precision = 1; for (; len < precision || intval != 0; len++) { *--numptr = '0' + intval%10; intval /= 10; } if (left_justify) { n = writefunc(numptr, len, arg1, arg2); total += n; if (n != len) break; } while (len < width) { if (1 != writefunc(padstr, 1, arg1, arg2)) break; total++; width--; } if (!left_justify) total += writefunc(numptr, len, arg1, arg2); break; } /* case 'd', 'i' */ case 'c': { const char *padstr = zero_pad ? "0" : " "; unsigned char c = (unsigned char) intval; if (left_justify) { if (1 != writefunc(&c, 1, arg1, arg2)) break; total++; } while (width > 1) { if (1 != writefunc(padstr, 1, arg1, arg2)) break; total++; width--; } if (!left_justify) total += writefunc(&c, 1, arg1, arg2); break; } /* case 'c' */ case 's': { const char *padstr = zero_pad ? "0" : " "; int len; /* Catch null strings */ if (strval == NULL) { total += writefunc("(null)", 6, arg1, arg2); break; } /* 日本語文字コード変換 */ strval = jcode_local(strval); if (!strval) { total += writefunc("(error)", 7, arg1, arg2); break; } strval = strdup(strval); if (!strval) { total += writefunc("(error)", 7, arg1, arg2); break; } len = strlen(strval); if (precision < 0 || precision > len) precision = len; if (left_justify && len > 0) { n = writefunc(strval, precision, arg1, arg2); total += n; if (n != precision) { free(strval); break; } } while (width > precision) { if (1 != writefunc(padstr, 1, arg1, arg2)) { free(strval); break; } total++; width--; } if (!left_justify && len > 0) total += writefunc(strval, precision, arg1, arg2); free(strval); break; } /* case 's' */ } /* switch (*format++) */ startptr = format; /* Start again after this %-token */ } /* while (*format) */ /* Write anything left over. */ if (startptr != format) total += writefunc(startptr, format-startptr, arg1, arg2); /* Return total bytes written. */ return total; } /*************************************************************************/ /* 文字列対象 / String-target functions */ static int str_writefunc(const char *buf, size_t len, char **string, size_t *size) { if (size) { if (*size <= 0) return 0; if (len > (*size)-1) len = (*size)-1; if (len == 0) return 0; *size -= len; } memcpy(*string, buf, len); (*string) += len; (*string)[0] = 0; return len; } int jvsprintf(char *string, const char *format, va_list args) { int ret; ret = _pfmt(format, args, (_pfmt_writefunc_t) str_writefunc, &string, NULL); return ret; } int jvsnprintf(char *string, size_t size, const char *format, va_list args) { int ret; if (size <= 0) return 0; ret = _pfmt(format, args, (_pfmt_writefunc_t) str_writefunc, &string, &size); return ret; } int jsprintf(char *string, const char *format, ...) { va_list args; va_start(args, format); return jvsprintf(string, format, args); } int jsnprintf(char *string, size_t size, const char *format, ...) { va_list args; va_start(args, format); return jvsnprintf(string, size, format, args); } /*************************************************************************/ /* ファイル対象 / File-target functions */ static int file_writefunc(const char *buf, size_t len, FILE *fp) { return fwrite(buf, 1, len, fp); } int jvfprintf(FILE *fp, const char *format, va_list args) { int ret; ret = _pfmt(format, args, (_pfmt_writefunc_t) file_writefunc, fp, NULL); return ret; } int jvprintf(const char *format, va_list args) { return jvfprintf(stdout, format, args); } int jfprintf(FILE *fp, const char *format, ...) { va_list args; va_start(args, format); return jvfprintf(fp, format, args); } int jprintf(const char *format, ...) { va_list args; va_start(args, format); return jvprintf(format, args); } /*************************************************************************/