diff options
Diffstat (limited to 'config.c')
-rw-r--r-- | config.c | 806 |
1 files changed, 806 insertions, 0 deletions
diff --git a/config.c b/config.c new file mode 100644 index 0000000..f086883 --- /dev/null +++ b/config.c @@ -0,0 +1,806 @@ +/* Copyright 1996-2005,2007-2009,2011 Alain Knaff. + * This file is part of mtools. + * + * Mtools 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. + * + * Mtools 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 Mtools. If not, see <http://www.gnu.org/licenses/>. + * + */ +#include "sysincludes.h" +#include "mtools.h" +#include "codepage.h" +#include "mtoolsPaths.h" + +/* global variables */ +/* they are not really harmful here, because there is only one configuration + * file per invocations */ + +#define MAX_LINE_LEN 256 + +/* scanner */ +static char buffer[MAX_LINE_LEN+1]; /* buffer for the whole line */ +static char *pos; /* position in line */ +static char *token; /* last scanned token */ +static size_t token_length; /* length of the token */ +static FILE *fp; /* file pointer for configuration file */ +static int linenumber; /* current line number. Only used for printing + * error messages */ +static int lastTokenLinenumber; /* line numnber for last token */ +static const char *filename=NULL; /* current file name. Used for printing + * error messages, and for storing in + * the device definition (mtoolstest) */ +static int file_nr=0; + + +static int flag_mask; /* mask of currently set flags */ + +/* devices */ +static int cur_devs; /* current number of defined devices */ +static int cur_dev; /* device being filled in. If negative, none */ +static int trusted=0; /* is the currently parsed device entry trusted? */ +static int nr_dev; /* number of devices that the current table can hold */ +struct device *devices; /* the device table */ +static int token_nr; /* number of tokens in line */ + +static char default_drive='\0'; /* default drive */ + +/* "environment" variables */ +unsigned int mtools_skip_check=0; +unsigned int mtools_fat_compatibility=0; +unsigned int mtools_ignore_short_case=0; +unsigned int mtools_rate_0=0; +unsigned int mtools_rate_any=0; +unsigned int mtools_no_vfat=0; +unsigned int mtools_numeric_tail=1; +unsigned int mtools_dotted_dir=0; +unsigned int mtools_twenty_four_hour_clock=1; +unsigned int mtools_default_codepage=850; +const char *mtools_date_string="yyyy-mm-dd"; +char *country_string=0; + +typedef struct switches_l { + const char *name; + caddr_t address; + enum { + T_INT, + T_STRING, + T_UINT + } type; +} switches_t; + +static switches_t global_switches[] = { + { "MTOOLS_LOWER_CASE", (caddr_t) & mtools_ignore_short_case, T_UINT }, + { "MTOOLS_FAT_COMPATIBILITY", (caddr_t) & mtools_fat_compatibility, T_UINT }, + { "MTOOLS_SKIP_CHECK", (caddr_t) & mtools_skip_check, T_UINT }, + { "MTOOLS_NO_VFAT", (caddr_t) & mtools_no_vfat, T_UINT }, + { "MTOOLS_RATE_0", (caddr_t) &mtools_rate_0, T_UINT }, + { "MTOOLS_RATE_ANY", (caddr_t) &mtools_rate_any, T_UINT }, + { "MTOOLS_NAME_NUMERIC_TAIL", (caddr_t) &mtools_numeric_tail, T_UINT }, + { "MTOOLS_DOTTED_DIR", (caddr_t) &mtools_dotted_dir, T_UINT }, + { "MTOOLS_TWENTY_FOUR_HOUR_CLOCK", + (caddr_t) &mtools_twenty_four_hour_clock, T_UINT }, + { "MTOOLS_DATE_STRING", + (caddr_t) &mtools_date_string, T_STRING }, + { "DEFAULT_CODEPAGE", (caddr_t) &mtools_default_codepage, T_UINT } +}; + +typedef struct { + const char *name; + int flag; +} flags_t; + +static flags_t openflags[] = { +#ifdef O_SYNC + { "sync", O_SYNC }, +#endif +#ifdef O_NDELAY + { "nodelay", O_NDELAY }, +#endif +#ifdef O_EXCL + { "exclusive", O_EXCL }, +#endif + { "none", 0 } /* hack for those compilers that choke on commas + * after the last element of an array */ +}; + +static flags_t misc_flags[] = { +#ifdef USE_XDF + { "use_xdf", USE_XDF_FLAG }, +#endif + { "scsi", SCSI_FLAG }, + { "nolock", NOLOCK_FLAG }, + { "mformat_only", MFORMAT_ONLY_FLAG }, + { "filter", FILTER_FLAG }, + { "privileged", PRIV_FLAG }, + { "vold", VOLD_FLAG }, + { "remote", FLOPPYD_FLAG }, + { "swap", SWAP_FLAG }, +}; + +static struct { + const char *name; + signed char fat_bits; + int tracks; + unsigned short heads; + unsigned short sectors; +} default_formats[] = { + { "hd514", 12, 80, 2, 15 }, + { "high-density-5-1/4", 12, 80, 2, 15 }, + { "1.2m", 12, 80, 2, 15 }, + + { "hd312", 12, 80, 2, 18 }, + { "high-density-3-1/2", 12, 80, 2, 18 }, + { "1.44m", 12, 80, 2, 18 }, + + { "dd312", 12, 80, 2, 9 }, + { "double-density-3-1/2", 12, 80, 2, 9 }, + { "720k", 12, 80, 2, 9 }, + + { "dd514", 12, 40, 2, 9 }, + { "double-density-5-1/4", 12, 40, 2, 9 }, + { "360k", 12, 40, 2, 9 }, + + { "320k", 12, 40, 2, 8 }, + { "180k", 12, 40, 1, 9 }, + { "160k", 12, 40, 1, 8 } +}; + +#define OFFS(x) ((caddr_t)&((struct device *)0)->x) + +static switches_t dswitches[]= { + { "FILE", OFFS(name), T_STRING }, + { "OFFSET", OFFS(offset), T_UINT }, + { "PARTITION", OFFS(partition), T_UINT }, + { "FAT", OFFS(fat_bits), T_INT }, + { "FAT_BITS", OFFS(fat_bits), T_UINT }, + { "MODE", OFFS(mode), T_UINT }, + { "TRACKS", OFFS(tracks), T_UINT }, + { "CYLINDERS", OFFS(tracks), T_UINT }, + { "HEADS", OFFS(heads), T_UINT }, + { "SECTORS", OFFS(sectors), T_UINT }, + { "HIDDEN", OFFS(hidden), T_UINT }, + { "PRECMD", OFFS(precmd), T_STRING }, + { "BLOCKSIZE", OFFS(blocksize), T_UINT }, + { "CODEPAGE", OFFS(codepage), T_UINT } +}; + +static void maintain_default_drive(char drive) +{ + if(default_drive == ':') + return; /* we have an image */ + if(default_drive == '\0' || + default_drive > drive) + default_drive = drive; +} + +char get_default_drive(void) +{ + if(default_drive != '\0') + return default_drive; + else + return 'A'; +} + +static void syntax(const char *msg, int thisLine) +{ + char drive='\0'; + if(thisLine) + lastTokenLinenumber = linenumber; + if(cur_dev >= 0) + drive = devices[cur_dev].drive; + fprintf(stderr,"Syntax error at line %d ", lastTokenLinenumber); + if(drive) fprintf(stderr, "for drive %c: ", drive); + if(token) fprintf(stderr, "column %ld ", (long)(token - buffer)); + fprintf(stderr, "in file %s: %s\n", filename, msg); + exit(1); +} + +static void get_env_conf(void) +{ + char *s; + unsigned int i; + + for(i=0; i< sizeof(global_switches) / sizeof(*global_switches); i++) { + s = getenv(global_switches[i].name); + if(s) { + if(global_switches[i].type == T_INT) + * ((int *)global_switches[i].address) = (int) strtol(s,0,0); + if(global_switches[i].type == T_UINT) + * ((int *)global_switches[i].address) = (unsigned int) strtoul(s,0,0); + else if (global_switches[i].type == T_STRING) + * ((char **)global_switches[i].address) = s; + } + } +} + +static int mtools_getline(void) +{ + if(!fp || !fgets(buffer, MAX_LINE_LEN, fp)) + return -1; + linenumber++; + pos = buffer; + token_nr = 0; + buffer[MAX_LINE_LEN] = '\0'; + if(strlen(buffer) == MAX_LINE_LEN) + syntax("line too long", 1); + return 0; +} + +static void skip_junk(int expect) +{ + lastTokenLinenumber = linenumber; + while(!pos || !*pos || strchr(" #\n\t", *pos)) { + if(!pos || !*pos || *pos == '#') { + if(mtools_getline()) { + pos = 0; + if(expect) + syntax("end of file unexpected", 1); + return; + } + } else + pos++; + } + token_nr++; +} + +/* get the next token */ +static char *get_next_token(void) +{ + skip_junk(0); + if(!pos) { + token_length = 0; + token = 0; + return 0; + } + token = pos; + token_length = strcspn(token, " \t\n#:="); + pos += token_length; + return token; +} + +static int match_token(const char *template) +{ + return (strlen(template) == token_length && + !strncasecmp(template, token, token_length)); +} + +static void expect_char(char c) +{ + char buf[11]; + + skip_junk(1); + if(*pos != c) { + sprintf(buf, "expected %c", c); + syntax(buf, 1); + } + pos++; +} + +static char *get_string(void) +{ + char *end, *str; + + skip_junk(1); + if(*pos != '"') + syntax(" \" expected", 0); + str = pos+1; + end = strchr(str, '\"'); + if(!end) + syntax("unterminated string constant", 1); + *end = '\0'; + pos = end+1; + return str; +} + +static unsigned int get_unumber(void) +{ + char *last; + unsigned int n; + + skip_junk(1); + last = pos; + n=(unsigned int) strtoul(pos, &pos, 0); + if(last == pos) + syntax("numeral expected", 0); + pos++; + token_nr++; + return n; +} + +static unsigned int get_number(void) +{ + char *last; + int n; + + skip_junk(1); + last = pos; + n=(int) strtol(pos, &pos, 0); + if(last == pos) + syntax("numeral expected", 0); + pos++; + token_nr++; + return n; +} + +/* purge all entries pertaining to a given drive from the table */ +static void purge(char drive, int fn) +{ + int i,j; + + drive = toupper(drive); + for(j=0, i=0; i < cur_devs; i++) { + if(devices[i].drive != drive || + devices[i].file_nr == fn) + devices[j++] = devices[i]; + } + cur_devs = j; +} + +static void grow(void) +{ + if(cur_devs >= nr_dev - 2) { + nr_dev = (cur_devs + 2) << 1; + if(!(devices=Grow(devices, nr_dev, struct device))){ + printOom(); + exit(1); + } + } +} + + +static void init_drive(void) +{ + memset((char *)&devices[cur_dev], 0, sizeof(struct device)); + devices[cur_dev].ssize = 2; +} + +/* prepends a device to the table */ +static void prepend(void) +{ + int i; + + grow(); + for(i=cur_devs; i>0; i--) + devices[i] = devices[i-1]; + cur_dev = 0; + cur_devs++; + init_drive(); +} + + +/* appends a device to the table */ +static void append(void) +{ + grow(); + cur_dev = cur_devs; + cur_devs++; + init_drive(); +} + + +static void finish_drive_clause(void) +{ + if(cur_dev == -1) { + trusted = 0; + return; + } + if(!devices[cur_dev].name) + syntax("missing filename", 0); + if(devices[cur_dev].tracks || + devices[cur_dev].heads || + devices[cur_dev].sectors) { + if(!devices[cur_dev].tracks || + !devices[cur_dev].heads || + !devices[cur_dev].sectors) + syntax("incomplete geometry: either indicate all of track/heads/sectors or none of them", 0); + if(!(devices[cur_dev].misc_flags & + (MFORMAT_ONLY_FLAG | FILTER_FLAG))) + syntax("if you supply a geometry, you also must supply one of the `mformat_only' or `filter' flags", 0); + } + devices[cur_dev].file_nr = file_nr; + devices[cur_dev].cfg_filename = filename; + if(! (flag_mask & PRIV_FLAG) && IS_SCSI(&devices[cur_dev])) + devices[cur_dev].misc_flags |= PRIV_FLAG; + if(!trusted && (devices[cur_dev].misc_flags & PRIV_FLAG)) { + fprintf(stderr, + "Warning: privileged flag ignored for drive %c: defined in file %s\n", + toupper(devices[cur_dev].drive), filename); + devices[cur_dev].misc_flags &= ~PRIV_FLAG; + } + trusted = 0; + cur_dev = -1; +} + +static int set_var(struct switches_l *switches, int nr, + caddr_t base_address) +{ + int i; + for(i=0; i < nr; i++) { + if(match_token(switches[i].name)) { + expect_char('='); + if(switches[i].type == T_UINT) + * ((int *)((long)switches[i].address+base_address)) = + get_unumber(); + if(switches[i].type == T_INT) + * ((int *)((long)switches[i].address+base_address)) = + get_number(); + else if (switches[i].type == T_STRING) + * ((char**)((long)switches[i].address+base_address))= + strdup(get_string()); + return 0; + } + } + return 1; +} + +static int set_openflags(struct device *dev) +{ + unsigned int i; + + for(i=0; i < sizeof(openflags) / sizeof(*openflags); i++) { + if(match_token(openflags[i].name)) { + dev->mode |= openflags[i].flag; + return 0; + } + } + return 1; +} + +static int set_misc_flags(struct device *dev) +{ + unsigned int i; + + for(i=0; i < sizeof(misc_flags) / sizeof(*misc_flags); i++) { + if(match_token(misc_flags[i].name)) { + flag_mask |= misc_flags[i].flag; + skip_junk(0); + if(pos && *pos == '=') { + pos++; + switch(get_number()) { + case 0: + return 0; + case 1: + break; + default: + syntax("expected 0 or 1", 0); + } + } + dev->misc_flags |= misc_flags[i].flag; + return 0; + } + } + return 1; +} + +static int set_def_format(struct device *dev) +{ + unsigned int i; + + for(i=0; i < sizeof(default_formats)/sizeof(*default_formats); i++) { + if(match_token(default_formats[i].name)) { + if(!dev->ssize) + dev->ssize = 2; + if(!dev->tracks) + dev->tracks = default_formats[i].tracks; + if(!dev->heads) + dev->heads = default_formats[i].heads; + if(!dev->sectors) + dev->sectors = default_formats[i].sectors; + if(!dev->fat_bits) + dev->fat_bits = default_formats[i].fat_bits; + return 0; + } + } + return 1; +} + +static int parse_one(int privilege); + +void set_cmd_line_image(char *img) { + char *ofsp; + + prepend(); + devices[cur_dev].drive = ':'; + default_drive = ':'; + + ofsp = strstr(img, "@@"); + if (ofsp == NULL) { + /* no separator => no offset */ + devices[cur_dev].name = strdup(img); + devices[cur_dev].offset = 0; + } else { + devices[cur_dev].name = strndup(img, ofsp - img); + devices[cur_dev].offset = str_to_offset(ofsp+2); + } + + devices[cur_dev].fat_bits = 0; + devices[cur_dev].tracks = 0; + devices[cur_dev].heads = 0; + devices[cur_dev].sectors = 0; + if (strchr(devices[cur_dev].name, '|')) { + char *pipechar = strchr(devices[cur_dev].name, '|'); + *pipechar = 0; + strncpy(buffer, pipechar+1, MAX_LINE_LEN); + buffer[MAX_LINE_LEN] = '\0'; + fp = NULL; + filename = "{command line}"; + linenumber = 0; + lastTokenLinenumber = 0; + pos = buffer; + token = 0; + while (parse_one(0)); + } +} + +static void parse_old_device_line(char drive) +{ + char name[MAXPATHLEN]; + int items; + long offset; + + /* finish any old drive */ + finish_drive_clause(); + + /* purge out data of old configuration files */ + purge(drive, file_nr); + + /* reserve slot */ + append(); + items = sscanf(token,"%c %s %i %i %i %i %li", + &devices[cur_dev].drive,name,&devices[cur_dev].fat_bits, + &devices[cur_dev].tracks,&devices[cur_dev].heads, + &devices[cur_dev].sectors, &offset); + devices[cur_dev].offset = (off_t) offset; + switch(items){ + case 2: + devices[cur_dev].fat_bits = 0; + /* fall thru */ + case 3: + devices[cur_dev].sectors = 0; + devices[cur_dev].heads = 0; + devices[cur_dev].tracks = 0; + /* fall thru */ + case 6: + devices[cur_dev].offset = 0; + /* fall thru */ + default: + break; + case 0: + case 1: + case 4: + case 5: + syntax("bad number of parameters", 1); + exit(1); + } + if(!devices[cur_dev].tracks){ + devices[cur_dev].sectors = 0; + devices[cur_dev].heads = 0; + } + + devices[cur_dev].drive = toupper(devices[cur_dev].drive); + maintain_default_drive(devices[cur_dev].drive); + if (!(devices[cur_dev].name = strdup(name))) { + printOom(); + exit(1); + } + finish_drive_clause(); + pos=0; +} + +static int parse_one(int privilege) +{ + int action=0; + + get_next_token(); + if(!token) + return 0; + + if((match_token("drive") && ((action = 1)))|| + (match_token("drive+") && ((action = 2))) || + (match_token("+drive") && ((action = 3))) || + (match_token("clear_drive") && ((action = 4))) ) { + /* finish off the previous drive */ + finish_drive_clause(); + + get_next_token(); + if(token_length != 1) + syntax("drive letter expected", 0); + + if(action==1 || action==4) + /* replace existing drive */ + purge(token[0], file_nr); + if(action==4) + return 1; + if(action==3) + prepend(); + else + append(); + memset((char*)(devices+cur_dev), 0, sizeof(*devices)); + trusted = privilege; + flag_mask = 0; + devices[cur_dev].drive = toupper(token[0]); + maintain_default_drive(devices[cur_dev].drive); + expect_char(':'); + return 1; + } + if(token_nr == 1 && token_length == 1) { + parse_old_device_line(token[0]); + return 1; + } + + if((cur_dev < 0 || + (set_var(dswitches, + sizeof(dswitches)/sizeof(*dswitches), + (caddr_t)&devices[cur_dev]) && + set_openflags(&devices[cur_dev]) && + set_misc_flags(&devices[cur_dev]) && + set_def_format(&devices[cur_dev]))) && + set_var(global_switches, + sizeof(global_switches)/sizeof(*global_switches), 0)) + syntax("unrecognized keyword", 1); + return 1; +} + +static int parse(const char *name, int privilege) +{ + if(fp) { + fprintf(stderr, "File descriptor already set (%p)!\n", fp); + exit(1); + } + fp = fopen(name, "r"); + if(!fp) + return 0; + file_nr++; + filename = name; /* no strdup needed: although lifetime of variable + exceeds this function (due to dev->cfg_filename), + we know that the name is always either + 1. a constant + 2. a statically allocate buffer + 3. an environment variable that stays unchanged + */ + linenumber = 0; + lastTokenLinenumber = 0; + pos = 0; + token = 0; + cur_dev = -1; /* no current device */ + + while(parse_one(privilege)); + finish_drive_clause(); + fclose(fp); + filename = NULL; + fp = NULL; + return 1; +} + +void read_config(void) +{ + char *homedir; + char *envConfFile; + static char conf_file[MAXPATHLEN+sizeof(CFG_FILE1)]; + + + /* copy compiled-in devices */ + file_nr = 0; + cur_devs = nr_const_devices; + nr_dev = nr_const_devices + 2; + devices = NewArray(nr_dev, struct device); + if(!devices) { + printOom(); + exit(1); + } + if(nr_const_devices) + memcpy(devices, const_devices, + nr_const_devices*sizeof(struct device)); + + (void) ((parse(CONF_FILE,1) | + parse(LOCAL_CONF_FILE,1) | + parse(SYS_CONF_FILE,1)) || + (parse(OLD_CONF_FILE,1) | + parse(OLD_LOCAL_CONF_FILE,1))); + /* the old-name configuration files only get executed if none of the + * new-name config files were used */ + + homedir = get_homedir(); + if ( homedir ){ + strncpy(conf_file, homedir, MAXPATHLEN ); + conf_file[MAXPATHLEN]='\0'; + strcat( conf_file, CFG_FILE1); + parse(conf_file,0); + } + memset((char *)&devices[cur_devs],0,sizeof(struct device)); + + envConfFile = getenv("MTOOLSRC"); + if(envConfFile) + parse(envConfFile,0); + + /* environmental variables */ + get_env_conf(); + if(mtools_skip_check) + mtools_fat_compatibility=1; +} + +void mtoolstest(int argc, char **argv, int type UNUSEDP) +{ + /* testing purposes only */ + struct device *dev; + char drive='\0'; + + if(argc > 1 && argv[1][0] && argv[1][1] == ':') { + drive = toupper(argv[1][0]); + } + + for (dev=devices; dev->name; dev++) { + if(drive && drive != dev->drive) + continue; + printf("drive %c:\n", dev->drive); + printf("\t#fn=%d mode=%d ", + dev->file_nr, dev->mode); + if(dev->cfg_filename) + printf("defined in %s\n", dev->cfg_filename); + else + printf("builtin\n"); + printf("\tfile=\"%s\" fat_bits=%d \n", + dev->name,dev->fat_bits); + printf("\ttracks=%d heads=%d sectors=%d hidden=%d\n", + dev->tracks, dev->heads, dev->sectors, dev->hidden); + printf("\toffset=0x%lx\n", (long) dev->offset); + printf("\tpartition=%d\n", dev->partition); + + if(dev->misc_flags) + printf("\t"); + + if(DO_SWAP(dev)) + printf("swap "); + if(IS_SCSI(dev)) + printf("scsi "); + if(IS_PRIVILEGED(dev)) + printf("privileged"); + if(IS_MFORMAT_ONLY(dev)) + printf("mformat_only "); + if(SHOULD_USE_VOLD(dev)) + printf("vold "); +#ifdef USE_XDF + if(SHOULD_USE_XDF(dev)) + printf("use_xdf "); +#endif + if(dev->misc_flags) + printf("\n"); + + if(dev->mode) + printf("\t"); +#ifdef O_SYNC + if(dev->mode & O_SYNC) + printf("sync "); +#endif +#ifdef O_NDELAY + if((dev->mode & O_NDELAY)) + printf("nodelay "); +#endif +#ifdef O_EXCL + if((dev->mode & O_EXCL)) + printf("exclusive "); +#endif + if(dev->mode) + printf("\n"); + + if(dev->precmd) + printf("\tprecmd=%s\n", dev->precmd); + + printf("\n"); + } + + printf("mtools_fat_compatibility=%d\n",mtools_fat_compatibility); + printf("mtools_skip_check=%d\n",mtools_skip_check); + printf("mtools_lower_case=%d\n",mtools_ignore_short_case); + + exit(0); +} |