2001-10-15 02:49:10 +00:00
|
|
|
/*
|
2003-02-08 04:17:25 +00:00
|
|
|
* avrdude - A Downloader/Uploader for AVR device programmers
|
2004-12-22 01:52:45 +00:00
|
|
|
* Copyright (C) 2000-2004 Brian S. Dean <bsd@bsdhome.com>
|
2001-10-15 02:49:10 +00:00
|
|
|
*
|
2003-02-06 19:08:33 +00:00
|
|
|
* This program 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 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
2001-10-15 02:49:10 +00:00
|
|
|
*
|
2003-02-06 19:08:33 +00:00
|
|
|
* This program 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.
|
2001-10-15 02:49:10 +00:00
|
|
|
*
|
2003-02-06 19:08:33 +00:00
|
|
|
* You should have received a copy of the GNU General Public License
|
2012-11-20 14:03:50 +00:00
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2001-10-15 02:49:10 +00:00
|
|
|
*/
|
|
|
|
|
2001-10-14 23:17:26 +00:00
|
|
|
/* $Id$ */
|
|
|
|
|
2003-02-14 20:34:03 +00:00
|
|
|
#include "ac_cfg.h"
|
|
|
|
|
2007-01-24 22:43:46 +00:00
|
|
|
#include <errno.h>
|
2001-10-14 23:17:26 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <string.h>
|
2022-08-09 20:20:44 +00:00
|
|
|
#include <ctype.h>
|
2001-10-14 23:17:26 +00:00
|
|
|
|
2007-01-24 22:43:46 +00:00
|
|
|
#include "avrdude.h"
|
2014-05-19 10:01:59 +00:00
|
|
|
#include "libavrdude.h"
|
2014-06-17 20:08:28 +00:00
|
|
|
#include "config.h"
|
2014-05-19 10:01:59 +00:00
|
|
|
|
2003-02-14 20:34:03 +00:00
|
|
|
#include "config_gram.h"
|
2001-10-14 23:17:26 +00:00
|
|
|
|
2022-08-09 20:45:04 +00:00
|
|
|
const char *default_programmer;
|
|
|
|
const char *default_parallel;
|
|
|
|
const char *default_serial;
|
|
|
|
const char *default_spi;
|
2011-08-26 20:30:26 +00:00
|
|
|
double default_bitclock;
|
2003-02-21 18:46:51 +00:00
|
|
|
|
2001-10-14 23:17:26 +00:00
|
|
|
LISTID string_list;
|
|
|
|
LISTID number_list;
|
|
|
|
PROGRAMMER * current_prog;
|
|
|
|
AVRPART * current_part;
|
2001-11-21 02:46:55 +00:00
|
|
|
AVRMEM * current_mem;
|
2001-10-14 23:17:26 +00:00
|
|
|
LISTID part_list;
|
|
|
|
LISTID programmers;
|
Alias keyword (#868)
Implementation for an "alias" keyword.
By now, only applied inside memory descriptions.
* Make "mem_alias" a separate nonterminal.
The previous implementation attempt caused a syntax error in
yacc code, and separating mem_alias on the same level as
mem_spec appears to be the cleaner solution anyway.
* Maintain real memory aliases.
Instead of duplicating the aliased memory with a new name, maintain a
second list of memory aliases (per device) that contains a pointer to
the memory area it is aliased to. That way, a memory name can be
clearly distinguished between the canonical one and any aliases.
* Check p->mem_alias != NULL before touching it
* Add avr_find_memalias()
This takes a memory region as input, and searches whether an
alias can be found for it.
* We need to add a list structure for the mem_alias list, always.
By that means, mem_alias won't ever be NULL, so no need to check
later.
Also, in avr_dup_part(), duplicate the alias list.
* In a memory alias, actually remember the current name.
* In avr_dup_part(), adjust pointers of aliased memories
While walking the list of memories, for each entry, see if there is an
alias pointing to it. If so, allocate a duplicated one, and fix its
aliased_mem pointer to point to the duplicated memory region instead
of the original one.
* Add avr_locate_mem_noalias()
When looking whether any memory region has already been defined for
the current part while parsing the config file, only non-aliased names
must be considered. Otherwise, a newly defined alias would kick out
the memory definition it is being aliased to.
* When defining a mem_alias, drop any existing one of that name.
* Actually use avr_find_memalias() to find aliases
* Add declaration for avr_find_memalias()
* When defining a memory, also search for an existing alias
If the newly defined name has the same as an existing alias, the alias
can be removed.
Note that we do explicitly *not* remove any memory by the same name of
a later defined alias, as this might invalidate another alias'es
pointer. If someone defines that, the alias name just won't ever be
found by avr_locate_mem().
2022-02-10 19:39:19 +00:00
|
|
|
bool is_alias;
|
2001-10-14 23:17:26 +00:00
|
|
|
|
2022-07-18 17:10:09 +00:00
|
|
|
int cfg_lineno;
|
|
|
|
char * cfg_infile;
|
2007-01-24 21:07:54 +00:00
|
|
|
|
|
|
|
extern char * yytext;
|
2001-10-14 23:17:26 +00:00
|
|
|
|
|
|
|
#define DEBUG 0
|
|
|
|
|
2012-01-17 20:56:37 +00:00
|
|
|
void cleanup_config(void)
|
|
|
|
{
|
2012-01-31 17:03:43 +00:00
|
|
|
ldestroy_cb(part_list, (void(*)(void*))avr_free_part);
|
|
|
|
ldestroy_cb(programmers, (void(*)(void*))pgm_free);
|
|
|
|
ldestroy_cb(string_list, (void(*)(void*))free_token);
|
|
|
|
ldestroy_cb(number_list, (void(*)(void*))free_token);
|
2012-01-17 20:56:37 +00:00
|
|
|
}
|
2001-10-14 23:17:26 +00:00
|
|
|
|
|
|
|
int init_config(void)
|
|
|
|
{
|
|
|
|
string_list = lcreat(NULL, 0);
|
|
|
|
number_list = lcreat(NULL, 0);
|
|
|
|
current_prog = NULL;
|
|
|
|
current_part = NULL;
|
2012-01-17 20:56:37 +00:00
|
|
|
current_mem = NULL;
|
2001-10-14 23:17:26 +00:00
|
|
|
part_list = lcreat(NULL, 0);
|
|
|
|
programmers = lcreat(NULL, 0);
|
Alias keyword (#868)
Implementation for an "alias" keyword.
By now, only applied inside memory descriptions.
* Make "mem_alias" a separate nonterminal.
The previous implementation attempt caused a syntax error in
yacc code, and separating mem_alias on the same level as
mem_spec appears to be the cleaner solution anyway.
* Maintain real memory aliases.
Instead of duplicating the aliased memory with a new name, maintain a
second list of memory aliases (per device) that contains a pointer to
the memory area it is aliased to. That way, a memory name can be
clearly distinguished between the canonical one and any aliases.
* Check p->mem_alias != NULL before touching it
* Add avr_find_memalias()
This takes a memory region as input, and searches whether an
alias can be found for it.
* We need to add a list structure for the mem_alias list, always.
By that means, mem_alias won't ever be NULL, so no need to check
later.
Also, in avr_dup_part(), duplicate the alias list.
* In a memory alias, actually remember the current name.
* In avr_dup_part(), adjust pointers of aliased memories
While walking the list of memories, for each entry, see if there is an
alias pointing to it. If so, allocate a duplicated one, and fix its
aliased_mem pointer to point to the duplicated memory region instead
of the original one.
* Add avr_locate_mem_noalias()
When looking whether any memory region has already been defined for
the current part while parsing the config file, only non-aliased names
must be considered. Otherwise, a newly defined alias would kick out
the memory definition it is being aliased to.
* When defining a mem_alias, drop any existing one of that name.
* Actually use avr_find_memalias() to find aliases
* Add declaration for avr_find_memalias()
* When defining a memory, also search for an existing alias
If the newly defined name has the same as an existing alias, the alias
can be removed.
Note that we do explicitly *not* remove any memory by the same name of
a later defined alias, as this might invalidate another alias'es
pointer. If someone defines that, the alias name just won't ever be
found by avr_locate_mem().
2022-02-10 19:39:19 +00:00
|
|
|
is_alias = false;
|
2001-10-14 23:17:26 +00:00
|
|
|
|
2022-07-18 17:10:09 +00:00
|
|
|
cfg_lineno = 1;
|
|
|
|
cfg_infile = NULL;
|
2001-10-14 23:17:26 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-08-09 20:20:44 +00:00
|
|
|
void *cfg_malloc(const char *funcname, size_t n) {
|
|
|
|
void *ret = malloc(n);
|
|
|
|
if(!ret) {
|
|
|
|
avrdude_message(MSG_INFO, "%s: out of memory in %s\n", progname, funcname);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
memset(ret, 0, n);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char *cfg_strdup(const char *funcname, const char *s) {
|
|
|
|
char *ret = strdup(s);
|
|
|
|
if(!ret) {
|
|
|
|
avrdude_message(MSG_INFO, "%s: out of memory in %s\n", progname, funcname);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
2001-10-14 23:17:26 +00:00
|
|
|
|
|
|
|
|
|
|
|
int yywrap()
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-17 20:08:28 +00:00
|
|
|
int yyerror(char * errmsg, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
char message[512];
|
|
|
|
|
|
|
|
va_start(args, errmsg);
|
|
|
|
|
|
|
|
vsnprintf(message, sizeof(message), errmsg, args);
|
2022-07-18 17:10:09 +00:00
|
|
|
avrdude_message(MSG_INFO, "%s: error at %s:%d: %s\n", progname, cfg_infile, cfg_lineno, message);
|
2014-06-17 20:08:28 +00:00
|
|
|
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int yywarning(char * errmsg, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
char message[512];
|
|
|
|
|
|
|
|
va_start(args, errmsg);
|
|
|
|
|
|
|
|
vsnprintf(message, sizeof(message), errmsg, args);
|
2022-07-18 17:10:09 +00:00
|
|
|
avrdude_message(MSG_INFO, "%s: warning at %s:%d: %s\n", progname, cfg_infile, cfg_lineno, message);
|
2014-06-17 20:08:28 +00:00
|
|
|
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-08-09 20:20:44 +00:00
|
|
|
TOKEN * new_token(int primary) {
|
|
|
|
TOKEN * tkn = (TOKEN *) cfg_malloc("new_token()", sizeof(TOKEN));
|
2001-10-14 23:17:26 +00:00
|
|
|
tkn->primary = primary;
|
|
|
|
return tkn;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void free_token(TOKEN * tkn)
|
|
|
|
{
|
|
|
|
if (tkn) {
|
2012-01-31 19:28:01 +00:00
|
|
|
switch (tkn->value.type) {
|
|
|
|
case V_STR:
|
2001-10-14 23:17:26 +00:00
|
|
|
if (tkn->value.string)
|
|
|
|
free(tkn->value.string);
|
|
|
|
tkn->value.string = NULL;
|
|
|
|
break;
|
|
|
|
}
|
2012-11-04 17:18:59 +00:00
|
|
|
|
2001-10-14 23:17:26 +00:00
|
|
|
free(tkn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void free_tokens(int n, ...)
|
|
|
|
{
|
|
|
|
TOKEN * t;
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, n);
|
|
|
|
while (n--) {
|
|
|
|
t = va_arg(ap, TOKEN *);
|
|
|
|
free_token(t);
|
|
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-08-09 20:20:44 +00:00
|
|
|
TOKEN *number(const char *text) {
|
|
|
|
struct token_t *tkn = new_token(TKN_NUMBER);
|
2001-10-14 23:17:26 +00:00
|
|
|
tkn->value.type = V_NUM;
|
2012-11-04 17:18:59 +00:00
|
|
|
tkn->value.number = atoi(text);
|
2001-10-14 23:17:26 +00:00
|
|
|
|
|
|
|
#if DEBUG
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "NUMBER(%d)\n", tkn->value.number);
|
2001-10-14 23:17:26 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
return tkn;
|
|
|
|
}
|
|
|
|
|
2022-08-09 20:20:44 +00:00
|
|
|
TOKEN *number_real(const char *text) {
|
|
|
|
struct token_t * tkn = new_token(TKN_NUMBER);
|
2012-11-04 17:18:59 +00:00
|
|
|
tkn->value.type = V_NUM_REAL;
|
|
|
|
tkn->value.number_real = atof(text);
|
|
|
|
|
|
|
|
#if DEBUG
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "NUMBER(%g)\n", tkn->value.number_real);
|
2012-11-04 17:18:59 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
return tkn;
|
|
|
|
}
|
2001-10-14 23:17:26 +00:00
|
|
|
|
2022-08-09 20:20:44 +00:00
|
|
|
TOKEN *hexnumber(const char *text) {
|
|
|
|
struct token_t *tkn = new_token(TKN_NUMBER);
|
2001-10-14 23:17:26 +00:00
|
|
|
char * e;
|
|
|
|
|
|
|
|
tkn->value.type = V_NUM;
|
|
|
|
tkn->value.number = strtoul(text, &e, 16);
|
|
|
|
if ((e == text) || (*e != 0)) {
|
2014-06-17 20:08:28 +00:00
|
|
|
yyerror("can't scan hex number \"%s\"", text);
|
2020-03-14 22:34:45 +00:00
|
|
|
free_token(tkn);
|
2014-06-17 20:08:28 +00:00
|
|
|
return NULL;
|
2001-10-14 23:17:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#if DEBUG
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "HEXNUMBER(%g)\n", tkn->value.number);
|
2001-10-14 23:17:26 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
return tkn;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-08-09 20:20:44 +00:00
|
|
|
TOKEN *string(const char *text) {
|
|
|
|
struct token_t *tkn = new_token(TKN_STRING);
|
2001-10-14 23:17:26 +00:00
|
|
|
tkn->value.type = V_STR;
|
2022-08-09 20:20:44 +00:00
|
|
|
tkn->value.string = cfg_strdup("string()", text);
|
2001-10-14 23:17:26 +00:00
|
|
|
|
|
|
|
#if DEBUG
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "STRING(%s)\n", tkn->value.string);
|
2001-10-14 23:17:26 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
return tkn;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-08-09 20:20:44 +00:00
|
|
|
TOKEN * keyword(int primary) {
|
|
|
|
return new_token(primary);
|
2001-10-14 23:17:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void print_token(TOKEN * tkn)
|
|
|
|
{
|
|
|
|
if (!tkn)
|
|
|
|
return;
|
|
|
|
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "token = %d = ", tkn->primary);
|
2012-01-31 19:28:01 +00:00
|
|
|
switch (tkn->value.type) {
|
2012-11-04 17:18:59 +00:00
|
|
|
case V_NUM:
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "NUMBER, value=%d", tkn->value.number);
|
2012-11-04 17:18:59 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case V_NUM_REAL:
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "NUMBER, value=%g", tkn->value.number_real);
|
2001-10-14 23:17:26 +00:00
|
|
|
break;
|
|
|
|
|
2012-11-04 17:18:59 +00:00
|
|
|
case V_STR:
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "STRING, value=%s", tkn->value.string);
|
2001-10-14 23:17:26 +00:00
|
|
|
break;
|
|
|
|
|
2012-11-04 17:18:59 +00:00
|
|
|
default:
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "<other>");
|
2001-10-14 23:17:26 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "\n");
|
2001-10-14 23:17:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void pyytext(void)
|
|
|
|
{
|
|
|
|
#if DEBUG
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "TOKEN: \"%s\"\n", yytext);
|
2001-10-14 23:17:26 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-01-31 17:03:43 +00:00
|
|
|
#ifdef HAVE_YYLEX_DESTROY
|
|
|
|
/* reset lexer and free any allocated memory */
|
|
|
|
extern int yylex_destroy(void);
|
|
|
|
#endif
|
|
|
|
|
2007-01-30 13:41:54 +00:00
|
|
|
int read_config(const char * file)
|
2007-01-24 22:43:46 +00:00
|
|
|
{
|
|
|
|
FILE * f;
|
2014-06-17 20:08:28 +00:00
|
|
|
int r;
|
2007-01-24 22:43:46 +00:00
|
|
|
|
2022-07-18 17:10:09 +00:00
|
|
|
if(!(cfg_infile = realpath(file, NULL))) {
|
|
|
|
avrdude_message(MSG_INFO, "%s: can't determine realpath() of config file \"%s\": %s\n",
|
|
|
|
progname, file, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
f = fopen(cfg_infile, "r");
|
2007-01-24 22:43:46 +00:00
|
|
|
if (f == NULL) {
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "%s: can't open config file \"%s\": %s\n",
|
2022-07-18 17:10:09 +00:00
|
|
|
progname, cfg_infile, strerror(errno));
|
|
|
|
free(cfg_infile);
|
|
|
|
cfg_infile = NULL;
|
2007-01-24 22:43:46 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-07-18 17:10:09 +00:00
|
|
|
cfg_lineno = 1;
|
2007-01-24 22:43:46 +00:00
|
|
|
yyin = f;
|
|
|
|
|
2014-06-17 20:08:28 +00:00
|
|
|
r = yyparse();
|
2007-01-24 22:43:46 +00:00
|
|
|
|
2012-01-17 20:56:37 +00:00
|
|
|
#ifdef HAVE_YYLEX_DESTROY
|
|
|
|
/* reset lexer and free any allocated memory */
|
|
|
|
yylex_destroy();
|
|
|
|
#endif
|
|
|
|
|
2007-01-24 22:43:46 +00:00
|
|
|
fclose(f);
|
|
|
|
|
2022-07-18 17:10:09 +00:00
|
|
|
if(cfg_infile) {
|
|
|
|
free(cfg_infile);
|
|
|
|
cfg_infile = NULL;
|
|
|
|
}
|
|
|
|
|
2014-06-17 20:08:28 +00:00
|
|
|
return r;
|
2007-01-24 22:43:46 +00:00
|
|
|
}
|
2022-07-18 17:10:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Linear-search cache for a few often-referenced strings
|
2022-08-08 15:52:09 +00:00
|
|
|
const char *cache_string(const char *file) {
|
2022-07-18 17:10:09 +00:00
|
|
|
static char **fnames;
|
|
|
|
static int n=0;
|
|
|
|
|
|
|
|
if(!file)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
// Exists in cache?
|
|
|
|
for(int i=0; i<n; i++)
|
|
|
|
if(strcmp(fnames[i], file) == 0)
|
|
|
|
return fnames[i];
|
|
|
|
|
|
|
|
// Expand cache?
|
|
|
|
if(n%128 == 0) {
|
|
|
|
if(!(fnames = realloc(fnames, (n+128)*sizeof*fnames))) {
|
|
|
|
yyerror("cache_string(): out of memory");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-09 20:20:44 +00:00
|
|
|
fnames[n] = cfg_strdup("cache_string()", file);
|
2022-07-18 17:10:09 +00:00
|
|
|
|
|
|
|
return fnames[n++];
|
|
|
|
}
|
2022-08-08 15:52:09 +00:00
|
|
|
|
|
|
|
// Captures comments during parsing
|
|
|
|
int capture_comment_char(int c) {
|
|
|
|
return c;
|
|
|
|
}
|
2022-08-09 20:20:44 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Convert the next n hex digits of s to a hex number
|
|
|
|
static unsigned int tohex(const unsigned char *s, unsigned int n) {
|
|
|
|
int ret, c;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
while(n--) {
|
|
|
|
ret *= 16;
|
|
|
|
c = *s++;
|
|
|
|
ret += c >= '0' && c <= '9'? c - '0': c >= 'a' && c <= 'f'? c - 'a' + 10: c - 'A' + 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a utf-8 character sequence from a single unicode character.
|
|
|
|
* Permissive for some invalid unicode sequences but not for those with
|
|
|
|
* high bit set). Returns numbers of characters written (0-6).
|
|
|
|
*/
|
|
|
|
static int wc_to_utf8str(unsigned int wc, unsigned char *str) {
|
|
|
|
if(!(wc & ~0x7fu)) {
|
|
|
|
*str = (char) wc;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if(!(wc & ~0x7ffu)) {
|
|
|
|
*str++ = (char) ((wc >> 6) | 0xc0);
|
|
|
|
*str++ = (char) ((wc & 0x3f) | 0x80);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
if(!(wc & ~0xffffu)) {
|
|
|
|
*str++ = (char) ((wc >> 12) | 0xe0);
|
|
|
|
*str++ = (char) (((wc >> 6) & 0x3f) | 0x80);
|
|
|
|
*str++ = (char) ((wc & 0x3f) | 0x80);
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
if(!(wc & ~0x1fffffu)) {
|
|
|
|
*str++ = (char) ((wc >> 18) | 0xf0);
|
|
|
|
*str++ = (char) (((wc >> 12) & 0x3f) | 0x80);
|
|
|
|
*str++ = (char) (((wc >> 6) & 0x3f) | 0x80);
|
|
|
|
*str++ = (char) ((wc & 0x3f) | 0x80);
|
|
|
|
return 4;
|
|
|
|
}
|
|
|
|
if(!(wc & ~0x3ffffffu)) {
|
|
|
|
*str++ = (char) ((wc >> 24) | 0xf8);
|
|
|
|
*str++ = (char) (((wc >> 18) & 0x3f) | 0x80);
|
|
|
|
*str++ = (char) (((wc >> 12) & 0x3f) | 0x80);
|
|
|
|
*str++ = (char) (((wc >> 6) & 0x3f) | 0x80);
|
|
|
|
*str++ = (char) ((wc & 0x3f) | 0x80);
|
|
|
|
return 5;
|
|
|
|
}
|
|
|
|
if(!(wc & ~0x7fffffffu)) {
|
|
|
|
*str++ = (char) ((wc >> 30) | 0xfc);
|
|
|
|
*str++ = (char) (((wc >> 24) & 0x3f) | 0x80);
|
|
|
|
*str++ = (char) (((wc >> 18) & 0x3f) | 0x80);
|
|
|
|
*str++ = (char) (((wc >> 12) & 0x3f) | 0x80);
|
|
|
|
*str++ = (char) (((wc >> 6) & 0x3f) | 0x80);
|
|
|
|
*str++ = (char) ((wc & 0x3f) | 0x80);
|
|
|
|
return 6;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unescape C-style strings, destination d must hold enough space (and can be source s)
|
|
|
|
unsigned char *cfg_unescapeu(unsigned char *d, const unsigned char *s) {
|
|
|
|
unsigned char *ret = d;
|
|
|
|
int n, k;
|
|
|
|
|
|
|
|
while(*s) {
|
|
|
|
switch (*s) {
|
|
|
|
case '\\':
|
|
|
|
switch (*++s) {
|
|
|
|
case 'n':
|
|
|
|
*d = '\n';
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
*d = '\t';
|
|
|
|
break;
|
|
|
|
case 'a':
|
|
|
|
*d = '\a';
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
*d = '\b';
|
|
|
|
break;
|
|
|
|
case 'e': // Non-standard ESC
|
|
|
|
*d = 27;
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
*d = '\f';
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
*d = '\r';
|
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
*d = '\v';
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
*d = '?';
|
|
|
|
break;
|
|
|
|
case '`':
|
|
|
|
*d = '`';
|
|
|
|
break;
|
|
|
|
case '"':
|
|
|
|
*d = '"';
|
|
|
|
break;
|
|
|
|
case '\'':
|
|
|
|
*d = '\'';
|
|
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
*d = '\\';
|
|
|
|
break;
|
|
|
|
case '0':
|
|
|
|
case '1':
|
|
|
|
case '2':
|
|
|
|
case '3':
|
|
|
|
case '4':
|
|
|
|
case '5':
|
|
|
|
case '6':
|
|
|
|
case '7': // 1-3 octal digits
|
|
|
|
n = *s - '0';
|
|
|
|
for(k = 0; k < 2 && s[1] >= '0' && s[1] <= '7'; k++) // Max 2 more octal characters
|
|
|
|
n *= 8, n += s[1] - '0', s++;
|
|
|
|
*d = n;
|
|
|
|
break;
|
|
|
|
case 'x': // Unlimited hex digits
|
|
|
|
for(k = 0; isxdigit(s[k + 1]); k++)
|
|
|
|
continue;
|
|
|
|
if(k > 0) {
|
|
|
|
*d = tohex(s + 1, k);
|
|
|
|
s += k;
|
|
|
|
} else { // No hex digits after \x? copy \x
|
|
|
|
*d++ = '\\';
|
|
|
|
*d = 'x';
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'u': // Exactly 4 hex digits and valid unicode
|
|
|
|
if(isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && isxdigit(s[4]) &&
|
|
|
|
(n = wc_to_utf8str(tohex(s+1, 4), d))) {
|
|
|
|
d += n - 1;
|
|
|
|
s += 4;
|
|
|
|
} else { // Invalid \u sequence? copy \u
|
|
|
|
*d++ = '\\';
|
|
|
|
*d = 'u';
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'U': // Exactly 6 hex digits and valid unicode
|
|
|
|
if(isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && isxdigit(s[4]) && isxdigit(s[5]) && isxdigit(s[6]) &&
|
|
|
|
(n = wc_to_utf8str(tohex(s+1, 6), d))) {
|
|
|
|
d += n - 1;
|
|
|
|
s += 6;
|
|
|
|
} else { // Invalid \U sequence? copy \U
|
|
|
|
*d++ = '\\';
|
|
|
|
*d = 'U';
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default: // Keep the escape sequence (C would warn and remove \)
|
|
|
|
*d++ = '\\';
|
|
|
|
*d = *s;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: // Not an escape sequence: just copy the character
|
|
|
|
*d = *s;
|
|
|
|
}
|
|
|
|
d++;
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
*d = *s; // Terminate
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unescape C-style strings, destination d must hold enough space (and can be source s)
|
|
|
|
char *cfg_unescape(char *d, const char *s) {
|
|
|
|
return (char *) cfg_unescapeu((unsigned char *) d, (const unsigned char *) s);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return an escaped string that looks like a C-style input string incl quotes, memory is malloc()ed
|
|
|
|
char *cfg_escape(const char *s) {
|
|
|
|
char *ret = (char *) cfg_malloc("cfg_escape()", 4*strlen(s)+2+3), *d = ret;
|
|
|
|
|
|
|
|
*d++ = '"';
|
|
|
|
for(; *s; s++) {
|
|
|
|
switch(*s) {
|
|
|
|
case '\n':
|
|
|
|
*d++ = '\\'; *d++ = 'n';
|
|
|
|
break;
|
|
|
|
case '\t':
|
|
|
|
*d++ = '\\'; *d++ = 't';
|
|
|
|
break;
|
|
|
|
case '\a':
|
|
|
|
*d++ = '\\'; *d++ = 'a';
|
|
|
|
break;
|
|
|
|
case '\b':
|
|
|
|
*d++ = '\\'; *d++ = 'b';
|
|
|
|
break;
|
|
|
|
case '\f':
|
|
|
|
*d++ = '\\'; *d++ = 'f';
|
|
|
|
break;
|
|
|
|
#if '\r' != '\n'
|
|
|
|
case '\r':
|
|
|
|
*d++ = '\\'; *d++ = 'r';
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case '\v':
|
|
|
|
*d++ = '\\'; *d++ = 'v';
|
|
|
|
break;
|
|
|
|
case '\"':
|
|
|
|
*d++ = '\\'; *d++ = '\"';
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if(*s == 0x7f || (*s >= 0 && *s < 32)) {
|
|
|
|
sprintf(d, "\\%03o", *s);
|
|
|
|
d += strlen(d);
|
|
|
|
} else
|
|
|
|
*d++ = *s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*d++ = '"';
|
|
|
|
*d = 0;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|