📄 jsprf.c
字号:
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** *//*** Portable safe sprintf code.**** Author: Kipp E.B. Hickman*/#include "jsstddef.h"#include <stdarg.h>#include <stdio.h>#include <string.h>#include <stdlib.h>#include "jsprf.h"#include "jslong.h"#include "jsutil.h" /* Added by JSIFY */#include <glib.h>#define VARARGS_ASSIGN(foo, bar) G_VA_COPY(foo, bar)/*** WARNING: This code may *NOT* call JS_LOG (because JS_LOG calls it)*//*** XXX This needs to be internationalized!*/typedef struct SprintfStateStr SprintfState;struct SprintfStateStr { int (*stuff)(SprintfState *ss, const char *sp, JSUint32 len); char *base; char *cur; JSUint32 maxlen; int (*func)(void *arg, const char *sp, JSUint32 len); void *arg;};/*** Numbered Arguement State*/struct NumArgState{ int type; /* type of the current ap */ va_list ap; /* point to the corresponding position on ap */};#define NAS_DEFAULT_NUM 20 /* default number of NumberedArgumentState array */#define TYPE_INT16 0#define TYPE_UINT16 1#define TYPE_INTN 2#define TYPE_UINTN 3#define TYPE_INT32 4#define TYPE_UINT32 5#define TYPE_INT64 6#define TYPE_UINT64 7#define TYPE_STRING 8#define TYPE_DOUBLE 9#define TYPE_INTSTR 10#define TYPE_UNKNOWN 20#define FLAG_LEFT 0x1#define FLAG_SIGNED 0x2#define FLAG_SPACED 0x4#define FLAG_ZEROS 0x8#define FLAG_NEG 0x10/*** Fill into the buffer using the data in src*/static int fill2(SprintfState *ss, const char *src, int srclen, int width, int flags){ char space = ' '; int rv; width -= srclen; if ((width > 0) && ((flags & FLAG_LEFT) == 0)) { /* Right adjusting */ if (flags & FLAG_ZEROS) { space = '0'; } while (--width >= 0) { rv = (*ss->stuff)(ss, &space, 1); if (rv < 0) { return rv; } } } /* Copy out the source data */ rv = (*ss->stuff)(ss, src, (JSUint32)srclen); if (rv < 0) { return rv; } if ((width > 0) && ((flags & FLAG_LEFT) != 0)) { /* Left adjusting */ while (--width >= 0) { rv = (*ss->stuff)(ss, &space, 1); if (rv < 0) { return rv; } } } return 0;}/*** Fill a number. The order is: optional-sign zero-filling conversion-digits*/static int fill_n(SprintfState *ss, const char *src, int srclen, int width, int prec, int type, int flags){ int zerowidth = 0; int precwidth = 0; int signwidth = 0; int leftspaces = 0; int rightspaces = 0; int cvtwidth; int rv; char sign; if ((type & 1) == 0) { if (flags & FLAG_NEG) { sign = '-'; signwidth = 1; } else if (flags & FLAG_SIGNED) { sign = '+'; signwidth = 1; } else if (flags & FLAG_SPACED) { sign = ' '; signwidth = 1; } } cvtwidth = signwidth + srclen; if (prec > 0) { if (prec > srclen) { precwidth = prec - srclen; /* Need zero filling */ cvtwidth += precwidth; } } if ((flags & FLAG_ZEROS) && (prec < 0)) { if (width > cvtwidth) { zerowidth = width - cvtwidth; /* Zero filling */ cvtwidth += zerowidth; } } if (flags & FLAG_LEFT) { if (width > cvtwidth) { /* Space filling on the right (i.e. left adjusting) */ rightspaces = width - cvtwidth; } } else { if (width > cvtwidth) { /* Space filling on the left (i.e. right adjusting) */ leftspaces = width - cvtwidth; } } while (--leftspaces >= 0) { rv = (*ss->stuff)(ss, " ", 1); if (rv < 0) { return rv; } } if (signwidth) { rv = (*ss->stuff)(ss, &sign, 1); if (rv < 0) { return rv; } } while (--precwidth >= 0) { rv = (*ss->stuff)(ss, "0", 1); if (rv < 0) { return rv; } } while (--zerowidth >= 0) { rv = (*ss->stuff)(ss, "0", 1); if (rv < 0) { return rv; } } rv = (*ss->stuff)(ss, src, (JSUint32)srclen); if (rv < 0) { return rv; } while (--rightspaces >= 0) { rv = (*ss->stuff)(ss, " ", 1); if (rv < 0) { return rv; } } return 0;}/*** Convert a long into its printable form*/static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix, int type, int flags, const char *hexp){ char cvtbuf[100]; char *cvt; int digits; /* according to the man page this needs to happen */ if ((prec == 0) && (num == 0)) { return 0; } /* ** Converting decimal is a little tricky. In the unsigned case we ** need to stop when we hit 10 digits. In the signed case, we can ** stop when the number is zero. */ cvt = cvtbuf + sizeof(cvtbuf); digits = 0; while (num) { int digit = (((unsigned long)num) % radix) & 0xF; *--cvt = hexp[digit]; digits++; num = (long)(((unsigned long)num) / radix); } if (digits == 0) { *--cvt = '0'; digits++; } /* ** Now that we have the number converted without its sign, deal with ** the sign and zero padding. */ return fill_n(ss, cvt, digits, width, prec, type, flags);}/*** Convert a 64-bit integer into its printable form*/static int cvt_ll(SprintfState *ss, JSInt64 num, int width, int prec, int radix, int type, int flags, const char *hexp){ char cvtbuf[100]; char *cvt; int digits; JSInt64 rad; /* according to the man page this needs to happen */ if ((prec == 0) && (JSLL_IS_ZERO(num))) { return 0; } /* ** Converting decimal is a little tricky. In the unsigned case we ** need to stop when we hit 10 digits. In the signed case, we can ** stop when the number is zero. */ JSLL_I2L(rad, radix); cvt = cvtbuf + sizeof(cvtbuf); digits = 0; while (!JSLL_IS_ZERO(num)) { JSInt32 digit; JSInt64 quot, rem; JSLL_UDIVMOD(", &rem, num, rad); JSLL_L2I(digit, rem); *--cvt = hexp[digit & 0xf]; digits++; num = quot; } if (digits == 0) { *--cvt = '0'; digits++; } /* ** Now that we have the number converted without its sign, deal with ** the sign and zero padding. */ return fill_n(ss, cvt, digits, width, prec, type, flags);}/*** Convert a double precision floating point number into its printable** form.**** XXX stop using sprintf to convert floating point*/static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1){ char fin[20]; char fout[300]; int amount = fmt1 - fmt0; JS_ASSERT((amount > 0) && (amount < (int)sizeof(fin))); if (amount >= (int)sizeof(fin)) { /* Totally bogus % command to sprintf. Just ignore it */ return 0; } memcpy(fin, fmt0, (size_t)amount); fin[amount] = 0; /* Convert floating point using the native sprintf code */#ifdef DEBUG { const char *p = fin; while (*p) { JS_ASSERT(*p != 'L'); p++; } }#endif sprintf(fout, fin, d); /* ** This assert will catch overflow's of fout, when building with ** debugging on. At least this way we can track down the evil piece ** of calling code and fix it! */ JS_ASSERT(strlen(fout) < sizeof(fout)); return (*ss->stuff)(ss, fout, strlen(fout));}/*** Convert a string into its printable form. "width" is the output** width. "prec" is the maximum number of characters of "s" to output,** where -1 means until NUL.*/static int cvt_s(SprintfState *ss, const char *s, int width, int prec, int flags){ int slen; if (prec == 0) return 0; /* Limit string length by precision value */ slen = s ? strlen(s) : 6; if (prec > 0) { if (prec < slen) { slen = prec; } } /* and away we go */ return fill2(ss, s ? s : "(null)", slen, width, flags);}/*** BuildArgArray stands for Numbered Argument list Sprintf** for example,** fmp = "%4$i, %2$d, %3s, %1d";** the number must start from 1, and no gap among them*/static struct NumArgState* BuildArgArray( const char *fmt, va_list ap, int* rv, struct NumArgState* nasArray ){ int number = 0, cn = 0, i; const char *p; char c; struct NumArgState *nas; /* ** first pass: ** detemine how many legal % I have got, then allocate space */ p = fmt; *rv = 0; i = 0; while( ( c = *p++ ) != 0 ){ if( c != '%' ) continue; if( ( c = *p++ ) == '%' ) /* skip %% case */ continue; while( c != 0 ){ if( c > '9' || c < '0' ){ if( c == '$' ){ /* numbered argument csae */ if( i > 0 ){ *rv = -1; return NULL; } number++; } else { /* non-numbered argument case */ if( number > 0 ){ *rv = -1; return NULL; } i = 1; } break; } c = *p++; } } if( number == 0 ){ return NULL; } if( number > NAS_DEFAULT_NUM ){ nas = (struct NumArgState*)malloc( number * sizeof( struct NumArgState ) ); if( !nas ){ *rv = -1; return NULL; } } else { nas = nasArray; } for( i = 0; i < number; i++ ){ nas[i].type = TYPE_UNKNOWN; } /* ** second pass: ** set nas[].type */ p = fmt; while( ( c = *p++ ) != 0 ){ if( c != '%' ) continue; c = *p++; if( c == '%' ) continue; cn = 0; while( c && c != '$' ){ /* should improve error check later */ cn = cn*10 + c - '0'; c = *p++; } if( !c || cn < 1 || cn > number ){ *rv = -1; break; } /* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */ cn--; if( nas[cn].type != TYPE_UNKNOWN ) continue; c = *p++; /* width */ if (c == '*') { /* not supported feature, for the argument is not numbered */ *rv = -1; break; } while ((c >= '0') && (c <= '9')) { c = *p++; } /* precision */ if (c == '.') { c = *p++; if (c == '*') { /* not supported feature, for the argument is not numbered */ *rv = -1; break; } while ((c >= '0') && (c <= '9')) { c = *p++; } } /* size */ nas[cn].type = TYPE_INTN; if (c == 'h') { nas[cn].type = TYPE_INT16; c = *p++; } else if (c == 'L') { /* XXX not quite sure here */ nas[cn].type = TYPE_INT64; c = *p++; } else if (c == 'l') { nas[cn].type = TYPE_INT32; c = *p++; if (c == 'l') { nas[cn].type = TYPE_INT64; c = *p++; } } /* format */ switch (c) { case 'd': case 'c': case 'i': case 'o': case 'u': case 'x': case 'X': break; case 'e': case 'f': case 'g': nas[ cn ].type = TYPE_DOUBLE; break; case 'p': /* XXX should use cpp */ if (sizeof(void *) == sizeof(JSInt32)) { nas[ cn ].type = TYPE_UINT32; } else if (sizeof(void *) == sizeof(JSInt64)) { nas[ cn ].type = TYPE_UINT64; } else if (sizeof(void *) == sizeof(JSIntn)) { nas[ cn ].type = TYPE_UINTN; } else { nas[ cn ].type = TYPE_UNKNOWN; } break; case 'C': case 'S': case 'E': case 'G': /* XXX not supported I suppose */ JS_ASSERT(0); nas[ cn ].type = TYPE_UNKNOWN; break; case 's': nas[ cn ].type = TYPE_STRING; break; case 'n': nas[ cn ].type = TYPE_INTSTR; break; default: JS_ASSERT(0); nas[ cn ].type = TYPE_UNKNOWN; break; } /* get a legal para. */ if( nas[ cn ].type == TYPE_UNKNOWN ){ *rv = -1; break; } } /* ** third pass ** fill the nas[cn].ap */ if( *rv < 0 ){ if( nas != nasArray ) free( nas ); return NULL; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -