configfile.c

来自「minix操作系统最新版本(3.1.1)的源代码」· C语言 代码 · 共 575 行

C
575
字号
/*	config_read(), _delete(), _length() - Generic config file routines. *							Author: Kees J. Bot *								5 Jun 1999 */#define nil ((void*)0)#if __minix_vmd#include <minix/stubs.h>#else#define fstat _fstat#define stat _stat#endif#include <sys/types.h>#include <stdio.h>#include <stddef.h>#include <stdlib.h>#include <errno.h>#include <string.h>#include <time.h>#include <sys/stat.h>#if __minix_vmd#include <minix/asciictype.h>#else#include <ctype.h>#endif#define _c /* not const */#include <configfile.h>typedef struct configfile {	/* List of (included) configuration files. */	struct configfile *next;	/* A list indeed. */	time_t		ctime;		/* Last changed time, -1 if no file. */	char		name[1];	/* File name. */} configfile_t;/* Size of a configfile_t given a file name of length 'len'. */#define configfilesize(len)	(offsetof(configfile_t, name) + 1 + (len))typedef struct firstconfig {	/* First file and first word share a slot. */	configfile_t	*filelist;	char		new;		/* Set when created. */	config_t	config1;} firstconfig_t;/* Size of a config_t given a word of lenght 'len'.  Same for firstconfig_t. */#define config0size()		(offsetof(config_t, word))#define configsize(len)		(config0size() + 1 + (len))#define firstconfigsize(len)	\			(offsetof(firstconfig_t, config1) + configsize(len))/* Translate address of first config word to enclosing firstconfig_t and vv. */#define cfg2fcfg(p)	\    ((firstconfig_t *) ((char *) (p) - offsetof(firstconfig_t, config1)))#define fcfg2cfg(p)	(&(p)->config1)/* Variables used while building data. */static configfile_t *c_files;		/* List of (included) config files. */static int c_flags;			/* Flags argument of config_read(). */static FILE *c_fp;			/* Current open file. */static char *c_file;			/* Current open file name. */static unsigned c_line;			/* Current line number. */static int c;				/* Next character. */static void *allocate(void *mem, size_t size)/* Like realloc(), but checked. */{    if ((mem= realloc(mem, size)) == nil) {	fprintf(stderr, "\"%s\", line %u: Out of memory\n", c_file, c_line);	exit(1);    }    return mem;}#define deallocate(mem)	free(mem)static void delete_filelist(configfile_t *cfgf)/* Delete configuration file list. */{    void *junk;    while (cfgf != nil) {	junk= cfgf;	cfgf= cfgf->next;	deallocate(junk);    }}static void delete_config(config_t *cfg)/* Delete configuration file data. */{    config_t *next, *list, *junk;    next= cfg;    list= nil;    for (;;) {	if (next != nil) {	    /* Push the 'next' chain in reverse on the 'list' chain, putting	     * a leaf cell (next == nil) on top of 'list'.	     */	    junk= next;	    next= next->next;	    junk->next= list;	    list= junk;	} else	if (list != nil) {	    /* Delete the leaf cell.  If it has a sublist then that becomes	     * the 'next' chain.	     */	    junk= list;	    next= list->list;	    list= list->next;	    deallocate(junk);	} else {	    /* Both chains are gone. */	    break;	}    }}void config_delete(config_t *cfg1)/* Delete configuration file data, being careful with the odd first one. */{    firstconfig_t *fcfg= cfg2fcfg(cfg1);    delete_filelist(fcfg->filelist);    delete_config(fcfg->config1.next);    delete_config(fcfg->config1.list);    deallocate(fcfg);}static void nextc(void)/* Read the next character of the current file into 'c'. */{    if (c == '\n') c_line++;    c= getc(c_fp);    if (c == EOF && ferror(c_fp)) {	fprintf(stderr, "\"%s\", line %u: %s\n",	    c_file, c_line, strerror(errno));	exit(1);    }}static void skipwhite(void)/* Skip whitespace and comments. */{    while (isspace(c)) {	nextc();	if (c == '#') {	    do nextc(); while (c != EOF && c != '\n');	}    }}static void parse_err(void)/* Tell user that you can't parse past the current character. */{    char sc[2];    sc[0]= c;    sc[1]= 0;    fprintf(stderr, "\"%s\", line %u: parse error at '%s'\n",	c_file, c_line, c == EOF ? "EOF" : sc);    exit(1);}static config_t *read_word(void)/* Read a word or string. */{    config_t *w;    size_t i, len;    int q;    static char SPECIAL[] = "!#$%&*+-./:<=>?[\\]^_|~";    i= 0;    len= 32;    w= allocate(nil, configsize(32));    w->next= nil;    w->list= nil;    w->file= c_file;    w->line= c_line;    w->flags= 0;    /* Is it a quoted string? */    if (c == '\'' || c == '"') {	q= c;	/* yes */	nextc();    } else {	q= -1;	/* no */    }    for (;;) {	if (i == len) {	    len+= 32;	    w= allocate(w, configsize(len));	}	if (q == -1) {	    /* A word consists of letters, numbers and a few special chars. */	    if (!isalnum(c) && c < 0x80 && strchr(SPECIAL, c) == nil) break;	} else {	    /* Strings are made up of anything except newlines. */	    if (c == EOF || c == '\n') {		fprintf(stderr,		    "\"%s\", line %u: string at line %u not closed\n",		    c_file, c_line, w->line);		exit(1);		break;	    }	    if (c == q) {	/* Closing quote? */		nextc();		break;	    }	}	if (c != '\\') {	/* Simply add non-escapes. */	    w->word[i++]= c;	    nextc();	} else {		/* Interpret an escape. */	    nextc();	    if (isspace(c)) {		skipwhite();		continue;	    }	    if (c_flags & CFG_ESCAPED) {		w->word[i++]= '\\';	/* Keep the \ for the caller. */		if (i == len) {		    len+= 32;		    w= allocate(w, configsize(len));		}		w->flags |= CFG_ESCAPED;	    }	    if (isdigit(c)) {		/* Octal escape */		int n= 3;		int d= 0;		do {		    d= d * 010 + (c - '0');		    nextc();		} while (--n > 0 && isdigit(c));		w->word[i++]= d;	    } else	    if (c == 'x' || c == 'X') {	/* Hex escape */		int n= 2;		int d= 0;		nextc();		if (!isxdigit(c)) {		    fprintf(stderr, "\"%s\", line %u: bad hex escape\n",			c_file, c_line);		    exit(1);		}		do {		    d= d * 0x10 + (islower(c) ? (c - 'a' + 0xa) :				    isupper(c) ? (c - 'A' + 0xA) :				    (c - '0'));		    nextc();		} while (--n > 0 && isxdigit(c));		w->word[i++]= d;	    } else {		switch (c) {		case 'a':	c= '\a';	break;		case 'b':	c= '\b';	break;		case 'e':	c= '\033';	break;		case 'f':	c= '\f';	break;		case 'n':	c= '\n';	break;		case 'r':	c= '\r';	break;		case 's':	c= ' ';		break;		case 't':	c= '\t';	break;		case 'v':	c= '\v';	break;		default:	/* Anything else is kept as-is. */;		}		w->word[i++]= c;		nextc();	    }	}    }    w->word[i]= 0;    if (q != -1) {	w->flags |= CFG_STRING;    } else {	int f;	char *end;	static char base[]= { 0, 010, 10, 0x10 };	if (i == 0) parse_err();	/* Can the word be used as a number? */	for (f= 0; f < 4; f++) {	    (void) strtol(w->word, &end, base[f]);	    if (*end == 0) w->flags |= 1 << (f + 0);	    (void) strtoul(w->word, &end, base[f]);	    if (*end == 0) w->flags |= 1 << (f + 4);	}    }    return allocate(w, configsize(i));}static config_t *read_file(const char *file);static config_t *read_list(void);static config_t *read_line(void)/* Read and return one line of the config file. */{    config_t *cline, **pcline, *clist;    cline= nil;    pcline= &cline;    for (;;) {	skipwhite();	if (c == EOF || c == '}') {if(0)	    if (cline != nil) parse_err();	    break;	} else	if (c == ';') {	    nextc();	    if (cline != nil) break;	} else	if (cline != nil && c == '{') {	    /* A sublist. */	    nextc();	    clist= allocate(nil, config0size());	    clist->next= nil;	    clist->file= c_file;	    clist->line= c_line;	    clist->list= read_list();	    clist->flags= CFG_SUBLIST;	    *pcline= clist;	    pcline= &clist->next;	    if (c != '}') parse_err();	    nextc();	} else {	    *pcline= read_word();	    pcline= &(*pcline)->next;	}    }    return cline;}static config_t *read_list(void)/* Read and return a list of config file commands. */{    config_t *clist, **pclist, *cline;    clist= nil;    pclist= &clist;    while ((cline= read_line()) != nil) {	if (strcmp(cline->word, "include") == 0) {	    config_t *file= cline->next;	    if (file == nil || file->next != nil || !config_isatom(file)) {		fprintf(stderr,		    "\"%s\", line %u: 'include' command requires an argument\n",		    c_file, cline->line);		exit(1);	    }	    if (file->flags & CFG_ESCAPED) {		char *p, *q;		p= q= file->word;		for (;;) {		    if ((*q = *p) == '\\') *q = *++p;		    if (*q == 0) break;		    p++;		    q++;		}	    }	    file= read_file(file->word);	    delete_config(cline);	    *pclist= file;	    while (*pclist != nil) pclist= &(*pclist)->next;	} else {	    config_t *cfg= allocate(nil, config0size());	    cfg->next= nil;	    cfg->list= cline;	    cfg->file= cline->file;	    cfg->line= cline->line;	    cfg->flags= CFG_SUBLIST;	    *pclist= cfg;	    pclist= &cfg->next;	}    }    return clist;}static config_t *read_file(const char *file)/* Read and return a configuration file. */{    configfile_t *cfgf;    config_t *cfg;    struct stat st;    FILE *old_fp;	/* old_* variables store current file context. */    char *old_file;    unsigned old_line;    int old_c;    size_t n;    char *slash;    old_fp= c_fp;    old_file= c_file;    old_line= c_line;    old_c= c;    n= 0;    if (file[0] != '/' && old_file != nil			&& (slash= strrchr(old_file, '/')) != nil) {	n= slash - old_file + 1;    }    cfgf= allocate(nil, configfilesize(n + strlen(file)));    memcpy(cfgf->name, old_file, n);    strcpy(cfgf->name + n, file);    cfgf->next= c_files;    c_files= cfgf;    c_file= cfgf->name;    c_line= 0;    if ((c_fp= fopen(file, "r")) == nil || fstat(fileno(c_fp), &st) < 0) {	if (errno != ENOENT) {	    fprintf(stderr, "\"%s\", line 1: %s\n", file, strerror(errno));	    exit(1);	}	cfgf->ctime= -1;	c= EOF;    } else {	cfgf->ctime= st.st_ctime;	c= '\n';    }    cfg= read_list();    if (c != EOF) parse_err();    if (c_fp != nil) fclose(c_fp);    c_fp= old_fp;    c_file= old_file;    c_line= old_line;    c= old_c;    return cfg;}config_t *config_read(const char *file, int flags, config_t *cfg)/* Read and parse a configuration file. */{    if (cfg != nil) {	/* First check if any of the involved files has changed. */	firstconfig_t *fcfg;	configfile_t *cfgf;	struct stat st;	fcfg= cfg2fcfg(cfg);	for (cfgf= fcfg->filelist; cfgf != nil; cfgf= cfgf->next) {	    if (stat(cfgf->name, &st) < 0) {		if (errno != ENOENT) break;		st.st_ctime= -1;	    }	    if (st.st_ctime != cfgf->ctime) break;	}	if (cfgf == nil) return cfg;	/* Everything as it was. */	config_delete(cfg);		/* Otherwise delete and reread. */    }    errno= 0;    c_files= nil;    c_flags= flags;    cfg= read_file(file);    if (cfg != nil) {	/* Change first word to have a hidden pointer to a file list. */	size_t len= strlen(cfg->word);	firstconfig_t *fcfg;	fcfg= allocate(cfg, firstconfigsize(len));	memmove(&fcfg->config1, fcfg, configsize(len));	fcfg->filelist= c_files;	fcfg->new= 1;	return fcfg2cfg(fcfg);    }    /* Couldn't read (errno != 0) of nothing read (errno == 0). */    delete_filelist(c_files);    delete_config(cfg);    return nil;}int config_renewed(config_t *cfg){    int new;    if (cfg == nil) {	new= 1;    } else {	new= cfg2fcfg(cfg)->new;	cfg2fcfg(cfg)->new= 0;    }    return new;}size_t config_length(config_t *cfg)/* Count the number of items on a list. */{    size_t n= 0;    while (cfg != nil) {	n++;	cfg= cfg->next;    }    return n;}#if TEST#include <unistd.h>static void print_list(int indent, config_t *cfg);static void print_words(int indent, config_t *cfg){    while (cfg != nil) {	if (config_isatom(cfg)) {	    if (config_isstring(cfg)) fputc('"', stdout);	    printf("%s", cfg->word);	    if (config_isstring(cfg)) fputc('"', stdout);	} else {	    printf("{\n");	    print_list(indent+4, cfg->list);	    printf("%*s}", indent, "");	}	cfg= cfg->next;	if (cfg != nil) fputc(' ', stdout);    }    printf(";\n");}static void print_list(int indent, config_t *cfg){    while (cfg != nil) {	if (!config_issub(cfg)) {	    fprintf(stderr, "Cell at \"%s\", line %u is not a sublist\n");	    break;	}	printf("%*s", indent, "");	print_words(indent, cfg->list);	cfg= cfg->next;    }}static void print_config(config_t *cfg){    if (!config_renewed(cfg)) {	printf("# Config didn't change\n");    } else {	print_list(0, cfg);    }}int main(int argc, char **argv){    config_t *cfg;    int c;    if (argc != 2) {	fprintf(stderr, "One config file name please\n");	exit(1);    }    cfg= nil;    do {	cfg= config_read(argv[1], CFG_ESCAPED, cfg);	print_config(cfg);	if (!isatty(0)) break;	while ((c= getchar()) != EOF && c != '\n') {}    } while (c != EOF);    return 0;}#endif /* TEST */

⌨️ 快捷键说明

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