📄 output.c
字号:
/*
* Note: CPRFLAG and _UNICODE cases are currently mutually exclusive.
*/
/* prototypes */
#ifdef CPRFLAG
#define WRITE_CHAR(ch, pnw) write_char(ch, pnw)
#define WRITE_MULTI_CHAR(ch, num, pnw) write_multi_char(ch, num, pnw)
#define WRITE_STRING(s, len, pnw) write_string(s, len, pnw)
#define WRITE_WSTRING(s, len, pnw) write_wstring(s, len, pnw)
LOCAL(void) write_char(int ch, int *pnumwritten);
LOCAL(void) write_multi_char(int ch, int num, int *pnumwritten);
LOCAL(void) write_string(char *string, int len, int *numwritten);
LOCAL(void) write_wstring(wchar_t *string, int len, int *numwritten);
#elif defined (_UNICODE)
#define WRITE_CHAR(ch, pnw) write_char(ch, stream, pnw)
#define WRITE_MULTI_CHAR(ch, num, pnw) write_multi_char(ch, num, stream, pnw)
#define WRITE_STRING(s, len, pnw) write_string(s, len, stream, pnw)
LOCAL(void) write_char(wchar_t ch, FILE *f, int *pnumwritten);
LOCAL(void) write_multi_char(wchar_t ch, int num, FILE *f, int *pnumwritten);
LOCAL(void) write_string(wchar_t *string, int len, FILE *f, int *numwritten);
#else /* defined (_UNICODE) */
#define WRITE_CHAR(ch, pnw) write_char(ch, stream, pnw)
#define WRITE_MULTI_CHAR(ch, num, pnw) write_multi_char(ch, num, stream, pnw)
#define WRITE_STRING(s, len, pnw) write_string(s, len, stream, pnw)
#define WRITE_WSTRING(s, len, pnw) write_wstring(s, len, stream, pnw)
LOCAL(void) write_char(int ch, FILE *f, int *pnumwritten);
LOCAL(void) write_multi_char(int ch, int num, FILE *f, int *pnumwritten);
LOCAL(void) write_string(char *string, int len, FILE *f, int *numwritten);
LOCAL(void) write_wstring(wchar_t *string, int len, FILE *f, int *numwritten);
#endif /* defined (_UNICODE) */
LOCAL(int) get_int_arg(va_list *pargptr);
LOCAL(short) get_short_arg(va_list *pargptr);
#if !LONG_IS_INT
LOCAL(long) get_long_arg(va_list *pargptr);
#endif /* !LONG_IS_INT */
LOCAL(__int64) get_int64_arg(va_list *pargptr);
#ifdef _UNICODE
LOCAL(int) __cdecl _woutput(FILE *, const char *, va_list);
#endif /* _UNICODE */
#ifdef CPRFLAG
LOCAL(int) output(const char *, va_list);
/***
*int _cprintf(format, arglist) - write formatted output directly to console
*
*Purpose:
* Writes formatted data like printf, but uses console I/O functions.
*
*Entry:
* char *format - format string to determine data formats
* arglist - list of POINTERS to where to put data
*
*Exit:
* returns number of characters written
*
*Exceptions:
*
*******************************************************************************/
int __cdecl _cprintf (
const char * format,
...
)
{
va_list arglist;
va_start(arglist, format);
return output(format, arglist);
}
#endif /* CPRFLAG */
/***
*int _output(stream, format, argptr), static int output(format, argptr)
*
*Purpose:
* Output performs printf style output onto a stream. It is called by
* printf/fprintf/sprintf/vprintf/vfprintf/vsprintf to so the dirty
* work. In multi-thread situations, _output assumes that the given
* stream is already locked.
*
* Algorithm:
* The format string is parsed by using a finite state automaton
* based on the current state and the current character read from
* the format string. Thus, looping is on a per-character basis,
* not a per conversion specifier basis. Once the format specififying
* character is read, output is performed.
*
*Entry:
* FILE *stream - stream for output
* char *format - printf style format string
* va_list argptr - pointer to list of subsidiary arguments
*
*Exit:
* Returns the number of characters written, or -1 if an output error
* occurs.
*
*Exceptions:
*
*******************************************************************************/
#ifdef CPRFLAG
LOCAL(int) output (
#elif defined (_UNICODE)
LOCAL(int) _woutput (
FILE *stream,
#else /* defined (_UNICODE) */
int __cdecl _output (
FILE *stream,
#endif /* defined (_UNICODE) */
const char *format,
va_list argptr
)
{
int hexadd; /* offset to add to number to get 'a'..'f' */
char ch; /* character just read */
int flags; /* flag word -- see #defines above for flag values */
enum STATE state; /* current state */
enum CHARTYPE chclass; /* class of current character */
int radix; /* current conversion radix */
int charsout; /* characters currently written so far, -1 = IO error */
int fldwidth; /* selected field with -- 0 means default */
int precision; /* selected precision -- -1 means default */
char prefix[2]; /* numeric prefix -- up to two characters */
int prefixlen; /* length of prefix -- 0 means no prefix */
int capexp; /* non-zero = 'E' exponent signifiet, zero = 'e' */
int no_output; /* non-zero = prodcue no output for this specifier */
char *text; /* pointer text to be printed, not zero terminated */
int textlen; /* length of the text in bytes to be printed */
char buffer[BUFFERSIZE]; /* buffer for conversions */
#ifdef _UNICODE
/* textlen is in multibyte or wide characters for _UNICODE versions */
wchar_t wchar; /* temp wchar_t */
wchar_t *wchar_p; /* temp wchar_t pointer */
int bufferiswide; /* non-zero = buffer contains wide chars already */
#endif /* _UNICODE */
int _tflag=0;
charsout = 0; /* no characters written yet */
state = ST_NORMAL; /* starting state */
/* main loop -- loop while format character exist and no I/O errors */
while ((ch = *format++) != '\0' && charsout >= 0) {
chclass = find_char_class(ch); /* find character class */
state = find_next_state(chclass, state); /* find next state */
/* execute code for each state */
switch (state) {
NORMAL_STATE:
case ST_NORMAL:
/* normal state -- just write character */
#ifndef _UNICODE
if (isleadbyte((int)ch)) {
WRITE_CHAR(ch, &charsout);
ch = *format++;
_ASSERTE (ch != '0'); /* UNDONE: don't fall off format string */
}
WRITE_CHAR(ch, &charsout);
#else /* _UNICODE */
format += (mbtowc (&wchar, format-1, MB_CUR_MAX) - 1);
/* UNDONE: check for mbtowc failure */
WRITE_CHAR(wchar, &charsout);
#endif /* _UNICODE */
break;
case ST_PERCENT:
/* set default value of conversion parameters */
prefixlen = fldwidth = no_output = capexp = 0;
flags = 0;
precision = -1;
#ifdef _UNICODE
bufferiswide = 0;
#endif /* _UNICODE */
break;
case ST_FLAG:
/* set flag based on which flag character */
switch (ch) {
case '-':
flags |= FL_LEFT; /* '-' => left justify */
break;
case '+':
flags |= FL_SIGN; /* '+' => force sign indicator */
break;
case ' ':
flags |= FL_SIGNSP; /* ' ' => force sign or space */
break;
case '#':
flags |= FL_ALTERNATE; /* '#' => alternate form */
break;
case '0':
flags |= FL_LEADZERO; /* '0' => pad with leading zeros */
break;
}
break;
case ST_WIDTH:
/* update width value */
if (ch == '*') {
/* get width from arg list */
fldwidth = get_int_arg(&argptr);
if (fldwidth < 0) {
/* ANSI says neg fld width means '-' flag and pos width */
flags |= FL_LEFT;
fldwidth = -fldwidth;
}
}
else {
/* add digit to current field width */
fldwidth = fldwidth * 10 + (ch - '0');
}
break;
case ST_DOT:
/* zero the precision, since dot with no number means 0
not default, according to ANSI */
precision = 0;
break;
case ST_PRECIS:
/* update precison value */
if (ch == '*') {
/* get precision from arg list */
precision = get_int_arg(&argptr);
if (precision < 0)
precision = -1; /* neg precision means default */
}
else {
/* add digit to current precision */
precision = precision * 10 + (ch - '0');
}
break;
case ST_SIZE:
/* just read a size specifier, set the flags based on it */
switch (ch) {
#if !LONG_IS_INT
case 'l':
flags |= FL_LONG; /* 'l' => long int */
break;
#endif /* !LONG_IS_INT */
#if !LONGDOUBLE_IS_DOUBLE
case 'L':
flags |= FL_LONGDOUBLE; /* 'L' => long double */
break;
#endif /* !LONGDOUBLE_IS_DOUBLE */
case 'I':
/*
* In order to handle the I64 size modifier, we depart from
* the simple deterministic state machine. The code below
* scans
*/
if ( (*format == '6') && (*(format + 1) == '4') ) {
format += 2;
flags |= FL_I64; /* I64 => __int64 */
}
else {
state = ST_NORMAL;
goto NORMAL_STATE;
}
break;
#if !SHORT_IS_INT
case 'h':
flags |= FL_SHORT; /* 'h' => short int */
break;
#endif /* !SHORT_IS_INT */
}
break;
case ST_TYPE:
/* we have finally read the actual type character, so we */
/* now format and "print" the output. We use a big switch */
/* statement that sets 'text' to point to the text that should */
/* be printed, and 'textlen' to the length of this text. */
/* Common code later on takes care of justifying it and */
/* other miscellaneous chores. Note that cases share code, */
/* in particular, all integer formatting is doen in one place. */
/* Look at those funky goto statements! */
/*
* Generic string handling support: %tc, %ts accept
* either chars or wide-chars depending on _tflag.
* _tflag == 1 means wide-chars.
*/
if (ch == 't') {
if (_tflag == 1)
ch = 'w';
else {
ch = *format++;
_ASSERTE (ch!='0'); /* UNDONE: don't fall off format string */
}
}
switch (ch) {
case 'c': {
/* print a single character specified by int argument */
buffer[0] = (char) get_int_arg(&argptr); /* get char to print */
text = buffer;
textlen = 1; /* print just a single character */
}
break;
case 's': {
/* print a string -- */
/* ANSI rules on how much of string to print: */
/* all if precision is default, */
/* min(precision, length) if precision given. */
/* prints '(null)' if a null string is passed */
int i;
char *p; /* temps */
text = get_ptr_arg(&argptr);
if (text == NULL) {
/* null ptr passed, use special string */
text = nullstring;
}
/* At this point it is tempting to use strlen(), but */
/* if a precision is specified, we're not allowed to */
/* scan past there, because there might be no null */
/* at all. Thus, we must do our own scan. */
i = (precision == -1) ? INT_MAX : precision;
p = text;
/* scan for null upto i characters */
#ifndef _UNICODE
while (i-- && *p)
++p;
textlen = p - text; /* length of the string */
#else /* _UNICODE */
for (textlen=0; textlen<i && *p; textlen++) {
if (isleadbyte((int)*p))
++p;
++p;
}
/* textlen now contains length in multibyte chars */
#endif /* _UNICODE */
}
break;
case 'w': {
/* 'wc' print a wide character
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -