| /* source: dalan.c */ |
| /* Copyright Gerhard Rieger and contributors (see file CHANGES) */ |
| /* Published under the GNU General Public License V.2, see file COPYING */ |
| |
| /* idea of a low level data description language. currently only a most |
| primitive subset exists. */ |
| |
| #include "config.h" |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #if HAVE_STDBOOL_H |
| #include <stdbool.h> |
| #endif |
| #include <ctype.h> |
| #include "dalan.h" |
| |
| /* test structure to find maximal alignment */ |
| static struct { |
| char a; |
| long double b; |
| } maxalign; |
| |
| /* test structure to find minimal alignment */ |
| static struct { |
| char a; |
| char b; |
| } minalign; |
| |
| /* test union to find kind of byte ordering */ |
| static union { |
| char a[2]; |
| short b; |
| } byteorder = { "01" }; |
| |
| struct dalan_opts_s dalan_opts = { |
| sizeof(int), |
| sizeof(short), |
| sizeof(long), |
| sizeof(char), |
| sizeof(float), |
| sizeof(double) |
| } ; |
| |
| /* fill the dalan_opts structure with machine dependent defaults values. */ |
| static void _dalan_dflts(struct dalan_opts_s *dlo) { |
| dlo->c_int = sizeof(int); |
| dlo->c_short = sizeof(short); |
| dlo->c_long = sizeof(long); |
| dlo->c_char = sizeof(char); |
| dlo->c_float = sizeof(float); |
| dlo->c_double = sizeof(double); |
| dlo->maxalign = (char *)&maxalign.b-&maxalign.a; |
| dlo->minalign = &minalign.b-&minalign.a; |
| dlo->byteorder = (byteorder.b!=7711); |
| } |
| |
| /* allocate a new dalan_opts structure, fills it with machine dependent |
| defaults values, and returns the pointer. */ |
| struct dalan_opts_s *dalan_props(void) { |
| struct dalan_opts_s *dlo; |
| dlo = malloc(sizeof(struct dalan_opts_s)); |
| if (dlo == NULL) { |
| return NULL; |
| } |
| _dalan_dflts(dlo); |
| return dlo; |
| } |
| |
| void dalan_init(void) { |
| _dalan_dflts(&dalan_opts); |
| } |
| |
| /* Parses and coverts a data item. |
| Returns 0 on success, |
| -1 if the data was cut due to n limit, |
| 1 if a syntax error occurred |
| 2 if the actual character is white space |
| 3 if the first character is not a type specifier |
| */ |
| static int dalan_item(int c, const char **line0, uint8_t *data, size_t *p, size_t n) { |
| const char *line = *line0; |
| size_t p1 = *p; |
| |
| switch (c) { |
| case ' ': |
| case '\t': |
| case '\r': |
| case '\n': |
| return 2; |
| case '"': |
| while (1) { |
| switch (c = *line++) { |
| case '\0': fputs("unterminated string\n", stderr); |
| *line0 = line; |
| return 1; |
| case '"': |
| break; |
| case '\\': |
| if (!(c = *line++)) { |
| fputs("continuation line not implemented\n", stderr); |
| *line0 = line; |
| return 1; |
| } |
| switch (c) { |
| case 'n': c = '\n'; break; |
| case 'r': c = '\r'; break; |
| case 't': c = '\t'; break; |
| case 'f': c = '\f'; break; |
| case 'b': c = '\b'; break; |
| case 'a': c = '\a'; break; |
| #if 0 |
| case 'e': c = '\e'; break; |
| #else |
| case 'e': c = '\033'; break; |
| #endif |
| case '0': c = '\0'; break; |
| } |
| /* PASSTHROUGH */ |
| default: |
| if (p1 >= n) { |
| *p = p1; |
| *line0 = line; |
| return -1; |
| } |
| data[p1++] = c; |
| continue; |
| } |
| if (c == '"') |
| break; |
| } |
| break; |
| case '\'': |
| switch (c = *line++) { |
| case '\0': fputs("unterminated character\n", stderr); |
| *line0 = line; |
| return 1; |
| case '\'': fputs("error in character\n", stderr); |
| *line0 = line; |
| return 1; |
| case '\\': |
| if (!(c = *line++)) { |
| fputs("continuation line not implemented\n", stderr); |
| *line0 = line; |
| return 1; |
| } |
| switch (c) { |
| case 'n': c = '\n'; break; |
| case 'r': c = '\r'; break; |
| case 't': c = '\t'; break; |
| case 'f': c = '\f'; break; |
| case 'b': c = '\b'; break; |
| case 'a': c = '\a'; break; |
| #if 0 |
| case 'e': c = '\e'; break; |
| #else |
| case 'e': c = '\033'; break; |
| #endif |
| } |
| /* PASSTHROUGH */ |
| default: |
| if (p1 >= n) { *p = p1; return -1; } |
| data[p1++] = c; |
| break; |
| } |
| if (*line != '\'') { |
| fputs("error in character termination\n", stderr); |
| *p = p1; |
| *line0 = line; |
| return 1; |
| } |
| ++line; |
| break; |
| case 'x': |
| /* expecting hex data, must be an even number of digits!! */ |
| while (true) { |
| int x; |
| c = *line; |
| if (isdigit(c&0xff)) { |
| x = (c-'0') << 4; |
| } else if (isxdigit(c&0xff)) { |
| x = ((c&0x07) + 9) << 4; |
| } else |
| break; |
| ++line; |
| c = *line; |
| if (isdigit(c&0xff)) { |
| x |= (c-'0'); |
| } else if (isxdigit(c&0xff)) { |
| x |= (c&0x07) + 9; |
| } else { |
| fputs("odd number of hexadecimal digits\n", stderr); |
| *p = p1; |
| *line0 = line; |
| return 1; |
| } |
| ++line; |
| if (p1 >= n) { |
| *p = p1; |
| *line0 = line; |
| return -1; |
| } |
| data[p1++] = x; |
| } |
| break; |
| case 'l': |
| /* expecting decimal number, with target type long */ |
| { |
| char *endptr; |
| *(long *)&data[p1] = strtol(line, &endptr, 10); |
| p1 += sizeof(long); |
| line = endptr; |
| } |
| break; |
| case 'L': |
| /* expecting decimal number, with target type unsigned long */ |
| { |
| char *endptr; |
| *(unsigned long *)&data[p1] = strtol(line, &endptr, 10); |
| p1 += sizeof(unsigned long); |
| line = endptr; |
| } |
| break; |
| case 'i': |
| /* expecting decimal number, with target type int */ |
| { |
| char *endptr; |
| *(int *)&data[p1] = strtol(line, &endptr, 10); |
| p1 += sizeof(int); |
| line = endptr; |
| } |
| break; |
| case 'I': |
| /* expecting decimal number, with target type unsigned int */ |
| { |
| char *endptr; |
| *(unsigned int *)&data[p1] = strtol(line, &endptr, 10); |
| p1 += sizeof(unsigned int); |
| line = endptr; |
| } |
| break; |
| case 's': |
| /* expecting decimal number, with target type short */ |
| { |
| char *endptr; |
| *(short *)&data[p1] = strtol(line, &endptr, 10); |
| p1 += sizeof(short); |
| line = endptr; |
| } |
| break; |
| case 'S': |
| /* expecting decimal number, with target type unsigned short */ |
| { |
| char *endptr; |
| *(unsigned short *)&data[p1] = strtol(line, &endptr, 10); |
| p1 += sizeof(unsigned short); |
| line = endptr; |
| } |
| break; |
| case 'b': |
| /* expecting decimal number, with target type byte (int8_t) */ |
| { |
| char *endptr; |
| *(int8_t *)&data[p1] = strtoul(line, &endptr, 10); |
| p1 += sizeof(uint8_t); |
| line = endptr; |
| } |
| case 'B': |
| /* expecting decimal number, with target type byte (uint8_t) */ |
| { |
| char *endptr; |
| *(uint8_t *)&data[p1] = strtoul(line, &endptr, 10); |
| p1 += sizeof(uint8_t); |
| line = endptr; |
| } |
| break; |
| default: |
| *line0 = line; |
| return 3; |
| } |
| *p = p1; |
| *line0 = line; |
| return 0; |
| } |
| |
| /* read data description from line (\0-terminated), write result to data; do |
| not write so much data that *p exceeds n ! |
| *p must be initialized. |
| return 0 on success, |
| -1 if the data was cut due to n limit, |
| 1 if a syntax error occurred |
| *p is a global data counter; especially it must be used when calculating |
| alignment. On successful return from the function *p must be actual! |
| */ |
| int dalan(const char *line, uint8_t *data, size_t *p, size_t n, char deflt) { |
| size_t p0; |
| char c; |
| int rc; |
| |
| while (1) { |
| /* assume there is a type specifier on beginning of rest of line */ |
| c = *line++; |
| if (c == '\0') |
| break; |
| p0 = *p; |
| rc = dalan_item(c, &line, data, p, n); |
| if (rc == 0) { |
| deflt = c; /* continue with this type as default */ |
| } else if (rc == 2) { |
| /* white space */ |
| continue; |
| } else if (rc == 3) { |
| const char *line0; |
| --line; |
| line0 = line; |
| /* No, we did not recognize c as type specifier, try default type */ |
| rc = dalan_item(deflt, &line, data, p, n); |
| if (line == line0) { |
| /* Nothing was parsed */ |
| return 1; |
| } |
| } |
| if (rc != 0) { |
| return rc; |
| } |
| /* rc == 0 */ |
| n -= (*p-p0); |
| } |
| return 0; |
| } |