diff --git a/include/nuttx/streams.h b/include/nuttx/streams.h index 21f63f22d64..b8fb4ce787d 100644 --- a/include/nuttx/streams.h +++ b/include/nuttx/streams.h @@ -685,6 +685,18 @@ int lib_snoflush(FAR struct lib_sostream_s *self); int lib_sprintf(FAR struct lib_outstream_s *stream, FAR const IPTR char *fmt, ...) printf_like(2, 3); +/**************************************************************************** + * Name: lib_bsprintf + * + * Description: + * Implementation of sprintf formatted output buffer data. Structure data + * types must be one-byte aligned. + * + ****************************************************************************/ + +int lib_bsprintf(FAR struct lib_outstream_s *s, FAR const IPTR char *fmt, + FAR const void *buf); + /**************************************************************************** * Name: lib_sprintf_internal * diff --git a/libs/libc/stdio/CMakeLists.txt b/libs/libc/stdio/CMakeLists.txt index 36b16a1faa0..8f89bae2098 100644 --- a/libs/libc/stdio/CMakeLists.txt +++ b/libs/libc/stdio/CMakeLists.txt @@ -40,6 +40,7 @@ set(SRCS lib_sscanf.c lib_vsscanf.c lib_libvscanf.c + lib_libbsprintf.c lib_libvsprintf.c lib_remove.c lib_tempnam.c diff --git a/libs/libc/stdio/Make.defs b/libs/libc/stdio/Make.defs index 7ddd9bdd74e..c38594746f3 100644 --- a/libs/libc/stdio/Make.defs +++ b/libs/libc/stdio/Make.defs @@ -28,7 +28,7 @@ CSRCS += lib_perror.c lib_putchar.c lib_getchar.c lib_puts.c CSRCS += lib_gets_s.c lib_gets.c lib_libdgets.c CSRCS += lib_sscanf.c lib_vsscanf.c lib_libvscanf.c lib_libvsprintf.c CSRCS += lib_remove.c lib_tempnam.c lib_tmpnam.c lib_ultoa_invert.c -CSRCS += lib_renameat.c lib_putwchar.c +CSRCS += lib_renameat.c lib_putwchar.c lib_libbsprintf.c ifeq ($(CONFIG_LIBC_FLOATINGPOINT),y) CSRCS += lib_dtoa_engine.c lib_dtoa_data.c diff --git a/libs/libc/stdio/README.md b/libs/libc/stdio/README.md new file mode 100644 index 00000000000..41fbbfbdb38 --- /dev/null +++ b/libs/libc/stdio/README.md @@ -0,0 +1,95 @@ +# lib_libbsprintf + This function is mainly used to output the contents of the input structure. Most standards follow the standards of printf and scanf. + For detailed parameters, see: + 1. https://en.cppreference.com/w/c/io/fprintf + 2. https://en.cppreference.com/w/c/io/fscanf + +- **special**: + 1. Float use %hf, and double for all others. + 2. The char array is specified with %.xs. for example: "char t[30]" is specified with "%.30s", char a [20] - " %.20s " + 3. "%u" is unsigned int. + 4. "%d" is int. + 5. When using %f to format a double data type, the double is truncated to 6 decimal places by default. + 6. It is recommended that the "char[]" array be placed at the end of the structure to prevent parameter configuration errors such as "%.20s" from causing problems in parsing the entire buffer. +- **demo** + 1. **struct**: + ~~~ + begin_packed_struct + struct test + { + uint8_t a; + uint16_t b; + uint32_t c; + int8_t d; + int16_t e; + int32_t f; + float g; + double h; + char i[32]; + uint64_t j; + int64_t k; + char l; + unsigned char m; + short int n; + unsigned short int o; + int p; + unsigned int q; + long r; + unsigned long s; + long long t; + unsigned long long u; + size_t v; + long double w; + }end_packed_struct; + ~~~ + 1. **format string**: + ~~~ + const char* sg = " uint8_t:[%hhu]\n" \ + " uint16_t:[%hu]\n" \ + " uint32_t:[%u]\n" \ + " int8_t:[%hhd]\n" \ + " int16_t:[%hd]\n" \ + " int32_t:[%d]\n" \ + " float:[%hf]\n" \ + " double:[%f]\n" \ + " char[]:[%.32s]\n" \ + " uint64_t:[%lu]\n" \ + " int64_t:[%ld]\n" \ + " char:[%hhd]\n" \ + " unsigned char:[%hhu]\n" \ + " short int:[%hd]\n" \ + "unsigned short int:[%hu]\n" \ + " int:[%d]\n" \ + " unsigned int:[%u]\n" \ + " long:[%ld]\n" \ + " unsigned long:[%lu]\n" \ + " long long:[%lld]\n" \ + "unsigned long long:[%llu]\n" \ + " size_t:[%uz]\n" \ + " long double:[%Lf]\n"; + ~~~ + 1. **use**: + - output to terminal: + ~~~ + #ifdef CONFIG_FILE_STREAM + struct lib_stdoutstream_s stdoutstream; + + lib_stdoutstream(&stdoutstream, stdout); + + flockfile(stdout); + lib_bsprintf(&stdoutstream.common, sv, &test_v); + lib_bsprintf(&stdoutstream.common, sg, &test_g); + funlockfile(stdout); + #else + struct lib_rawoutstream_s rawoutstream; + struct lib_bufferedoutstream_s outstream; + + lib_rawoutstream(&rawoutstream, STDOUT_FILENO); + lib_bufferedoutstream(&outstream, &rawoutstream.common); + + lib_bsprintf(&outstream.common, sv, &test_v); + lib_bsprintf(&outstream.common, sg, &test_g); + + lib_stream_flush(&outstream.common); + #endif + ~~~ \ No newline at end of file diff --git a/libs/libc/stdio/lib_libbsprintf.c b/libs/libc/stdio/lib_libbsprintf.c new file mode 100644 index 00000000000..53ef74a936e --- /dev/null +++ b/libs/libc/stdio/lib_libbsprintf.c @@ -0,0 +1,207 @@ +/**************************************************************************** + * libs/libc/stdio/lib_libbsprintf.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int lib_bsprintf(FAR struct lib_outstream_s *s, FAR const IPTR char *fmt, + FAR const void *buf) +{ + begin_packed_struct union + { + char c; + short int si; + int i; + long l; +#ifdef CONFIG_HAVE_LONG_LONG + long long ll; +#endif + intmax_t im; + size_t sz; + ptrdiff_t pd; + uintptr_t p; +#ifdef CONFIG_HAVE_DOUBLE + float f; + double d; +# ifdef CONFIG_HAVE_LONG_DOUBLE + long double ld; +# endif +#endif + } + + end_packed_struct *var; + FAR const char *prec = NULL; + FAR const char *data = buf; + char fmtstr[64]; + bool infmt = false; + size_t offset = 0; + size_t ret = 0; + size_t len = 0; + char c; + + while ((c = *fmt++) != '\0') + { + if (c != '%' && !infmt) + { + lib_stream_putc(s, c); + ret++; + continue; + } + + if (!infmt) + { + len = 0; + infmt = true; + memset(fmtstr, 0, sizeof(fmtstr)); + } + + var = (FAR void *)((char *)buf + offset); + fmtstr[len++] = c; + + if (c == 'c' || c == 'd' || c == 'i' || c == 'u' || + c == 'o' || c == 'x' || c == 'X') + { + if (*(fmt - 2) == 'j') + { + offset += sizeof(var->im); + ret += lib_sprintf(s, fmtstr, var->im); + } +#ifdef CONFIG_HAVE_LONG_LONG + else if (*(fmt - 2) == 'l' && *(fmt - 3) == 'l') + { + offset += sizeof(var->ll); + ret += lib_sprintf(s, fmtstr, var->ll); + } +#endif + else if (*(fmt - 2) == 'l') + { + offset += sizeof(var->l); + ret += lib_sprintf(s, fmtstr, var->l); + } + else if (*(fmt - 2) == 'z') + { + offset += sizeof(var->sz); + ret += lib_sprintf(s, fmtstr, var->sz); + } + else if (*(fmt - 2) == 't') + { + offset += sizeof(var->pd); + ret += lib_sprintf(s, fmtstr, var->pd); + } + else if (*(fmt - 2) == 'h' && *(fmt - 3) == 'h') + { + offset += sizeof(var->c); + ret += lib_sprintf(s, fmtstr, var->c); + } + else if (*(fmt - 2) == 'h') + { + offset += sizeof(var->si); + ret += lib_sprintf(s, fmtstr, var->si); + } + else + { + offset += sizeof(var->i); + ret += lib_sprintf(s, fmtstr, var->i); + } + + infmt = false; + } + else if (c == 'e' || c == 'f' || c == 'g' || c == 'a' || + c == 'A' || c == 'E' || c == 'F' || c == 'G') + { +#ifdef CONFIG_HAVE_DOUBLE + if (*(fmt - 2) == 'h') + { + offset += sizeof(var->f); + ret += lib_sprintf(s, fmtstr, var->f); + } +# ifdef CONFIG_HAVE_LONG_DOUBLE + else if (*(fmt - 2) == 'L') + { + offset += sizeof(var->ld); + ret += lib_sprintf(s, fmtstr, var->ld); + } +# endif + else + { + offset += sizeof(var->d); + ret += lib_sprintf(s, fmtstr, var->d); + } + + infmt = false; +#endif + } + else if (c == '*') + { + itoa(var->i, fmtstr + len - 1, 10); + len = strlen(fmtstr); + offset += sizeof(var->i); + } + else if (c == 's') + { + FAR const char *value = data + offset; + + if (prec != NULL) + { + offset += strtol(prec, NULL, 10); + prec = NULL; + } + else + { + offset += strlen(value) + 1; + } + + ret += lib_sprintf(s, fmtstr, value); + infmt = false; + } + else if (c == 'p') + { + offset += sizeof(var->p); + ret += lib_sprintf(s, fmtstr, var->p); + infmt = false; + } + else if (c == '.') + { + prec = fmt; + } + } + + if (*(fmt - 2) != '\n') + { + lib_stream_putc(s, '\n'); + ret++; + } + + return ret; +}