⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 readconf.c

📁 与Casio BOSS进行串口通讯的程序
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Generic configuration file handling. * By Andy McFadden.  This software is in the public domain. * Code formatted with 4-space tabs (use ":set ts=4" in vi). * * Page down a few screens for instructions.  Basically, set up a config * description table, then either call: * *	read_config_file(argv[0], config_tab, config_count, filename); * or *	read_config(argv[0], config_tab, config_count, fp); * * To free up any allocated space, call: * *	dispose_config(argv[0], config_tab, config_count); * * Compile with -DSYSV on systems that use <string.h> instead of <strings.h>. * If you're missing strcasecmp(), add -DNEED_STRCASECMP. * * * readconf.c revision history: * * v1.1    ATM  15-Feb-96 *	Reformatted source, converting to ANSI C.  Added "dispose" functions. *  Made config table an argument.  Added cf_forgiving and cf_check_alloc. * v1.0    ATM  22-Jul-94 *	Seems to work. */#include <stdio.h>#ifdef SYSV# include <stdlib.h># include <string.h>#else# include <string.h># include <strings.h>#endif#include <math.h>	/* for strtod() */#include <malloc.h>#include <errno.h>#include "readconf.h"#ifndef TRUE# define TRUE	1# define FALSE	0#endif /*TRUE*//* * Tweak these as needed (change values, make them public, etc). */static int cf_debug = FALSE;		/* print debugging info */static int cf_verbose = FALSE;		/* print progress info */static int cf_forgiving = FALSE;	/* allow unknown tags? */static int cf_check_alloc = TRUE;	/* only malloc onto NULL pointer? *//*#define NEED_STRCASECMP*//* * The basic configuration line looks like: * *	tag_name : value * * Leading and trailing whitespace is removed.  Whitespace before and after * the colon is removed.  Blank lines and lines starting with '#' are * ignored (this happens after the leading w/s is removed, so any line where * a '#' is the first non-whitespace char is ignored). * * Double quotes and backslashes may be used to escape whitespace and break * long lines.  The maximum length of a complete line (i.e. including parts * broken across newlines with '\') is CF_MAX_LINE_LEN.  The maximum length * of the tag is CF_MAX_TAG_LEN.  Note that the tag portion may NOT include * quotes or backslashes (well, they won't be interpreted specially). * * Some of the ANSI C character escape sequences (e.g. \n, \t) are supported. * * * The following classes of configuration statements are supported: * * CF_BOOLEAN		boolean_value: True * *	The corresponding variable is set to 0 if the rhs is "off" or "false", *	or 1 if the rhs is "on" or "true".  The values are case-insensitive. * * CF_INT		int_value: 12345 * *	The corresponding variable is set to the value of the rhs as it's *	evaluated by strtol(str, NULL, 0).  So, "0x1234" and "01234" are *	treated as hex and octal, respectively. * * CF_DOUBLE		double_value: 12345.0 * *	The corresponding variable is set to the value of the rhs as it's *	evaluated by strtod(str, NULL).  If the system is lame, atof(str) *	may be used instead. * * CF_STRING		string_value: The quick brown fox. * *	The value, stripped of leading and trailing whitespace, is copied *	with substitutions into the storage space indicated (see below).  The *	length of the space should be given in the "size" field.  If "size" *	is zero, and "reference" points to a NULL char*, then space will be *	allocated with malloc(3).  "size"==0, *"ref"!=NULL generates an error. * * CF_MULTI_STRING	multi_value: The quick brown fox. * *	The difference between CF_STRING and CF_MULTI_STRING is that the *	latter parses the string into separate components, and then passes *	them as individual arguments to a subroutine (see below).  The *	"delim" field is a pointer to a string with the field delimiters, *	usually whitespace ("\t ") or a colon (":").  The "size" field is *	not used here. * *	The function called by a CF_MULTI_STRING line takes two arguments, *	argc and argv, which are identical in form to the arguments supplied *	to main().  The strings are part of a static buffer, and the *	argument vector pointers are dynamically allocated space which will *	be freed after the procedure call, so the arguments must be copied *	if they are to be kept. * * CF_MULTI_LINE	multi_line_start *			line1 *			line2 *			line3 *			multi_line_end * *	When you need a whole collection of free-form lines, use *	CF_MULTI_LINE.  Each line is passed verbatim (NO w/s stripping, NO *	'\' evaluation, etc) to the specified routine.  An end tag should be *	pointed to be the "delim" field; when the parser sees it at the start *	of a line it goes on to the next tag. * *	The function called by CF_MULTI_LINE takes one argument, the *	un-parsed rhs.  This is also in a static buffer, and must be copied. *//* * The structure defining the config file has five fields: * * kind *	What kind of field this is, e.g. CF_STRING. * * tag *	What tag will be used to identify this item, i.e. what word comes *	before the ':'.  Matching of tags is case-insensitive. * * reference *	Variable to change (for BOOLEAN, INT, DOUBLE, and STRING) or function *	to call (for MULTI_STRING and MULTI_LINE).  Note that string is *	either a (char *) for size != 0 or a (char **) for size == 0. * * size *	Used by STRING to specify the size of a buffer.  If set to zero, *	*and* the variable to change is NULL, space will be allocated with *	malloc().  This value should be equal to the TOTAL size of the *	buffer, i.e. "size" of 16 means 15 characters plus the '\0'. * * delim *	Used by MULTI_STRING to define where the word breaks are, and used *	by MULTI_LINE to define where the list ends. * * flags *	Currently not used.  See next section for possible uses. *//* * Miscellaneous ideas: * * - allow a combined MULTI_STRING and MULTI_LINE for convenience. * - allow /bin/sh-style variable substitution, including environment *   variables. * - support all of the ANSI C escape sequences, especially \x07 and \007. * * - use the "flags" field to define items as "configure-once-only", so *   that a second instance of that tag generates an error. * - use the "flags" field to define MULTI_STRING delimiters as "single" *   or "many".  Right now "ack   foo  bar" results in 'ack', 'foo', and *   'bar', which is desirable.  However, parsing an /etc/passwd entry like *   "daemon:*:1:1::/:" will result in a missing field, because the "::" is *   meant to indicate an empty string for the fifth entry, but the current *   implementation will combine the delimiters together. * - use the "flags" field to define whether MULTI_LINE fields must have a *   closing delimiter, or if it's acceptable to just let them run until EOF. * - use the "flags" field to decide if we really want to strip off trailing *   whitespace.  Right now, it will cut off trailing spaces even if they *   are inside double quotes.  (Better yet, fix the code so that it deals *   with trailing w/s correctly.) * * - could move the configuration struct, along with all of these comments, *   into a separate header file for ease of use.  But that creates yet *   another file to deal with, which probably isn't desirable. */#ifdef NEED_STRCASECMP		/* not uncommon for libc to be missing this *//* * Like strcmp(), but case independent. */intstrcasecmp(register const char *str1, register const char *str2){	while (*str1 && *str2 && toupper(*str1) == toupper(*str2))		str1++, str2++;	return (*str1 - *str2);}/* * Like strncmp(), but case independent. */intstrncasecmp(register const char *str1, register const char *str2, size_t n){	while (n && *str1 && *str2 && toupper(*str1) == toupper(*str2))		str1++, str2++, n--;	if (n)		return (*str1 - *str2);	else		return (0);	/* no mismatch in first n chars */}#endif /*NEED_STRCASECMP*/typedef int (*CF_INTFUNCPTR)();	/* pointer to function returning int *//* * Local prototypes. */static int add_ms(char *str);static int convert_ms(void);static const CONFIG * lookup_tag(const CONFIG *config_tab, int config_count, char *tag_name);static int read_line(FILE *fp);static int get_full_line(const CONFIG *config_tab, int config_size, FILE *fp);static int eval_boolean(const CONFIG *configp);static int eval_int(const CONFIG *configp);static int eval_double(const CONFIG *configp);static int eval_string(const CONFIG *configp);static int eval_multi_string(const CONFIG *configp);static int eval_multi_line(const CONFIG *configp, FILE *fp);#define CF_MAX_LINE_LEN	1024#define CF_MAX_TAG_LEN 32#define CF_MAX_MS_TOKEN 128			/* just a sanity check */const char *default_argv0 = "<caller unknown>";/* private definition of isspace(); only tab and space */#define cf_isspace(x)	((x) == ' ' || (x) == '\t')#define cf_iseol(x)		((x) == '\n' || (x) == '\0')/* local buffers */static char line_buf[CF_MAX_LINE_LEN+1];		/* used with fgets() */static char full_line_buf[CF_MAX_LINE_LEN+1];	/* post-processing version */static char tag_buf[CF_MAX_TAG_LEN+1];			/* the tag, in lower case */static int line;				/* current line # in input */static char *cf_err_str = NULL;		/* error message *//* MULTI_STRING data */typedef struct list_t {	char *data;	struct list_t *next;} LIST;static int ms_argc;static char **ms_argv;static LIST *head = NULL, *tail;/* * Initialize MULTI_STRING stuff. */static intinit_ms(void){	if (head != NULL) {		fprintf(stderr, "init_ms(): called twice without convert_ms()\n");		return (-1);	}	ms_argc = 0;	ms_argv = (char **)NULL;	return (0);}/* * Add a new token to the MULTI_STRING list. */static intadd_ms(char *str){	LIST *lp;	if (ms_argc >= CF_MAX_MS_TOKEN) {		cf_err_str = "too many tokens on one line";		return (-1);	}	ms_argc++;	lp = (LIST *)malloc(sizeof(LIST));	if (lp == NULL) {		fprintf(stderr, "add_ms(): malloc() failed\n");		return (-1);	}	lp->data = str;	lp->next = NULL;	if (head == NULL) {		head = tail = lp;	} else {		tail->next = lp;		tail = lp;	}	return (0);}/* * Convert the list of tokens into an argument vector.  Frees the list nodes. */static intconvert_ms(void){	LIST *lp, *tmplp;	int i;	/* allocate space for the string vector; freed in eval_multi_string */	ms_argv = (char **)malloc(sizeof(char *) * ms_argc);	/* copy the pointers from the list to the vector, freeing the list nodes */	i = 0;	lp = head;	while (lp != NULL) {		ms_argv[i++] = lp->data;		tmplp = lp->next;		free(lp);		lp = tmplp;	}	head = tail = NULL;	/* if the last token got eaten as trailing whitespace, nuke it */	if (*(ms_argv[ms_argc-1]) == '\0')		ms_argc--;	if (cf_debug) {		int j;		for (j = 0; j < ms_argc; j++)			printf("  TOKEN: '%s'\n", ms_argv[j]);	}	return (0);}/* * Look up a tag in the config[] array. * * Returns a pointer to the CONFIG struct if found, NULL if not. */static const CONFIG *lookup_tag(const CONFIG *config_tab, int config_count, char *tag_name){	const CONFIG *conp;	int i;	for (conp = config_tab, i = 0; i < config_count; i++, conp++) {		if (strcasecmp(conp->tag, tag_name) == 0) {			if (cf_debug) printf("lookup: '%s' found\n", tag_name);			return (conp);		}	}	if (cf_debug) printf("lookup: '%s' NOT FOUND\n", tag_name);	return (NULL);}/* * Read a line of input. * * Returns 0 on success, 1 on EOF reached, -1 on error.  Results are placed * into line_buf. */static intread_line(FILE *fp){	if ((fgets(line_buf, CF_MAX_LINE_LEN, fp)) == NULL) {		if (ferror(fp)) return (-1);		if (feof(fp)) return (1);		cf_err_str = "fgets() failed, but I don't know why";		return (-1);	}	line++;	if (cf_debug) printf("LINE(%d): '%s'\n", line, line_buf);	return (0);}/* * Read a full line of input from the configuration file, doing all of the * appropriate processing. * * Uses a DFA to process the input.  I cheat a bit and allow transitions * without eating a character. * * Returns 0 on success, 1 on EOF reached, and -1 on error.  The results * of the routine are placed into tag_buf and full_line_buf. */static intget_full_line(const CONFIG *config_tab, int config_size, FILE *fp){	enum {  R_CHAOS, R_START, R_TAG, R_PRECOLON, R_POSTCOLON,		R_RHS, R_RHS_MS, R_DQUOTE, R_BACK, R_DQ_BACK, R_ERROR, R_DONE	} state;	const CONFIG *configp = NULL;	char *inp, *outp, *cp;	int val, is_ms = 0;	full_line_buf[0] = '\0';	inp = outp = NULL;	init_ms();	state = R_CHAOS;	/*	 * Welcome to the machine.	 */	while (state != R_DONE) {		switch (state) {		case R_CHAOS:				/* in the beginning, there was... */			if ((val = read_line(fp)))				return (val);			/* bail on error or EOF */			inp = line_buf;			state = R_START;			break;		case R_START:				/* skip leading whitespace */			if (cf_isspace(*inp)) {		/* WS --> loop */				inp++;				break;			}			if (*inp == '#' || cf_iseol(*inp)) {				state = R_CHAOS;		/* '#' or blank --> R_CHAOS */				break;			}			state = R_TAG;				/* else --> R_TAG */			outp = tag_buf;			break;		case R_TAG:					/* copy tag into tag_buf */			/* terminate outp in case we exit */			*outp = '\0';			if (cf_isspace(*inp)) {		/* WS --> R_PRECOLON */				inp++;				state = R_PRECOLON;				break;			}			if (*inp == ':') {			/* ':' --> R_POSTCOLON */				inp++;				state = R_POSTCOLON;				break;			}			/* this happens for MULTI_LINE stuff */			if (cf_iseol(*inp)) {		/* EOL --> R_DONE */				state = R_DONE;				break;			}			*outp++ = *inp++;			/* else --> loop */			break;		case R_PRECOLON:			/* tag done, waiting for colon */			if (*inp == ':') {			/* ':' --> R_POSTCOLON */				inp++;				state = R_POSTCOLON;				break;			}			/* again, MULTI_LINE stuff here */			if (cf_iseol(*inp)) {		/* EOL --> R_DONE */				state = R_DONE;				break;			}			if (cf_isspace(*inp)) {		/* WS --> loop */				inp++;				break;			}			cf_err_str = "invalid lhs - more than one word before colon";			state = R_ERROR;			break;		case R_POSTCOLON:			/* got colon, eat whitespace */			if (cf_isspace(*inp)) {		/* WS --> loop */				inp++;				break;			}			if (cf_iseol(*inp)) {		/* EOL --> R_ERROR */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -