Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2015 Cisco and/or its affiliates. |
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | * you may not use this file except in compliance with the License. |
| 5 | * You may obtain a copy of the License at: |
| 6 | * |
| 7 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | * |
| 9 | * Unless required by applicable law or agreed to in writing, software |
| 10 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | * See the License for the specific language governing permissions and |
| 13 | * limitations under the License. |
| 14 | */ |
| 15 | /* |
| 16 | Copyright (c) 2001, 2002, 2003 Eliot Dresselhaus |
| 17 | |
| 18 | Permission is hereby granted, free of charge, to any person obtaining |
| 19 | a copy of this software and associated documentation files (the |
| 20 | "Software"), to deal in the Software without restriction, including |
| 21 | without limitation the rights to use, copy, modify, merge, publish, |
| 22 | distribute, sublicense, and/or sell copies of the Software, and to |
| 23 | permit persons to whom the Software is furnished to do so, subject to |
| 24 | the following conditions: |
| 25 | |
| 26 | The above copyright notice and this permission notice shall be |
| 27 | included in all copies or substantial portions of the Software. |
| 28 | |
| 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| 30 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| 31 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| 32 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| 33 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| 34 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| 35 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 36 | */ |
| 37 | |
| 38 | #ifndef included_format_h |
| 39 | #define included_format_h |
| 40 | |
| 41 | #include <stdarg.h> |
| 42 | |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 43 | #include <vppinfra/clib.h> /* for CLIB_UNIX, etc. */ |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 44 | #include <vppinfra/vec.h> |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 45 | #include <vppinfra/error.h> /* for ASSERT */ |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 46 | #include <vppinfra/string.h> |
| 47 | |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 48 | typedef u8 *(format_function_t) (u8 * s, va_list * args); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 49 | |
Neale Ranns | 0bfe5d8 | 2016-08-25 15:29:12 +0100 | [diff] [blame] | 50 | u8 *va_format (u8 * s, const char *format, va_list * args); |
| 51 | u8 *format (u8 * s, const char *format, ...); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 52 | |
| 53 | #ifdef CLIB_UNIX |
| 54 | |
| 55 | #include <stdio.h> |
| 56 | |
| 57 | #else /* ! CLIB_UNIX */ |
| 58 | |
| 59 | /* We're not Unix and have not stdio.h */ |
| 60 | #define FILE void |
| 61 | #define stdin ((FILE *) 0) |
| 62 | #define stdout ((FILE *) 1) |
| 63 | #define stderr ((FILE *) 2) |
| 64 | |
| 65 | #endif |
| 66 | |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 67 | word va_fformat (FILE * f, char *fmt, va_list * va); |
| 68 | word fformat (FILE * f, char *fmt, ...); |
| 69 | word fdformat (int fd, char *fmt, ...); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 70 | |
Christophe Fontaine | d3c008d | 2017-10-02 18:10:54 +0200 | [diff] [blame] | 71 | always_inline u32 |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 72 | format_get_indent (u8 * s) |
| 73 | { |
Christophe Fontaine | d3c008d | 2017-10-02 18:10:54 +0200 | [diff] [blame] | 74 | u32 indent = 0; |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 75 | u8 *nl; |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 76 | |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 77 | if (!s) |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 78 | return indent; |
| 79 | |
| 80 | nl = vec_end (s) - 1; |
| 81 | while (nl >= s) |
| 82 | { |
| 83 | if (*nl-- == '\n') |
| 84 | break; |
| 85 | indent++; |
| 86 | } |
| 87 | return indent; |
| 88 | } |
| 89 | |
Damjan Marion | dae1c7e | 2020-10-17 13:32:25 +0200 | [diff] [blame] | 90 | #define _(f) __clib_export u8 * f (u8 * s, va_list * va) |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 91 | |
| 92 | /* Standard user-defined formats. */ |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 93 | _(format_vec32); |
| 94 | _(format_vec_uword); |
| 95 | _(format_ascii_bytes); |
| 96 | _(format_hex_bytes); |
Damjan Marion | 3860a77 | 2019-06-17 23:20:27 +0200 | [diff] [blame] | 97 | _(format_hex_bytes_no_wrap); |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 98 | _(format_white_space); |
| 99 | _(format_f64); |
| 100 | _(format_time_interval); |
Nathan Skrzypczak | 6de58f5 | 2022-01-24 17:10:41 +0100 | [diff] [blame] | 101 | _ (format_duration); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 102 | |
| 103 | #ifdef CLIB_UNIX |
| 104 | /* Unix specific formats. */ |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 105 | _(format_address_family); |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 106 | _(format_network_address); |
| 107 | _(format_network_protocol); |
| 108 | _(format_network_port); |
| 109 | _(format_sockaddr); |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 110 | _(format_timeval); |
| 111 | _(format_time_float); |
| 112 | _(format_signal); |
| 113 | _(format_ucontext_pc); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 114 | #endif |
| 115 | |
| 116 | #undef _ |
| 117 | |
| 118 | /* Unformat. */ |
| 119 | |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 120 | typedef struct _unformat_input_t |
| 121 | { |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 122 | /* Input buffer (vector). */ |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 123 | u8 *buffer; |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 124 | |
| 125 | /* Current index in input buffer. */ |
| 126 | uword index; |
| 127 | |
| 128 | /* Vector of buffer marks. Used to delineate pieces of the buffer |
| 129 | for error reporting and for parse recovery. */ |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 130 | uword *buffer_marks; |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 131 | |
| 132 | /* User's function to fill the buffer when its empty |
| 133 | (and argument). */ |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 134 | uword (*fill_buffer) (struct _unformat_input_t * i); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 135 | |
| 136 | /* Return values for fill buffer function which indicate whether not |
| 137 | input has been exhausted. */ |
| 138 | #define UNFORMAT_END_OF_INPUT (~0) |
| 139 | #define UNFORMAT_MORE_INPUT 0 |
| 140 | |
| 141 | /* User controlled argument to fill buffer function. */ |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 142 | void *fill_buffer_arg; |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 143 | } unformat_input_t; |
| 144 | |
| 145 | always_inline void |
| 146 | unformat_init (unformat_input_t * i, |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 147 | uword (*fill_buffer) (unformat_input_t *), |
| 148 | void *fill_buffer_arg) |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 149 | { |
Dave Barach | b7b9299 | 2018-10-17 10:38:51 -0400 | [diff] [blame] | 150 | clib_memset (i, 0, sizeof (i[0])); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 151 | i->fill_buffer = fill_buffer; |
| 152 | i->fill_buffer_arg = fill_buffer_arg; |
| 153 | } |
| 154 | |
| 155 | always_inline void |
| 156 | unformat_free (unformat_input_t * i) |
| 157 | { |
| 158 | vec_free (i->buffer); |
| 159 | vec_free (i->buffer_marks); |
Dave Barach | b7b9299 | 2018-10-17 10:38:51 -0400 | [diff] [blame] | 160 | clib_memset (i, 0, sizeof (i[0])); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 161 | } |
| 162 | |
| 163 | always_inline uword |
| 164 | unformat_check_input (unformat_input_t * i) |
| 165 | { |
| 166 | /* Low level fill input function. */ |
| 167 | extern uword _unformat_fill_input (unformat_input_t * i); |
| 168 | |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 169 | if (i->index >= vec_len (i->buffer) && i->index != UNFORMAT_END_OF_INPUT) |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 170 | _unformat_fill_input (i); |
| 171 | |
| 172 | return i->index; |
| 173 | } |
| 174 | |
| 175 | /* Return true if input is exhausted */ |
| 176 | always_inline uword |
| 177 | unformat_is_eof (unformat_input_t * input) |
| 178 | { |
| 179 | return unformat_check_input (input) == UNFORMAT_END_OF_INPUT; |
| 180 | } |
| 181 | |
| 182 | /* Return next element in input vector, |
| 183 | possibly calling fill input to get more. */ |
| 184 | always_inline uword |
| 185 | unformat_get_input (unformat_input_t * input) |
| 186 | { |
| 187 | uword i = unformat_check_input (input); |
| 188 | if (i < vec_len (input->buffer)) |
| 189 | { |
| 190 | input->index = i + 1; |
| 191 | i = input->buffer[i]; |
| 192 | } |
| 193 | return i; |
| 194 | } |
| 195 | |
| 196 | /* Back up input pointer by one. */ |
| 197 | always_inline void |
| 198 | unformat_put_input (unformat_input_t * input) |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 199 | { |
| 200 | input->index -= 1; |
| 201 | } |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 202 | |
| 203 | /* Peek current input character without advancing. */ |
| 204 | always_inline uword |
| 205 | unformat_peek_input (unformat_input_t * input) |
| 206 | { |
| 207 | uword c = unformat_get_input (input); |
| 208 | if (c != UNFORMAT_END_OF_INPUT) |
| 209 | unformat_put_input (input); |
| 210 | return c; |
| 211 | } |
| 212 | |
| 213 | /* Skip current input line. */ |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 214 | always_inline void |
| 215 | unformat_skip_line (unformat_input_t * i) |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 216 | { |
| 217 | uword c; |
| 218 | |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 219 | while ((c = unformat_get_input (i)) != UNFORMAT_END_OF_INPUT && c != '\n') |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 220 | ; |
| 221 | } |
| 222 | |
| 223 | uword unformat_skip_white_space (unformat_input_t * input); |
| 224 | |
| 225 | /* Unformat function. */ |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 226 | typedef uword (unformat_function_t) (unformat_input_t * input, |
| 227 | va_list * args); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 228 | |
| 229 | /* External functions. */ |
| 230 | |
| 231 | /* General unformatting function with programmable input stream. */ |
Neale Ranns | 32e1c01 | 2016-11-22 17:07:28 +0000 | [diff] [blame] | 232 | uword unformat (unformat_input_t * i, const char *fmt, ...); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 233 | |
| 234 | /* Call user defined parse function. |
| 235 | unformat_user (i, f, ...) is equivalent to unformat (i, "%U", f, ...) */ |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 236 | uword unformat_user (unformat_input_t * input, unformat_function_t * func, |
| 237 | ...); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 238 | |
| 239 | /* Alternate version which allows for extensions. */ |
Neale Ranns | 32e1c01 | 2016-11-22 17:07:28 +0000 | [diff] [blame] | 240 | uword va_unformat (unformat_input_t * i, const char *fmt, va_list * args); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 241 | |
| 242 | /* Setup for unformat of Unix style command line. */ |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 243 | void unformat_init_command_line (unformat_input_t * input, char *argv[]); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 244 | |
| 245 | /* Setup for unformat of given string. */ |
BenoƮt Ganne | 82f618c | 2021-10-13 11:35:15 +0200 | [diff] [blame] | 246 | void unformat_init_string (unformat_input_t *input, const char *string, |
| 247 | int string_len); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 248 | |
| 249 | always_inline void |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 250 | unformat_init_cstring (unformat_input_t * input, char *string) |
| 251 | { |
| 252 | unformat_init_string (input, string, strlen (string)); |
| 253 | } |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 254 | |
| 255 | /* Setup for unformat of given vector string; vector will be freed by unformat_string. */ |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 256 | void unformat_init_vector (unformat_input_t * input, u8 * vector_string); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 257 | |
| 258 | /* Format function for unformat input usable when an unformat error |
| 259 | has occurred. */ |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 260 | u8 *format_unformat_error (u8 * s, va_list * va); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 261 | |
| 262 | #define unformat_parse_error(input) \ |
| 263 | clib_error_return (0, "parse error `%U'", format_unformat_error, input) |
| 264 | |
| 265 | /* Print all input: not just error context. */ |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 266 | u8 *format_unformat_input (u8 * s, va_list * va); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 267 | |
| 268 | /* Unformat (parse) function which reads a %s string and converts it |
| 269 | to and unformat_input_t. */ |
| 270 | unformat_function_t unformat_input; |
| 271 | |
| 272 | /* Parse a line ending with \n and return it. */ |
| 273 | unformat_function_t unformat_line; |
| 274 | |
| 275 | /* Parse a line ending with \n and return it as an unformat_input_t. */ |
| 276 | unformat_function_t unformat_line_input; |
| 277 | |
| 278 | /* Parse a token containing given set of characters. */ |
| 279 | unformat_function_t unformat_token; |
| 280 | |
| 281 | /* Parses a hexstring into a vector of bytes. */ |
| 282 | unformat_function_t unformat_hex_string; |
| 283 | |
| 284 | /* Returns non-zero match if input is exhausted. |
| 285 | Useful to ensure that the entire input matches with no trailing junk. */ |
| 286 | unformat_function_t unformat_eof; |
| 287 | |
| 288 | /* Parse memory size e.g. 100, 100k, 100m, 100g. */ |
| 289 | unformat_function_t unformat_memory_size; |
| 290 | |
Nathan Skrzypczak | 162ff5e | 2021-11-09 18:18:21 +0100 | [diff] [blame] | 291 | /* Format base 10 e.g. 100, 100K, 100M, 100G */ |
| 292 | u8 *format_base10 (u8 *s, va_list *va); |
| 293 | |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 294 | /* Unparse memory size e.g. 100, 100k, 100m, 100g. */ |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 295 | u8 *format_memory_size (u8 * s, va_list * va); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 296 | |
Damjan Marion | b509504 | 2020-09-11 22:13:46 +0200 | [diff] [blame] | 297 | /* Parse memory page size e.g. 4K, 2M */ |
| 298 | unformat_function_t unformat_log2_page_size; |
| 299 | |
| 300 | /* Unparse memory page size e.g. 4K, 2M */ |
| 301 | u8 *format_log2_page_size (u8 * s, va_list * va); |
| 302 | |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 303 | /* Format c identifier: e.g. a_name -> "a name". */ |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 304 | u8 *format_c_identifier (u8 * s, va_list * va); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 305 | |
Damjan Marion | a7e83ce | 2016-06-09 12:38:22 +0200 | [diff] [blame] | 306 | /* Format hexdump with both hex and printable chars - compatible with text2pcap */ |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 307 | u8 *format_hexdump (u8 * s, va_list * va); |
Damjan Marion | a7e83ce | 2016-06-09 12:38:22 +0200 | [diff] [blame] | 308 | |
Damjan Marion | e6387b2 | 2021-12-22 21:58:54 +0100 | [diff] [blame] | 309 | /* Format bitmap of array of u64 numbers */ |
| 310 | u8 *format_u64_bitmap (u8 *s, va_list *va); |
| 311 | |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 312 | /* Unix specific formats. */ |
| 313 | #ifdef CLIB_UNIX |
| 314 | /* Setup input from Unix file. */ |
Dave Barach | 59b2565 | 2017-09-10 15:04:27 -0400 | [diff] [blame] | 315 | void unformat_init_clib_file (unformat_input_t * input, int file_descriptor); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 316 | |
| 317 | /* Take input from Unix environment variable; returns |
| 318 | 1 if variable exists zero otherwise. */ |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 319 | uword unformat_init_unix_env (unformat_input_t * input, char *var); |
Damjan Marion | a54230d | 2017-06-21 11:57:07 +0200 | [diff] [blame] | 320 | |
| 321 | /* Unformat unix group id (gid) specified as integer or string */ |
| 322 | unformat_function_t unformat_unix_gid; |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 323 | #endif /* CLIB_UNIX */ |
| 324 | |
MathiasRaoul | 579b165 | 2019-10-04 09:53:45 +0000 | [diff] [blame] | 325 | uword unformat_data_size (unformat_input_t * input, va_list * args); |
| 326 | |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 327 | /* Test code. */ |
| 328 | int test_format_main (unformat_input_t * input); |
| 329 | int test_unformat_main (unformat_input_t * input); |
| 330 | |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 331 | /* This is not the right place for this, but putting it in vec.h |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 332 | created circular dependency problems. */ |
| 333 | int test_vec_main (unformat_input_t * input); |
| 334 | |
| 335 | #endif /* included_format_h */ |
Dave Barach | c379999 | 2016-08-15 11:12:27 -0400 | [diff] [blame] | 336 | |
| 337 | /* |
| 338 | * fd.io coding-style-patch-verification: ON |
| 339 | * |
| 340 | * Local Variables: |
| 341 | * eval: (c-set-style "gnu") |
| 342 | * End: |
| 343 | */ |