kolibrios/programs/develop/open watcom/trunk/clib/streamio/scnf.c

1117 lines
30 KiB
C
Raw Normal View History

/****************************************************************************
*
* Open Watcom Project
*
* Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
*
* ========================================================================
*
* This file contains Original Code and/or Modifications of Original
* Code as defined in and that are subject to the Sybase Open Watcom
* Public License version 1.0 (the 'License'). You may not use this file
* except in compliance with the License. BY USING THIS FILE YOU AGREE TO
* ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
* provided with the Original Code and Modifications, and is also
* available at www.sybase.com/developer/opensource.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
* ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
* NON-INFRINGEMENT. Please see the License for the specific language
* governing rights and limitations under the License.
*
* ========================================================================
*
* Description: Platform independent worker routines for scanf().
*
****************************************************************************/
#define __LONG_LONG_SUPPORT__
#if !defined( __NETWARE__ ) && !defined( __UNIX__ )
#define USE_MBCS_TRANSLATION
#endif
#include "variety.h"
#ifdef SAFE_SCANF
#include "saferlib.h"
#endif
#include "widechar.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __WIDECHAR__
#include <wctype.h>
#else
#include <ctype.h>
#endif
#include <stdarg.h>
#include "scanf.h"
#include "prtscncf.h"
#include "fixpoint.h"
#include "ftos.h"
#include "farsupp.h"
#include "myvalist.h"
#if defined( __WIDECHAR__ ) || defined( USE_MBCS_TRANSLATION )
#include <mbstring.h>
#endif
#define TRUE 1
#define FALSE 0
#define STOP_CHR 0xFFFFFFFF
#define EFG_SCANF (*__EFG_scanf)
/* internal file/string get, unget routines */
#ifdef __WINDOWS_386__
#ifdef __SW_3S
#pragma aux cget modify [eax edx ecx fs gs]
#pragma aux uncget modify [eax edx ecx fs gs]
#else
#pragma aux cget modify [fs gs]
#pragma aux uncget modify [fs gs]
#endif
#endif
#if defined(__HUGE__)
#define SCNF_FAR _WCFAR
#else
#define SCNF_FAR
#endif
/* Macros to reduce the already large number of ifdefs in the code */
#ifdef SAFE_SCANF
#define GET_MAXELEM(x) x = va_arg( arg->v, size_t )
#define DEFINE_VARS(x,y) size_t x, y = 0
#define CHECK_ELEMS(x,y,z) if( x < ++y ) return( z )
#else
#define GET_MAXELEM(x)
#define DEFINE_VARS(x,y)
#define CHECK_ELEMS(x,y,z)
#endif
static int cget( PTR_SCNF_SPECS specs )
{
return( (*((specs)->cget_rtn))( specs ) );
}
static void uncget( int c, PTR_SCNF_SPECS specs )
{
((*((specs)->uncget_rtn))( c, specs ));
}
/*
* get_opt -- get option string for current conversion directive
* and fills in the SCNF_SPECS structure.
* returns advanced format string pointer.
*/
static const CHAR_TYPE *get_opt( const CHAR_TYPE *opt_str, PTR_SCNF_SPECS specs )
{
int c, width;
specs->assign = TRUE;
specs->far_ptr = 0;
specs->near_ptr = 0;
specs->char_var = 0;
specs->short_var = 0;
specs->long_var = 0;
specs->long_long_var = 0;
specs->long_double_var = 0;
specs->p_format = 0; /* 21-nov-89 */
specs->width = -1;
if( *opt_str == '*' ) {
specs->assign = FALSE;
++opt_str;
}
c = *opt_str;
if( __F_NAME(isdigit,iswdigit)( c ) ) {
width = 0;
do {
width *= 10;
width += ( c - '0' );
c = *++opt_str;
} while( __F_NAME(isdigit,iswdigit)( c ) );
specs->width = width;
}
switch( *opt_str ) {
case 'N':
specs->near_ptr = 1;
++opt_str;
break;
#if defined( __FAR_SUPPORT__ )
case 'F': /* conflicts with ISO-defined 'F' conversion */
/* fall through */
#endif
case 'W':
specs->far_ptr = 1;
++opt_str;
break;
}
switch( *opt_str ) {
case 'h':
if( opt_str[1] == 'h' ) {
specs->char_var = 1;
opt_str += 2;
break;
}
specs->short_var = 1;
++opt_str;
break;
case 'l':
#if defined( __LONG_LONG_SUPPORT__ )
if( opt_str[1] == 'l' ) {
specs->long_long_var = 1;
opt_str += 2;
break;
}
#endif
/* fall through */
ZSPEC_CASE_LONG
TSPEC_CASE_LONG
case 'w':
specs->long_var = 1;
++opt_str;
break;
#if defined( __LONG_LONG_SUPPORT__ )
JSPEC_CASE_LLONG
/* fall through */
#endif
case 'L':
specs->long_double_var = 1;
specs->long_long_var = 1;
++opt_str;
break;
#if defined( __LONG_LONG_SUPPORT__ )
case 'I':
if( opt_str[1] == '6' && opt_str[2] == '4' ) {
specs->long_long_var = 1;
opt_str += 3;
}
break;
#endif
#if defined( TSPEC_IS_INT ) || defined( ZSPEC_IS_INT )
TSPEC_CASE_INT /* If either 't' or 'z' spec corresponds to 'int', */
ZSPEC_CASE_INT /* we need to parse and ignore the spec. */
++opt_str;
break;
#endif
}
return( opt_str );
}
/*
* scan_white -- scan white space from input stream
*/
static int scan_white( PTR_SCNF_SPECS specs )
{
int c, len;
len = 0;
for( ;; ) {
c = cget( specs );
if( !__F_NAME(isspace,iswspace)( c ) )
break;
++len;
}
if( !specs->eoinp )
uncget( c, specs );
return( len );
}
/*
* scan_char -- handles %c and %C
*/
static int scan_char( PTR_SCNF_SPECS specs, my_va_list *arg )
{
int len;
int width;
FAR_STRING str;
int c;
DEFINE_VARS( maxelem, nelem );
if( specs->assign ) {
#if defined( __FAR_SUPPORT__ )
if( specs->far_ptr ) {
str = va_arg( arg->v, CHAR_TYPE _WCFAR * );
} else if( specs->near_ptr ) {
str = va_arg( arg->v, CHAR_TYPE _WCNEAR * );
} else {
str = va_arg( arg->v, CHAR_TYPE * );
}
#else
str = va_arg( arg->v, CHAR_TYPE * );
#endif
GET_MAXELEM( maxelem );
}
len = 0;
if( (width = specs->width) == -1 )
width = 1;
while( width > 0 ) {
c = cget( specs );
if( specs->eoinp )
break;
++len;
--width;
if( specs->assign ) {
CHECK_ELEMS( maxelem, nelem, -1 );
#if defined( __WIDECHAR__ ) && defined( USE_MBCS_TRANSLATION )
if( specs->short_var ) {
char mbBuf[MB_CUR_MAX];
if( wctomb( mbBuf, c ) != -1 ) {
*(FAR_ASCII_STRING)str = mbBuf[0];
str = (FAR_STRING) ( (FAR_ASCII_STRING)str + 1 );
if( _ismbblead( mbBuf[0] ) ) {
CHECK_ELEMS( maxelem, nelem, -1 );
*(FAR_ASCII_STRING)str = mbBuf[1];
str = (FAR_STRING) ( (FAR_ASCII_STRING)str + 1 );
}
} else {
return( 0 );
}
} else {
*str++ = c;
}
#elif defined( USE_MBCS_TRANSLATION )
if( specs->long_var ) {
wchar_t wc;
char mbBuf[MB_CUR_MAX];
mbBuf[0] = c;
if( _ismbblead( mbBuf[0] ) )
mbBuf[1] = cget( specs );
if( mbtowc( &wc, mbBuf, MB_CUR_MAX ) != -1 ) {
*(FAR_UNI_STRING)str = wc;
str = (FAR_STRING) ( (FAR_UNI_STRING)str + 1 );
} else {
return( 0 );
}
} else {
*str++ = c;
}
#else
*str++ = c;
#endif
}
}
return( len );
}
/*
* cgetw -- cget which keeps track of field width.
* returns STOP_CHR on end of field or end of file.
*/
static long cgetw( PTR_SCNF_SPECS specs )
{
int c;
if( specs->width-- == 0 )
return( STOP_CHR );
c = cget( specs );
return( !( specs->eoinp ) ? c : STOP_CHR );
}
/*
* scan_string -- handles %s and %S
*/
static int scan_string( PTR_SCNF_SPECS specs, my_va_list *arg )
{
int c;
int len;
FAR_ASCII_STRING str;
char chsize;
DEFINE_VARS( maxelem, nelem );
if( specs->long_var ) { /* %ls or %ws */
chsize = sizeof( wchar_t );
} else if( specs->short_var ) { /* %hs */
chsize = 1;
} else { /* %s */
chsize = CHARSIZE;
}
if( specs->assign ) {
#if defined( __FAR_SUPPORT__ )
if( specs->far_ptr ) {
str = va_arg( arg->v, char _WCFAR * );
} else if( specs->near_ptr ) {
str = va_arg( arg->v, char _WCNEAR * );
} else {
str = va_arg( arg->v, char * );
}
#else
str = va_arg( arg->v, char * );
#endif
GET_MAXELEM( maxelem );
}
len = 0;
for( ;; ) {
c = cget( specs );
if( !__F_NAME(isspace,iswspace)( c ) )
break;
++len;
}
if( specs->eoinp ) {
len = 0; /* since this is eof, no input done */
goto done;
}
if( specs->width-- == 0 )
goto ugdone;
do {
++len;
if( specs->assign ) {
CHECK_ELEMS( maxelem, nelem, -1 );
if( chsize == 1 ) {
#if defined( __WIDECHAR__ ) && defined( USE_MBCS_TRANSLATION )
char mbBuf[MB_CUR_MAX];
if( wctomb( mbBuf, c ) != -1 ) {
*(FAR_ASCII_STRING)str = mbBuf[0];
if( _ismbblead( mbBuf[0] ) ) {
CHECK_ELEMS( maxelem, nelem, -1 );
str++;
*(FAR_ASCII_STRING)str = mbBuf[1];
}
} else {
return( 0 );
}
#else
*str = c;
#endif
} else {
#if !defined( __WIDECHAR__ ) && defined( USE_MBCS_TRANSLATION )
wchar_t wc;
char mbBuf[MB_CUR_MAX];
mbBuf[0] = c;
if( _ismbblead( mbBuf[0] ) )
mbBuf[1] = cget( specs );
if( mbtowc( &wc, mbBuf, MB_CUR_MAX ) != -1 ) {
*(FAR_UNI_STRING)str = wc;
} else {
return( 0 );
}
#else
*(FAR_UNI_STRING)str = c;
#endif
}
str += chsize;
}
if( (c = cgetw( specs )) == STOP_CHR ) {
goto done;
}
} while( !__F_NAME(isspace,iswspace)( c ) );
ugdone:
uncget( c, specs );
done:
if( specs->assign && len > 0 ) {
CHECK_ELEMS( maxelem, nelem, -1 );
if( chsize == 1 ) {
*str = '\0';
} else {
*(FAR_UNI_STRING)str = 0;
}
}
return( len );
}
/*
* report_scan -- handles %n
*/
static void report_scan( PTR_SCNF_SPECS specs, my_va_list *arg, int match )
{
FAR_INT iptr;
if( specs->assign ) {
#if defined( __FAR_SUPPORT__ )
if( specs->far_ptr ) {
iptr = va_arg( arg->v, int _WCFAR * );
} else if( specs->near_ptr ) {
iptr = va_arg( arg->v, int _WCNEAR * );
} else {
iptr = va_arg( arg->v, int * );
}
#else
iptr = va_arg( arg->v, int * );
#endif
if( specs->char_var ) {
*((FAR_CHAR)iptr) = match;
} else if( specs->short_var ) {
*((FAR_SHORT)iptr) = match;
} else if( specs->long_var ) {
*((FAR_LONG)iptr) = match;
#if defined( __LONG_LONG_SUPPORT__ )
} else if( specs->long_long_var ) {
*((FAR_INT64)iptr) = match;
#endif
} else {
*iptr = match;
}
}
}
#if !defined( __WIDECHAR__ )
#define SCANSET_LENGTH (256 / 8)
static const char lst_mask[8] = {
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
};
/*
* makelist -- create scanset for %[ directive.
* scanset is stored as 256 bit flags in a 32 byte array.
*/
static const char *makelist( const char *format, char *scanset )
{
int lst_chr;
memset( scanset, 0, SCANSET_LENGTH );
if( (lst_chr = *format++) == '\0' )
return( format );
do {
scanset[lst_chr >> 3] |= lst_mask[lst_chr & 0x07];
if( (lst_chr = *format) == '\0' )
break;
++format;
} while( lst_chr != ']' );
return( format );
}
#endif
/*
* scan_arb -- handles %[
*/
static int scan_arb( PTR_SCNF_SPECS specs, my_va_list *arg, const CHAR_TYPE **format )
{
unsigned width;
FAR_STRING str;
int len, c, not_flag;
#if defined( __WIDECHAR__ )
const CHAR_TYPE *list;
CHAR_TYPE ch;
char in_list;
#else
char scanset[SCANSET_LENGTH];
#endif
DEFINE_VARS( maxelem, nelem );
if( not_flag = (**format == '^') ) {
++(*format);
}
#if !defined( __WIDECHAR__ )
*format = makelist( *format, scanset );
#endif
if( specs->assign ) {
#if defined( __FAR_SUPPORT__ )
if( specs->far_ptr ) {
str = va_arg( arg->v, CHAR_TYPE _WCFAR * );
} else if( specs->near_ptr ) {
str = va_arg( arg->v, CHAR_TYPE _WCNEAR * );
} else {
str = va_arg( arg->v, CHAR_TYPE * );
}
#else
str = va_arg( arg->v, CHAR_TYPE * );
#endif
GET_MAXELEM( maxelem );
}
len = 0;
width = specs->width;
while( width > 0 ) {
c = cget( specs );
if( specs->eoinp )
break;
#if defined( __WIDECHAR__ )
list = *format;
ch = *list;
in_list = TRUE;
while( c != ch ) {
list++;
ch = *list;
if( ch == ']' ) {
in_list = FALSE;
break;
}
}
if( in_list == not_flag ) {
uncget( c, specs );
break;
}
#else
if( ((scanset[c >> 3] & lst_mask[c & 0x07]) == 0) != not_flag ) {
uncget( c, specs );
break;
}
#endif
++len;
--width;
if( specs->assign ) {
CHECK_ELEMS( maxelem, nelem, -1 );
*str++ = c;
}
}
if( specs->assign && len > 0 ) {
CHECK_ELEMS( maxelem, nelem, -1 );
*str = '\0';
}
#if defined( __WIDECHAR__ )
while( *(*format)++ != ']' ) /* skip past format specifier */
;
#endif
return( len );
}
/*
* scan_float -- handles floating point numerical conversion
* *** should implement buffer overflow protection ***
*/
static int scan_float( PTR_SCNF_SPECS specs, my_va_list *arg )
{
double value;
char *num_str, buf[80];
int len;
int pref_len;
long c;
int digit_found;
FAR_FLOAT fptr;
char *p;
T32 at;
T32 ft;
num_str = buf;
pref_len = len = 0;
for( ;; ) {
c = cget( specs );
if( !__F_NAME(isspace,iswspace)( c ) )
break;
++pref_len;
}
if( specs->eoinp )
goto done;
if( specs->width-- == 0 )
goto ugdone;
if( c == '+' || c == '-' ) {
*num_str++ = c;
++pref_len;
if( (c = cgetw( specs )) == STOP_CHR ) {
goto done;
}
}
if( !__F_NAME(isdigit,iswdigit)( c ) && c != '.' )
goto ugdone;
at.uWhole = 0;
digit_found = FALSE;
if( __F_NAME(isdigit,iswdigit)( c ) ) {
digit_found = TRUE;
do {
*num_str++ = c;
if( specs->short_var )
at.wd.hi = at.wd.hi * 10 + c - '0';
++len;
if( (c = cgetw( specs )) == STOP_CHR ) {
goto done;
}
} while( __F_NAME(isdigit,iswdigit)( c ) );
}
if( c == '.' ) {
*num_str++ = c;
++len; /* account for the '.' */
if( (c = cgetw( specs )) == STOP_CHR )
goto done;
if( !digit_found && !__F_NAME(isdigit,iswdigit)(c) )
goto ugdone;
while( __F_NAME(isdigit,iswdigit)( c ) ) {
*num_str++ = c;
++len;
if( (c = cgetw( specs )) == STOP_CHR ) {
break;
}
}
if( specs->short_var ) { /* %hf fixed-point format 05-feb-92 */
ft.uWhole = 0;
p = num_str;
for( ;; ) {
--p;
if( *p == '.' )
break;
ft.bite.b3 = *p - '0';
ft.uWhole = ft.uWhole / 10;
}
at.wd.lo = ft.wd.lo;
}
if( c == STOP_CHR ) {
goto done;
}
}
if( specs->short_var == 0 && (c == 'e' || c == 'E') ) {
*num_str++ = c;
++len;
if( (c = cgetw( specs )) == STOP_CHR )
goto done;
if( c == '+' || c == '-' ) {
*num_str++ = c;
++len;
if( (c = cgetw( specs )) == STOP_CHR ) {
goto done;
}
}
if( !__F_NAME(isdigit,iswdigit)( c ) ) {
len = 0; /* fast way to flag error */
} else {
do {
*num_str++ = c;
++len;
if( (c = cgetw( specs )) == STOP_CHR ) {
goto done;
}
} while( __F_NAME(isdigit,iswdigit)( c ) );
}
}
ugdone:
uncget( (int)c, specs );
done:
if( len > 0 ) {
len += pref_len;
if( specs->assign ) {
*num_str = NULLCHAR;
if( specs->short_var ) {
if( buf[0] == '-' ) {
at.sWhole = - at.sWhole;
}
} else {
EFG_SCANF( buf, (void *)&value ); /* 27-mar-90 */
}
#if defined( __FAR_SUPPORT__ )
if( specs->far_ptr ) {
fptr = va_arg( arg->v, float _WCFAR * );
} else if( specs->near_ptr ) {
fptr = va_arg( arg->v, float _WCNEAR * );
} else {
fptr = va_arg( arg->v, float * );
}
#else
fptr = va_arg( arg->v, float * );
#endif
if( specs->short_var ) { /* 05-feb-92 */
*((FAR_LONG) fptr) = at.uWhole;
} else if( specs->long_var || specs->long_double_var ) {
*((FAR_DOUBLE) fptr) = value;
} else {
*fptr = value;
}
}
}
return( len );
}
static int radix_value( int c )
{
if( c >= '0' && c <= '9' )
return( c - '0' );
c = __F_NAME(tolower,towlower)( c );
if( c >= 'a' && c <= 'f' )
return( c - 'a' + 10 );
return( 16 );
}
/*
* scan_int -- handles integer numeric conversion
*/
static int scan_int( PTR_SCNF_SPECS specs, my_va_list *arg, int base, int sign_flag )
{
long value;
int len;
int pref_len;
int c;
int sign;
int digit;
FAR_INT iptr;
#if defined( __LONG_LONG_SUPPORT__ )
unsigned long long long_value;
FAR_INT64 llptr;
long_value = 0;
#endif
value = 0;
pref_len = len = 0;
for( ;; ) {
c = cget( specs );
if( !__F_NAME(isspace,iswspace)( c ) )
break;
++pref_len;
}
if( specs->eoinp )
goto done;
if( specs->width-- == 0 )
goto ugdone;
sign = '+';
if( sign_flag && (c == '+' || c == '-') ) {
sign = c;
++pref_len;
if( (c = cgetw( specs )) == STOP_CHR ) {
goto done;
}
}
if( base == 0 ) {
if( c == '0' ) {
len = 1;
if( (c = cgetw( specs )) == STOP_CHR )
goto done;
if( c == 'x' || c == 'X' ) {
len = 0;
++pref_len; /* for the '0' */
++pref_len; /* for the 'x' */
if( (c = cgetw( specs )) == STOP_CHR )
goto done;
base = 16;
} else {
base = 8;
}
} else {
base = 10;
}
} else if( base == 16 ) {
if( c == '0' ) {
len = 1;
if( (c = cgetw( specs )) == STOP_CHR )
goto done;
if( c == 'x' || c == 'X' ) {
len = 0;
++pref_len; /* for the '0' */
++pref_len; /* for the 'x' */
if( (c = cgetw( specs )) == STOP_CHR ) {
goto done;
}
}
}
}
#if defined( __LONG_LONG_SUPPORT__ )
if( specs->long_long_var ) {
for( ;; ) {
digit = radix_value( c );
if( digit >= base )
break;
long_value = long_value * base + digit;
++len;
if( (c = cgetw( specs )) == STOP_CHR ) {
goto done;
}
}
if( c == ':' && specs->p_format ) {
for( ;; ) {
++len;
if( (c = cgetw( specs )) == STOP_CHR )
goto done;
digit = radix_value( c );
if( digit >= base )
break;
long_value = long_value * base + digit;
}
}
} else
#endif
{
for( ;; ) {
digit = radix_value( c );
if( digit >= base )
break;
value = value * base + digit;
++len;
if( (c = cgetw( specs )) == STOP_CHR ) {
goto done;
}
}
if( c == ':' && specs->p_format ) {
for( ;; ) {
++len;
if( (c = cgetw( specs )) == STOP_CHR )
goto done;
digit = radix_value( c );
if( digit >= base )
break;
value = value * base + digit;
}
}
}
ugdone:
uncget( c, specs );
done:
#if defined( __LONG_LONG_SUPPORT__ )
if( specs->long_long_var ) {
if( sign == '-' ) {
long_value =- long_value;
}
if( len > 0 ) {
len += pref_len;
if( specs->assign ) {
#if defined( __FAR_SUPPORT__ )
if( specs->far_ptr ) {
llptr = va_arg( arg->v, unsigned long long _WCFAR * );
} else if( specs->near_ptr ) {
llptr = va_arg( arg->v, unsigned long long _WCNEAR * );
} else {
llptr = va_arg( arg->v, unsigned long long * );
}
#else
llptr = va_arg( arg->v, unsigned long long * );
#endif
*llptr = long_value;
}
}
} else
#endif
{
if( sign == '-' ) {
value = -value;
}
if( len > 0 ) {
len += pref_len;
if( specs->assign ) {
#if defined( __FAR_SUPPORT__ )
if( specs->far_ptr ) {
iptr = va_arg( arg->v, int _WCFAR * );
} else if( specs->near_ptr ) {
iptr = va_arg( arg->v, int _WCNEAR * );
} else {
iptr = va_arg( arg->v, int * );
}
#else
iptr = va_arg( arg->v, int * );
#endif
if( specs->char_var ) {
*((FAR_CHAR)iptr) = value;
} else if( specs->short_var ) {
*((FAR_SHORT)iptr) = value;
} else if( specs->long_var ) {
*((FAR_LONG)iptr) = value;
} else {
*iptr = value;
}
}
}
}
return( len );
}
#ifdef SAFE_SCANF
/*
* null_arg -- check for a null pointer passed in arguments
*/
static int null_arg( PTR_SCNF_SPECS specs, my_va_list *arg )
{
FAR_STRING str = NULL;
va_list args_copy;
va_copy( args_copy, arg->v );
#if defined( __FAR_SUPPORT__ )
if( specs->far_ptr ) {
str = va_arg( args_copy, CHAR_TYPE _WCFAR * );
} else if( specs->near_ptr ) {
CHAR_TYPE _WCNEAR *ptr;
ptr = va_arg( args_copy, CHAR_TYPE _WCNEAR * );
/* The following should work:
*
* str = (ptr == NULL) ? (void _WCFAR *)NULL : ptr;
*
* but doesn't due to a bug in C compiler introduced in
* 11.0 and fixe in OW 1.4. Ternary operator may be used
* when building with OW 1.5. See also similar code in prtf.c.
*/
if( ptr == NULL ) {
str = NULL;
} else {
str = ptr;
}
} else {
CHAR_TYPE *ptr;
ptr = va_arg( args_copy, CHAR_TYPE * );
if( ptr == NULL ) {
str = NULL;
} else {
str = ptr;
}
}
#else
str = va_arg( args_copy, CHAR_TYPE * );
#endif
va_end( args_copy );
return( str == NULL ? 1 : 0 );
}
#endif
#ifdef SAFE_SCANF
int __F_NAME(__scnf_s,__wscnf_s)( PTR_SCNF_SPECS specs, const CHAR_TYPE *format, const char **msg, va_list args )
#else
int __F_NAME(__scnf,__wscnf)( PTR_SCNF_SPECS specs, const CHAR_TYPE *format, va_list args )
#endif
{
int char_match;
int items_converted;
int items_assigned;
int match_len;
int c;
int fmt_chr;
my_va_list margs;
margs = MY_VA_LIST( args );
char_match = items_assigned = items_converted = 0;
specs->eoinp = 0;
while( (fmt_chr = *format++) != NULLCHAR ) {
if( __F_NAME(isspace,iswspace)( fmt_chr ) ) {
char_match += scan_white( specs );
} else if( fmt_chr != '%' ) {
if( (c = cget( specs )) != fmt_chr ) {
if( !specs->eoinp )
uncget( c, specs );
break;
}
++char_match; /* 27-oct-88 */
} else { /* fmt_chr == '%' */
format = get_opt( format, specs );
if( (fmt_chr = *format) != NULLCHAR )
++format;
#ifdef SAFE_SCANF
if( fmt_chr != '%' ) {
/* The '%' specifier is the only one not expecting pointer arg */
if( specs->assign && null_arg( specs, &margs ) ) {
*msg = "%ptr -> NULL";
return( __F_NAME(EOF,WEOF) );
}
}
#endif
switch( fmt_chr ) {
case 'd':
match_len = scan_int( specs, &margs, 10, TRUE );
goto check_match;
case 'i':
match_len = scan_int( specs, &margs, 0, TRUE );
goto check_match;
case 'o':
match_len = scan_int( specs, &margs, 8, TRUE );
goto check_match;
case 'u':
match_len = scan_int( specs, &margs, 10, TRUE );
goto check_match;
case 'p':
#if defined( __BIG_DATA__ )
specs->long_var = 1; /* indicate far pointer */
specs->p_format = 1; /* ... */
#endif
// fall through
case 'x':
case 'X':
match_len = scan_int( specs, &margs, 16, TRUE );
goto check_match;
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
match_len = scan_float( specs, &margs );
goto check_match;
#if !defined(__WIDECHAR__) && !defined(__NETWARE__)
case 'S':
specs->long_var = 1;
/* fall through to %s handler */
#endif
case 's':
match_len = scan_string( specs, &margs );
goto check_match;
case '[':
match_len = scan_arb( specs, &margs, &format );
goto check_match;
#if !defined(__WIDECHAR__) && !defined(__NETWARE__)
case 'C':
specs->long_var = 1;
/* fall through to %c handler */
#endif
case 'c':
match_len = scan_char( specs, &margs );
check_match:
if( match_len > 0 ) {
char_match += match_len;
++items_converted;
if( specs->assign ) {
++items_assigned;
}
} else {
#ifdef SAFE_SCANF
if( match_len < 0 ) {
/* Matching failure caused by insufficient space in output
* is not input failure, hence we won't return EOF regardless
* of specs->eoinp state.
*/
++items_converted;
}
#endif
goto fail;
}
break;
case 'n':
report_scan( specs, &margs, char_match );
break;
case '%':
if( (c = cget( specs )) != '%' ) {
if( !specs->eoinp )
uncget( c, specs );
goto fail;
} else {
char_match += 1;
}
break;
}
}
if( specs->eoinp ) {
while( *format == '%' ) {
++format;
format = get_opt( format, specs );
if( *format == 'n' ) {
++format;
#ifdef SAFE_SCANF
if( specs->assign && null_arg( specs, &margs ) ) {
*msg = "%ptr -> NULL";
return( __F_NAME(EOF,WEOF) );
}
#endif
report_scan( specs, &margs, char_match );
} else {
break;
}
}
break;
}
}
fail:
if( items_converted == 0 && specs->eoinp )
return( __F_NAME(EOF,WEOF) );
return( items_assigned );
}