| /* $Id: log-prf.c,v 1.4 2009/08/30 16:56:42 fredette Exp $ */ |
| |
| /* libtme/log-prf.c - a printf function body: */ |
| |
| /* |
| * Copyright (c) 2003 Matt Fredette |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. All advertising materials mentioning features or use of this software |
| * must display the following acknowledgement: |
| * This product includes software developed by Matt Fredette. |
| * 4. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
| * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
| * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| { |
| |
| /* start the variable arguments: */ |
| #ifdef HAVE_STDARG_H |
| va_start(prf_args, prf_format); |
| #else /* HAVE_STDARG_H */ |
| va_start(prf_args); |
| #endif /* HAVE_STDARG_H */ |
| |
| /* we are in state zero and our aggregate begins at the |
| beginning of the format: */ |
| prf_state = 0; |
| prf_agg = prf_format; |
| |
| /* to silence gcc -Wuninitialized: */ |
| prf_flag_ls = (const char *) NULL; |
| prf_flag_0 = FALSE; |
| prf_width = -1; |
| |
| /* process format characters until we get to the NUL: */ |
| for(;;) { |
| |
| /* get the next format character: */ |
| prf_char = *(prf_format++); |
| |
| /* handle a 'NUL' specially: */ |
| if (prf_char == '\0') { |
| |
| /* if we were in state zero, dump any aggregate: */ |
| if (prf_state == 0) { |
| if ((prf_format - prf_agg) > 1) { |
| PRF_OUT_MEM(prf_agg, (prf_format - prf_agg) - 1); |
| } |
| } |
| |
| /* we're done: */ |
| break; |
| } |
| |
| /* dispatch on our state: */ |
| switch (prf_state) { |
| |
| /* state 0: "": */ |
| case 0: |
| |
| /* on a '%', dump any aggregate and move to state one: */ |
| if (prf_char == '%') { |
| assert(prf_format > prf_agg); |
| if ((prf_format - prf_agg) > 1) { |
| PRF_OUT_MEM(prf_agg, (prf_format - prf_agg) - 1); |
| } |
| prf_state = 1; |
| } |
| |
| /* otherwise, remain in state zero, and the character gets added |
| automatically to the aggregate: */ |
| else { |
| /* nothing */ |
| } |
| break; |
| |
| /* state 1: "%": */ |
| case 1: |
| |
| /* if this is another '%', print a single '%' and move to state zero: */ |
| if (prf_char == '%') { |
| PRF_OUT_CHAR('%'); |
| prf_agg = prf_format; |
| prf_state = 0; |
| break; |
| } |
| |
| /* reset all flags, precisions, etc., and enter state two: */ |
| prf_flag_ls = "ll" + 2; |
| prf_flag_0 = FALSE; |
| prf_width = -1; |
| prf_state = 2; |
| |
| /* FALLTHROUGH */ |
| |
| /* state 2: "%" followed by zero or more flags, precisions, etc: */ |
| case 2: |
| |
| /* dispatch on this character: */ |
| prf_digit = 9; |
| switch (prf_char) { |
| |
| /* the 'l' flag: */ |
| case 'l': prf_flag_ls -= (prf_flag_ls[0] == '\0' || prf_flag_ls[1] == '\0'); break; |
| |
| /* a width: */ |
| case '0': |
| if (prf_width < 0) { |
| prf_flag_0 = TRUE; |
| } |
| prf_digit--; |
| /* FALLTHROUGH */ |
| case '1': prf_digit--; |
| case '2': prf_digit--; |
| case '3': prf_digit--; |
| case '4': prf_digit--; |
| case '5': prf_digit--; |
| case '6': prf_digit--; |
| case '7': prf_digit--; |
| case '8': prf_digit--; |
| case '9': |
| if (prf_width < 0) { |
| prf_width = 0; |
| } |
| prf_width = (prf_width * 10) + prf_digit; |
| break; |
| |
| /* the 'd', 'u', 'x', and 'X' conversions: */ |
| case 'd': |
| case 'u': |
| case 'x': |
| case 'X': |
| if (prf_width < 0) { |
| sprintf(prf_format_buffer, |
| "%%%s%c", |
| prf_flag_ls, |
| prf_char); |
| prf_width = 0; |
| } |
| else { |
| sprintf(prf_format_buffer, |
| "%%%s%d%s%c", |
| (prf_flag_0 |
| ? "0" |
| : ""), |
| prf_width, |
| prf_flag_ls, |
| prf_char); |
| } |
| /* NB: we always allocate at least the specified field width, |
| plus the worst-case number of characters needed to |
| represent the value in decimal, plus one for the NUL. this |
| should avoid buffer overflows entirely: */ |
| if (prf_flag_ls[0] == '\0') { |
| prf_value_d = va_arg(prf_args, int); |
| prf_value_buffer = tme_new(char, prf_width + ((sizeof(prf_value_d) * 3) + 1)); |
| sprintf(prf_value_buffer, prf_format_buffer, prf_value_d); |
| PRF_OUT_ARG_CODE(TME_LOG_ARG_CODE_INT); |
| } |
| else if (prf_flag_ls[1] == '\0') { |
| prf_value_ld = va_arg(prf_args, long int); |
| prf_value_buffer = tme_new(char, prf_width + ((sizeof(prf_value_ld) * 3) + 1)); |
| sprintf(prf_value_buffer, prf_format_buffer, prf_value_ld); |
| PRF_OUT_ARG_CODE(TME_LOG_ARG_CODE_LONG_INT); |
| } |
| else { |
| #if (0 prf_lld(+ 1)) |
| prf_value_lld = va_arg(prf_args, long long int); |
| prf_value_buffer = tme_new(char, prf_width + ((sizeof(prf_value_lld) * 3) + 1)); |
| sprintf(prf_value_buffer, prf_format_buffer, prf_value_lld); |
| PRF_OUT_ARG_CODE(TME_LOG_ARG_CODE_LONG_LONG_INT); |
| #else /* long long int not supported */ |
| abort(); |
| #endif /* long long int not supported */ |
| } |
| PRF_OUT_MEM(prf_value_buffer, strlen(prf_value_buffer)); |
| tme_free(prf_value_buffer); |
| |
| /* enter state zero: */ |
| prf_agg = prf_format; |
| prf_state = 0; |
| break; |
| |
| /* the 's' conversion: */ |
| case 's': |
| prf_value_s = va_arg(prf_args, const char *); |
| PRF_OUT_ARG_CODE(TME_LOG_ARG_CODE_STRING); |
| PRF_OUT_MEM(prf_value_s, strlen(prf_value_s)); |
| |
| /* enter state zero: */ |
| prf_agg = prf_format; |
| prf_state = 0; |
| break; |
| |
| /* the 'c' conversion: */ |
| case 'c': |
| prf_value_c = va_arg(prf_args, int); |
| PRF_OUT_ARG_CODE(TME_LOG_ARG_CODE_CHAR); |
| PRF_OUT_CHAR(prf_value_c); |
| |
| /* enter state zero: */ |
| prf_agg = prf_format; |
| prf_state = 0; |
| break; |
| |
| /* ignore anything else: */ |
| default: |
| prf_agg = prf_format; |
| prf_state = 0; |
| break; |
| } |
| break; |
| |
| default: |
| assert(FALSE); |
| } |
| } |
| |
| /* end the variable arguments: */ |
| va_end(prf_args); |
| } |