/**
* \file char-mapper.c
*
* This is the main routine for char-mapper.
*
* This file is part of char-mapper.
* char-mapper Copyright (C) 2003-2013 by Bruce Korb - all rights reserved
*
* char-mapper is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* char-mapper is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "char-mapper.h"
int
main(int argc, char ** argv)
{
program = strrchr(argv[0], '/');
if (program++ == NULL)
program = argv[0];
if (argc > 1) {
char * pz = argv[1];
if (*pz == '-')
parse_help(pz);
if (freopen(argv[1], "r", stdin) != stdin) {
fprintf(stderr, "fs error %d (%s) reopening '%s' as stdin\n",
errno, strerror(errno), pz);
exit(EXIT_FAILURE);
}
} else if (isatty(STDIN_FILENO))
parse_help(NULL);
else argv[1] = "stdin";
bit_count = read_data();
init_names();
emit_leader(argv[1]);
emit_macros(bit_count);
emit_table(bit_count);
if (optimize_code)
emit_opt_functions();
else
emit_functions();
return EXIT_SUCCESS;
}
void
die(char const * fmt, ...)
{
va_list ap;
fputs("char-mapper error: ", stderr);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
exit(EXIT_FAILURE);
}
static void
emit_leader(char * input)
{
if (add_test_code) {
if (out_file_nm != NULL)
printf(test_script_fmt, base_fn_nm, base_ucase, out_file_nm);
}
{
time_t tm = time(NULL);
struct tm * tmp = localtime(&tm);
char const * mask_type =
(bit_count <= 8) ? type_8bits
: ((bit_count <= 16) ? type_16bits
: ((bit_count <= 32) ? type_32bits
: type_64bits ));
strftime(buffer, BUF_SIZE, "%x at %X", tmp);
printf(leader_fmt, bit_count, total_map_ct, buffer,
commentary, file_guard, mask_type, program);
}
if (fseek(stdin, 0, SEEK_SET) == 0)
copy_input_text(input);
{
int width = (bit_count+3)/4;
if (width < 2) width = 2;
sprintf(mask_fmt, mask_fmt_fmt, width);
if (width > 8)
strcat(mask_fmt, "ULL");
}
}
static void
init_names(void)
{
if (table_name == NULL) {
/*
* If the guard is unchanged from the initial default,
*/
if (file_guard == char_map_gd)
table_name = "char_type_table";
else {
static char const grd[] = "_GUARD";
char * p = NULL;
char const * e = file_guard + strlen(file_guard)
- (sizeof(grd) - 1);
/*
* If the guard ends with "_GUARD", then set "e" back to the end.
* Otherwise, if it ends with "_H_GUARD", then trim the "_H" too.
*/
if (strcmp(e, grd) != 0)
e += sizeof(grd) - 1;
else if ((e[-2] == '_') && (e[-1] == 'H'))
e -= 2;
/*
* Make the table name be the downcased, trimmed guard.
*/
size_t copy_len = e - file_guard;
table_name = p = malloc(copy_len + table_sfx_LEN + 1);
memcpy(p, file_guard, copy_len);
memcpy(p + copy_len, table_sfx, table_sfx_LEN + 1);
for (; p < table_name + copy_len; p++)
*p = tolower((int)*p);
}
}
/*
* If "%guard" has not been specified (as either empty or with a name),
* then create an ifdef name to guard the data tables.
*/
if (! table_is_static && (data_guard == NULL)) {
char * p = malloc(strlen(table_name) + define_fmt_LEN);
sprintf(p, define_fmt, table_name);
make_define_name(p);
data_guard = p;
}
{
static char const msk[] = "_mask_t";
size_t base_name_len = strlen(table_name); ///< base name length
/*
* If the table name already has a "_table" suffix, don't count it in
* the base name length. Otherwise, "table_name" is our base name.
*/
if (strcmp(table_name + base_name_len - table_sfx_LEN, table_sfx) == 0)
base_name_len -= table_sfx_LEN;
char * p = malloc(base_name_len + table_sfx_LEN + 1);
memcpy(p, table_name, base_name_len);
memcpy(p + base_name_len, msk, sizeof(msk));
mask_name = p;
p = malloc(base_name_len + 1);
memcpy(p, table_name, base_name_len);
p[base_name_len] = NUL;
base_fn_nm = p; // base name of most everything
p = malloc(base_name_len + 1);
memcpy(p, table_name, base_name_len);
p[base_name_len] = NUL;
make_define_name(p);
base_ucase = p; // upper case flavor of our base name
}
}
/**
* Copy out the input to the output as a comment.
* The input file was successfully rewound, so we simply re-read it.
*
* @param name the name of the input file.
*/
static void
copy_input_text(char const * name)
{
bool skip_comment = false;
printf(copy_input_start, name);
while (fgets(buffer, BUF_SIZE, stdin) != NULL) {
if (strstr(buffer, "%comment") != NULL) {
skip_comment = true;
fputs("// %comment -- see above\n", stdout);
continue;
}
if (strstr(buffer, "%emit") != NULL) {
skip_comment = true;
fputs("// %emit code -- see below\n", stdout);
continue;
}
if (skip_comment) {
int bfix = 0;
while (isspace(buffer[bfix])) bfix++;
if (buffer[bfix] != '%') continue;
skip_comment = false;
}
printf("// %s", buffer);
}
fwrite(copy_input_end, copy_input_end_LEN, 1, stdout);
}
/**
* See of the command argument matches a help request.
* We accept "-h" and "--h[e[l[p]]]".
*
* @param name the name of the input file.
*/
void
parse_help(char const * opt_pz)
{
int exit_code = EXIT_SUCCESS;
FILE * fp = stdout;
char const * pz = opt_pz;
if (opt_pz == NULL) {
fp = stderr;
fprintf(stderr, opterrmsg, input_is_tty, must_be_file);
fwrite(usage_text, usage_text_LEN, 1, fp);
exit_code = EXIT_FAILURE;
} else
switch (pz[1]) {
case '-':
pz += 2;
static char const help[] = "help";
char const * p = help;
for (;;) {
if (*pz != *p)
goto bad_opt;
if (*(p++) == NUL)
break;
if (*++pz == NUL)
break;
}
/* FALLTHROUGH */
case 'h':
fwrite(usage_text, usage_text_LEN, 1, fp);
break;
default:
bad_opt:
exit_code = EXIT_FAILURE;
fp = stderr;
fprintf(stderr, opterrmsg, "unknown option", opt_pz);
pz = usage_text;
do putc(*pz, stderr);
while (*(pz++) != '\n');
}
exit(exit_code);
}
/**
* Emit all the macros for doing the char classification tests.
* "NBBY" is "Number of Bits per BYte".
*
* @param bit_count the number of bits in use
*/
void
emit_macros(int bit_count)
{
char fill[CLASS_NAME_LIMIT+3];
int map_ix = 0;
/*
* The type to use for the mask depends on the number of bits
* in the output. Make the selection and emit the mask typedef.
*/
{
char const * mask_type;
switch (bit_count) {
case 1 ... 8: mask_type = "uint8_t"; break;
case 9 ... 16: mask_type = "uint16_t"; break;
case 17 ... 32: mask_type = "uint32_t"; break;
case 33 ... 64: mask_type = "uint64_t"; break;
default:
die("too many char types (%u max)\n", sizeof(long long) * NBBY);
}
printf(typedef_mask, mask_type, mask_name);
}
/*
* Make a padding string so our output looks pretty.
*/
if (max_name_len < CLASS_NAME_LIMIT-2)
max_name_len += 2;
memset(fill, ' ', max_name_len);
fill[max_name_len - 1] = NUL;
for (value_map_t * map = all_map.next; map != NULL; map = map->next) {
size_t ln = strlen(map->vname);
char * pz = fill + ln; // padding pointer
char z[24], y[24]; // 24 >= ceil(log10(1 << 63)) + 1
snprintf(z, sizeof(z), mask_fmt, map->mask);
if (optimize_code)
sprintf(y, "%u", map_ix++);
else
strcpy(y, z);
printf(macro_def_fmt, map->vname, pz, base_fn_nm, z, y);
if (add_backup_code)
printf(backup_def_fmt, map->vname, pz, base_fn_nm, y);
}
putc('\n', stdout);
}
/**
* produce a test main procedure that shows the classsifications.
* Iterate character values from 0 through 0x7F.
*/
static void
emit_test_code(void)
{
int ix = 0;
value_map_t * map;
printf(testit_fmt, base_ucase);
for (map = all_map.next; map != NULL; map = map->next) {
printf(testit_class_names, ix++, map->vname);
}
fputs(testit_class_hdrs, stdout);
for (map = all_map.next, ix=0;
map != NULL;
map = map->next) {
printf(" %02X", ix++);
}
fputs(test_loop, stdout);
for (map = all_map.next, ix=0;
map != NULL;
map = map->next) {
char z[24]; // 24 >= ceil(log10(1 << 63)) + 1
snprintf(z, sizeof(z), mask_fmt, map->mask);
printf(each_test, base_fn_nm, z);
ix++;
}
fputs(" putchar('\\n');\n", stdout);
printf(endtest_fmt, base_ucase);
}
void
emit_functions(void)
{
if (! table_is_static)
printf(declare_tbl, mask_name, table_name, TABLE_SIZE);
printf(inline_functions,
base_fn_nm, mask_name, TABLE_SIZE, table_name);
if (add_backup_code)
printf(inline_backup, base_fn_nm, mask_name);
if (add_on_text != NULL)
printf(emit_text_fmt, add_on_text);
if (add_test_code)
emit_test_code();
printf(endif_fmt, file_guard);
}
void
emit_opt_functions(void)
{
if (! table_is_static)
printf(declare_opt_tbl, mask_name, base_fn_nm,
TABLE_SIZE, base_ucase, total_map_ct);
printf(inline_opt_functions, base_fn_nm, mask_name, TABLE_SIZE);
if (add_backup_code)
printf(inline_opt_backup, base_fn_nm);
if (add_on_text != NULL)
printf(emit_text_fmt, add_on_text);
if (add_test_code)
emit_test_code();
printf(endif_fmt, file_guard);
}
void
emit_table(int bit_count)
{
char const * type_pz;
char * fmt;
int ix = 0;
/**
* How many entries can be initialized on one line? We can do four
* uint8_t's, uint16_t's or even uint32_t's. Only two uint64_t's tho.
*/
int init_ct = (bit_count > 32) ? 2 : 4;
int entry_ct = init_ct;
if (table_is_static)
printf(start_static_table_fmt, mask_name, table_name, TABLE_SIZE);
else
printf(start_table_fmt, data_guard, mask_name, table_name, TABLE_SIZE);
for (;;) {
if (isprint(ix))
printf(" /* %c */ ", (char)ix);
else switch (ix) {
case NUL: fputs(" /*NUL*/ ", stdout); break;
case '\a': fputs(" /*BEL*/ ", stdout); break;
case '\b': fputs(" /* BS*/ ", stdout); break;
case '\t': fputs(" /* HT*/ ", stdout); break;
case '\n': fputs(" /* NL*/ ", stdout); break;
case '\v': fputs(" /* VT*/ ", stdout); break;
case '\f': fputs(" /* FF*/ ", stdout); break;
case '\r': fputs(" /* CR*/ ", stdout); break;
case 0x1B: fputs(" /*ESC*/ ", stdout); break;
default: printf(" /*x%02X*/ ", ix); break;
}
printf(mask_fmt, all_map.values[ix]);
if (++ix >= TABLE_SIZE)
break;
putc(',', stdout);
if (--entry_ct <= 0) {
putc('\n', stdout);
putc(' ', stdout);
entry_ct = init_ct;
}
}
fputs(end_table, stdout);
if (optimize_code) {
static char const fmtfmt[] = " %%s, /* %%-%us*/\n";
char const * static_pfx = table_is_static ? "static " : "";
char const * locking_fmt = pthread_code ? pthread_locking : no_locking;
char fmt[256];
printf(optimize_fmt, static_pfx, base_fn_nm, total_map_ct);
snprintf(fmt, sizeof(fmt), fmtfmt, (unsigned int)max_name_len);
for (value_map_t * map = all_map.next; map != NULL; map = map->next) {
char z[24]; // 24 >= ceil(log10(1 << 63)) + 1
snprintf(z, sizeof(z), mask_fmt, map->mask);
printf(fmt, z, map->vname);
}
printf(locking_fmt, base_fn_nm);
printf(fill_opt_fmt, base_fn_nm, static_pfx);
}
if (! table_is_static)
printf(endif_fmt, data_guard);
}
int
read_data(void)
{
int bit_num = 0;
while (fgets(buffer, BUF_SIZE, stdin) != NULL) {
char nm_buf[CLASS_NAME_LIMIT+1];
char * scan = trim(buffer);
value_map_t * map = NULL;
if (scan == NULL) continue;
scan = get_name(scan, nm_buf, sizeof(nm_buf));
if (curr_name_len > max_name_len)
max_name_len = curr_name_len;
while (scan != NULL) {
switch (*scan) {
case NUL: goto end_while;
case '"':
if (map == NULL)
map = new_map(nm_buf);
if (map->bit_no == ~0) {
map->mask |= 1 << bit_num;
map->bit_no = bit_num;
bit_num++;
}
scan = scan_quoted_value(scan+1, map, VAL_ADD);
break;
case ' ': case '\t':
while (isspace(*scan)) scan++;
break;
case '+':
case '-':
if (map == NULL)
map = new_map(nm_buf);
if (scan[1] == '"')
scan = scan_quoted_value(scan+2, map, VAL_REMOVE);
else scan = copy_another_value(scan, map);
break;
case '%':
scan = parse_directive(scan);
break;
default:
die("value must start with quote or '+' or '-'\n\t%s\n", scan);
}
} end_while:;
}
return bit_num;
}
char *
eat_a_byte(char * scan, int * byte_val)
{
if (*scan != '\\') {
*byte_val = (unsigned char)*(scan++);
} else {
int chval = '\\';
switch (*++scan) {
case NUL: break;
case '\\': scan++; break;
case 'a': chval = '\a'; scan++; break;
case 'b': chval = '\b'; scan++; break;
case 't': chval = '\t'; scan++; break;
case 'n': chval = '\n'; scan++; break;
case 'v': chval = '\v'; scan++; break;
case 'f': chval = '\f'; scan++; break;
case 'r': chval = '\r'; scan++; break;
case '"': chval = '"'; scan++; break;
case '0' ... '7':
{
char octbuf[4];
octbuf[0] = *(scan++);
if ((*scan < '0') || (*scan > '7'))
octbuf[1] = NUL;
else {
octbuf[1] = *(scan++);
if ((*scan < '0') || (*scan > '7'))
octbuf[2] = NUL;
else {
octbuf[2] = *(scan++);
octbuf[3] = NUL;
}
}
chval = (int)strtoul(octbuf, NULL, 8);
if (chval > 0xFF) {
scan -= 2;
goto invalid_escape;
}
break;
}
case 'x': case 'X':
{
char hexbuf[4];
if (! isxdigit(*++scan)) goto invalid_escape;
hexbuf[0] = *(scan++);
if (! isxdigit(*scan))
hexbuf[1] = NUL;
else {
hexbuf[1] = *(scan++);
hexbuf[2] = NUL;
}
chval = (int)strtoul(hexbuf, NULL, 16);
break;
}
default:
invalid_escape:
die("invalid escape sequence: %s\n", scan-1);
}
*byte_val = chval;
}
return scan;
}
char *
scan_quoted_value(char * scan, value_map_t * map, quoted_val_action_t act)
{
value_bits_t mask = 1 << map->bit_no;
if (act == VAL_REMOVE)
mask = ~mask;
for (;;) {
int lo_ix, hi_ix;
switch (*scan) {
case NUL: return scan;
case '"': return scan + 1;
default: break;
}
scan = eat_a_byte(scan, &lo_ix);
if ((*scan == '-') && (scan[1] != NUL) && (scan[1] != '"')) {
scan = eat_a_byte(scan + 1, &hi_ix);
} else {
hi_ix = lo_ix;
}
switch (act) {
case VAL_ADD:
do {
all_map.values[lo_ix] |= (map->values[lo_ix] = mask);
} while (++lo_ix <= hi_ix);
break;
case VAL_REMOVE:
map->values[lo_ix] = 0;
do {
all_map.values[lo_ix] &= mask;
} while (++lo_ix <= hi_ix);
}
}
}
char *
trim(char * buf)
{
while (isspace(*buf)) buf++;
{
char * pe = buf + strlen(buf);
while ((pe > buf) && isspace(pe[-1])) pe--;
*pe = NUL;
}
switch (*buf) {
case NUL:
case '#':
return NULL;
default:
return buf;
}
}
char *
get_name(char * scan, char * nm_buf, size_t buf_size)
{
char * buf_end = nm_buf + buf_size - 2;
size_t nm_len = 0;
while (isalnum(*scan) || (*scan == '_') || (*scan == '-')) {
int ch = *(scan++);
if (ch == '-')
ch = '_';
else if (islower(ch))
ch = toupper(ch);
*(nm_buf++) = ch;
nm_len++;
if (nm_buf >= buf_end)
nm_buf--;
}
if (scan == nm_buf)
die("input line does not start with a name:\n\t%s\n", nm_buf);
while (isspace(*scan)) scan++;
*nm_buf = NUL;
curr_name_len = nm_len;
return scan;
}
value_map_t *
new_map(char * name)
{
value_map_t * map = malloc(sizeof(*map));
memset(map, 0, sizeof(*map));
*end_map = map;
end_map = &(map->next);
strcpy(map->vname, name);
map->bit_no = ~0;
total_map_ct++;
return map;
}
char *
copy_another_value(char * scan, value_map_t * map)
{
int add_in = (*scan == '+');
char z[CLASS_NAME_LIMIT+1];
value_map_t * mp = all_map.next;
scan = get_name(scan+1, z, sizeof(z));
for (;;mp = mp->next) {
if (mp == NULL)
die("No entry named %s\n", z);
if (strcmp(z, mp->vname) == 0)
break;
}
if (add_in) {
map->mask |= mp->mask;
}
else if (map->bit_no == ~0)
die("You cannot remove bits that are not there.\n");
else {
value_bits_t mask = ~(1 << map->bit_no);
value_bits_t skip = mp->mask;
value_bits_t * this = map->values;
value_bits_t * all = all_map.values;
int ct = sizeof(mp->values) / sizeof(mp->values[0]);
do {
/*
* We are negating, so if the named value has this bit set,
* turn off the current bit in the global mask and turn off
* all bits in the for-this-named-value array of masks.
*/
if ((*all & skip) != 0) {
*all &= mask;
*this = 0;
}
this++; all++;
} while (--ct > 0);
}
return scan;
}
static void
make_define_name(char * ptr)
{
while (*ptr) {
if (islower(*ptr))
*ptr = toupper(*ptr);
else if (! isalnum(*ptr))
*ptr = '_';
ptr++;
}
}
char *
parse_directive(char * scan)
{
while (isspace(*++scan))
/* skip leading '%' and white space */;
if (! isalpha(*scan))
die("directives must begin with an alphabetic character: %s\n", scan);
return handle_cm_opt_disp(scan);
}
static char *
add_text(char ** buff, size_t * sz, char * curr, char * add, int pfx)
{
size_t len = (add == NULL) ? 0 : strlen(add);
char * e = *buff + *sz - 4;
if (*buff + len >= e) {
size_t o = curr - *buff;
*sz += BUF_SIZE;
*buff = realloc(*buff, *sz);
curr = *buff + o;
}
if (pfx)
*(curr++) = ' ', *(curr++) = '*';
if (len == 0) {
*(curr++) = '\n';
} else {
if (pfx)
*(curr++) = ' ', *(curr++) = ' ';
memcpy(curr, add, len);
curr += len;
}
return curr;
}
static char *
load_text(int pfx)
{
char * scan_in;
char * com_buf = malloc(BUF_SIZE);
char * scan_out = com_buf;
size_t com_buf_size = BUF_SIZE;
int line_ct = 0;
int blank_ct = 0;
for (;;) {
if (fgets(buffer, BUF_SIZE, stdin) == NULL)
die("incomplete comment section");
scan_in = buffer;
while (isspace(*scan_in)) scan_in++;
/*
* if scan is NULL, we've got a comment
*/
if (*scan_in == NUL) {
blank_ct++;
continue;
}
if (*scan_in == '%')
break;
if (line_ct++ == 0) {
blank_ct = 0;
} else while (blank_ct > 0) {
scan_out =
add_text(&com_buf, &com_buf_size, scan_out, NULL, pfx);
blank_ct--;
line_ct++;
}
scan_out = add_text(&com_buf, &com_buf_size, scan_out, scan_in, pfx);
}
*scan_out = NUL;
return com_buf;
}
/* * * * * * * * * * * * * * * *
The following handler functions document and define the embedded
directives. The first line of the comment must be exactly
"handle" + one space + directive name + one space + "directive".
Dispatch tables and documentation is derived from this.
* * * * * * * * * * * * * * * */
/**
* handle file directive.
*
* Specifies the output file name. The multi-inclusion guard is derived
* from this name. If %file is not specified, that guard defaults to
* CHAR_MAPPER_H_GUARD. The default output is to stdout.
*
* @param scan current scan point
* @returns end of guard scan
*/
char *
handle_file(cm_opt_enum_t id, char const * txt)
{
char * scan = (char *)(unsigned long)txt;
char * pz;
size_t fname_len;
if (! isspace(*scan)) die(bad_directive, scan-4);
while (isspace(*scan)) scan++;
if (freopen(scan, "w", stdout) != stdout)
die("fs error %d (%s) reopening %s as stdout\n", errno,
strerror(errno), scan);
fname_len = strlen(scan);
file_guard = pz = malloc(fname_len + guard_fmt_LEN);
sprintf(pz, guard_fmt, scan);
make_define_name(pz);
out_file_nm = pz = malloc(fname_len + 1);
strcpy(out_file_nm, scan);
*scan = NUL;
return scan;
}
/**
* handle comment directive.
*
* Specifies the text to insert in a comment block at the head of the output
* file. The comment text starts on the next line and ends with the first
* input line with a percent ('%') in the first column. The default is:
* char-mapper Character Classifications
*
* @param scan current scan point
* @returns end of guard scan
*/
char *
handle_comment(cm_opt_enum_t id, char const * txt)
{
char * scan = (char *)(unsigned long)txt;
commentary = load_text(1);
*scan = NUL;
return scan;
}
/**
* handle emit directive.
*
* Collect text to be emitted at the end of the header. Like "%comment",
* the block of text also ends with a line with a '%' character in column 1.
*
* @param scan current scan point
* @returns The '%' starting the next directive.
*/
char *
handle_emit(cm_opt_enum_t id, char const * txt)
{
char * scan = (char *)(unsigned long)txt;
add_on_text = load_text(0);
*scan = NUL;
return scan;
}
/**
* handle table directive.
*
* Specifies the name of the output table. If not specified, it defaults to
* the base file name, suffixed with "_table" and "char_type_table" if %file
* is not specified. Normally, this table is "extern" in scope, but may be
* made static by specifying an empty %guard.
*
* @param scan current scan point
* @returns end of guard scan
*/
char *
handle_table(cm_opt_enum_t id, char const * txt)
{
char * scan = (char *)(unsigned long)txt;
char * pz;
if (! isspace(*scan)) die(bad_directive, scan-5);
while (isspace(*scan)) scan++;
table_name = pz = strdup(scan);
for (;*pz; pz++) {
if (! isalnum((int)*pz))
*pz = '_';
}
return scan + strlen(scan);
}
/**
* handle optimize directive.
*
* Specifies setting up optimization tables for span/break scanning operations
* on character classes. A static array of pointers is defined and populated
* on the first use of a scan/break on each character classification.
*
* @param scan current scan point
* @returns end of guard scan
*/
char *
handle_optimize(cm_opt_enum_t id, char const * txt)
{
char * scan = (char *)(unsigned long)txt;
optimize_code = true;
return scan + strlen(scan);
}
/**
* handle pthread directive.
*
* Specifies pthread locking on allocation of optimization maps. This has no
* effect unless %optimize has been specified. Pthread-ed multi-threaded
* applications *must* specify this if optimization maps are emitted.
* Spanning maps get allocated dynamically when first used. Only one thread
* should do the work.
*
* @param scan current scan point
* @returns end of guard scan
*/
char *
handle_pthread(cm_opt_enum_t id, char const * txt)
{
char * scan = (char *)(unsigned long)txt;
pthread_code = true;
return scan + strlen(scan);
}
/**
* handle guard directive.
*
* Specifies the name of a '#ifdef' guard protecting the compilation of
* the bit mask table. One compilation unit must '#define' this name.
* The default is the table name surrounded by "DEFINE_" and "_TABLE".
* If empty, the output table is unguarded and made static in scope.
*
* @param scan current scan point
* @returns end of guard scan
*/
char *
handle_guard(cm_opt_enum_t id, char const * txt)
{
char * scan = (char *)(unsigned long)txt;
char * pz;
if (! isspace(*scan)) {
table_is_static = true;
return scan;
}
while (isspace(*scan)) scan++;
data_guard = pz = strdup(scan);
make_define_name(pz);
return scan + strlen(scan);
}
/**
* handle backup directive.
*
* Specifies emitting code to backup over matching text at the end of a string.
* The macro takes two arguments: a start point and an end point.
* The "end point" is returned after backing up over skipped characters,
* but it will be at or after the start pointer. The second pointer may also
* be NULL or set to the start pointer. In that case, it is advanced to the
* next NUL byte at or after where the start pointer points.
*
* @param scan current scan point
* @returns end of guard scan
*/
char *
handle_backup(cm_opt_enum_t id, char const * txt)
{
char * scan = (char *)(unsigned long)txt;
add_backup_code = true;
return scan + strlen(scan);
}
/**
* handle test directive.
*
* Specifies that a main procedure is to be emitted under an ifdef guard.
* The first few lines of the output can also be processed by a Bourne
* shell to compile and test that main procedure. That shell script is
* invisible to the C compiler. It is under a "#if 0" guard.
*
* @param scan current scan point
* @returns end of guard scan
*/
char *
handle_test(cm_opt_enum_t id, char const * txt)
{
char * scan = (char *)(unsigned long)txt;
add_test_code = true;
return scan + strlen(scan);
}
/**
* handle invalid directive.
*
* This function gets called when input is messed up. This function does not
* return.
*
* @param scan current scan point
* @returns not
*/
char *
handle_invalid(cm_opt_enum_t id, char const * txt)
{
char * scan = (char *)(unsigned long)txt;
die(bad_directive, scan);
}
/*
* Local Variables:
* mode: C
* c-file-style: "stroustrup"
* indent-tabs-mode: nil
* End:
* end of char-mapper.c */