RF24Log  0.1.3
Unified logging library
PrintfParser.cpp
Go to the documentation of this file.
1 
15 #include "PrintfParser.h"
16 #if defined(ARDUINO_ARCH_AVR)
17 #include <avr/pgmspace.h> // pgm_read_byte()
18 #endif
19 
20 #include "FormatSpecifier.h" // FormatSpecifier struct
21 
22 /****************************************************************************/
23 
24 #if defined(ARDUINO_ARCH_AVR)
25 void RF24LogPrintfParser::write(uint8_t logLevel,
26  const __FlashStringHelper *vendorId,
27  const __FlashStringHelper *message,
28  va_list *args)
29 {
30  PGM_P p = reinterpret_cast<PGM_P>(message);
31  char c = pgm_read_byte(p++);
32  do
33  {
34  // print header
35  #if defined(RF24LOG_NO_EOL)
37  #endif
38  descTimeLevel(logLevel);
39  PGM_P id = reinterpret_cast<PGM_P>(vendorId);
40  char v = pgm_read_byte(id++);
41  if (v)
42  {
43  appendStr(vendorId);
45  }
46 
47  // print formatted message (or at least 1 line at a time)
48  while (c
49  #if !defined(RF24LOG_NO_EOL)
50  && c != '\n'
51  #endif
52  )
53  {
54  if (c == '%')
55  {
56  FormatSpecifier fmt_parser;
57  c = pgm_read_byte(p++); // get ready to feed the parser
58  while (c && fmt_parser.isFlagged(c)) { c = pgm_read_byte(p++); }
59  while (c && fmt_parser.isPaddPrec(c)) { c = pgm_read_byte(p++); }
60  while (c && fmt_parser.isFmtOption(c)) { c = pgm_read_byte(p++); }
61  if (fmt_parser.specifier)
62  {
63  appendFormat(&fmt_parser, args);
64  // fmt_parser.isFmtOption() stops parsing on a non-fmt-specifying char
65  // if the `while(isFmtOption())` loop above iterated more than once, then
66  // we have to prevent disposing of the left over char here
67  if (fmt_parser.specifier != c)
68  {
69  p--; // let the next iteration handle it
70  }
71  }
72  else
73  {
74  appendChar(c);
75  }
76  }
77  #if defined(RF24LOG_TAB_SIZE)
78  else if (c == '\t') { appendChar(' ', RF24LOG_TAB_SIZE); }
79  #endif
80  else
81  #if defined(RF24LOG_NO_EOL)
82  if (c != '\n') // dispose char; we control new line feeds ourselves
83  #endif
84  {
85  appendChar(c);
86  }
87  c = pgm_read_byte(p++);
88  }
89  #if !defined(RF24LOG_NO_EOL)
90  if (c == '\n') { c = pgm_read_byte(p++); } // dispose char; we control new line feeds ourselves
91  appendChar('\n');
92  #endif
93  } while (c);
94 }
95 #endif
96 
97 /****************************************************************************/
98 
99 void RF24LogPrintfParser::write(uint8_t logLevel,
100  const char *vendorId,
101  const char *message,
102  va_list *args)
103 {
104  char *c = (char *)message;
105  do
106  {
107  // print header
108 #if defined(RF24LOG_NO_EOL)
110 #endif
111  descTimeLevel(logLevel);
112  if (*vendorId)
113  {
114  appendStr(vendorId);
116  }
117 
118  // print formatted message (or at least 1 line at a time)
119  while (*c
120 #if !defined(RF24LOG_NO_EOL)
121  && *c != '\n'
122 #endif
123  )
124  {
125  if (*c == '%')
126  {
127  FormatSpecifier fmt_parser;
128  ++c; // get ready to feed the parser
129  while (*c && fmt_parser.isFlagged(*c)) { ++c; }
130  while (*c && fmt_parser.isPaddPrec(*c)) { ++c; }
131  while (*c && fmt_parser.isFmtOption(*c)) { ++c; }
132  // fmt_parser.isFmtOption() stops parsing on a non-fmt-specifying char
133  if (fmt_parser.specifier)
134  {
135  appendFormat(&fmt_parser, args);
136  // fmt_parser.isFmtOption() stops parsing on a non-fmt-specifying char
137  // if the `while(isFmtOption())` loop above iterated more than once, then
138  // we have to prevent disposing of the left over char here
139  if (fmt_parser.specifier != *c) { --c; } // let the next iteration handle it
140  }
141  else
142  {
143  appendChar(*c);
144  }
145  }
146 #if defined(RF24LOG_TAB_SIZE)
147  else if (*c == '\t') { appendChar(' ', RF24LOG_TAB_SIZE); }
148 #endif
149  else
150 #if defined(RF24LOG_NO_EOL)
151  if (*c != '\n') // dispose char; we control the new line feeds ourselves
152 #endif
153  {
154  appendChar(*c);
155  }
156  ++c;
157  }
158 #if !defined(RF24LOG_NO_EOL)
159  if (*c == '\n') { ++c; } // dispose char; we control the new line feeds ourselves
160  appendChar('\n');
161 #endif
162  } while (*c);
163 }
generic struct that allows customization of a printf-like parser
Mechanisms for parsing the log messages before they are output to a stream.
#define RF24LOG_NO_EOL
macro (when defined) disables line feeds at the end of all log messages.
#define RF24LOG_TAB_SIZE
macro (when defined) relaces all \t characters with a specified quantity of spaces
#define RF24LOG_DELIMITER
Change The Delimiter character used in the header prefix of log messages.
virtual void appendStr(const char *data)=0
append a c-string
virtual void appendChar(char data, uint16_t depth=1)=0
append a character a number of times
void appendFormat(FormatSpecifier *fmt_parser, va_list *args)
output a data according to the format specifier
void descTimeLevel(uint8_t logLevel)
Automate the output of the header' timestamp and level description.
void write(uint8_t logLevel, const char *vendorId, const char *message, va_list *args)
Some data about a format specifier.
bool isPaddPrec(char c)
is a character a valid specifier padding/precision quantity
char specifier
datatype specifier
bool isFlagged(char c)
is a character a valid specifier flag
bool isFmtOption(char c)
is a character a valid/supported specifier format option