/* * (C) Copyright 1992, ..., 2005 the "DOSEMU-Development-Team". * * for details see file COPYING in the DOSEMU distribution */ /* parser.y * * Parser version 1 ... before 0.66.5 * Parser version 2 at state of 0.66.5 97/05/30 * Parser version 3 at state of 0.97.0.1 98/01/03 * * Note: starting with version 2, you may protect against version 3 via * * ifdef parser_version_3 * # version 3 style parser * else * # old style parser * endif * * Note2: starting with version 3 you _need_ atleast _one_ statement such as * * $XYZ = "something" * * to make the 'new version style check' happy, else dosemu will abort. */ %{ #define PARSER_VERSION_STRING "parser_version_3" #include "config.h" #include #include #include #include #include #include #include #include #include /* structure stat */ #include /* prototype for stat() */ #include #include #include #include #include #include #include #ifdef __linux__ #include #endif #include "dosemu_config.h" #include "emu.h" #ifdef X86_EMULATOR #include "cpu-emu.h" #endif #include "disks.h" #include "port.h" #define allow_io port_allow_io #include "lpt.h" #include "video.h" #include "mouse.h" #include "serial.h" #include "timers.h" #include "keymaps.h" #include "keyb_server.h" #include "memory.h" #include "mapping.h" #include "utilities.h" #include "aspi.h" #include "pktdrvr.h" #include "iodev.h" /* for TM_BIOS / TM_PIT / TM_LINUX */ #include "parsglob.h" #define USERVAR_PREF "dosemu_" static int user_scope_level; static int after_secure_check = 0; static serial_t *sptr; static serial_t nullser; static mouse_t *mptr = &config.mouse; static int c_ser = 0; static struct disk *dptr; static struct disk nulldisk; static int c_hdisks = 0; static int c_fdisks = 0; int dexe_running = 0; static int dexe_forbid_disk = 1; char own_hostname[128]; static struct printer nullptr; static struct printer *pptr = &nullptr; static int c_printers = 0; static int ports_permission = IO_RDWR; static unsigned int ports_ormask = 0; static unsigned int ports_andmask = 0xFFFF; static unsigned int portspeed = 0; static char dev_name[255]= ""; static int errors = 0; static int warnings = 0; static int priv_lvl = 0; static int saved_priv_lvl = 0; static char *file_being_parsed; /* this to ensure we are parsing a new style */ static int parser_version_3_style_used = 0; #define CONFNAME_V3USED "version_3_style_used" /* local procedures */ static void start_ports(void); static void start_mouse(void); static void stop_mouse(void); static void start_debug(void); static void start_video(void); static void stop_video(void); static void set_vesamodes(int width, int height, int color_bits); static int detect_vbios_seg(void); static int detect_vbios_size(void); static void stop_ttylocks(void); static void start_serial(void); static void stop_serial(void); static void start_printer(void); static void stop_printer(void); static void start_keyboard(void); static void keytable_start(int layout); static void keytable_stop(void); static void dump_keytables_to_file(char *name); static void stop_terminal(void); static void start_disk(void); static void do_part(char *); static void start_bootdisk(void); static void start_floppy(void); static void stop_disk(int token); static void start_vnet(char *); static FILE* open_file(char* filename); static void close_file(FILE* file); static void write_to_syslog(char *message); static void set_irq_value(int bits, int i1); static void set_irq_range(int bits, int i1, int i2); static int undefine_config_variable(char *name); static void check_user_var(char *name); static char *run_shell(char *command); static int for_each_handling(int loopid, char *varname, char *delim, char *list); static void enter_user_scope(int incstackptr); static void leave_user_scope(int incstackptr); static void handle_features(int which, int value); static void set_joy_device(char *devstring); static int parse_timemode(const char *); /* class stuff */ #define IFCLASS(m) if (is_in_allowed_classes(m)) #define CL_ALL -1 #define CL_VAR 0x200 #define CL_VPORT 0x2000 #define CL_PORT 0x20000 #define CL_PCI 0x40000 #define CL_IRQ 0x200000 #define CL_HARDRAM 0x1000000 #define CL_NET 0x2000000 static int is_in_allowed_classes(int mask); /* variables in lexer.l */ %} %start lines %pure-parser %union { int i_value; char *s_value; float r_value; long long all_value; ExprType t_value; }; %{ #include "lexer.h" %} %token INTEGER L_OFF L_ON L_AUTO L_YES L_NO CHIPSET_TYPE %token KEYB_LAYOUT %token REAL %token STRING VARIABLE /* needed for expressions */ %token EXPRTEST %token INTCAST REALCAST %left AND_OP OR_OP XOR_OP SHR_OP SHL_OP %left NOT_OP /* logical NOT */ %left EQ_OP GE_OP LE_OP '=' '<' '>' NEQ_OP %left STR_EQ_OP STR_NEQ_OP %left L_AND_OP L_OR_OP %left '+' '-' %left '*' '/' %left UMINUS UPLUS BIT_NOT_OP %token STRLEN STRTOL STRNCMP STRCAT STRPBRK STRSPLIT STRCHR STRRCHR STRSTR %token STRDEL STRSPN STRCSPN SHELL %token DEFINED %type expression %type string_expr variable_content strarglist strarglist_item /* flow control */ %token DEFINE UNDEF IFSTATEMENT WHILESTATEMENT FOREACHSTATEMENT %token ENTER_USER_SPACE LEAVE_USER_SPACE /* variable handling */ %token CHECKUSERVAR /* main options */ %token DOSBANNER FASTFLOPPY TIMINT HOGTHRESH SPEAKER IPXSUPPORT IPXNETWORK NOVELLHACK %token NETDEV VNET %token DEBUG MOUSE SERIAL COM KEYBOARD TERMINAL VIDEO EMURETRACE TIMER %token MATHCO CPU CPUSPEED RDTSC BOOTA BOOTB BOOTC %token L_XMS L_DPMI DPMI_BASE PM_DOS_API PORTS DISK DOSMEM EXT_MEM %token L_EMS L_UMB EMS_SIZE EMS_FRAME TTYLOCKS L_SOUND L_SND_OSS L_JOYSTICK FULL_FILE_LOCKS %token DEXE ALLOWDISK FORCEXDOS XDOSONLY %token ABORT WARN %token BOOTDISK L_FLOPPY EMUSYS EMUINI L_X %token DOSEMUMAP LOGBUFSIZE LOGFILESIZE MAPPINGDRIVER %token LFN_SUPPORT /* speaker */ %token EMULATED NATIVE /* cpuemu */ %token CPUEMU VM86 FULL /* keyboard */ %token RAWKEYBOARD %token PRESTROKE %token KEYTABLE SHIFT_MAP ALT_MAP NUMPAD_MAP DUMP %token DGRAVE DACUTE DCIRCUM DTILDE DBREVE DABOVED DDIARES DABOVER DDACUTE DCEDILLA DIOTA DOGONEK DCARON /* ipx */ %token NETWORK PKTDRIVER /* lock files */ %token DIRECTORY NAMESTUB BINARY /* serial */ %token BASE IRQ INTERRUPT DEVICE CHARSET BAUDRATE VIRTUAL /* mouse */ %token MICROSOFT MS3BUTTON LOGITECH MMSERIES MOUSEMAN HITACHI MOUSESYSTEMS BUSMOUSE PS2 IMPS2 %token INTERNALDRIVER EMULATE3BUTTONS CLEARDTR /* x-windows */ %token L_DISPLAY L_TITLE X_TITLE_SHOW_APPNAME ICON_NAME X_KEYCODE X_BLINKRATE X_SHARECMAP X_MITSHM X_FONT %token X_FIXED_ASPECT X_ASPECT_43 X_LIN_FILT X_BILIN_FILT X_MODE13FACT X_WINSIZE %token X_GAMMA X_FULLSCREEN VGAEMU_MEMSIZE VESAMODE X_LFB X_PM_INTERFACE X_MGRAB_KEY X_BACKGROUND_PAUSE /* video */ %token VGA MGA CGA EGA NONE CONSOLE GRAPHICS CHIPSET FULLREST PARTREST %token MEMSIZE VBIOS_SIZE_TOK VBIOS_SEG VBIOS_FILE VBIOS_COPY VBIOS_MMAP DUALMON %token VBIOS_POST %token FORCE_VT_SWITCH PCI /* terminal */ %token UPDATEFREQ UPDATELINES COLOR ESCCHAR XTERM_TITLE /* %token UPDATEFREQ UPDATELINES COLOR CORNER METHOD NORMAL XTERM NCURSES FAST */ /* debug */ %token IO PORT CONFIG READ WRITE KEYB PRINTER WARNING GENERAL HARDWARE %token L_IPC SOUND %token TRACE CLEAR /* printer */ %token COMMAND TIMEOUT OPTIONS L_FILE /* disk */ %token L_PARTITION BOOTFILE WHOLEDISK THREEINCH THREEINCH_720 THREEINCH_2880 FIVEINCH ATAPI READONLY LAYOUT %token SECTORS CYLINDERS TRACKS HEADS OFFSET HDIMAGE DISKCYL4096 /* ports/io */ %token RDONLY WRONLY RDWR ORMASK ANDMASK RANGE FAST SLOW /* Silly interrupts */ %token SILLYINT USE_SIGIO /* hardware ram mapping */ %token HARDWARE_RAM /* Sound Emulation */ %token SB_BASE SB_IRQ SB_DMA SB_HDMA SB_MIXER SB_DSP MPU_BASE SOUND_DRIVER %token OSS_MIN_FRAGS OSS_MAX_FRAGS OSS_STALLED_FRAGS OSS_DO_POST OSS_MIN_EXTRA_FRAGS %token OSS_DAC_FREQ /* CD-ROM */ %token CDROM /* ASPI driver */ %token ASPI DEVICETYPE TARGET /* features */ %token FEATURE /* joystick */ %token JOYSTICK JOY_DEVICE JOY_DOS_MIN JOY_DOS_MAX JOY_GRANULARITY JOY_LATENCY /* Hacks */ %token CLI_TIMEOUT PIC_WATCHDOG %token TIMEMODE /* we know we have 1 shift/reduce conflict :-( * and tell the parser to ignore that */ /* %expect 1 */ %type int_bool irq_bool bool speaker floppy_bool cpuemu %% lines : line | lines line | lines optdelim line ; optdelim : ';' | optdelim ';' ; line : HOGTHRESH expression { config.hogthreshold = $2; } | DEFINE STRING { IFCLASS(CL_VAR){ define_config_variable($2);} free($2); } | UNDEF STRING { IFCLASS(CL_VAR){ undefine_config_variable($2);} free($2); } | IFSTATEMENT '(' expression ')' { /* NOTE: * We _need_ absolutely to return to state stack 0 * because we 'backward' modify the input stream for * the parser (in lexer). Hence, _if_ the parser needs * to read one token more than needed, we are lost, * because we can't discard it. So please, don't * play with the grammar without knowing what you do. * The ')' _will_ return to state stack 0, but fiddling * with brackets '()' in the underlaying 'expression' * rules may distroy this. * -- Hans 971231 */ tell_lexer_if($3); } /* Note: the below syntax of the while__yy__ statement * is internal and _not_ visible out side. * The visible syntax is: * while ( expression ) * * done */ | WHILESTATEMENT INTEGER ',' '(' expression ')' { tell_lexer_loop($2, $5); } | FOREACHSTATEMENT INTEGER ',' VARIABLE '(' string_expr ',' strarglist ')' { tell_lexer_loop($2, for_each_handling($2,$4,$6,$8)); free($4); free($6); free($8); } | SHELL '(' strarglist ')' { char *s = run_shell($3); if (s) free(s); free($3); } | ENTER_USER_SPACE { enter_user_scope($1); } | LEAVE_USER_SPACE { leave_user_scope($1); } | VARIABLE '=' strarglist { if (!parser_version_3_style_used) { parser_version_3_style_used = 1; define_config_variable(CONFNAME_V3USED); } if ((strpbrk($1, "uhc") == $1) && ($1[1] == '_')) yyerror("reserved variable %s can't be set\n", $1); else { if (user_scope_level) { char *s = malloc(strlen($1)+sizeof(USERVAR_PREF)); strcpy(s, USERVAR_PREF); strcat(s,$1); setenv(s, $3, 1); free(s); } else IFCLASS(CL_VAR) setenv($1, $3, 1); } free($1); free($3); } | CHECKUSERVAR check_user_var_list | EXPRTEST expression { if (EXPRTYPE($2) == TYPE_REAL) c_printf("CONF TESTING: exprtest real %f %08x\n", VAL_R($2), $2); else c_printf("CONF TESTING: exprtest int %d %08x\n", VAL_I($2), $2); } /* abandoning 'single' abort due to shift/reduce conflicts Just use ' abort "" ' | ABORT { exit(99); } */ | ABORT strarglist { if ($2[0]) fprintf(stderr,"CONF aborted with: %s\n", $2); exit(99); } | WARN strarglist { c_printf("CONF: %s\n", $2); free($2); } | EMUSYS string_expr { free(config.emusys); config.emusys = $2; c_printf("CONF: config.emusys = '%s'\n", $2); } | EMUSYS '{' string_expr '}' { free(config.emusys); config.emusys = $3; c_printf("CONF: config.emusys = '%s'\n", $3); } | DOSEMUMAP string_expr { #ifdef USE_MHPDBG free(DOSEMU_MAP_PATH); DOSEMU_MAP_PATH = $2; c_printf("CONF: dosemu.map path = '%s'\n", $2); #else free($2); #endif } | MAPPINGDRIVER string_expr { free(config.mappingdriver); config.mappingdriver = $2; c_printf("CONF: mapping driver = '%s'\n", $2); } | EMUINI string_expr { free(config.emuini); config.emuini = $2; c_printf("CONF: config.emuini = '%s'\n", $2); } | EMUINI '{' string_expr '}' { free(config.emuini); config.emuini = $3; c_printf("CONF: config.emuini = '%s'\n", $3); } | FULL_FILE_LOCKS bool { config.full_file_locks = ($2!=0); } | LFN_SUPPORT bool { config.lfn = ($2!=0); } | FASTFLOPPY floppy_bool { config.fastfloppy = ($2!=0); c_printf("CONF: fastfloppy = %d\n", config.fastfloppy); } | CPU expression { int cpu = cpu_override (($2%100)==86?($2/100)%10:0); if (cpu > 0) { c_printf("CONF: CPU set to %d86\n",cpu); vm86s.cpu_type = cpu; } else yyerror("error in CPU user override\n"); } | CPU EMULATED { vm86s.cpu_type = 5; #ifdef X86_EMULATOR config.cpuemu = 1; c_printf("CONF: CPUEMU set to %d for %ld86\n", config.cpuemu, vm86s.cpu_type); #endif } | CPUEMU cpuemu { #ifdef X86_EMULATOR config.cpuemu = $2; c_printf("CONF: CPUEMU set to %d for %ld86\n", config.cpuemu, vm86s.cpu_type); #endif } | CPUSPEED expression { #if 0 /* no longer used, but left in for dosemu.conf compatibility */ if (config.realcpu >= CPU_586) { config.cpu_spd = ((double)LLF_US)/TOF($2); config.cpu_tick_spd = ((double)LLF_TICKS)/TOF($2); c_printf("CONF: CPU speed = %g\n", ((double)TOF($2))); } #endif } | CPUSPEED INTEGER INTEGER { #if 0 /* no longer used, but left in for dosemu.conf compatibility */ if (config.realcpu >= CPU_586) { config.cpu_spd = (LLF_US*$3)/$2; config.cpu_tick_spd = (LLF_TICKS*$3)/$2; c_printf("CONF: CPU speed = %d/%d\n", $2, $3); } #endif } | RDTSC bool { config.rdtsc = ($2!=0); c_printf("CONF: %sabling use of pentium timer\n", (config.rdtsc?"En":"Dis")); } | PCI bool { IFCLASS(CL_PCI); config.pci = ($2!=0); } | BOOTA { config.hdiskboot = 0; } | BOOTC { config.hdiskboot = 1; } | BOOTB { config.hdiskboot = 2; } | TIMER expression { config.freq = $2; if ($2) { config.update = 1000000 / $2; } else { config.update = 54925; config.freq = 18; } c_printf("CONF: timer freq=%d, update=%d\n",config.freq,config.update); } | LOGBUFSIZE expression { char *b; flush_log(); b = malloc($2+1024); if (!b) { error("cannot get logbuffer\n"); exit(1); } logptr = logbuf = b; logbuf_size = $2; } | LOGFILESIZE expression { logfile_limit = $2; } | DOSBANNER bool { config.dosbanner = ($2!=0); c_printf("CONF: dosbanner %s\n", ($2) ? "on" : "off"); } | EMURETRACE bool { IFCLASS(CL_VPORT){ if ($2 && !config.emuretrace && priv_lvl) yyerror("Can not modify video port access in user config file"); config.emuretrace = ($2!=0); c_printf("CONF: emu_retrace %s\n", ($2) ? "on" : "off"); }} | L_EMS '{' ems_flags '}' | L_EMS int_bool { if ($2 >= 0) config.ems_size = $2; if ($2 > 0) c_printf("CONF: %dk bytes EMS memory\n", $2); } | L_XMS int_bool { if ($2 >= 0) config.xms_size = $2; if ($2 > 0) c_printf("CONF: %dk bytes XMS memory\n", $2); } | L_UMB bool { config.max_umb = ($2!=0); if ($2 > 0) c_printf("CONF: maximize umb's %s\n", ($2) ? "on" : "off"); } | L_DPMI int_bool { if ($2>=0) config.dpmi = $2; c_printf("CONF: DPMI-Server %s (%#x)\n", ($2) ? "on" : "off", ($2)); } | DPMI_BASE int_bool { config.dpmi_base = $2; c_printf("CONF: DPMI base addr = %#x\n", $2); } | PM_DOS_API bool { config.pm_dos_api = ($2!=0); c_printf("CONF: PM DOS API Translator %s\n", ($2) ? "on" : "off"); } | DOSMEM int_bool { if ($2>=0) config.mem_size = $2; } | EXT_MEM int_bool { if ($2>=0) config.ext_mem = $2; } | MATHCO bool { config.mathco = ($2!=0); } | IPXSUPPORT bool { config.ipxsup = ($2!=0); c_printf("CONF: IPX support %s\n", ($2) ? "on" : "off"); } | IPXNETWORK int_bool { config.ipx_net = $2; } | PKTDRIVER bool { if (config.vnet == VNET_TYPE_TAP || $2 == 0 || is_in_allowed_classes(CL_NET)) { config.pktdrv = ($2!=0); c_printf("CONF: Packet Driver %s.\n", ($2) ? "enabled" : "disabled"); } } | NETDEV string_expr { free(config.netdev); config.netdev = $2; } | NOVELLHACK bool { config.pktflags = ($2!=0); } | VNET string_expr { start_vnet($2); free($2); } | SPEAKER speaker { if ($2 == SPKR_NATIVE) { if (can_do_root_stuff) { c_printf("CONF: allowing speaker port access!\n"); } else { c_printf("CONF: native speaker not allowed: emulate\n"); $2 = SPKR_EMULATED; } } else c_printf("CONF: not allowing speaker port access\n"); config.speaker = $2; } | VIDEO { start_video(); } '{' video_flags '}' { stop_video(); } | XTERM_TITLE string_expr { free(config.xterm_title); config.xterm_title = $2; } | TERMINAL '{' term_flags '}' { stop_terminal(); } | DEBUG strarglist { parse_debugflags($2, 1); free($2); } | DEBUG { start_debug(); } '{' debug_flags '}' | MOUSE { start_mouse(); } '{' mouse_flags '}' { stop_mouse(); } | TTYLOCKS '{' ttylocks_flags '}' { stop_ttylocks(); } | SERIAL { start_serial(); } '{' serial_flags '}' { stop_serial(); } | KEYBOARD { start_keyboard(); } '{' keyboard_flags '}' | KEYTABLE KEYB_LAYOUT {keytable_start($2);} '{' keyboard_mods '}' {keytable_stop();} | PRESTROKE string_expr { append_pre_strokes($2); c_printf("CONF: appending pre-strokes '%s'\n", $2); free($2); } | KEYTABLE DUMP string_expr { dump_keytables_to_file($3); free($3); } | PORTS { IFCLASS(CL_PORT) start_ports(); } '{' port_flags '}' | TRACE PORTS '{' trace_port_flags '}' | DISK { start_disk(); } '{' disk_flags '}' { stop_disk(DISK); } | BOOTDISK { start_bootdisk(); } '{' disk_flags '}' { stop_disk(BOOTDISK); } | L_FLOPPY { start_floppy(); } '{' disk_flags '}' { stop_disk(L_FLOPPY); } | CDROM '{' string_expr '}' { static int which = 0; if (which >= 3) { c_printf("CONF: too many cdrom drives defined\n"); free($3); } else { Path_cdrom[which] = $3; c_printf("CONF: cdrom MSCD000%d on %s\n", which+1 ,$3); which++; } } | ASPI '{' string_expr DEVICETYPE string_expr TARGET expression '}' { #ifdef ASPI_SUPPORT char *s = aspi_add_device($3, $5, $7); if (s) { c_printf("CONF: aspi available for %s\n", s); free(s); } else c_printf("CONF: aspi device %s not available\n", $3); #endif free($3); free($5); } | PRINTER { start_printer(); } '{' printer_flags '}' { stop_printer(); } | L_X '{' x_flags '}' | L_SOUND bool { if (! $2) config.sound = $2; } | L_SOUND { config.sound = 1; } '{' sound_flags '}' | L_SND_OSS '{' snd_oss_flags '}' | L_JOYSTICK bool { if (! $2) { config.joy_device[0] = config.joy_device[1] = NULL; } } | L_JOYSTICK '{' joystick_flags '}' | SILLYINT { IFCLASS(CL_IRQ) config.sillyint=0; } '{' sillyint_flags '}' | SILLYINT irq_bool { IFCLASS(CL_IRQ) if ($2) { config.sillyint = 1 << $2; c_printf("CONF: IRQ %d for irqpassing\n", $2); } } | DEXE '{' dexeflags '}' | HARDWARE_RAM { IFCLASS(CL_HARDRAM); if (priv_lvl) yyerror("Can not change hardware ram access settings in user config file"); } '{' hardware_ram_flags '}' | FEATURE '{' expression '=' expression '}' { handle_features($3, $5); } | CLI_TIMEOUT int_bool { config.cli_timeout = $2; } | PIC_WATCHDOG int_bool { config.pic_watchdog = $2; } | TIMEMODE string_expr { config.timemode = parse_timemode($2); c_printf("CONF: time mode = '%s'\n", $2); free($2); } | STRING { yyerror("unrecognized command '%s'", $1); free($1); } | error ; /* expressions */ expression: INTEGER {TYPINT($$);} | REAL {TYPREAL($$);} | expression '+' expression {V_VAL($$,$1,TOF($1) + TOF($3)); } | expression '-' expression {V_VAL($$,$1,TOF($1) - TOF($3)); } | expression '*' expression {V_VAL($$,$1,TOF($1) * TOF($3)); } | expression '/' expression { if ($3) V_VAL($$,$1,TOF($1) / TOF($3)); else V_VAL($$,$1,TOF($3)); } | '-' expression %prec UMINUS {I_VAL($$) = -TOF($2); } | '+' expression %prec UPLUS {V_VAL($$,$2,TOF($2)); } | expression AND_OP expression {I_VAL($$) = VAL_I($1) & VAL_I($3); } | expression OR_OP expression {I_VAL($$) = VAL_I($1) | VAL_I($3); } | expression XOR_OP expression {I_VAL($$) = VAL_I($1) ^ VAL_I($3); } | BIT_NOT_OP expression %prec BIT_NOT_OP {I_VAL($$) = VAL_I($2) ^ (-1); } | expression SHR_OP expression { unsigned int shift = (1 << VAL_I($3)); if (!shift) ALL($$) = ALL($1); else V_VAL($$, $1, TOF($1) / shift);} | expression SHL_OP expression { unsigned int shift = (1 << VAL_I($3)); if (!shift) ALL($$) = ALL($1); else V_VAL($$, $1, TOF($1) * shift);} | expression EQ_OP expression {B_VAL($$) = TOF($1) == TOF($3); } | expression NEQ_OP expression {B_VAL($$) = TOF($1) != TOF($3); } | expression GE_OP expression {B_VAL($$) = TOF($1) >= TOF($3); } | expression LE_OP expression {B_VAL($$) = TOF($1) <= TOF($3); } | expression '<' expression {B_VAL($$) = TOF($1) < TOF($3); } | expression '>' expression {B_VAL($$) = TOF($1) > TOF($3); } | expression L_AND_OP expression {B_VAL($$) = TOF($1) && TOF($3); } | expression L_OR_OP expression {B_VAL($$) = TOF($1) || TOF($3); } | string_expr STR_EQ_OP string_expr {B_VAL($$) = strcmp($1,$3) == 0; free($1); free($3); } | string_expr STR_NEQ_OP string_expr {B_VAL($$) = strcmp($1,$3) != 0; free($1); free($3); } | NOT_OP expression {B_VAL($$) = (TOF($2) ? 0:1); } | L_YES {B_VAL($$) = -2; } | L_NO {B_VAL($$) = 0; } | L_ON {B_VAL($$) = -2; } | L_OFF {B_VAL($$) = 0; } | L_AUTO {I_VAL($$) = -1; } | INTCAST '(' expression ')' {I_VAL($$) = TOF($3);} | REALCAST '(' expression ')' {R_VAL($$) = TOF($3);} | STRTOL '(' string_expr ')' { I_VAL($$) = strtol($3,0,0); free($3); } | STRLEN '(' string_expr ')' { I_VAL($$) = strlen($3); free($3); } | STRNCMP '(' string_expr ',' string_expr ',' expression ')' { I_VAL($$) = strncmp($3,$5,$7); free($3); free($5); } | STRPBRK '(' string_expr ',' string_expr ')' { char *s = strpbrk($3,$5); if (s) I_VAL($$) = (int)s - (int)$3; else I_VAL($$) = -1; free($3); free($5); } | STRCHR '(' string_expr ',' string_expr ')' { char *s = strchr($3,$5[0]); if (s) I_VAL($$) = (int)s - (int)$3; else I_VAL($$) = -1; free($3); free($5); } | STRRCHR '(' string_expr ',' string_expr ')' { char *s = strrchr($3,$5[0]); if (s) I_VAL($$) = (int)s - (int)$3; else I_VAL($$) = -1; free($3); free($5); } | STRSTR '(' string_expr ',' string_expr ')' { char *s = strstr($3,$5); if (s) I_VAL($$) = (int)s - (int)$3; else I_VAL($$) = -1; free($3); free($5); } | STRSPN '(' string_expr ',' string_expr ')' { I_VAL($$) = strspn($3,$5); free($3); free($5); } | STRCSPN '(' string_expr ',' string_expr ')' { I_VAL($$) = strcspn($3,$5); free($3); free($5); } | DEFINED '(' STRING ')' { B_VAL($$) = get_config_variable($3) !=0; free($3); } | variable_content { char *s; I_VAL($$) = strtol($1,&s,0); switch (*s) { case '.': case 'e': case 'E': /* we assume a real number */ R_VAL($$) = strtod($1,0); } free($1); } | '(' expression ')' {ALL($$) = ALL($2);} | STRING { if ( $1[0] && !$1[1] && (EXPRTYPE($1) == TYPE_STRING1) ) I_VAL($$) = $1[0]; else yyerror("unrecognized expression '%s'", $1); free($1); } ; variable_content: VARIABLE { char *s = $1; if (get_config_variable(s)) s = "1"; else if (strncmp("c_",s,2) && strncmp("u_",s,2) && strncmp("h_",s,2) ) { s = checked_getenv(s); if (!s) s = ""; } else s = "0"; S_VAL($$) = strdup(s); free($1); } ; string_expr: STRING {S_VAL($$) = $1;} | STRCAT '(' strarglist ')' { S_VAL($$) = $3; } | STRSPLIT '(' string_expr ',' expression ',' expression ')' { int i = (int)TOF($5); int len = (int)TOF($7); int slen = strlen($3); if ((i >=0) && (i < slen) && (len > 0)) { if ((i+len) > slen) len = slen - i; $3[i+len] = 0; S_VAL($$) = strdup($3 + i); } else S_VAL($$) = strdup(""); free($3); } | STRDEL '(' string_expr ',' expression ',' expression ')' { int i = (int)TOF($5); int len = (int)TOF($7); int slen = strlen($3); char *s = strdup($3); if ((i >=0) && (i < slen) && (len > 0)) { if ((i+len) > slen) s[i] = 0; else memmove(s+i, s+i+len, slen-i-len+1); } free($3); S_VAL($$) = s; } | SHELL '(' strarglist ')' { S_VAL($$) = run_shell($3); free($3); } | variable_content {ALL($$) = ALL($1);} ; strarglist: strarglist_item | strarglist ',' strarglist_item { char *s = malloc(strlen($1)+strlen($3)+1); strcpy(s, $1); strcat(s, $3); S_VAL($$) = s; free($1); free($3); } ; strarglist_item: string_expr | '(' expression ')' { char buf[128]; if (EXPRTYPE($2) == TYPE_REAL) sprintf(buf, "%g", VAL_R($2)); else sprintf(buf, "%d", VAL_I($2)); S_VAL($$) = strdup(buf); } ; check_user_var_list: VARIABLE { check_user_var($1); free($1); } | check_user_var_list ',' VARIABLE { check_user_var($3); free($3); } ; /* x-windows */ x_flags : x_flag | x_flags x_flag ; x_flag : UPDATELINES expression { config.X_updatelines = $2; } | UPDATEFREQ expression { config.X_updatefreq = $2; } | L_DISPLAY string_expr { free(config.X_display); config.X_display = $2; } | L_TITLE string_expr { free(config.X_title); config.X_title = $2; } | X_TITLE_SHOW_APPNAME bool { config.X_title_show_appname = ($2!=0); } | ICON_NAME string_expr { free(config.X_icon_name); config.X_icon_name = $2; } | X_KEYCODE expression { config.X_keycode = $2; } | X_BLINKRATE expression { config.X_blinkrate = $2; } | X_SHARECMAP { config.X_sharecmap = 1; } | X_MITSHM { config.X_mitshm = 1; } | X_MITSHM bool { config.X_mitshm = ($2!=0); } | X_FONT string_expr { free(config.X_font); config.X_font = $2; } | X_FIXED_ASPECT bool { config.X_fixed_aspect = ($2!=0); } | X_ASPECT_43 { config.X_aspect_43 = 1; } | X_LIN_FILT { config.X_lin_filt = 1; } | X_BILIN_FILT { config.X_bilin_filt = 1; } | X_MODE13FACT expression { config.X_mode13fact = $2; } | X_WINSIZE INTEGER INTEGER { config.X_winsize_x = $2; config.X_winsize_y = $3; } | X_WINSIZE expression ',' expression { config.X_winsize_x = $2; config.X_winsize_y = $4; } | X_GAMMA expression { config.X_gamma = $2; } | X_FULLSCREEN bool { config.X_fullscreen = $2; } | VGAEMU_MEMSIZE expression { config.vgaemu_memsize = $2; } | VESAMODE INTEGER INTEGER { set_vesamodes($2,$3,0);} | VESAMODE INTEGER INTEGER INTEGER { set_vesamodes($2,$3,$4);} | VESAMODE expression ',' expression { set_vesamodes($2,$4,0);} | VESAMODE expression ',' expression ',' expression { set_vesamodes($2,$4,$6);} | X_LFB bool { config.X_lfb = ($2!=0); } | X_PM_INTERFACE bool { config.X_pm_interface = ($2!=0); } | X_MGRAB_KEY string_expr { free(config.X_mgrab_key); config.X_mgrab_key = $2; } | X_BACKGROUND_PAUSE bool { config.X_background_pause = ($2!=0); } ; dexeflags : dexeflag | dexeflags dexeflag ; dexeflag : ALLOWDISK { if (!priv_lvl) dexe_forbid_disk = 0; } | FORCEXDOS { char *env = getenv("DISPLAY"); if (env && env[0] && dexe_running) config.X = 1; } | XDOSONLY { char *env = getenv("DISPLAY"); if (env && env[0] && dexe_running) config.X = 1; else if (dexe_running) { yyerror("this DEXE requires X, giving up"); exit(99); } } ; /* sb emulation */ sound_flags : sound_flag | sound_flags sound_flag ; sound_flag : SB_BASE expression { config.sb_base = $2; } | SB_DMA expression { config.sb_dma = $2; } | SB_HDMA expression { config.sb_hdma = $2; } | SB_IRQ expression { config.sb_irq = $2; } | SB_MIXER string_expr { free(config.sb_mixer); config.sb_mixer = $2; } | SB_DSP string_expr { free(config.sb_dsp); config.sb_dsp = $2; } | MPU_BASE expression { config.mpu401_base = $2; } | SOUND_DRIVER string_expr { free(config.sound_driver); config.sound_driver = $2; } ; snd_oss_flags : snd_oss_flag | snd_oss_flags snd_oss_flag ; snd_oss_flag : OSS_MIN_FRAGS expression { config.oss_min_frags = $2; } | OSS_MAX_FRAGS expression { config.oss_max_frags = $2; } | OSS_STALLED_FRAGS expression { config.oss_stalled_frags = $2; } | OSS_DO_POST bool { config.oss_do_post = $2; } | OSS_MIN_EXTRA_FRAGS expression { config.oss_min_extra_frags = $2; } | OSS_DAC_FREQ expression { config.oss_dac_freq = $2; } ; /* joystick emulation */ joystick_flags : joystick_flag | joystick_flags joystick_flag ; joystick_flag : JOY_DEVICE string_expr { set_joy_device($2); } | JOY_DOS_MIN expression { config.joy_dos_min = $2; } | JOY_DOS_MAX expression { config.joy_dos_max = $2; } | JOY_GRANULARITY expression { config.joy_granularity = $2; } | JOY_LATENCY expression { config.joy_latency = $2; } ; /* video */ video_flags : video_flag | video_flags video_flag ; video_flag : VGA { config.cardtype = CARD_VGA; } | MGA { config.cardtype = CARD_MDA; } | CGA { config.cardtype = CARD_CGA; } | EGA { config.cardtype = CARD_EGA; } | NONE { config.cardtype = CARD_NONE; } | CHIPSET CHIPSET_TYPE { config.chipset = $2; c_printf("CHIPSET: %d\n", $2); config.pci_video=1; } | MEMSIZE expression { config.gfxmemsize = $2; } | GRAPHICS { config.vga = 1; } | CONSOLE { config.console_video = 1; } | FULLREST { config.fullrestore = 1; } | PARTREST { config.fullrestore = 0; } | VBIOS_FILE string_expr { free(config.vbios_file); config.vbios_file = $2; config.mapped_bios = 1; config.vbios_copy = 0; } | VBIOS_COPY { free(config.vbios_file); config.vbios_file = NULL; config.mapped_bios = 1; config.vbios_copy = 1; } | VBIOS_MMAP { free(config.vbios_file); config.vbios_file = NULL; config.mapped_bios = 1; config.vbios_copy = 1; } | VBIOS_SEG expression { config.vbios_seg = $2; c_printf("CONF: VGA-BIOS-Segment %x\n", $2); if (($2 != 0xe000) && ($2 != 0xc000)) { config.vbios_seg = detect_vbios_seg(); if (config.vbios_seg == -1) config.vbios_seg = 0xc000; c_printf("CONF: VGA-BIOS-Segment set to 0x%x\n", config.vbios_seg); } } | VBIOS_SIZE_TOK expression { config.vbios_size = $2; c_printf("CONF: VGA-BIOS-Size %x\n", $2); if (($2 != 0x8000) && ($2 != 0x10000)) { config.vbios_size = detect_vbios_size(); if (config.vbios_size == -1) config.vbios_size = 0x10000; c_printf("CONF: VGA-BIOS-Size set to 0x%x\n", config.vbios_size); } } | VBIOS_POST { config.vbios_post = 1; } | DUALMON { config.dualmon = 1; } | FORCE_VT_SWITCH { config.force_vt_switch = 1; } | PCI { config.pci_video = 1; } | STRING { yyerror("unrecognized video option '%s'", $1); free($1); } | error ; /* terminal */ term_flags : term_flag | term_flags term_flag ; term_flag : ESCCHAR expression { config.term_esc_char = $2; } | UPDATEFREQ expression { config.term_updatefreq = $2; } | COLOR bool { config.term_color = ($2!=0); } | STRING { yyerror("unrecognized terminal option '%s'", $1); free($1); } | error ; /* method_val : FAST { $$ = METHOD_FAST; } */ /* | NCURSES { $$ = METHOD_NCURSES; } */ /* ; */ /* debugging */ debug_flags : debug_flag | debug_flags debug_flag ; debug_flag : VIDEO bool { set_debug_level('v', ($2!=0)); } | L_OFF { set_debug_level('a', 0); } | SERIAL bool { set_debug_level('s', ($2!=0)); } | CONFIG bool { set_debug_level('c', ($2!=0)); } | DISK bool { set_debug_level('d', ($2!=0)); } | READ bool { set_debug_level('R', ($2!=0)); } | WRITE bool { set_debug_level('W', ($2!=0)); } | KEYB bool { set_debug_level('k', ($2!=0)); } | KEYBOARD bool { set_debug_level('k', ($2!=0)); } | PRINTER bool { set_debug_level('p', ($2!=0)); } | IO bool { set_debug_level('i', ($2!=0)); } | PORT bool { set_debug_level('i', ($2!=0)); } | WARNING bool { set_debug_level('w', ($2!=0)); } | GENERAL bool { set_debug_level('g', ($2!=0)); } | L_XMS bool { set_debug_level('X', ($2!=0)); } | L_DPMI bool { set_debug_level('M', ($2!=0)); } | MOUSE bool { set_debug_level('m', ($2!=0)); } | HARDWARE bool { set_debug_level('h', ($2!=0)); } | L_IPC bool { set_debug_level('I', ($2!=0)); } | L_EMS bool { set_debug_level('E', ($2!=0)); } | NETWORK bool { set_debug_level('n', ($2!=0)); } | L_X bool { set_debug_level('X', ($2!=0)); } | SOUND bool { set_debug_level('S', ($2!=0)); } | JOYSTICK bool { set_debug_level('j', ($2!=0)); } | STRING { yyerror("unrecognized debug flag '%s'", $1); free($1); } | error ; /* mouse */ mouse_flags : mouse_flag | mouse_flags mouse_flag ; mouse_flag : DEVICE string_expr { free(mptr->dev); mptr->dev = $2; } | INTERNALDRIVER { mptr->intdrv = TRUE; } | EMULATE3BUTTONS { mptr->emulate3buttons = TRUE; } | BAUDRATE expression { mptr->baudRate = $2; } | CLEARDTR { if (mptr->type == MOUSE_MOUSESYSTEMS) mptr->cleardtr = TRUE; else yyerror("option CLEARDTR is only valid for MicroSystems-mice"); } | MICROSOFT { mptr->type = MOUSE_MICROSOFT; mptr->flags = CS7 | CREAD | CLOCAL | HUPCL; } | MS3BUTTON { mptr->type = MOUSE_MS3BUTTON; mptr->flags = CS7 | CREAD | CLOCAL | HUPCL; } | MOUSESYSTEMS { mptr->type = MOUSE_MOUSESYSTEMS; mptr->flags = CS8 | CREAD | CLOCAL | HUPCL; /* is cstopb needed? mptr->flags = CS8 | CSTOPB | CREAD | CLOCAL | HUPCL; */ } | MMSERIES { mptr->type = MOUSE_MMSERIES; mptr->flags = CS8 | PARENB | PARODD | CREAD | CLOCAL | HUPCL; } | LOGITECH { mptr->type = MOUSE_LOGITECH; mptr->flags = CS8 | CSTOPB | CREAD | CLOCAL | HUPCL; } | PS2 { mptr->type = MOUSE_PS2; mptr->flags = 0; } | IMPS2 { mptr->type = MOUSE_IMPS2; mptr->flags = 0; } | MOUSEMAN { mptr->type = MOUSE_MOUSEMAN; mptr->flags = CS7 | CREAD | CLOCAL | HUPCL; } | HITACHI { mptr->type = MOUSE_HITACHI; mptr->flags = CS8 | CREAD | CLOCAL | HUPCL; } | BUSMOUSE { mptr->type = MOUSE_BUSMOUSE; mptr->flags = 0; } | STRING { yyerror("unrecognized mouse flag '%s'", $1); free($1); } | error ; /* keyboard */ keyboard_flags : keyboard_flag | keyboard_flags keyboard_flag ; keyboard_flag : LAYOUT KEYB_LAYOUT { keyb_layout($2); } | LAYOUT KEYB_LAYOUT {keyb_layout($2);} '{' keyboard_mods '}' | LAYOUT L_NO { keyb_layout(KEYB_NO); } | LAYOUT L_AUTO { keyb_layout(-1); } | RAWKEYBOARD bool { config.console_keyb = $2; } | STRING { yyerror("unrecognized keyboard flag '%s'", $1); free($1);} | error ; keyboard_mods : keyboard_mod | keyboard_mods keyboard_mod ; keyboard_mod : expression '=' { keyb_mod(' ', $1, 0); } keyboard_modvals | SHIFT_MAP expression '=' { keyb_mod('S', $2, 0); } keyboard_modvals | ALT_MAP expression '=' { keyb_mod('A', $2, 0); } keyboard_modvals | NUMPAD_MAP expression '=' { keyb_mod('N', $2, 0); } keyboard_modvals ; keyboard_modvals: keyboard_modval | keyboard_modvals ',' keyboard_modval ; keyboard_modval : INTEGER { keyb_mod(0, $1, 0); } | '(' expression ')' { keyb_mod(0, $2, 0); } | STRING { char *p = $1; while (*p) keyb_mod(0, *p++, 0); free($1); } ; /* lock files */ ttylocks_flags : ttylocks_flag | ttylocks_flags ttylocks_flag ; ttylocks_flag : DIRECTORY string_expr { free(config.tty_lockdir); config.tty_lockdir = $2; } | NAMESTUB string_expr { free(config.tty_lockfile); config.tty_lockfile = $2; } | BINARY { config.tty_lockbinary = TRUE; } | STRING { yyerror("unrecognized ttylocks flag '%s'", $1); free($1); } | error ; /* serial ports */ serial_flags : serial_flag | serial_flags serial_flag ; serial_flag : DEVICE string_expr { free(sptr->dev); sptr->dev = $2; if (!strcmp("/dev/mouse",sptr->dev)) sptr->mouse=1; } | VIRTUAL { if (isatty(0)) { sptr->virtual = TRUE; sptr->pseudo = TRUE; no_local_video = 1; sptr->dev = strdup(ttyname(0)); } else { error("FD 0 is not a tty, can't " "use a virtual com port\n"); exit(1); } } | COM expression { sptr->real_comport = $2; com_port_used[$2] = 1; } | BASE expression { sptr->base_port = $2; } | IRQ expression { sptr->interrupt = $2; } | INTERRUPT expression { sptr->interrupt = $2; } | MOUSE { sptr->mouse = 1; } | STRING { yyerror("unrecognized serial flag '%s'", $1); free($1); } | error ; /* printer */ printer_flags : printer_flag | printer_flags printer_flag ; printer_flag : COMMAND string_expr { pptr->prtcmd = $2; } | TIMEOUT expression { pptr->delay = $2; } | L_FILE string_expr { pptr->dev = $2; } | BASE expression { pptr->base_port = $2; } | STRING { yyerror("unrecognized printer flag %s", $1); free($1); } | error ; /* disks */ optbootfile : BOOTFILE string_expr { if (dptr->boot_name != NULL) yyerror("Two names for a boot-image file or device given."); dptr->boot_name = $2; } | /* empty */ ; disk_flags : disk_flag | disk_flags disk_flag ; disk_flag : READONLY { dptr->wantrdonly = 1; } | THREEINCH { dptr->default_cmos = THREE_INCH_FLOPPY; } | THREEINCH_2880 { dptr->default_cmos = THREE_INCH_288MFLOP; } | THREEINCH_720 { dptr->default_cmos = THREE_INCH_720KFLOP; } | ATAPI { dptr->default_cmos = ATAPI_FLOPPY; } | FIVEINCH { dptr->default_cmos = FIVE_INCH_FLOPPY; } | DISKCYL4096 { dptr->diskcyl4096 = 1; } | SECTORS expression { dptr->sectors = $2; } | CYLINDERS expression { dptr->tracks = $2; } | TRACKS expression { dptr->tracks = $2; } | HEADS expression { dptr->heads = $2; } | OFFSET expression { dptr->header = $2; } | DEVICE string_expr optbootfile { if (dptr->dev_name != NULL) yyerror("Two names for a disk-image file or device given."); dptr->dev_name = $2; } | L_FILE string_expr { if (dptr->dev_name != NULL) yyerror("Two names for a disk-image file or device given."); dptr->dev_name = $2; } | HDIMAGE string_expr { if (dptr->dev_name != NULL) yyerror("Two names for a harddisk-image file given."); dptr->type = IMAGE; dptr->header = HEADER_SIZE; dptr->dev_name = $2; } | WHOLEDISK STRING optbootfile { if (priv_lvl) yyerror("Can not use DISK/WHOLEDISK in the user config file\n"); if (dptr->dev_name != NULL) yyerror("Two names for a harddisk given."); dptr->type = HDISK; dptr->dev_name = $2; } | L_FLOPPY string_expr { if (dptr->dev_name != NULL) yyerror("Two names for a floppy-device given."); dptr->type = FLOPPY; dptr->dev_name = $2; } | L_PARTITION string_expr INTEGER optbootfile { yywarn("{ partition \"%s\" %d } the" " token '%d' is ignored and can be removed.", $2,$3,$3); do_part($2); } | L_PARTITION string_expr optbootfile { do_part($2); } | DIRECTORY string_expr { if (dptr->dev_name != NULL) yyerror("Two names for a directory given."); dptr->type = DIR_TYPE; dptr->dev_name = $2; } | STRING { yyerror("unrecognized disk flag '%s'\n", $1); free($1); } | error ; /* i/o ports */ port_flags : port_flag | port_flags port_flag ; port_flag : INTEGER { c_printf("CONF: I/O port 0x%04x\n", (unsigned short)$1); allow_io($1, 1, ports_permission, ports_ormask, ports_andmask, portspeed, (char*)dev_name); if (abs(portspeed)) portspeed += ((portspeed>0) ? 1 : -1); } | '(' expression ')' { allow_io($2, 1, ports_permission, ports_ormask, ports_andmask, portspeed, (char*)dev_name); if (abs(portspeed)) portspeed += ((portspeed>0) ? 1 : -1); } | RANGE INTEGER INTEGER { if (abs(portspeed) > 1) portspeed = 0; c_printf("CONF: range of I/O ports 0x%04x-0x%04x\n", (unsigned short)$2, (unsigned short)$3); allow_io($2, $3 - $2 + 1, ports_permission, ports_ormask, ports_andmask, portspeed, (char*)dev_name); portspeed=0; strcpy(dev_name,""); } | RANGE expression ',' expression { if (abs(portspeed) > 1) portspeed = 0; c_printf("CONF: range of I/O ports 0x%04x-0x%04x\n", (unsigned short)$2, (unsigned short)$4); allow_io($2, $4 - $2 + 1, ports_permission, ports_ormask, ports_andmask, portspeed, (char*)dev_name); portspeed=0; strcpy(dev_name,""); } | RDONLY { ports_permission = IO_READ; } | WRONLY { ports_permission = IO_WRITE; } | RDWR { ports_permission = IO_RDWR; } | ORMASK expression { ports_ormask = $2; } | ANDMASK expression { ports_andmask = $2; } | FAST { portspeed = 1; } | SLOW { portspeed = -1; } | DEVICE string_expr { strcpy(dev_name,$2); free($2); } | STRING { yyerror("unrecognized port command '%s'", $1); free($1); } | error ; trace_port_flags : trace_port_flag | trace_port_flags trace_port_flag ; trace_port_flag : INTEGER { register_port_traceing($1, $1); } | '(' expression ')' { register_port_traceing($2, $2); } | RANGE INTEGER INTEGER { register_port_traceing($2, $3); } | RANGE expression ',' expression { register_port_traceing($2, $4); } | CLEAR { clear_port_traceing(); } | STRING { yyerror("unrecognized port trace command '%s'", $1); free($1); } | error ; /* IRQ definition for Silly Interrupt Generator */ sillyint_flags : sillyint_flag | sillyint_flags sillyint_flag ; sillyint_flag : INTEGER { set_irq_value(1, $1); } | '(' expression ')' { set_irq_value(1, $2); } | USE_SIGIO expression { set_irq_value(0x10001, $2); } | RANGE INTEGER INTEGER { set_irq_range(1, $2, $3); } | RANGE expression ',' expression { set_irq_range(1, $2, $4); } | USE_SIGIO RANGE INTEGER INTEGER { set_irq_range(0x10001, $3, $4); } | USE_SIGIO RANGE expression ',' expression { set_irq_range(0x10001, $3, $5); } | STRING { yyerror("unrecognized irqpassing command '%s'", $1); free($1); } | error ; /* EMS definitions */ ems_flags : ems_flag | ems_flags ems_flag ; ems_flag : INTEGER { config.ems_size = $1; if ($1 > 0) c_printf("CONF: %dk bytes EMS memory\n", $1); } | '(' expression ')' { config.ems_size = $2; if ($2 > 0) c_printf("CONF: %dk bytes EMS memory\n", $2); } | EMS_SIZE expression { config.ems_size = $2; if ($2 > 0) c_printf("CONF: %dk bytes EMS memory\n", $2); } | EMS_FRAME expression { /* is there a technical reason why the EMS frame can't be at 0xC0000 or 0xA0000 if there's space? */ #if 0 if ( (($2 & 0xfc00)>=0xc800) && (($2 & 0xfc00)<=0xe000) ) { config.ems_frame = $2 & 0xfc00; c_printf("CONF: EMS-frame = 0x%04x\n", config.ems_frame); } else yyerror("wrong EMS-frame: 0x%04x", $2); #endif config.ems_frame = $2 & 0xfc00; c_printf("CONF: EMS-frame = 0x%04x\n", config.ems_frame); } | STRING { yyerror("unrecognized ems command '%s'", $1); free($1); } | error ; /* memory areas to spare for hardware (adapter) ram */ hardware_ram_flags : hardware_ram_flag | hardware_ram_flags hardware_ram_flag ; hardware_ram_flag : INTEGER { if (!register_hardware_ram('h', $1, PAGE_SIZE)) { yyerror("wrong hardware ram address : 0x%08x", $1); } } | '(' expression ')' { if (!register_hardware_ram('h', $2, PAGE_SIZE)) { yyerror("wrong hardware ram address : 0x%08x", $2); } } | RANGE INTEGER INTEGER { if (register_hardware_ram('h', $2, $3 - $2)) c_printf("CONF: hardware ram pages at 0x%08x-%08x\n", $2, $3); else yyerror("wrong hardware ram address : 0x%08x", $2); } | RANGE expression ',' expression { if (register_hardware_ram('h', $2, $4 - $2)) c_printf("CONF: hardware ram pages at 0x%08x-%08x\n", $2, $4); else yyerror("wrong hardware ram address : 0x%08x", $2); } | STRING { yyerror("unrecognized hardware ram command '%s'", $1); free($1); } | error ; /* booleans */ bool: expression ; floppy_bool: expression ; int_bool: expression { if ($1 == -2) { yyerror("got 'on', expected 'off' or an integer"); } } ; irq_bool: expression { if ( $1 && (($1 < 2) || ($1 > 15)) ) { yyerror("got '%d', expected 'off' or an integer 2..15", $1); } } ; /* speaker values */ speaker : L_OFF { $$ = SPKR_OFF; } | NATIVE { $$ = SPKR_NATIVE; } | EMULATED { $$ = SPKR_EMULATED; } | STRING { yyerror("got '%s', expected 'emulated' or 'native'", $1); free($1); } | error { yyerror("expected 'emulated' or 'native'"); } ; /* cpuemu value */ cpuemu : L_OFF { $$ = 0; } | VM86 { $$ = 3; } | FULL { $$ = 4; } | STRING { yyerror("got '%s', expected 'off', 'vm86' or 'full'", $1); free($1); } | error { yyerror("expected 'off', 'vm86' or 'full'"); } ; %% /* features */ static void handle_features(int which, int value) { if ((which < 0) || (which >= (sizeof(config.features) / sizeof(config.features[0])))) { c_printf("CONF: wrong feature number %d\n", which); return; } config.features[which] = value; c_printf("CONF: feature %d set to %d\n", which, value); } /* joystick */ static void set_joy_device(char *devstring) { char *spacepos; free(config.joy_device[0]); config.joy_device[0] = devstring; config.joy_device[1] = NULL; spacepos = strchr(devstring, ' '); if (spacepos != NULL) { *spacepos = '\0'; config.joy_device[1] = spacepos + 1; } } /* mouse */ static void start_mouse(void) { mptr = &config.mouse; mptr->fd = -1; } static void stop_mouse(void) { c_printf("MOUSE: %s, type %x using internaldriver: %s, emulate3buttons: %s baudrate: %d\n", mptr->dev && mptr->dev[0] ? mptr->dev : "no device specified", mptr->type, mptr->intdrv ? "yes" : "no", mptr->emulate3buttons ? "yes" : "no", mptr->baudRate); } /* debug */ static void start_ports(void) { if (priv_lvl) yyerror("Can not change port privileges in user config file"); ports_permission = IO_RDWR; ports_ormask = 0; ports_andmask = 0xFFFF; } /* debug */ static void start_debug(void) { set_debug_level('a', 0); /* Default is no debugging output at all */ } /* video */ static void start_video(void) { free(config.vbios_file); config.vbios_file = NULL; config.vbios_copy = 0; config.vbios_seg = 0xc000; config.vbios_size = 0x10000; config.console_video = 0; config.cardtype = CARD_NONE; config.chipset = PLAINVGA; config.mapped_bios = 0; config.vga = 0; config.gfxmemsize = 256; config.fullrestore = 0; config.dualmon = 0; config.force_vt_switch = 0; } static void stop_video(void) { if (priv_lvl) config.console_video = 0; if ((config.cardtype != CARD_VGA) || !config.console_video) { config.vga = 0; } if (config.vga) { config.mapped_bios = 1; config.console_video = 1; } } /* vesa modes */ static void set_vesamodes(int width, int height, int color_bits) { vesamode_type *vmt = malloc(sizeof *vmt); if(vmt != NULL) { vmt->width = width; vmt->height = height; vmt->color_bits = color_bits; vmt->next = config.vesamode_list; config.vesamode_list = vmt; } } /* vbios detection */ static int auto_vbios_seg = -1; static int auto_vbios_size = -1; static void detect_vbios(void) { unsigned char c[0x21]; int foffset; if (auto_vbios_seg != -1) return; if (!can_do_root_stuff || config.vbios_file) return; for (foffset = 0xc0000; foffset < 0xf0000; foffset += 0x800) { load_file("/dev/mem", foffset, c, sizeof(c)); if (c[0]==0x55 && c[1]==0xaa && c[0x1e]=='I' && c[0x1f]=='B' && c[0x20]=='M') { auto_vbios_seg = foffset >> 4; auto_vbios_size = c[2]*0x200; break; } } } static int detect_vbios_seg(void) { detect_vbios(); return auto_vbios_seg; } static int detect_vbios_size(void) { detect_vbios(); return auto_vbios_size; } static void stop_ttylocks(void) { c_printf("SER: directory %s namestub %s binary %s\n", config.tty_lockdir, config.tty_lockfile,(config.tty_lockbinary?"Yes":"No")); } /* serial */ static void start_serial(void) { if (c_ser >= MAX_SER) sptr = &nullser; else { /* The defaults for interrupt, base_port, real_comport and dev are ** automatically filled in inside the do_ser_init routine of serial.c */ sptr = &com[c_ser]; sptr->dev = NULL; sptr->interrupt = 0; sptr->base_port = 0; sptr->real_comport = 0; sptr->fd = -1; sptr->mouse = 0; sptr->virtual = FALSE; sptr->pseudo = FALSE; } } static void stop_serial(void) { if (sptr->dev[0] == '\0') return; if (c_ser >= MAX_SER) { c_printf("SER: too many ports, ignoring %s\n", sptr->dev); return; } c_ser++; config.num_ser = c_ser; c_printf("SER: %s port %x int %x\n", sptr->dev, sptr->base_port, sptr->interrupt); } /* keyboard */ static int keyboard_statement_already = 0; static void start_keyboard(void) { keyb_layout(KEYB_USER); /* NOTE: the default has changed, --Hans, 971204 */ config.console_keyb = 0; keyboard_statement_already = 1; } static void stop_terminal(void) { if (config.term_updatefreq > 100) { yywarn("terminal updatefreq too large (too slow)!"); config.term_updatefreq = 100; } } /* printer */ static void start_printer(void) { pptr->prtcmd = NULL; pptr->dev = NULL; pptr->file = NULL; pptr->remaining = -1; pptr->delay = 10; } static void stop_printer(void) { printer_config(c_printers, pptr); c_printf("CONF(LPT%d) f: %s c: %s t: %d port: %x\n", c_printers, pptr->dev, pptr->prtcmd, pptr->delay, pptr->base_port); c_printers++; config.num_lpt = c_printers; } /* disk */ static void start_bootdisk(void) { if (config.bootdisk) /* Already a bootdisk configured ? */ yyerror("There is already a bootdisk configured"); dptr = &bootdisk; /* set pointer do bootdisk-struct */ dptr->diskcyl4096 = 0; dptr->sectors = 0; /* setup default-values */ dptr->heads = 0; dptr->tracks = 0; dptr->type = FLOPPY; dptr->default_cmos = THREE_INCH_FLOPPY; dptr->timeout = 0; dptr->dev_name = NULL; /* default-values */ dptr->boot_name = NULL; dptr->wantrdonly = 0; dptr->header = 0; } static void start_floppy(void) { if (c_fdisks >= MAX_FDISKS) { yyerror("There are too many floppy disks defined"); dptr = &nulldisk; /* Dummy-Entry to avoid core-dumps */ } else dptr = &disktab[c_fdisks]; dptr->sectors = 0; /* setup default values */ dptr->heads = 0; dptr->tracks = 0; dptr->type = FLOPPY; dptr->default_cmos = THREE_INCH_FLOPPY; dptr->timeout = 0; dptr->dev_name = NULL; /* default-values */ dptr->boot_name = NULL; dptr->wantrdonly = 0; dptr->header = 0; } static void start_disk(void) { if (c_hdisks >= MAX_HDISKS) { yyerror("There are too many hard disks defined"); dptr = &nulldisk; /* Dummy-Entry to avoid core-dumps */ } else dptr = &hdisktab[c_hdisks]; dptr->type = NODISK; dptr->sectors = -1; dptr->heads = -1; dptr->tracks = -1; dptr->timeout = 0; dptr->dev_name = NULL; /* default-values */ dptr->boot_name = NULL; dptr->wantrdonly = 0; dptr->header = 0; dptr->dexeflags = 0; } static void start_vnet(char *mode) { if (strcmp(mode, "tap") == 0) { config.vnet = VNET_TYPE_TAP; return; } IFCLASS(CL_NET); if (strlen(mode) == 0 || strcmp(mode, "direct") == 0) config.vnet = VNET_TYPE_ETH; else if (strcmp(mode, "dosnet") == 0) config.vnet = VNET_TYPE_DSN; else { error("Unknown vnet mode \"%s\"\n", mode); config.exitearly = 1; } } static void do_part(char *dev) { if (dptr->dev_name != NULL) yyerror("Two names for a partition given."); dptr->type = PARTITION; dptr->dev_name = dev; #ifdef __linux__ dptr->part_info.number = atoi(dptr->dev_name+8); #endif if (dptr->part_info.number == 0) yyerror("%s must be a PARTITION, can't find number suffix!\n", dptr->dev_name); } static void stop_disk(int token) { #ifdef __linux__ FILE *f; struct mntent *mtab; #endif int mounted_rw; if (dexe_running && dexe_forbid_disk) return; if (dptr == &nulldisk) /* is there any disk? */ return; /* no, nothing to do */ if (!dptr->dev_name) /* Is there a file/device-name? */ yyerror("disk: no device/file-name given!"); else /* check the file/device for existance */ { struct stat file_status; /* date for checking that file */ c_printf("device: %s ", dptr->dev_name); if (stat(dptr->dev_name,&file_status) != 0) /* Does this file exist? */ yyerror("Disk-device/file %s doesn't exist.",dptr->dev_name); } if (dptr->type == NODISK) /* Is it one of bootdisk, floppy, harddisk ? */ yyerror("disk: no device/file-name given!"); /* No, error */ else c_printf("type %d ", dptr->type); if (dptr->type == PARTITION) { c_printf("partition# %d ", dptr->part_info.number); #ifdef __linux__ mtab = NULL; if ((f = setmntent(MOUNTED, "r")) != NULL) { while ((mtab = getmntent(f))) if (!strcmp(dptr->dev_name, mtab->mnt_fsname)) break; endmntent(f); } if (mtab) { mounted_rw = ( hasmntopt(mtab, MNTOPT_RW) != NULL ); if (mounted_rw && !dptr->wantrdonly) yyerror("\n\nYou specified '%s' for read-write Direct Partition Access," "\nit is currently mounted read-write on '%s' !!!\n", dptr->dev_name, mtab->mnt_dir); else if (mounted_rw) yywarn("You specified '%s' for read-only Direct Partition Access," "\n it is currently mounted read-write on '%s'.\n", dptr->dev_name, mtab->mnt_dir); else if (!dptr->wantrdonly) yywarn("You specified '%s' for read-write Direct Partition Access," "\n it is currently mounted read-only on '%s'.\n", dptr->dev_name, mtab->mnt_dir); } #endif } if (dptr->header) c_printf("header_size: %ld ", (long) dptr->header); c_printf("h: %d s: %d t: %d", dptr->heads, dptr->sectors, dptr->tracks); if (token == BOOTDISK) { config.bootdisk = 1; use_bootdisk = 1; c_printf(" bootdisk\n"); } else if (token == L_FLOPPY) { c_printf(" floppy %c:\n", 'A'+c_fdisks); c_fdisks++; config.fdisks = c_fdisks; } else { c_printf(" drive %c:\n", 'C'+c_hdisks); c_hdisks++; config.hdisks = c_hdisks; } } /* keyboard */ void keyb_layout(int layout) { struct keytable_entry *kt = keytable_list; if (layout == -1) { /* auto: do it later */ config.keytable = NULL; return; } while (kt->name) { if (kt->keyboard == layout) { if (kt->flags & KT_ALTERNATE) { c_printf("CONF: Alternate keyboard-layout %s\n", kt->name); config.altkeytable = kt; } else { c_printf("CONF: Keyboard-layout %s\n", kt->name); config.keytable = kt; } return; } kt++; } c_printf("CONF: ERROR -- Keyboard has incorrect number!!!\n"); } static void keytable_start(int layout) { static struct keytable_entry *saved_kt = 0; if (layout == -1) { if (keyboard_statement_already) { if (config.keytable != saved_kt) { yywarn("keytable changed to %s table, but previously was defined %s\n", config.keytable->name, saved_kt->name); } } } else { saved_kt = config.keytable; keyb_layout(layout); } } static void keytable_stop(void) { keytable_start(-1); } static void dump_keytables_to_file(char *name) { FILE * f; struct keytable_entry *kt = keytable_list; f = fopen(name, "w"); if (!f) { error("cannot create keytable file %s\n", name); exit(1); } while (kt->name) { dump_keytable(f, kt); kt++; } fclose(f); exit(0); } static void set_irq_value(int bits, int i1) { if ((i1>2) && (i1<=15)) { config.sillyint |= (bits << i1); c_printf("CONF: IRQ %d for irqpassing", i1); if (bits & 0x10000) c_printf(" uses SIGIO\n"); else c_printf("\n"); } else yyerror("wrong IRQ for irqpassing command: %d", i1); } static void set_irq_range(int bits, int i1, int i2) { int i; if ( (i1<3) || (i1>15) || (i2<3) || (i2>15) || (i1 > i2 ) ) { yyerror("wrong IRQ range for irqpassing command: %d .. %d", i1, i2); } else { for (i=i1; i<=i2; i++) config.sillyint |= (bits << i); c_printf("CONF: range of IRQs for irqpassing %d .. %d", i1, i2); if (bits & 0x10000) c_printf(" uses SIGIO\n"); else c_printf("\n"); } } /* errors & warnings */ void yywarn(char* string, ...) { va_list vars; va_start(vars, string); fprintf(stderr, "Warning: "); vfprintf(stderr, string, vars); fprintf(stderr, "\n"); va_end(vars); warnings++; } void yyerror(char* string, ...) { va_list vars; va_start(vars, string); if (include_stack_ptr != 0 && !last_include) { int i; fprintf(stderr, "In file included from %s:%d\n", include_fnames[0], include_lines[0]); for(i = 1; i < include_stack_ptr; i++) { fprintf(stderr, " from %s:%d\n", include_fnames[i], include_lines[i]); } last_include = 1; } fprintf(stderr, "Error in %s: (line %.3d) ", include_fnames[include_stack_ptr], line_count); vfprintf(stderr, string, vars); fprintf(stderr, "\n"); va_end(vars); errors++; } /* * open_file - opens the configuration-file named *filename and returns * a file-pointer. The error/warning-counters are reset to zero. */ static FILE *open_file(char *filename) { errors = 0; /* Reset error counter */ warnings = 0; /* Reset counter for warnings */ if (!filename) return 0; return fopen(filename, "r"); /* Open config-file */ } /* * close_file - Close the configuration file and issue a message about * errors/warnings that occured. If there were errors, the * flag early-exit is that, so dosemu won't really. */ static void close_file(FILE * file) { if (file) fclose(file); /* Close the config-file */ if(errors) fprintf(stderr, "%d error(s) detected while parsing the configuration-file\n", errors); if(warnings) fprintf(stderr, "%d warning(s) detected while parsing the configuration-file\n", warnings); if (errors != 0) /* Exit dosemu on errors */ { config.exitearly = TRUE; } } /* write_to_syslog */ static void write_to_syslog(char *message) { openlog("dosemu", LOG_PID, LOG_USER | LOG_NOTICE); syslog(LOG_PID | LOG_USER | LOG_NOTICE, message); closelog(); } static void move_dosemu_lib_dir(char *path) { if (dosemu_lib_dir_path != path) { if (dosemu_lib_dir_path != dosemulib_default) free(dosemu_lib_dir_path); dosemu_lib_dir_path = strdup(path); } setenv("DOSEMU_LIB_DIR", dosemu_lib_dir_path, 1); if (keymap_load_base_path != keymaploadbase_default) free(keymap_load_base_path); keymap_load_base_path = assemble_path(path, "", 0); } static FILE *open_dosemu_users(void) { FILE *fp; fp = open_file(DOSEMU_USERS_FILE); if (fp) return fp; return 0; } static void setup_home_directories(void) { setenv("DOSEMU_HDIMAGE_DIR", dosemu_hdimage_dir_path, 1); LOCALDIR = get_dosemu_local_home(); RUNDIR = mkdir_under(LOCALDIR, "run", 0); DOSEMU_MIDI_PATH = assemble_path(RUNDIR, DOSEMU_MIDI, 0); DOSEMU_MIDI_IN_PATH = assemble_path(RUNDIR, DOSEMU_MIDI_IN, 0); } static void lax_user_checking(void) { char *p; define_config_variable("c_all"); move_dosemu_lib_dir(dosemu_lib_dir_path); p = getenv("USER"); if (!p) p = "guest"; setenv("DOSEMU_USER", p, 1); setenv("DOSEMU_REAL_USER", p, 1); setup_home_directories(); } /* Parse TimeMode, Paul Crawford and Andrew Brooks 2004-08-01 */ /* Accepts "bios", "pit", "linux", default is "bios". Use with TIMEMODE token */ int parse_timemode(const char *timemodestr) { if (timemodestr == NULL || timemodestr[0] == '\0') return(TM_BIOS); /* default */ if (strcmp(timemodestr, "linux")==0) return(TM_LINUX); if (strcmp(timemodestr, "pit")==0) return(TM_PIT); if (strcmp(timemodestr, "bios")==0) return(TM_BIOS); yyerror("Unrecognised time mode (not bios, pit or linux)"); return(TM_BIOS); } /* Parse Users for DOSEMU, by Alan Hourihane, alanh@fairlite.demon.co.uk */ /* Jan-17-1996: Erik Mouw (J.A.K.Mouw@et.tudelft.nl) * - added logging facilities * In 1998: Hans * - havy changes and re-arangments */ void parse_dosemu_users(void) { #define ALL_USERS "all" #define PBUFLEN 256 FILE *volatile fp; struct passwd *pwd; char buf[PBUFLEN]; int userok = 0; char *ustr; int log_syslog=0; int uid; int have_vars=0; /* We come here _very_ early (at top of main()) to avoid security conflicts. * priv_init() has already been called, but nothing more. * * We will exit, if /etc/dosemu.users says that the user has no right * to run a suid root dosemu (and we are on), but will continue, if the user * is running a non-suid copy _and_ is mentioned in dosemu users. * The dosemu.users entry for such a user is: * * joeodd ... nosuidroot * * The functions we call rely on the following setting: */ after_secure_check = 0; priv_lvl = 0; /* We first have to find out where the dosemu.users file is placed * by the system administrator. * We first look for /etc/dosemu.users, then for /etc/dosemu/dosemu.users * Once we know that, most other pathes are runtime configurable: * * - dosemu.users tells were to find the dosemu-lib-dir * (DOSEMULIB_DEFAULT per default) * - global.conf includes $DOSEMU_CONF_DIR/dosemu.conf * (which could be changed by the owner of global.conf). */ if (DOSEMU_USERS_FILE == NULL) { setenv("DOSEMU_CONF_DIR", "", 1); } else if (!exists_file(DOSEMU_USERS_FILE)) { DOSEMU_USERS_FILE = ALTERNATE_ETC "/" DOSEMU_USERS; DOSEMU_LOGLEVEL_FILE = ALTERNATE_ETC "/" DOSEMU_LOGLEVEL; setenv("DOSEMU_CONF_DIR", ALTERNATE_ETC, 1); } else setenv("DOSEMU_CONF_DIR", "/etc", 1); /* we check for some vital global settings * which we need before proceeding */ setenv("DOSEMU_LIB_DIR", dosemulib_default, 1); fp = open_dosemu_users(); if (fp) while (fgets(buf, PBUFLEN, fp) != NULL) { int l = strlen(buf); if (l && (buf[l-1] == '\n')) buf[l-1] = 0; ustr = strtok(buf, " \t\n=,;:"); if (ustr && (ustr[0] != '#')) { if (!strcmp(ustr, "default_lib_dir")) { ustr=strtok(0, " \t\n=,;:"); if (ustr) { if (!exists_dir(ustr)) { char *tx = "default_lib_dir %s does not exist\n"; fprintf(stderr, tx, ustr); fprintf(stdout, tx, ustr); exit(1); } move_dosemu_lib_dir(ustr); } } if (!strcmp(ustr, "default_hdimage_dir")) { ustr=strtok(0, " \t\n=,;:"); if (ustr) { if (!exists_dir(ustr)) { char *tx = "default_hdimage_dir %s does not exist\n"; fprintf(stderr, tx, ustr); fprintf(stdout, tx, ustr); exit(1); } if (dosemu_hdimage_dir_path != dosemuhdimage_default) free(dosemu_hdimage_dir_path); dosemu_hdimage_dir_path = strdup(ustr); dexe_load_path = dosemu_hdimage_dir_path; } } else if (!strcmp(ustr, "log_level")) { int ll = 0; ustr=strtok(0, " \t\n=,;:"); if (ustr) { ll = atoi(ustr); if (ll < 0) ll = 0; if (ll > 2) ll = 2; } log_syslog = ll; } else if (!strcmp(ustr, "config_script")) { ustr=strtok(0, " \t\n=,;:"); if (ustr && strcmp(ustr, DEFAULT_CONFIG_SCRIPT)) { config_script_path = strdup(ustr); if (!exists_file(config_script_path)) { char *tx = "config_script %s does not exist\n"; fprintf(stderr, tx, ustr); fprintf(stdout, tx, ustr); exit(1); } config_script_name = strdup(ustr); } } } } if (fp) fclose(fp); /* We want to test if the _user_ is allowed to do certain privileged */ /* things, o check if the username connected to the get_orig_uid() is */ /* in the DOSEMU_USERS_FILE file (usually /etc/dosemu.users). */ uid = get_orig_uid(); pwd = getpwuid(uid); /* Sanity Check, Shouldn't be anyone logged in without a userid */ if (pwd == (struct passwd *)0) { fprintf(stderr, "Illegal User!!!\n"); sprintf(buf, "Illegal DOSEMU user: uid=%i", uid); write_to_syslog(buf); exit(1); } if (!can_do_root_stuff || under_root_login) { /* simply ignore -- we're not suid-root */ lax_user_checking(); after_secure_check = 1; return; } /* preset DOSEMU_*USER env, such that a user can't fake it */ setenv("DOSEMU_USER", "unknown", 1); setenv("DOSEMU_REAL_USER", pwd->pw_name, 1); { int can_have_privsetup = 0; fp = open_dosemu_users(); if (fp) { for(userok=0; fgets(buf, PBUFLEN, fp) != NULL && !userok; ) { int l = strlen(buf); if (l && (buf[l-1] == '\n')) buf[l-1] = 0; ustr = strtok(buf, " \t\n,;:"); if (ustr && (ustr[0] != '#')) { if (strcmp(ustr, pwd->pw_name)== 0) userok = 1; else if (strcmp(ustr, ALL_USERS)== 0) userok = 1; if (userok) { setenv("DOSEMU_USER", ustr, 1); while ((ustr=strtok(0, " \t,;:")) !=0) { if (ustr[0] == '#') break; define_config_variable(ustr); have_vars = 1; if (!strcmp(ustr,"private_setup")) { can_have_privsetup = 1; } } if (!have_vars) define_config_variable("restricted"); } } } fclose(fp); } if (can_have_privsetup && ( get_config_variable("unrestricted") || under_root_login || !can_do_root_stuff)) { /* this user is allowed to have a privat ~/.dosemu/lib * (which replaces DOSEMULIB_DEFAULT if existing). * Hence this user can have its own global.conf e.t.c. * However, this is only possible with non-suid-root * binary or when running under root loggin or when 'unrestricted' * also is set. -- Hans */ char *lpath = get_path_in_HOME(LOCALDIR_BASE_NAME "/lib"); if (exists_dir(lpath)) move_dosemu_lib_dir(lpath); free(lpath); } } define_config_variable("c_all"); if(userok==0) { define_config_variable("restricted"); } if (get_config_variable("nosuidroot") || (get_config_variable("restricted") && !is_console(0))) { fprintf(stderr, "Dropping root privileges: guest or a restricted user not on console.\n"); priv_drop(); lax_user_checking(); after_secure_check = 1; return; } /* now we setup up our local DOSEMU home directory, where we * have (among other things) all temporary stuff in * (since 0.97.10.2) */ setup_home_directories(); after_secure_check = 1; } char *commandline_statements=0; static int has_dexe_magic(char *name) { int fd, magic, ret; fd = open(name, O_RDONLY); if (fd <0) return 0; ret = (read(fd, &magic, 4) == 4) && (magic == DEXE_MAGIC); close(fd); return ret; } static int stat_dexe(char *name) { struct stat s; if (stat(name, &s)) return 0; if ( ! S_ISREG(s.st_mode)) return 0; if ((s.st_mode & S_IXUSR) && (s.st_uid == get_orig_uid())) return has_dexe_magic(name); if ((s.st_mode & S_IXGRP) && (is_in_groups(s.st_gid))) return has_dexe_magic(name); if (s.st_mode & S_IXOTH) return has_dexe_magic(name); return 0; } static char *resolve_exec_path(char *dexename, char *ext) { enum { maxn=0x255 }; static char n[maxn+1]; static char name[maxn+1]; char *p, *path=getenv("PATH"); strncpy(name,dexename,maxn); name[maxn] = 0; n[maxn] = 0; p = rindex(name, '.'); if ( ext && ((p && strcmp(p, ext)) || !p) ) strncat(name,ext,maxn); /* first try the pure file name */ if (!path || (name[0] == '/') || (!strncmp(name, "./", 2))) { if (stat_dexe(name)) return name; return 0; } /* next try the standard path for DEXE files */ snprintf(n, maxn, "%s/%s", dexe_load_path, name); if (stat_dexe(n)) return n; /* now search in the users normal PATH */ path = strdup(path); p= strtok(path,":"); while (p) { snprintf(n, maxn, "%s/%s", p, name); if (stat_dexe(n)) { free(path); return n; } p=strtok(0,":"); } free(path); return 0; } void prepare_dexe_load(char *name) { char *n, *cbuf; int fd, csize; struct image_header ihdr; n = resolve_exec_path(name, ".dexe"); if (!n) { n = resolve_exec_path(name, 0); if (!n) { error("DEXE file not found or not executable\n"); exit(1); } } /* now we extract the configuration file and the access flags */ fd = open(n, O_RDONLY); if (read(fd, &ihdr, sizeof(struct image_header)) != sizeof(struct image_header)) { error("broken DEXE format, can't read image header\n"); close(fd); exit(1); } lseek(fd, HEADER_SIZE + 0x200, SEEK_SET); /* just behind the MBR */ if ((read(fd, &csize, 4) != 4) || (csize > 0x2000)) { error("broken DEXE format, configuration not found\n"); close(fd); exit(1); } /* we use the -I option to feed in the configuration, * and we put ours in front of eventually existing options */ if (commandline_statements) { cbuf = malloc(csize+1+strlen(commandline_statements)+1); read(fd, cbuf, csize); cbuf[csize] = '\n'; strcpy(cbuf+csize+1, commandline_statements); } else { cbuf = malloc(csize+1); read(fd, cbuf, csize); cbuf[csize] = 0; } commandline_statements = cbuf; close(fd); start_disk(); dptr->type = IMAGE; dptr->header = HEADER_SIZE; dptr->dev_name = n; dptr->dexeflags = ihdr.dexeflags | DISK_IS_DEXE; stop_disk(DISK); dexe_running = 1; } static void do_parse(FILE* fp, char *confname, char *errtx) { yyin = fp; line_count = 1; include_stack_ptr = 0; c_printf("CONF: Parsing %s file.\n", confname); file_being_parsed = strdup(confname); include_fnames[include_stack_ptr] = file_being_parsed; yyrestart(fp); if (yyparse()) yyerror(errtx, confname); close_file(fp); include_stack_ptr = 0; include_fnames[include_stack_ptr] = 0; free(file_being_parsed); } int parse_config(char *confname, char *dosrcname) { FILE *fd; int is_user_config; #if YYDEBUG != 0 yydebug = 1; #endif define_config_variable(PARSER_VERSION_STRING); /* Let's try confname if not null, and fail if not found */ /* Else try the user's own .dosrc (old) or .dosemurc (new) */ /* If that doesn't exist we will default to CONFIG_FILE */ { if (!dosrcname) { char *name = get_path_in_HOME(DOSEMU_RC); setenv("DOSEMU_RC", name, 1); free(name); } else { setenv("DOSEMU_RC",dosrcname,1); } /* privileged options allowed? */ is_user_config = (confname && config_script_path && strcmp(confname, config_script_path)); priv_lvl = !under_root_login && can_do_root_stuff && is_user_config; /* DEXE together with option F ? */ if (priv_lvl && dexe_running) { /* for security reasons we cannot allow this */ fprintf(stderr, "user cannot load DEXE file together with option -F\n"); exit(1); } if (is_user_config) define_config_variable("c_user"); else define_config_variable("c_system"); if (dexe_running) define_config_variable("c_dexerun"); yy_vbuffer = dosemu_conf; do_parse(NULL, "built-in dosemu.conf", "error in built-in dosemu.conf"); yy_vbuffer = global_conf; do_parse(NULL, "built-in global.conf", "error in built-in global.conf"); if (confname) { yy_vbuffer = NULL; fd = open_file(confname); if (!fd) { if (!dexe_running) { fprintf(stderr, "Cannot open base config file %s, Aborting DOSEMU.\n",confname); exit(1); } } do_parse(fd, confname, "error in configuration file %s"); } if (priv_lvl) undefine_config_variable("c_user"); else undefine_config_variable("c_system"); if (!get_config_variable(CONFNAME_V3USED)) { /* we obviously have an old configuration file * ( or a too simple one ) * giving up */ yyerror("\nYour %s script or configuration file is obviously\n" "an old style or a too simple one\n" "Please read README.txt on how to upgrade\n", confname); exit(1); } /* privileged options allowed for user's config? */ priv_lvl = !under_root_login && can_do_root_stuff; if (priv_lvl) define_config_variable("c_user"); /* Now we parse any commandline statements from option '-I' * We do this under priv_lvl set above, so we have the same secure level * as with .dosrc */ if (commandline_statements) { #define XX_NAME "commandline" open_file(0); define_config_variable("c_comline"); c_printf("Parsing " XX_NAME " statements.\n"); yyin=0; /* say: we have no input file */ yy_vbuffer=commandline_statements; /* this is the input to scan */ do_parse(0, XX_NAME, "error in user's %s statement"); undefine_config_variable("c_comline"); } } /* check for global settings, that should know the whole settings * This we only can do, after having parsed all statements */ if (dexe_running) { /* force a BootC, * regardless what ever was set in the config files */ config.hdiskboot = 1; } else { if (get_config_variable("c_dexeonly")) { c_printf("CONF: only execution of DEXE files allowed\n"); fprintf(stderr, "only execution of DEXE files allowed\n"); exit(99); } } #ifdef TESTING error("TESTING: parser is terminating program\n"); exit(0); #endif return 1; } #define MAX_CONFIGVARIABLES 128 char *config_variables[MAX_CONFIGVARIABLES+1] = {0}; static int config_variables_count = 0; static int config_variables_last = 0; static int allowed_classes = -1; static int saved_allowed_classes = -1; static int is_in_allowed_classes(int mask) { if (!(allowed_classes & mask)) { yyerror("insufficient class privilege to use this configuration option\n"); exit(99); } return 1; } struct config_classes { char *class; int mask; } config_classes[] = { {"c_all", CL_ALL}, {"c_normal", CL_ALL & (~(CL_VAR | CL_VPORT | CL_IRQ | CL_HARDRAM | CL_PCI | CL_NET))}, {"c_var", CL_VAR}, {"c_system", CL_ALL}, {"c_vport", CL_VPORT}, {"c_port", CL_PORT}, {"c_irq", CL_IRQ}, {"c_pci", CL_PCI}, {"c_hardram", CL_HARDRAM}, {"c_net", CL_NET}, {0,0} }; static int get_class_mask(char *name) { struct config_classes *p = &config_classes[0]; while (p->class) { if (!strcmp(p->class,name)) return p->mask; p++; } return 0; } static void update_class_mask(void) { int i, m; allowed_classes = 0; for (i=0; i< config_variables_count; i++) { if ((m=get_class_mask(config_variables[i])) != 0) { allowed_classes |= m; } } } static void enter_user_scope(int incstackptr) { if (user_scope_level) return; saved_priv_lvl = priv_lvl; priv_lvl = 1; saved_allowed_classes = allowed_classes; allowed_classes = 0; user_scope_level = incstackptr; c_printf("CONF: entered user scope, includelevel %d\n", incstackptr-1); } static void leave_user_scope(int incstackptr) { if (user_scope_level != incstackptr) return; priv_lvl = saved_priv_lvl; allowed_classes = saved_allowed_classes; user_scope_level = 0; c_printf("CONF: left user scope, includelevel %d\n", incstackptr-1); } char *get_config_variable(char *name) { int i; for (i=0; i< config_variables_count; i++) { if (!strcmp(name, config_variables[i])) { config_variables_last = i; return config_variables[i]; } } return 0; } int define_config_variable(char *name) { if (priv_lvl) { if (strcmp(name, CONFNAME_V3USED) && strncmp(name, "u_", 2)) { c_printf("CONF: not enough privilege to define config variable %s\n", name); return 0; } } if (!get_config_variable(name)) { if (config_variables_count < MAX_CONFIGVARIABLES) { config_variables[config_variables_count++] = strdup(name); if (!priv_lvl) update_class_mask(); } else { if (after_secure_check) c_printf("CONF: overflow on config variable list\n"); return 0; } } if (after_secure_check) c_printf("CONF: config variable %s set\n", name); return 1; } static int undefine_config_variable(char *name) { if (priv_lvl) { if (strncmp(name, "u_", 2)) { c_printf("CONF: not enough privilege to undefine config variable %s\n", name); return 0; } } if (get_config_variable(name)) { int i; if (!strcmp(name, CONFNAME_V3USED)) parser_version_3_style_used = 0; free(config_variables[config_variables_last]); for (i=config_variables_last; i<(config_variables_count-1); i++) { config_variables[i] = config_variables[i+1]; } config_variables_count--; if (!priv_lvl) update_class_mask(); c_printf("CONF: config variable %s unset\n", name); return 1; } return 0; } char *checked_getenv(const char *name) { if (user_scope_level) { char *s, *name_ = malloc(strlen(name)+sizeof(USERVAR_PREF)); strcpy(name_, USERVAR_PREF); strcat(name_, name); s = getenv(name_); free(name_); if (s) return s; } return getenv(name); } static void check_user_var(char *name) { char *name_; char *s; if (user_scope_level) return; name_ = malloc(strlen(name)+sizeof(USERVAR_PREF)); strcpy(name_, USERVAR_PREF); strcat(name_, name); s = getenv(name_); if (s) { if (getenv(name)) c_printf("CONF: variable %s replaced by user\n", name); setenv(name, s, 1); unsetenv(name_); } free(name_); } static char *run_shell(char *command) { int pipefds[2]; pid_t pid; char excode[16] = "1"; setenv("DOSEMU_SHELL_RETURN", excode, 1); if (pipe(pipefds)) return strdup(""); pid = fork(); if (pid == -1) return strdup(""); if (!pid) { /* child */ int ret; close(pipefds[0]); /* we won't read from the pipe */ dup2(pipefds[1], 1); /* make the pipe child's stdout */ priv_drop(); /* drop any priviledges */ ret = system(command); /* tell the parent: "we have finished", * this way we need not to play games with select() */ if (ret == -1) ret = errno; else ret >>=8; write(pipefds[1],"\0\0\0",4); close(pipefds[1]); _exit(ret); } else { /* parent */ char *buf = 0; int recsize = 128; int bufsize = 0; int ptr =0; int ret; int status; close(pipefds[1]); /* we won't write to the pipe */ do { bufsize = ptr + recsize; if (!buf) buf = malloc(bufsize); else buf = realloc(buf, bufsize); ret = read(pipefds[0], buf+ptr, recsize -1); if (ret > 0) { ptr += ret; } } while (ret >0 && *((int *)(buf+ptr-4)) ); close(pipefds[0]); waitpid(pid, &status, 0); buf[ptr] = 0; if (!buf[0]) { free(buf); buf = strdup(""); } sprintf(excode, "%d", WEXITSTATUS(status)); setenv("DOSEMU_SHELL_RETURN", excode, 1); return buf; } } struct for_each_entry { char *list; char *ptr; int skip_restart; }; #define FOR_EACH_DEPTH 16 static struct for_each_entry *for_each_list = 0; static int for_each_handling(int loopid, char *varname, char *delim, char *list) { struct for_each_entry *fe; char * new; char saved; if (!for_each_list) { int size = FOR_EACH_DEPTH * sizeof(struct for_each_entry); for_each_list = malloc(size); memset(for_each_list, 0, size); } if (loopid > FOR_EACH_DEPTH) { yyerror("too deeply nested foreach\n"); return 0; } fe = for_each_list + loopid; if (!fe->list) { /* starting the loop */ fe->ptr = fe->list = strdup(list); fe->skip_restart = 0; if (loopid) { /* in inner loops we need to skip the restart */ fe->skip_restart = 1; } } else { if (fe->skip_restart) { fe->skip_restart = 0; return 1; } } while (fe->ptr[0] && strchr(delim,fe->ptr[0])) fe->ptr++; /* subsequent call */ if (!fe->ptr[0]) { /* loop end */ free(fe->list); fe->list = 0; return (0); } new = strpbrk(fe->ptr, delim); if (!new) new = strchr(fe->ptr,0); saved = *new; *new = 0; setenv(varname,fe->ptr,1); if (saved) new++; fe->ptr = new; return (1); } #ifdef TESTING_MAIN static void die(char *reason) { error("par dead: %s\n", reason); exit(0); } int main(int argc, char **argv) { if (argc != 2) die("no filename!"); if (!parse_config(argv[1], argv[2] /* will be NULL if not given */)) die("parse failed!\n"); } #endif