Core functions for formatted input and output. More...
Defines | |
| #define | EOF (-1) |
| End of file marker. | |
Typedefs | |
| typedef int(* | BLC_FMT_PUT )(void **, const char *, size_t) |
| Prototype for the output function of print_xxx(). | |
| typedef int(* | BLC_FMT_GET )(void **) |
| Prototype for the input function of scan_int and scan_flt(). | |
| typedef int(* | BLC_FMT_UNG )(int, void **) |
| Prototype for the pushback function of scan_int() and scan_flt(). | |
Functions | |
| int | print_flt (const char *fmt, va_list arg, size_t len, BLC_FMT_PUT fun, void **par, int(*w2m)(char *, wchar_t **)) |
| Formatting core for all printf() type functions. | |
| int | print_int (const char *fmt, va_list arg, size_t len, BLC_FMT_PUT fun, void **par, int(*w2m)(char *, wchar_t **)) |
| Formatted print core without floating point conversions. | |
| int | scan_flt (const char *fmt, va_list arg, BLC_FMT_GET get, BLC_FMT_UNG ung, void **par, int(*m2w)(wchar_t **, const char *, size_t)) |
| Formatted input core. | |
| int | scan_int (const char *fmt, va_list arg, BLC_FMT_GET get, BLC_FMT_UNG ung, void **par, int(*m2w)(wchar_t **, const char *, size_t)) |
| Processing formatted input core without floating point conversions. | |
Core functions for formatted input and output.
The C standard defines a bunch of input/output functions. However, those functions depend on the notion of files and byte streams, defined and associated with physical devices by the underlying operating system. Since this library is targeted to embedded systems, where the concept of files and byte streams may or may not exist at all and device abstractions vary from system to system, this library contains only the formatting core of the input and output functions. These cores can be used to realise all the standard designated printf() and scanf() family functions. You have to write a little wrapper around the core, to provide the standard interface. Furthermore, you have to supply the lowest level byte I/O functions that will be called by the core functions. The details are explained in the function descriptions.
To use these functions you have to include stdio_format.h.
| typedef int(* BLC_FMT_GET)(void **) |
Prototype for the input function of scan_int and scan_flt().
This function is called whenever a scan_int() or scan_flt() needs a new character from the input. The interpretation of the argument is up to the function, scan_int() and scan_flt() just pass it to the function but never modify it. The function should return a positive character value if the read from the input was successful, EOF if the input was exhasuted and a negative error code (other than EOF) in case there was a read error. In the case of an error code being returned, scan_int() or scan_flt() will abort and return the same error code.
| typedef int(* BLC_FMT_PUT)(void **, const char *, size_t) |
Prototype for the output function of print_xxx().
This function is called whenever a print_int() or print_flt() function wants to write a partial result. The interpteration of the first argument is up to the function, the print_int() or print_flt() functions never modify that parameter. The second argument is a pointer to the string to be written and the third is the number of characters to write.
The function should return a non-negative number in case of success or a negative error code on failure. If the function returns error, the print_int() or print_flt() function will abort and return the same error that this function returned.
| typedef int(* BLC_FMT_UNG)(int, void **) |
Prototype for the pushback function of scan_int() and scan_flt().
This function is called whenever a scan_int() or scan_flt() wants to push a character back to the input. At most one character is pushed back before consuming the input again. After calling this function it is expected that the next read from the input will return the same character as was pushed back. Note that scan_int() and scan_flt() guarantees that the character pushed back will be the last character obtained from the input. The first argument for the function is the character to push back, the second is a parameter which can be interpreted by the function in any way. This is the same parameter that is passed to the character read function and it is never modified by scan_int() or scan_flt(). The function should return a non-negative value if the pushback was successful and a negative error code (other than EOF) if the pushback failed.
| int print_flt | ( | const char * | fmt, | |
| va_list | arg, | |||
| size_t | len, | |||
| BLC_FMT_PUT | fun, | |||
| void ** | par, | |||
| int(*)(char *, wchar_t **) | w2m | |||
| ) |
Formatting core for all printf() type functions.
The function implements the formatted print function, as specified in the C99 standard, with some exceptions (see Details). This is an internal function, called by the actual API functions from the printf() family. It is needed when you want to write your printf(), fprintf() etc. functions that this library can not provide, since they require knowledge of your system's handling of output devices.
| fmt | The format string. | |
| arg | The argument list (after the format string). Before you call this function, you have to call va_start() and pass the resulting va_list object in this argument. | |
| len | The maximum number of characters sent to the output. If you want no limit, pass -1. | |
| fun | The function that outputs a number of characters. | |
| par | This is the first parameter that is passed to (*fun)(). | |
| w2m | The address of the wcitomb() function. Since the actual library function that is invoked when wcitomb() is called depends on whether you used the -fshort-wchar compiler switch and whether you have defined the CONFIG_RFC3629_CONFORMANCE macro, print_flt() can not know the actual function when the library is built. If you pass NULL for this parameter, then the 'l' size modifier for 's' and 'c' conversions will be ignored. |
%[flags][width][.[precision]][length]conversion '+' ' ' ' ' and '+' are given, '+' is used. For non-numeric conversions the flag is ignored. '0' '-' '#' "hh" char or unsigned char argument. "h" short or unsigned short argument. "l" long, unsigned long or wchar_t argument. "ll" long long or unsigned long long or long double argument. "hh" char or unsigned char argument. "L" long double argument. "j" intmax_t argument. "t" ptrdiff_t argument. "z" size_t argument. 'd' 'i' 'u' unsigned short or unsigned char, respectively. If the precision is 0 and the number fetched is zero, then no output is generated. The '-' and '0' flags are obeyed, the '#', '+' and ' ' flags are ignored. If precision is not specified, the default value is 1. 'o' unsigned short or unsigned char, respectively. If the precision is 0 and the number fetched is zero, then no output is generated. If precision is not specified, the default value is 1. The '-' and '0' flags are obeyed, the '+' and ' ' flags are ignored. If the '#' flag is set and the number is not zero, then a '0' will be prepended to it to indicate an octal number. 'x' 'X' unsigned short or unsigned char, respectively. When the number is converted, for digits above 9 the letters abcdef will be used for x conversion and ABCDEF for X conversion. If the precision is 0 and the number fetched is zero, then no output is generated. If precision is not specified, the default value is 1. The '-' and '0' flags are obeyed, the '+' and ' ' flags are ignored. If the '#' flag is set and the number is not zero, then a "0x" or "0X" (for x and X conversions, respectively) will be prepended to the result. 'p' void * argument and prints it as a hexadecimal number. The length modifiers are ignored. If the pointer is NULL then the string (nil) is printed, otherwise the behaviour is the same as that of the %#...x conversion. 'n' void * argument. Depending on the length modifier, the pointer will be interpreted as char *, short *, int *, long * or long long * (the default being int *) and the number of output characters generated (but not necessarily emitted) so far will be stored at the pointed location. As a safety measure, the data is not stored if the pointer is NULL. Flags, field width and precision are all ignored and no output is generated. 'E' 'e' 'F' 'f' 'G' 'g' 'A' 'a' 'c' 's' const char *. It is interpreted as a pointer to a 0-terminated C string. The string is copied to the output, not including the terminating 0 character. If precision was specified, then at most precision characters will be used from the string.const wchar_t * and should point to an array of wchar_t that is terminated by a wide NUL character. The elements of the array are converted to UTF-8 and the result is sent to the output. If an array element is not a valid Unicode character, then it is simply ignored and skipped during the conversion. If precision is specified, then it is taken as a limit of bytes generated by the wchar_t to UTF-8 conversion. Note that the precision applies to the output and not to the wide character array itself. It is guaranteed that no partial UTF-8 sequence is sent out, therefore it is possible that the actual number of output bytes is less than the precision, even if the wide character array is not exhausted./* * Example implementation of printf() type functions. * * We assume that you have the following functions defined: * * void UartPutc( char data ); * * Writes a character to the UART. * * int write( int file, const void *data, size_t dlen ); * * Writes 'dlen' bytes from 'data' to a file, whatever that is on * your system. The file is identified by the 'file' integer. This * is pretty much the same as the write() function on a POSIX system. * * We then create the following functions: * * printf() - with full float and wide character support * fprintf() - with float but not wide character support * vfprintf() - with float but not wide character support * snprintf() - integer only * * as an example. That should be enough for you to write pretty much * any function in the printf() family. * * Please realise that while the example functions seem long, that's * due to the comments. In fact, the longest of them contains only * four C statements, and simple ones of that. */ #include <stdio.h> // It's your header for the functions in this file #include <files.h> // Your header, declaring write() #include <uart.h> // Your header, declaring UartPutc() #include <stdio_format.h> // Library header, declaring print_XXX() #include <wchar.h> // Library header, defining wide character support #include <string.h> // Library header, defining memcpy() #include <stdarg.h> // GCC header, declaring the vararg macros /* * Declaration of our local output functions */ static int uart_out( void **unused, const char *data, size_t len ); static int string_write( void **par, const char *str, size_t len ); /****************************/ /* printf() */ /****************************/ /* * Print to the UART, support everything */ int printf( const char *fmt, ... ) { va_list arg; // Variable argument list object int ret; // Return value // Get the variable argument list first va_start( arg, fmt ); // Call the library's formatting function, passing it everything and // returning with whatever that returned to us. ret = print_flt( // We need to call _flt() for float support fmt, // First argument is the format string arg, // Next the variable argument list -1, // No limit on output amount uart_out, // The wrapper function around UartPutc() NULL, // No parameter for the wrapper wcitomb ); // Function to transform a wide char to UTF-8 // We have to finish with the vararg. va_end( arg ); return ret; } /* * This is the UART output wrapper */ static int uart_out( void **unused, const char *data, size_t len ) { // We do not use the first parameter (void) unused; // We have to output 'len' bytes from 'data' on the UART while ( len-- ) UartPutc( *data++ ); // We have to return a positive number to indicate success return 1; } /****************************/ /* fprintf() */ /****************************/ /* * Print to file, support everything but wide char */ int fprintf( int file, const char *format, ... ) { va_list args; int ret; // Get the argument list va_start( args, fmt ); // This is an extremely simple case, as everything fits // the format function's requirements. The only thing is // that we have to cast write() to the relevant function // type and the file number to void **. The last parameter // is NULL, disabling wide character processing. ret = print_flt( format, // Format string args, // Argument list -1, // No limit on the amount of output (BLC_FMT_PUT) write, // The output function (void **) file, // Parameter to the output func NULL // Wide chars are not supported ); // Done va_end( arg ); return ret; } /****************************/ /* vfprintf() */ /****************************/ /* * Like above but with variable argument list passed as argument */ int vfprintf( int file, const char *format, va_list args ) { // This is even simpler than fprintf() because we already // have the va_list prepared. return( print_flt( format, args, -1, (BLC_FMT_PUT) write, (void **) file, NULL ) ); } /****************************/ /* snprintf() */ /****************************/ /* * Print to a string, with size limit. Integers only. */ int snprintf( char *res, size_t len, const char *fmt, ... ) { va_list arg; int ret; // Get the variable argument list va_start( arg, fmt ); // Call print_int() to do the work. The string_write() function // will be called with the address of the pointer to the result // string as its first argument. ret = print_int( fmt, arg, len, string_write, (void **) &res, NULL ); // We have to put a closing '\0' at the end of the string, but only // if we have not yet exhausted the buffer and there was no error. // Note that string_write() advanced the 'res' pointer to point to // the byte after the last byte written by format_int(). if ( ret >= 0 && ret < len ) *res = 0; // Our return value is whatever print_int() returned. va_end( arg ); return( ret ); } /* * Helper function to write into a string. * * The first parameter is a pointer to a pointer to the next character * in the output buffer. This function does not need to check if it * exhausted the available buffer space or not, the format function * itself takes care of that. */ static int string_write( void **par, const char *str, size_t len ) { char *p; // Get the pointer to the next character of the string p = *par; // Copy the data into the output buffer. We use the fast version // of memcpy(), memcpy_forw() that is also defined in this library. memcpy_forw( p, str, len ); // Store the new end of the string so that at the next // invocation we know wehere to write. *par = p + len; // Report success. Any positive number would do, but we // return the length, because that's what a normal write // function would do. return( len ); }
| int print_int | ( | const char * | fmt, | |
| va_list | arg, | |||
| size_t | len, | |||
| BLC_FMT_PUT | fun, | |||
| void ** | par, | |||
| int(*)(char *, wchar_t **) | w2m | |||
| ) |
Formatted print core without floating point conversions.
This function has the exact same semantics and behaviour as the print_flt() function, with the following exceptions:
Due to the omission of the floating point, this function is much smaller than using print_flt(). For details on the format string and usage examples see print_flt().
| fmt | The format string. | |
| arg | The argument list (after the format string). Before you call this function, you have to call va_start() and pass the resulting va_list object in this argument. | |
| len | The maximum number of characters sent to the output. If you want no limit, pass -1. | |
| fun | The function that outputs a number of characters. | |
| par | This is the first parameter that is passed to fun. | |
| w2m | An unused parameter. It is only here to make the prototypes of print_flt() and print_int() identical. It's best if you specify NULL here so that you don't have to care about type mismatches. |
| int scan_flt | ( | const char * | fmt, | |
| va_list | arg, | |||
| BLC_FMT_GET | get, | |||
| BLC_FMT_UNG | ung, | |||
| void ** | par, | |||
| int(*)(wchar_t **, const char *, size_t) | m2w | |||
| ) |
Formatted input core.
This function implements the core of the formatted input standard C library function set (the scanf() family functions). The library itself does not provide those functions as such, since your input/output configuration is unknown to the library. However, using this function you can implement those functions in only a few lines of C code, as shown by the examples below.
| fmt | The format string. | |
| arg | The argument list (after the format string). Before you call this function, you have to call va_start() and pass the resulting va_list object in this argument. | |
| get | The function that reads the next character from the input. | |
| ung | The function to call when a character must be pushed back. This function is called at most once. | |
| par | This is the parameter that is passed to get and ung. | |
| m2w | Pointer to the mbtowci() function. Since the size of the wchar_t type used by the application can not be known when the library is compiled, functions that manipulate memory storing wchar_t objects must be passed in invocation time, hence this parameter. If this argument is NULL, then the 'l' size modifier in the 'c', 's' and '[' conversions will be ignored. |
%[sup][width][size]specifier int argument from the argument list and use that as the width; if the value is negative, it will be taken as unset. "hh" char or unsigned char argument. "h" short or unsigned short argument. "l" double, long, unsigned long or wchar_t argument. "ll" long double, long long or unsigned long long argument. "L" ll. "j" intmax_t argument. "t" ptrdiff_t argument. "z" size_t argument. 'd' int, or to a char if the 'hh' size modifier was present, or to a short if the 'h' size modifier was present, or to a long if the 'l' or equivalent size modifier was present, or to a long long if the 'll' or equivalent modifier was present. 'i' int. 'u' unsigned int. 'o' unsigned int. 'x' 'X' unsigned int. 'p' (nil), then it is accepted and is interpreted as 0 (i.e. NULL pointer). 'n' int * argument and stores the number of characters consumed from the input at the given location. The "ll", "l", "h", "hh" or equivalent size modifiers, if present, are respected and the pointer is typecast to a pointer to, respectively, long long, long, short or char when storing the number. This conversion does not consume any input, does not increase the conversion count and can not fail. If assignment supression is also specified, then this conversion is a null operation, consuming neither the input nor the arguments. 'a' 'e' 'f' 'g' float, or if the "l" (or equivalent) size modifier was present, then to a double or if the "ll" (or equivalent) size modifier was present, to a long double. Note that the implementation always performs a string to double conversion and the result is then typecast to float or long double. 'c' char. No terminating NUL is added to the string. If the input is exhausted before the given number of characters are processed, then the conversion will stop but otherwise succeed, except if not even one character could be processed.wchar_t. The conversion of the input is according to the semantics of the mbtowc() function. Note that the field length is the number of wide characters and not the number of bytes used from the input. If at any point the UTF-8 to wchar_t conversion signals an error, a conversion mismatch is declared and the function returns. 's' '[' /* * Example implementation of scanf() type functions. * * We assume that you have the following functions defined: * * int UartGetc( void ); * * Reads a character from the UART. It returns the character itself. * It waits for the character. If an error occurs it returns a * negative error code (other than EOF). * * int UartUngetc( int lastchar ); * * Returns the character 'lastchar' to the UART's input buffer. * The next call to UartGetc() should return that character. * This function is called at most once, just before scanf() * returns. It should return a non-negative number on success * and a negative error code (but not EOF) on failure. * * int getc( FILE *file ); * int ungetc( int c, FILE *file ); * * These are the standard C library functions to get the next * character (or EOF) from a file or to push back the last * character to a file. In this example we assume that you defined * FILE to some type. The functions here do not care what that * type actually is. * * We then create the following functions: * * scanf() - with full float and wide character support * fscanf() - with float but not wide character support * vsscanf() - integer only * * as an example. That should be enough for you to write pretty much * any function in the scanf() family. * * Please realise that while the example functions seem long, that's * due to the comments. In fact, the longest of them contains only * four C statements, and simple ones of that. */ #include <stdio.h> // It's your header, declaring the functions in // this file as well as FILE, getc() and ungetc() #include <uart.h> // Your header, UartGetc() and UartUngetc() #include <stdio_format.h> // Library header, declaring scan_???() #include <wchar.h> // Library header, wide character support #include <stdarg.h> // GCC header, declaring the vararg macros /* * Declaration of our local functions */ static int uart_get( void **unused ); static int uart_ung( int c, void **unused ); static int str_get( void **p ); static int str_ung( int c, void **p ); /* * Scan from the UART, support everything */ int scanf( const char *fmt, ... ) { va_list arg; // Variable argument list object int ret; // Return value // Get the variable argument list first va_start( arg, fmt ); // Call the library's scanning function, passing it everything and // returning with whatever that returned to us. ret = scan_flt( // We need to call _flt() for float support fmt, // First argument is the format string arg, // Next the variable argument list uart_get, // Get a character uart_ung, // Push-back a character NULL, // No parameter for the wrapper mbtowci ); // Function to transform UTF-8 to a wide char // Finish the vararg list and return the result va_end( arg ); return ret; } /* * Wrappers around the UART functions */ static int uart_get( void **unused ) { (void) unused; return UartGetc(); } static int uart_ung( int c, void **unused ) { (void) unused; return UartUngetc( c ); } /* * Scan from a file, support everything but wide char */ int fscanf( FILE *file, const char *format, ... ) { va_list args; // Get the argument list va_start( args, fmt ); // This is an extremely simple case, as everything fits // the format function's requirements. The only thing is // that we have to cast getc() and ungetc() to the // relevant function type and the file pointer to void **. // The last parameter is NULL, disabling wide character // processing. ret = scan_flt( format, args, (BLC_FMT_GET) getc, (BLC_FMT_UNG) ungetc, (void **) file, NULL ); // Finish the vararg list and return the result va_end( arg ); return ret; } /* * Scan from a string, integers only. The vararg list is given to us */ int sscanf( char *str, const char *fmt, va_list arg ) { // Call the scan core. The get and unget functions are locally // defined little functions. Since we don't have to close the // vararg list, we can return straight from the scan function. return scan_int( fmt, arg, str_get, str_ung, (void **) &str, NULL ); } /* * Read the next character from a string */ static int str_get( void **p ) { char *s; int c // We received a pointer to the string pointer. Fetch it. s = * (char **) p; // Get the next character. We cast the pointer to unsigned to // make sure that we never get a negative number. c = * (unsigned char *) s++; // If the character is 0, that is, the end of the string, we return // EOF and do *not* update the string pointer. Otherwise, we update // the string pointer (that is, we consume the character) and return // whatever we fetched. if ( c ) * (char **) p = s; else c = EOF; return c; } static int str_ung( int c, void **p ) { // If the character to return is EOF, we do nothing. // Otherwise we simply decrement the current string // pointer. In either case we report success. if ( c != EOF ) (* (char **) p)--; return 0; }
| int scan_int | ( | const char * | fmt, | |
| va_list | arg, | |||
| BLC_FMT_GET | get, | |||
| BLC_FMT_UNG | ung, | |||
| void ** | par, | |||
| int(*)(wchar_t **, const char *, size_t) | m2w | |||
| ) |
Processing formatted input core without floating point conversions.
This function has the exact same semantics and behaviour as the scnan_flt() function, with the following exceptions:
Due to the omission of the floating point, this function is much smaller than using scan_flt(). For details on the format string and usage examples see scan_flt().
| fmt | The format string. | |
| arg | The argument list (after the format string). Before you call this function, you have to call va_start() and pass the resulting va_list object in this argument. | |
| get | The function that reads the next character from the input. | |
| ung | The function to call when a character must be pushed back. | |
| par | This is the parameter that is passed to get and ung. | |
| m2w | An unused parameter. It is only here to make the prototypes of scan_flt() and scan_int() identical. It's best if you specify NULL here so that you don't have to care about type mismatches. |
get function reported a read error from the input.
1.6.3