executor/src/main.c

2104 lines
55 KiB
C

/* Copyright 1988 - 2005 by Abacus Research and
* Development, Inc. All rights reserved.
*/
#if !defined (OMIT_RCSID_STRINGS)
char ROMlib_rcsid_main[] =
"$Id: main.c 127 2006-04-04 00:18:10Z ctm $";
#endif
/*
* Errors should be handled more cleanly.
*/
#include "rsys/common.h"
#include "QuickDraw.h"
#include "CQuickDraw.h"
#include "SegmentLdr.h"
#include "rsys/notmac.h"
#include "MemoryMgr.h"
#include "FontMgr.h"
#include "SysErr.h"
#include "OSUtil.h"
#include "ResourceMgr.h"
#include "TextEdit.h"
#include "FileMgr.h"
#include "DialogMgr.h"
#include "StdFilePkg.h"
#include "MenuMgr.h"
#include "ScrapMgr.h"
#include "SoundMgr.h"
#include "ScriptMgr.h"
#include "SANE.h"
#include "DeskMgr.h"
#include "DeviceMgr.h"
#include "ToolboxUtil.h"
#include "Gestalt.h"
#include "AppleTalk.h"
#include "AppleEvents.h"
#include "rsys/cquick.h"
#include "rsys/tesave.h"
#include "rsys/mman.h"
#include "rsys/menu.h"
#include "rsys/next.h"
#include "rsys/setuid.h"
#include "rsys/pstuff.h"
#include "rsys/prefs.h"
#include "rsys/misc.h"
#include "rsys/flags.h"
#include "rsys/option.h"
#include "rsys/host.h"
#include "rsys/syncint.h"
#include "rsys/vdriver.h"
#include "rsys/vbl.h"
#include "rsys/time.h"
#include "rsys/segment.h"
#include "rsys/keycode.h"
#include "rsys/version.h"
#include "rsys/m68kint.h"
#include "rsys/misc.h"
#include "rsys/blockinterrupts.h"
#include "rsys/rgbutil.h"
#include "rsys/refresh.h"
#include "rsys/executor.h"
#include "rsys/wind.h"
#include "rsys/osevent.h"
#include "rsys/image.h"
#include "rsys/dump.h"
#include "rsys/file.h"
#include "rsys/ctl.h"
#include "rsys/parseopt.h"
#include "rsys/print.h"
#include "rsys/memsize.h"
#include "rsys/splash.h"
#include "rsys/stdfile.h"
#include "rsys/autorefresh.h"
#include "rsys/sounddriver.h"
#include "rsys/desperate.h"
#include "rsys/dcache.h"
#include "rsys/system_error.h"
#include "rsys/filedouble.h"
#include "rsys/os.h"
#include "rsys/arch.h"
#include "rsys/checkpoint.h"
#include "rsys/cleanup.h"
#include "rsys/custom.h"
#include "rsys/gestalt.h"
#include "rsys/keyboards.h"
#include "rsys/launch.h"
#include "rsys/text.h"
#include "rsys/appearance.h"
#include "rsys/hfs_plus.h"
#include "rsys/check_structs.h"
#include "paramline.h"
#if defined (MSDOS)
#include "aspi.h"
#include "dosdisk.h"
#include "dosmem.h"
#include "dpmilock.h"
#include "vga.h"
#include "openmany.h"
#include "dosserial.h"
#include <process.h>
#endif /* MSDOS */
#include <ctype.h>
#if !defined (MSDOS) && !defined(CYGWIN32)
#include <sys/wait.h>
#endif
#if defined (MMAP_LOW_GLOBALS)
#include <sys/types.h>
#include <sys/mman.h>
#endif /* MMAP_LOW_GLOBALS */
#if defined (CYGWIN32)
#include "winfs.h"
#include "dosdisk.h"
#include "win_except.h"
#include "win_queue.h"
#include "win_clip.h"
#include "win_print.h"
#endif
#if defined(X)
#include "x.h"
#endif
BOOLEAN force_big_offset = CONFIG_OFFSET_P;
#if defined (NEXTSTEP)
#define main oldmain
char *ROMlib_xfervmaddr;
LONGINT ROMlib_xfervmsize = 0;
#endif /* NEXTSTEP */
PUBLIC int ROMlib_noclock = 0;
#if defined (NOMOUSE_COMMAND_LINE_OPTION)
PUBLIC int ROMlib_no_mouse = 1;
#endif
/* optional resolution other than 72dpix72dpi for printing */
PUBLIC INTEGER ROMlib_optional_res_x, ROMlib_optional_res_y;
/* A string approximating the original command line. */
PUBLIC const char *ROMlib_command_line;
/* Set to TRUE if there was any error parsing arguments. */
PRIVATE boolean_t bad_arg_p = FALSE;
/* Set to FALSE when we know that the command line switches will result
in no graphics. */
PRIVATE boolean_t graphics_p = TRUE;
/* for a description of flags declared here, see <rsys/flags.h> */
/* Initial screen size. This can be changed dynamically. */
INTEGER flag_width = 0, flag_height = 0; /* 0 means "use default". */
/* Initial bits per pixel. The screen depth can be changed dynamically. */
int flag_bpp = 0; /* 0 means "use default". */
INTEGER ROMlib_shadow_screen_p = TRUE;
INTEGER ROMlib_no_windows;
#if defined (MSDOS) || defined (CYGWIN32)
int ROMlib_drive_check = 0;
#endif
#if defined(SYN68K)
static boolean_t use_native_code_p = TRUE;
#endif
/* the system version that executor is currently reporting to
applications, set through the `-system' options. contains the
version number in the form `0xABC' which corresponds to system
version A.B.C */
uint32 system_version = 0x700; /* keep this in sync with Browser's .ecf file */
static option_t common_opts[] =
{
{ "sticky", "sticky menus", opt_no_arg, NULL },
{ "pceditkeys", "have Delete key delete one character forward",
opt_no_arg, NULL },
{ "nobrowser", "don't run Browser", opt_no_arg, NULL },
{ "bpp", "default screen depth", opt_sep, NULL },
{ "size", "default screen size", opt_sep, NULL },
{ "debug",
("enable certain debugging output and consistency checks. This "
"is primarily used by ARDI developers, but we are making it "
"available during the pre-beta period to expert users. The next "
"argument must be a list of comma-separated words describing which "
"debug options you want enabled. You can abbreviate the debug "
"options as long as the abbreviation is unambiguous. Here is a "
"list of the options (some of which will may do nothing): "
"\"all\" enables all debugging options, "
"\"fslog\" enables filesystem call logging, "
"\"memcheck\" enables heap consistency checking (slow!), "
"\"textcheck\" enables text record consistency checking (slow!), "
"\"trace\" enables miscellaneous trace information, "
"\"sound\" enables miscellaneous sound logging information, "
"\"trapfailure\" enables warnings when traps return error codes, "
"\"errno\" enables some C library-related warnings, "
"\"unexpected\" enables warnings for unexpected events, "
"\"unimplemented\" enables warnings for unimplemented traps. "
"Example: \"executor -debug unimp,trace\""),
opt_sep, NULL },
{ "nodiskcache", "disable internal disk cache.",
opt_no_arg, NULL },
{ "nosound", "disable any sound hardware",
opt_no_arg, NULL },
{ "info", "print information about your system",
opt_no_arg, NULL },
#if defined (SUPPORT_LOG_ERR_TO_RAM)
{ "ramlog",
"log debugging information to RAM; alt-shift-7 dumps the "
"accrued error log out via the normal mechanism.",
opt_no_arg, NULL },
#endif
#if defined (MSDOS)
{ "oldtimer",
"use old-style 18.2 Hz timer, instead of the newer 1024 Hz "
"timer. This will result in less accurate timer emulation, "
"but may work around problems with certain BIOSes.",
opt_no_arg, NULL },
{ "vga",
"use old-style VGA graphics modes only, and don't use SuperVGA. "
"This will limit you to a slow 640x480 with 16 colors, but may let "
"you run Executor even when Executor has problems with your "
"video driver.",
opt_no_arg, NULL },
{ "nofilescheck",
"bypass a startup check that attempts to determine if your FILES= "
"parameter is large enough.",
opt_no_arg, NULL },
{ "videospeedup",
"if you have a linear frame buffer (which requires a VBE 2.0 video "
"driver like SciTech Display Doctor) this can make graphics much faster "
"for games that bypass QuickDraw and therefore require \"refresh\" mode."
" This isn't needed under plain DOS if you are using cwsdpmi.exe. "
"The drawback is that enabling this feature removes some memory "
"protection from your system and makes it possible for Executor "
"to crash your computer or destroy data. On some systems this "
"switch will cause Executor to crash right away; you can't use it "
"on such systems, and there's nothing that can be done about it. "
"Use at your own risk!",
opt_no_arg, NULL },
{ "nosync",
"Allow disk data structures to stay in memory until Executor quits. "
"This will dramatically speed up Executor's creation and deletion of "
"many small files, but if Executor crashes the data in memory won't "
"be written to disk and HFV corruption could result. "
"Use at your own risk!",
opt_no_arg, NULL },
{ "skipaspi", "totally bypass ASPI drivers",
opt_no_arg, NULL },
#endif /* MSDOS */
#if defined (MSDOS) || defined (CYGWIN32)
{ "macdrives", "drive letters that represent Mac formatted media",
opt_sep, NULL },
{ "dosdrives", "drive letters that represent DOS formatted media",
opt_sep, NULL },
{ "skipdrives", "drive letters that represent drives to avoid",
opt_sep, NULL },
#endif
#if defined (LINUX)
{ "nodrivesearch",
"Do not look for a floppy drive, CD-ROM drive or any other drive "
"except as specified by the MacVolumes environment variable",
opt_no_arg, NULL },
#endif /* LINUX */
{ "keyboards", "list available keyboard mappings",
opt_no_arg, NULL },
{ "keyboard", "choose a specific keyboard map", opt_sep, NULL },
{ "print",
"tell program to print file; not useful unless you also "
"specify a program to run and one or more documents to print.",
opt_no_arg, NULL },
#if defined (NEXT) || defined (LINUX)
{ "nodotfiles", "do not display filenames that begin with dot", opt_no_arg,
NULL },
#endif
#if 0
{ "noclock", "disable timer", opt_no_arg, NULL },
#endif
#if defined (NOMOUSE_COMMAND_LINE_OPTION)
/* Hack Dr. Chung wanted */
{ "nomouse", "ignore missing mouse", opt_no_arg, NULL },
#endif
{ "noautorefresh",
"turns off automatic detection of programs that bypass QuickDraw.",
opt_no_arg, NULL },
{ "refresh",
"handle programs that bypass QuickDraw, at a performance penalty. "
"Follow -refresh with an number indicating how many 60ths of a second "
"to wait between each screen refresh, e.g. \"executor -refresh 10\".",
opt_optional, "10", },
{ "help", "print this help message", opt_no_arg, NULL, },
{ "version", "print the Executor version", opt_no_arg, NULL, },
#if defined (MALLOC_MAC_MEMORY)
{ "memory",
"specify the total memory you want reserved for use by the programs "
"run under Executor and for Executor's internal system software. "
"For example, \"executor -memory 5.5M\" would "
"make five and a half megabytes available to the virtual machine. "
"Executor will require extra memory above and beyond this amount "
"for other uses.",
opt_sep, NULL, },
{ "applzone",
"specify the memory to allocate for the application being run, "
"e.g. \"executor -applzone 4M\" would make four megabytes "
"of RAM available to the application. \"applzone\" stands for "
"\"application zone\".",
opt_sep, NULL, },
{ "stack",
"like -applzone, but specifies the amount of stack memory to allocate.",
opt_sep, NULL, },
{ "syszone", "like -applzone, but specifies the amount of memory to make "
"available to Executor's internal system software.",
opt_sep, NULL, },
#if defined (MM_MANY_APPLZONES)
{ "napplzones",
"debugging flag, specifies the number of applzones to use",
opt_sep, NULL },
#endif /* MM_MANY_APPLZONES */
#endif /* MALLOC_MAC_MEMORY */
{ "system",
"specify the system version that executor reports to applications",
opt_sep, NULL },
#if defined (SYN68K)
{ "notnative",
"don't use native code in syn68k",
opt_no_arg, NULL },
#endif
#if defined (MSDOS) || defined(VDRIVER_SVGALIB) || defined (CYGWIN32)
{ "logerr", "log diagnostic output to error log file", opt_no_arg,
NULL },
#endif
{ "grayscale", "\
specify that executor should run in grayscale mode even if it is \
capable of color.",
opt_no_arg, NULL },
{ "netatalk", "use netatalk naming conventions for AppleDouble files",
opt_no_arg, NULL },
{ "afpd", "use afpd conventions for AppleDouble files (implies -netatalk)",
opt_no_arg, NULL },
#if defined (MSDOS)
{ "modemport", "which PC serial port (1, 2, 3 or 4) should be used when"
" a Macintosh application access the modem port", opt_sep, NULL,
},
{ "printerport", "which PC serial port (1, 2, 3 or 4) should be used when"
" a Macintosh application *directly* accesses the printer port. This"
" option does not affect printing. It only affects communications type "
" programs that do their own serial port management.", opt_sep, NULL,
},
#endif
#if CONFIG_OFFSET_P == 0
{ "offset", "offset Mac memory (i.e. simulate Win32)", opt_no_arg, NULL },
#endif
{ "cities", "Don't use Helvetica for Geneva, Courier for Monaco and Times "
"for New York when generating PostScript", opt_no_arg, NULL },
#if defined (CYGWIN32)
{ "die", "allow Executor to die instead of catching trap", opt_no_arg,
NULL },
{ "noautoevents", "disable timer driven event checking", opt_no_arg,
NULL },
#endif
#if defined (MSDOS) || defined (CYGWIN32)
{ "nocheckpoint", "disable \"failure.txt\" checkpointing", opt_no_arg,
NULL },
#endif
{ "prvers",
"specify the printer version that executor reports to applications",
opt_sep, NULL },
{ "prres",
"specify an additional resolution available for printing, e.g. "
"\"executor -prres 600x600\" will make 600dpi resolution available "
"in addition to the standard 72dpi. Not all apps will be able to use "
"this additional resolution.", opt_sep, NULL },
#if defined (SDL)
{ "fullscreen", "try to run in full-screen mode", opt_no_arg, NULL },
{ "hwsurface", "UNSUPPORTED", opt_no_arg, NULL },
#if defined (CYGWIN)
{ "sdlaudio", "specify the audio driver to attempt to use first, e.g. "
"\"executor -sdlaudio waveout\" will tell Executor to use the old "
"windows sound drivers and not use DirectSound.", opt_sep, NULL, },
#else
{ "sdlaudio", "specify the audio driver to attempt to use first, e.g. "
"\"executor -sdlaudio esd\" will tell Executor to use esound, "
"the Enlightened Sound Daemon instead of /dev/dsp.", opt_sep, NULL, },
#endif
#endif
#if defined (CYGWIN32) && defined (SDL)
{ "clipleak", "UNSUPPORTED (ignored)", opt_no_arg, NULL },
#endif
#if defined(X) || defined (SDL)
{ "scancodes", "different form of key mapping (may be useful in "
"conjunction with -keyboard)", opt_no_arg, NULL },
#endif
{ "desperate", /* Handled specially; here for documentation purposes only. */
"run in \"desperation mode\". This will cause Executor "
"to use as few system features as possible, which is handy for "
"troubleshooting if Executor is having serious problems with "
"your system.",
opt_no_arg, NULL },
#if defined (powerpc) || defined (__ppc__)
{ "ppc", "try to execute the PPC native code if possible (UNSUPPORTED)", opt_no_arg, NULL },
#endif
#if defined (CYGWIN32)
{ "realmodecd", "try to use real-mode cd-rom driver", opt_no_arg, NULL },
#endif
{ "appearance", "(mac or windows) specify the appearance of windows and "
"menus. For example \"executor -appearance windows\" will make each "
"window have a blue title bar", opt_sep, NULL },
{ "hfsplusro", "unsupported -- do not use", opt_no_arg, NULL },
};
opt_database_t *common_db;
/* Prints the specified value in a representation appropriate for command
* line switches.
*/
static void
print_command_line_value (FILE *fp, int v)
{
if (v > 0 && v % 1024 == 0)
{
if (v % (1024 * 1024) == 0)
fprintf (fp, "%dM", v / (1024 * 1024));
else
fprintf (fp, "%dK", v / 1024);
}
else
fprintf (fp, "%d", v);
}
static void
check_arg (char *argname, int *arg, int min, int max)
{
if (*arg < min || *arg > max)
{
fprintf (stderr, "%s: invalid value for `%s': must be between ",
program_name, argname);
print_command_line_value (stderr, min);
fputs (" and ", stderr);
print_command_line_value (stderr, max);
fputs (" inclusive.\n", stderr);
bad_arg_p = TRUE;
}
}
#if defined (NEED_WAIT4)
/*
* NOTE: This is a replacement for wait4 that will work for what we use wait4.
* It has the side effect of acknowledging the death of other children.
*/
A4(PUBLIC, LONGINT, wait4, LONGINT, pid, union wait *, statusp, LONGINT,
options, struct rusage *, rusage)
{
LONGINT retval;
do
{
retval = wait3(statusp, options, rusage);
}
while (retval > 0 && retval != pid);
return retval;
}
#endif /* NEED_WAIT4 */
PUBLIC char ROMlib_startdir[MAXPATHLEN];
PUBLIC INTEGER ROMlib_startdirlen;
#if defined (MSDOS) || defined (CYGWIN32)
PUBLIC char ROMlib_start_drive;
#endif
PUBLIC char *ROMlib_appname;
#if !defined(LINUX)
#define SHELL "/bin/sh"
#define WHICH "which "
#else
#define SHELL "/bin/bash"
#define WHICH "type -path "
#endif
#define APPWRAP "/Executor.app"
ULONGINT ROMlib_ourmtime;
#if defined (NEXTSTEP)
#if !defined (STRICT_OPENSTEP)
#include <mach-o/loader.h>
#include <mach-o/swap.h>
#endif
A1(PRIVATE, void, misc_self_examination, char *, us)
{
struct mach_header head;
struct load_command load;
struct segment_command seg;
struct fat_header fat_head;
struct fat_arch arch;
struct stat sbuf;
int i;
BOOLEAN need_to_swap;
enum NXByteOrder targetorder;
LONGINT err;
FILE *ROMlib_fp;
if (!(ROMlib_fp = Ufopen(us, "r+"))) {
if (!(ROMlib_fp = Ufopen(us, "r"))) {
fprintf(stderr, "couldn't open '%s'\n", us);
exit(3);
}
}
if (fread(&fat_head, sizeof(fat_head), 1, ROMlib_fp) != 1) {
fprintf(stderr, "couldn't read fat_header\n");
exit(4);
}
if (fat_head.magic == CLC(FAT_MAGIC)) {
/*
* NOTE: we assume that every architecture supported will have a "lowseg"
* segment, hence we can just use the first one we find. If the
* binary is later thinned, the user will have to reregister anyway.
*/
if (fread(&arch, sizeof(arch), 1, ROMlib_fp) != 1) {
fprintf(stderr, "couldn't read arch info\n");
exit(6);
}
if (fseek(ROMlib_fp, CL(arch.offset), SEEK_SET) == -1) {
fprintf(stderr, "couldn't seek after load seg command\n");
exit(17);
}
} else {
rewind(ROMlib_fp);
arch.offset = 0;
}
if (fread(&head, sizeof(head), 1, ROMlib_fp) != 1) {
fprintf(stderr, "couldn't read header\n");
exit(4);
}
/*
* NOTE: In the following few sections of code we have to accept the
* possibility that the header information will be swapped relative
* to us. Normally we only care when things are swapped relative
* to a big endian machine. But if the first architecture we find
* is a big endian one and we're little endian, then we still have
* to do swappage. The alternative would be to store the registration
* information in the "lowseg" of the architecture that we're currently
* running on, but that means that for N archtectures, Executor would
* have to be registered N times. Ick.
*/
#if defined(LITTLEENDIAN)
targetorder = NX_LittleEndian;
#else /* !defined(LITTLEENDIAN) */
targetorder = NX_BigEndian;
#endif /* !defined(LITTLEENDIAN) */
if ((need_to_swap = head.magic != MH_MAGIC))
swap_mach_header(&head, targetorder);
if (head.magic != MH_MAGIC) {
fprintf(stderr, "bad magic number\n");
exit(5);
}
for (i = 0; i < head.ncmds; ++i) {
if (fread(&load, sizeof(load), 1, ROMlib_fp) != 1) {
fprintf(stderr, "couldn't read load command\n");
exit(6);
}
if (need_to_swap)
swap_load_command(&load, targetorder);
if (load.cmd == LC_SEGMENT) {
if (fread(&seg.segname, sizeof(seg) - sizeof(load), 1,
ROMlib_fp) != 1) {
fprintf(stderr, "couldn't read load segment command\n");
exit(7);
}
if (need_to_swap)
swap_segment_command(&seg, targetorder);
if (strcmp(seg.segname, "hfs_xfer") == 0) {
ROMlib_xfervmaddr = (char *) seg.vmaddr;
ROMlib_xfervmsize = seg.vmsize;
}
if (fseek(ROMlib_fp, load.cmdsize - sizeof(seg), SEEK_CUR) ==
-1) {
fprintf(stderr, "couldn't seek after load seg command\n");
exit(17);
}
} else {
if (fseek(ROMlib_fp, load.cmdsize - sizeof(load), SEEK_CUR) ==
-1) {
fprintf(stderr, "couldn't seek after load command\n");
exit(8);
}
}
}
fclose (ROMlib_fp);
#if !defined(LETGCCWAIL)
err = 0;
#endif
if (Ustat(us, &sbuf) == 0) {
if (!geteuid() && (sbuf.st_uid || (sbuf.st_mode & 07777) != 04755))
willsetperms();
if ((geteuid() != 0) && (sbuf.st_uid == 0) &&
((sbuf.st_mode & 04000) == 04000))
badfilesystem();
if (sbuf.st_uid)
err = Uchown(us, 0, -1);
if ((!sbuf.st_uid || err == 0) && (sbuf.st_mode & 07777) != 04755)
Uchmod(us, 04755);
}
}
#endif /* defined (NEXTSTEP) */
#if defined(MSDOS) || defined (CYGWIN32)
PUBLIC char ROMlib_savecwd[MAXPATHLEN];
PRIVATE void cd_back( void )
{
if (ROMlib_savecwd[0])
Uchdir(ROMlib_savecwd);
}
#endif
PRIVATE void
set_appname (char *argv0)
{
ROMlib_appname = strrchr(argv0, '/');
#if defined (MSDOS) || defined (CYGWIN32)
if (!ROMlib_appname)
ROMlib_appname = strrchr (argv0, '\\');
#endif
if (ROMlib_appname)
++ROMlib_appname;
else
ROMlib_appname = argv0;
}
A1(PRIVATE, void, setstartdir, char *, argv0)
{
#if !defined(MSDOS) && !defined(CYGWIN32)
LONGINT p[2], pid;
char buf[MAXPATHLEN];
INTEGER nread, arg0len;
char *lookhere, *suffix, *whichstr;
char savedir[MAXPATHLEN];
if (argv0[0] == '/' || Uaccess(argv0, X_OK) == 0)
lookhere = argv0;
else
{
pipe((void *) p);
if ((pid = fork()) == 0) {
ROMlib_setuid(getuid());
close(1);
dup(p[1]);
arg0len = strlen(argv0);
whichstr = alloca(arg0len + sizeof(WHICH));
memmove(whichstr, WHICH, sizeof(WHICH) - 1);
memmove(whichstr + sizeof(WHICH) - 1, argv0, arg0len + 1);
execl(SHELL, "sh", "-c", whichstr, (char *) 0);
fprintf (stderr, "``%s -c \"%s\"'' failed\n", SHELL, whichstr);
fflush (stderr);
_exit (127);
/* NOTREACHED */
#if !defined (LETGCCWAIL)
lookhere = 0;
#endif /* LETGCCWAIL */
} else {
close (p[1]);
nread = read(p[0], buf, sizeof(buf)-1);
wait4(pid, 0, 0, (struct rusage *) 0);
if (nread)
--nread; /* get rid of trailing \n */
buf[nread] = 0;
lookhere = buf;
}
}
#if defined(NEXTSTEP)
misc_self_examination (lookhere);
#endif
suffix = rindex(lookhere, '/');
if (suffix)
*suffix = 0;
getcwd(savedir, sizeof savedir);
Uchdir(lookhere);
if (suffix)
*suffix = '/';
getcwd(ROMlib_startdir, sizeof ROMlib_startdir);
Uchdir(savedir);
ROMlib_startdirlen = strlen(ROMlib_startdir) + 1;
#if defined(NEXTSTEP)
if (strcmp(ROMlib_startdir + ROMlib_startdirlen - sizeof(APPWRAP),
APPWRAP) != 0) {
memmove(ROMlib_startdir + ROMlib_startdirlen - 1, APPWRAP,
sizeof(APPWRAP));
ROMlib_startdirlen += sizeof(APPWRAP) - 1;
}
#endif
#else /* defined(MSDOS) || defined(CYGWIN32) */
#if defined (MSDOS) || defined (CYGWIN32)
atexit (call_cleanup_bat);
#endif
/*
* When run under DOS, argv[0] contains a full path name, including
* a dos-drive and colon at the beginning (e.g. C:/fratzand/executor).
* However, you get neither when running under DOS under gdb. Yahoo.
*/
if (!getcwd(ROMlib_savecwd, sizeof(ROMlib_savecwd)))
ROMlib_savecwd[0] = 0;
else
atexit(cd_back);
if (argv0[1] == ':')
{
char *lastslash;
#if 1
static char cd_to[] = "C:/";
ROMlib_start_drive = argv0[0];
cd_to[0] = argv0[0];
Uchdir(cd_to);
#if 0
strcpy(ROMlib_startdir, argv0 + 2);
#else
/* We now include the drive letter in with ROMlib_startdir. */
strcpy(ROMlib_startdir, argv0);
#endif
#else
strcpy(ROMlib_startdir, argv0);
#endif
lastslash = strrchr(ROMlib_startdir, '/');
#if defined (CYGWIN32)
if (!lastslash)
lastslash = strrchr(ROMlib_startdir, '\\');
#endif
if (lastslash)
*lastslash = 0;
}
else
{
ROMlib_start_drive = 0;
strcpy(ROMlib_startdir, ".");
}
ROMlib_startdirlen = strlen(ROMlib_startdir);
#endif /* defined(MSDOS) */
}
PUBLIC BOOLEAN ROMlib_startupscreen = TRUE;
char *program_name;
#if defined (SYN68K)
static syn68k_addr_t
unhandled_trap (syn68k_addr_t callback_address, void *arg)
{
static const char *trap_description[] = {
/* I've only put the really interesting ones in. */
NULL, NULL, NULL, NULL,
"Illegal instruction",
"Integer divide by zero",
"CHK/CHK2",
"FTRAPcc, TRAPcc, TRAPV",
"Privilege violation",
"Trace",
"A-line",
"FPU",
NULL,
NULL,
"Format error"
};
int trap_num;
char pc_str[128];
trap_num = (long) arg;
switch (trap_num) {
case 4: /* Illegal instruction. */
case 8: /* Privilege violation. */
case 10: /* A-line. */
case 11: /* F-line. */
case 14: /* Format error. */
case 15: /* Uninitialized interrupt. */
case 24: /* Spurious interrupt. */
case 25: /* Level 1 interrupt autovector. */
case 26: /* Level 2 interrupt autovector. */
case 27: /* Level 3 interrupt autovector. */
case 28: /* Level 4 interrupt autovector. */
case 29: /* Level 5 interrupt autovector. */
case 30: /* Level 6 interrupt autovector. */
case 31: /* Level 7 interrupt autovector. */
case 32: /* TRAP #0 vector. */
case 33: /* TRAP #1 vector. */
case 34: /* TRAP #2 vector. */
case 35: /* TRAP #3 vector. */
case 36: /* TRAP #4 vector. */
case 37: /* TRAP #5 vector. */
case 38: /* TRAP #6 vector. */
case 39: /* TRAP #7 vector. */
case 40: /* TRAP #8 vector. */
case 41: /* TRAP #9 vector. */
case 42: /* TRAP #10 vector. */
case 43: /* TRAP #11 vector. */
case 44: /* TRAP #12 vector. */
case 45: /* TRAP #13 vector. */
case 46: /* TRAP #14 vector. */
case 47: /* TRAP #15 vector. */
case 3: /* Address error. */
case 6: /* CHK/CHK2 instruction. */
case 7: /* FTRAPcc, TRAPcc, TRAPV instructions. */
case 9: /* Trace. */
case 5: /* Integer divide by zero. */
sprintf (pc_str, "0x%lX", (unsigned long) READUL (EM_A7 + 2));
break;
default:
strcpy (pc_str, "<unknown>");
break;
}
if (trap_num < (int) NELEM (trap_description)
&& trap_description[trap_num] != NULL)
gui_fatal ("Fatal error: unhandled trap %d at pc %s (%s)",
trap_num, pc_str, trap_description[trap_num]);
else
gui_fatal ("Fatal error: unhandled trap %d at pc %s", trap_num, pc_str);
/* It will never get here; this is just here to quiet gcc. */
return callback_address;
}
static void
setup_trap_vectors (void)
{
syn68k_addr_t timer_callback;
int i;
/* Set up the trap vector for the timer interrupt. */
timer_callback = callback_install (catchalarm, NULL);
*(syn68k_addr_t *)SYN68K_TO_US(M68K_TIMER_VECTOR * 4) = CL (timer_callback);
/* Fill in unhandled trap vectors so they cause graceful deaths.
* Skip over those trap vectors which are known to have legitimate
* values.
*/
for (i = 1; i < 64; i++)
if (i != 10 /* A line trap. */
#if defined (M68K_TIMER_VECTOR)
&& i != M68K_TIMER_VECTOR /* timer interrupt. */
#endif
#if defined (M68K_WATCHDOG_VECTOR)
&& i != M68K_WATCHDOG_VECTOR /* watchdog timer interrupt. */
#endif
#if defined (M68K_EVENT_VECTOR)
&& i != M68K_EVENT_VECTOR
#endif
#if defined (M68K_MOUSE_MOVED_VECTOR)
&& i != M68K_MOUSE_MOVED_VECTOR
#endif
#if defined (M68K_SOUND_VECTOR)
&& i != M68K_SOUND_VECTOR
#endif
&& i != 0x58 / 4) /* Magic ARDI vector. */
{
syn68k_addr_t c;
c = callback_install (unhandled_trap, (void *) i);
*(syn68k_addr_t *)SYN68K_TO_US(i * 4) = CL (c);
}
}
#endif /* SYN68K */
static void illegal_mode (void) __attribute__ ((noreturn));
static void
illegal_mode (void)
{
const vdriver_modes_t *vm;
int num_sizes, max_width, max_height, max_bpp;
vm = vdriver_mode_list;
num_sizes = vm->num_sizes;
max_bpp = vdriver_max_bpp;
if (num_sizes)
{
max_width = vm->size[num_sizes - 1].width;
max_height = vm->size[num_sizes - 1].height;
}
else
max_width = max_height = 0; /* quiet gcc */
vdriver_shutdown (); /* Just to be safe. */
if (num_sizes == 0)
fprintf (stderr, "Unable to find legal graphics mode.\n");
else
{
fprintf (stderr, "Invalid graphics mode. Largest allowable "
"screen %dx%d, %d bits per pixel.\n",
max_width, max_height, max_bpp);
#if defined (MSDOS)
if (!vesa_version)
{
fputs ("No VESA driver detected. To get more video modes, "
"make sure you have a\n"
"VESA-compatible video driver and a SuperVGA board.\n",
stderr);
}
#endif
}
/* Don't call ExitToShell here; ROMlib isn't set up enough yet to do that. */
exit (-1);
}
#if defined (MSDOS) || defined (CYGWIN32)
PUBLIC uint32 ROMlib_macdrives; /* default computed at runtime */
PUBLIC uint32 ROMlib_dosdrives = ~0;
PRIVATE uint32 skipdrives = 0;
PRIVATE boolean_t
drive_number_from_letter (char c, int *nump)
{
boolean_t retval;
if (isupper (c) || ((c >= '[' && c <= '`')))
{
*nump = c - 'A';
retval = TRUE;
}
else if (islower (c))
{
*nump = c - 'a';
retval = TRUE;
}
else
retval = FALSE;
return retval;
}
PRIVATE void
drive_error (const char *opt)
{
fprintf (stderr, "Invalid drive specification.\nUse something like "
"\"-%s A-EJ\" (for drives A, B, C, D, E and J).\n", opt);
bad_arg_p = TRUE;
}
PRIVATE const char *
munch_next_char (const char *p, uint32 *destp, const char *opt)
{
const char *retval;
int d;
if (drive_number_from_letter (p[0], &d))
{
if (p[1] == '-')
{
int d2;
if (drive_number_from_letter (p[2], &d2))
{
int lower, upper, i;
lower = MIN (d, d2);
upper = MAX (d, d2);
for (i = lower; i <= upper; ++i)
*destp |= (1 << i);
retval = p+3;
}
else
{
drive_error (opt);
retval = "";
}
}
else
{
*destp |= 1 << d;
retval = p+1;
}
}
else
{
drive_error (opt);
retval = "";
}
return retval;
}
PUBLIC uint32
parse_drive_opt (const char *opt_name, const char *opt_value)
{
uint32 retval;
const char *next_charp;
retval = 0;
for (next_charp = opt_value; *next_charp;)
next_charp = munch_next_char(next_charp, &retval, opt_name);
return retval;
}
#endif
#if !defined (MSDOS)
static void
print_info (void)
{
printf ("This is %s, compiled.\n",
ROMlib_executor_full_name);
#if defined (i386)
/* Print out CPU type. */
if (arch_type == ARCH_TYPE_I386)
printf ("CPU type is 80386.\n");
else
printf ("CPU type is 80486 or better.\n");
#endif /* i386 */
#define MB (1024 * 1024U)
/* Print out actual memory size chosen. */
printf ("Choosing %u.%02u MB for applzone, %u.%02u MB for syszone, "
"%u.%02u MB for stack\n",
ROMlib_applzone_size / MB,
(ROMlib_applzone_size % MB) * 100 / MB,
ROMlib_syszone_size / MB,
(ROMlib_syszone_size % MB) * 100 / MB,
ROMlib_stack_size / MB,
(ROMlib_stack_size % MB) * 100 / MB);
#undef MB
}
#endif /* !MSDOS */
/* This is to tell people about the switch from "-applzone 4096" to
* "-applzone 4M".
*/
static void
note_memory_syntax_change (const char *arg, unsigned val)
{
/* Make sane error messages when the supplied value is insane.
* Otherwise, try to print an example that illustrates how to
* use the value they are trying to generate.
*/
if (val < 100 || val > 1000000)
val = 2048;
fprintf (stderr, "Specified %s is too small. The syntax "
"has changed; now, for a %u.%02u\nmegabyte %s you would "
"say \"-%s ", arg, val / 1024, (val % 1024) * 100 / 1024,
arg, arg);
if (val % 1024 == 0)
fprintf (stderr, "%uM", val / 1024);
else if (val < 1024)
fprintf (stderr, "%uK", val);
else
fprintf (stderr, "%u.%02uM", val / 1024, (val % 1024) * 100 / 1024);
fputs ("\"\n", stderr);
bad_arg_p = TRUE;
}
/* Concatenates all strings in the array, separated by spaces. */
static const char *
construct_command_line_string (int argc, char **argv)
{
char *s;
unsigned long string_length;
int i, j;
for (i = 0, string_length = 0; i < argc; i++)
string_length += strlen (argv[i]) + 1;
s = malloc (string_length + 1);
s[0] = '\0';
for (j = 0; j < argc; j++)
sprintf (s + strlen (s), "%s%s", j ? " " : "", argv[j]);
return s;
}
PRIVATE int
zap_comments (char *buf, int n_left)
{
char *ip, *op;
boolean_t last_was_cr;
int retval;
ip = buf;
op = buf;
retval = 0;
last_was_cr = TRUE;
while (n_left > 0)
{
while (n_left > 0 && (*ip != '#' || !last_was_cr))
{
last_was_cr = *ip == '\n' || *ip == '\r';
*op++ = *ip++;
++retval;
--n_left;
}
if (n_left > 0)
{
while (n_left > 0 && *ip != '\n' && *ip != '\r')
{
++ip;
--n_left;
}
}
}
return retval;
}
#if !defined (LINUX)
#define EXECUTOR_DIR(filename) \
({ \
char *retval; \
\
retval = alloca (strlen (ROMlib_startdir) + 1 + strlen (filename) + 1); \
sprintf (retval, "%s/%s", ROMlib_startdir, (filename)); \
retval; \
})
#else
#define EXECUTOR_DIR(filename) \
({ \
char *s; \
char *retval; \
\
s = alloca (2 + strlen(filename) + 1); \
sprintf (s, "+/%s", filename); \
s = copystr (s); \
retval = alloca (strlen (s) + 1); \
strcpy (retval, s); \
free (s); \
retval; \
})
#endif
PUBLIC FILE *
executor_dir_fopen (const char *file, const char *perm)
{
char *pathname;
FILE *retval;
pathname = EXECUTOR_DIR (file);
retval = fopen (pathname, perm);
return retval;
}
PUBLIC int
executor_dir_remove (const char *file)
{
char *pathname;
int retval;
pathname = EXECUTOR_DIR (file);
retval = remove (pathname);
return retval;
}
PRIVATE void
read_args_from_file (const char *filename, int *argcp, char ***argvpp)
{
FILE *fp;
fp = executor_dir_fopen (filename, "r");
if (fp)
{
int nread, n_extra_params, i;
char buf[8192], *bufp;
char **saveargv;
nread = fread (buf, 1, sizeof buf, fp);
fclose (fp);
nread = zap_comments (buf, nread);
n_extra_params = count_params (buf, nread);
*argcp += n_extra_params;
saveargv = *argvpp;
*argvpp = malloc ((*argcp+1) * sizeof *argvpp);
bufp = buf;
memcpy (*argvpp, saveargv, (*argcp - n_extra_params) * sizeof **argvpp);
for (i = *argcp - n_extra_params; i < *argcp; ++i)
(*argvpp)[i] = get_param ((const char **) &bufp, &nread);
(*argvpp)[i] = 0;
}
}
#if defined (CYGWIN32)
PRIVATE uint32
win_drive_to_bit (const char *drive_namep)
{
uint32 retval;
if (drive_namep[1] == ':')
retval = 1 << (tolower(drive_namep[0]) - 'a');
else
{
warning_unexpected ("drive name = '%s'", drive_namep);
retval = 0;
}
return retval;
}
#endif
#if defined(LINUX)
#define PERSONALITY_HACK
#include <sys/personality.h>
#define READ_IMPLIES_EXEC 0x0400000
#endif
A2 (PUBLIC, int, main, int, argc, char **, argv)
{
check_structs ();
INTEGER i;
static unsigned short jmpl_to_ResourceStub[3] =
{
CWC (0x4EF9), 0, 0 /* Filled in below. */
};
long l;
ULONGINT save_trap_vectors[64];
virtual_int_state_t int_state;
THz saveSysZone, saveApplZone;
Ptr saveApplLimit;
#if defined (MSDOS)
boolean_t check_files_p; /* See if FILES= is big enough? */
#endif
static void (*reg_funcs[]) (void) =
{
vdriver_opt_register,
NULL,
};
char *arg;
#if defined(PERSONALITY_HACK)
int pers;
pers = personality(0xffffffff);
if ((pers & MMAP_PAGE_ZERO) == 0)
{
if (personality(pers|MMAP_PAGE_ZERO|READ_IMPLIES_EXEC) == 0)
execv(argv[0], argv);
}
#endif
int grayscale_p = FALSE;
#if defined (DISPLAY_SPLASH_SCREEN)
boolean_t splash_screen_displayed_p;
#endif
#if defined (VDRIVER_SVGALIB)
/* We need to do this right away, to give up suid root privileges. */
vga_init ();
#endif
#if defined (MSDOS)
/* Guarantee that ds and ss are the same, so we can use
* -fomit-frame-pointer and other fun hacks. We need to do this
* *right away*, before we might use %ebp as a general register.
*/
asm ("pushl %ds\n\t"
"popl %ss");
#endif /* MSDOS */
ROMlib_do_custom ();
ROMlib_command_line = construct_command_line_string (argc, argv);
if (!arch_init ())
{
fprintf (stderr, "Unable to initialize CPU information.\n");
exit (-100);
}
if (!os_init ())
{
fprintf (stderr, "Unable to initialize operating system features.\n");
exit (-101);
}
{
char *t;
t = strrchr (*argv, '/');
if (t)
program_name = &t[1];
else
program_name = *argv;
}
/* Guarantee various time variables are set up properly. */
msecs_elapsed ();
#if defined (MMAP_LOW_GLOBALS)
for (i = 1; i < argc && strcmp (argv[i], "-offset") != 0; ++i)
;
if (i == argc)
mmap_lowglobals ();
#endif /* MMAP_LOW_GLOBALS */
ROMlib_WriteWhen (WriteInOSEvent);
#if defined (NEXTSTEP)
ROMlib_seteuid (0); /* This is necessary because when people copy
setuid-root files from floppies, the setuid
bit stays, but the "root" part doesn't, so
even if they run as root, they're messed */
#endif
setstartdir (argv[0]);
set_appname (argv[0]);
#define COMMANDS "commands.txt"
read_args_from_file (COMMANDS, &argc, &argv);
#if defined (MSDOS) || defined (CYGWIN32)
read_args_from_file (CHECKPOINT_FILE, &argc, &argv);
checkpointp = checkpoint_init ();
#endif
/* Replace "-desperate" switch with what it implies. We must do
* this before normal command line processing.
*/
if (!handle_desperate_switch (&argc, &argv))
exit (-1);
opt_init ();
common_db = opt_alloc_db ();
opt_register ("common", common_opts, NELEM (common_opts));
opt_register_pre_note ("welcome to the executor help message.");
opt_register_pre_note ("usage: `executor [option...] "
"[program [document1 document2 ...]]'");
for (i = 0; reg_funcs[i]; i ++)
(*reg_funcs[i]) ();
if (!bad_arg_p)
bad_arg_p = opt_parse (common_db, common_opts, NELEM (common_opts),
&argc, argv);
if (opt_val (common_db, "version", NULL))
{
fprintf (stdout, "%s\n", EXECUTOR_VERSION);
exit (0);
}
/*
* If they ask for help, it's not an error -- it should go to stdout
*/
if (opt_val (common_db, "help", NULL))
{
fprintf (stdout, "%s", opt_help_message ());
exit (0);
}
/* Verify that the user input a legal bits per pixel. "0" is a legal
* value here; that means "use the vdriver's default."
*/
opt_int_val (common_db, "bpp", &flag_bpp, &bad_arg_p);
if (flag_bpp != 0 && flag_bpp != 1
&& flag_bpp != 2 && flag_bpp != 4 && flag_bpp != 8
&& flag_bpp != 16 && flag_bpp != 32)
{
fprintf (stderr, "Bits per pixel must be 1, 2, 4, 8, 16 or 32.\n");
bad_arg_p = TRUE;
}
#if defined (SDL)
if (opt_val (common_db, "fullscreen", NULL))
ROMlib_fullscreen_p = TRUE;
if (opt_val (common_db, "hwsurface", NULL))
ROMlib_hwsurface_p = TRUE;
#endif
#if defined(X) || (defined (CYGWIN32) && defined (SDL))
if (opt_val (common_db, "scancodes", NULL))
ROMlib_set_use_scancodes (TRUE);
#endif
#if defined (SDL)
if (opt_val (common_db, "sdlaudio", &arg))
ROMlib_set_sdl_audio_driver_name (arg);
#endif
if (opt_val (common_db, "ppc", NULL))
ROMlib_set_ppc (TRUE);
if (opt_val (common_db, "hfsplusro", NULL))
ROMlib_hfs_plus_support = TRUE;
#if defined (CYGWIN32)
if (opt_val (common_db, "realmodecd", NULL))
ROMlib_set_realmodecd (TRUE);
#endif
if (opt_val (common_db, "size", &arg))
bad_arg_p |= !parse_size_opt ("size", arg);
if (opt_val (common_db, "prres", &arg))
bad_arg_p |= !parse_prres_opt (&ROMlib_optional_res_x,
&ROMlib_optional_res_y, arg);
if (opt_val (common_db, "debug", &arg))
bad_arg_p |= !error_parse_option_string (arg);
#if defined (MSDOS) || defined (CYGWIN32)
if (opt_val (common_db, "macdrives", &arg))
ROMlib_macdrives = parse_drive_opt ("macdrives", arg);
else
{
#if !defined (CYGWIN32)
ROMlib_macdrives = 3; /* A: + B: */
{
int cdrom;
cdrom = dosdisk_find_cdrom ();
if (cdrom != -1)
ROMlib_macdrives |= (1 << cdrom);
}
#else
char buf[512];
if (win_GetLogicalDriveStrings (sizeof buf - 1, buf))
{
char *p;
for (p = buf; *p; p += strlen (p) + 1)
if (win_direct_accessible_disk (p))
ROMlib_macdrives |= win_drive_to_bit (p);
}
#endif
}
if (opt_val (common_db, "dosdrives", &arg))
ROMlib_dosdrives = parse_drive_opt ("dosdrives", arg);
if (opt_val (common_db, "skipdrives", &arg))
{
skipdrives = parse_drive_opt ("skipdrives", arg);
ROMlib_macdrives &= ~skipdrives;
ROMlib_dosdrives &= ~skipdrives;
}
#endif
#if defined (MSDOS)
{
int dangerous_video_p = FALSE;
opt_int_val (common_db, "dangerousvideospeedup",
&dangerous_video_p, &bad_arg_p);
if (!dangerous_video_p)
opt_int_val (common_db, "videospeedup",
&dangerous_video_p, &bad_arg_p);
try_to_use_fat_ds_vga_hack_p = dangerous_video_p;
}
opt_int_val (common_db, "nosync", &ROMlib_nosync, &bad_arg_p);
opt_int_val (common_db, "skipaspi", &ROMlib_skipaspi, &bad_arg_p);
{
int only_vga_p = FALSE;
opt_int_val (common_db, "vga", &only_vga_p, &bad_arg_p);
only_use_vga_p = only_vga_p;
}
#endif
{
int skip;
skip = 0;
opt_int_val (common_db, "nosound", &skip, &bad_arg_p);
sound_disabled_p = (skip != 0);
}
{
int nocache;
nocache = 0;
opt_int_val (common_db, "nodiskcache", &nocache, &bad_arg_p);
dcache_set_enabled (!nocache);
}
#if defined (SYN68K)
use_native_code_p = ! opt_val (common_db, "notnative", NULL);
#endif
afpd_conventions_p = opt_val (common_db, "afpd", NULL);
netatalk_conventions_p = (afpd_conventions_p ||
opt_val (common_db, "netatalk", NULL));
if (netatalk_conventions_p)
{
apple_double_quote_char = ':';
apple_double_fork_prefix = ".AppleDouble/";
}
else
{
apple_double_quote_char = '%';
apple_double_fork_prefix = "%";
}
apple_double_fork_prefix_length = strlen (apple_double_fork_prefix);
substitute_fonts_p = !opt_val (common_db, "cities", NULL);
if (opt_val (common_db, "offset", NULL))
force_big_offset = TRUE;
#if defined (CYGWIN32)
if (opt_val (common_db, "die", NULL))
uninstall_exception_handler ();
if (opt_val (common_db, "noautoevents", NULL))
set_timer_driven_events (FALSE);
#endif
#if defined (MSDOS) || defined (CYGWIN32)
if (opt_val (common_db, "nocheckpoint", NULL))
disable_checkpointing ();
#endif
/* Parse the "-memory" option. */
{
int total_memory;
if (opt_int_val (common_db, "memory", &total_memory, &bad_arg_p))
{
check_arg ("memory", &total_memory,
(MIN_APPLZONE_SIZE + DEFAULT_SYSZONE_SIZE
+ DEFAULT_STACK_SIZE),
(MAX_APPLZONE_SIZE + DEFAULT_SYSZONE_SIZE
+ DEFAULT_STACK_SIZE));
/* Set up the three memory sizes appropriately. For now we
* just allocate the defaults for syszone and stack, and
* put everything else in -applzone.
*/
ROMlib_syszone_size = DEFAULT_SYSZONE_SIZE;
ROMlib_stack_size = DEFAULT_STACK_SIZE;
ROMlib_applzone_size = (total_memory - ROMlib_syszone_size
- ROMlib_stack_size);
}
}
/* I bumped the minimal ROMlib_applzone to 512, since Loser needs
more than 256. I guess it's a little unfair to people who bypass
Loser, but it will prevent confusion. */
opt_int_val (common_db, "applzone", &ROMlib_applzone_size, &bad_arg_p);
if (ROMlib_applzone_size < 65536)
note_memory_syntax_change ("applzone", ROMlib_applzone_size);
else
check_arg ("applzone", &ROMlib_applzone_size, MIN_APPLZONE_SIZE,
MAX_APPLZONE_SIZE);
opt_int_val (common_db, "syszone", &ROMlib_syszone_size, &bad_arg_p);
if (ROMlib_syszone_size < 65536)
note_memory_syntax_change ("syszone", ROMlib_syszone_size);
else
check_arg ("syszone", &ROMlib_syszone_size, MIN_SYSZONE_SIZE,
MAX_SYSZONE_SIZE);
opt_int_val (common_db, "stack", &ROMlib_stack_size, &bad_arg_p);
if (ROMlib_stack_size < 32768)
note_memory_syntax_change ("stack", ROMlib_stack_size);
else
check_arg ("stack", &ROMlib_stack_size, MIN_STACK_SIZE, MAX_STACK_SIZE);
#if defined (MM_MANY_APPLZONES)
opt_int_val (common_db, "napplzones", &mm_n_applzones, &bad_arg_p);
check_arg ("napplzones", &mm_n_applzones, 1, /* random */ 255);
#endif /* !MM_MANY_APPLZONES */
ROMlib_InitZones (force_big_offset ? offset_big : offset_none);
{
uint32 save_a7;
save_a7 = EM_A7;
#if defined (SYN68K)
/* Set up syn68k. */
initialize_68k_emulator (vdriver_system_busy,
#if defined (__CHECKER__)
FALSE,
#else
use_native_code_p,
#endif
(uint32 *) SYN68K_TO_US (0),
#if defined (USE_BIOS_TIMER)
dos_int_flag.rm_segment * 16
#else /* !USE_BIOS_TIMER */
0
#endif /* !USE_BIOS_TIMER */
);
#endif /* SYN68K */
EM_A7 = save_a7;
}
if (opt_val (common_db, "keyboards", NULL))
graphics_p = FALSE;
/* Block virtual interrupts, until the system is fully set up. */
int_state = block_virtual_ints ();
#if defined (MSDOS)
/* We must switch to a non-moving sbrk before calling vdriver_init,
* if the user wants to try the fat %ds vga hack. Otherwise,
* the address of the frame buffer relative to the base of %ds
* could change after we've already determined its value.
*/
if (try_to_use_fat_ds_vga_hack_p)
switch_to_non_moving_sbrk ();
#endif
if (graphics_p && !vdriver_init (0, 0, 0, FALSE, &argc, argv))
{
fprintf (stderr, "Unable to initialize video driver.\n");
exit (-12);
}
#if defined (SYN68K)
/* Save the trap vectors away. */
memcpy (save_trap_vectors, SYN68K_TO_US(0), sizeof save_trap_vectors);
#endif
#if defined (MSDOS)
{
int use_old_timer_p = FALSE;
opt_int_val (common_db, "oldtimer", &use_old_timer_p, &bad_arg_p);
use_bios_timer_p = !use_old_timer_p;
}
{
int no_files_check_p = FALSE;
opt_int_val (common_db, "nofilescheck", &no_files_check_p, &bad_arg_p);
check_files_p = !no_files_check_p;
}
#endif
opt_int_val (common_db, "sticky", &ROMlib_sticky_menus_p, &bad_arg_p);
opt_int_val (common_db, "pceditkeys", &ROMlib_forward_del_p, &bad_arg_p);
opt_int_val (common_db, "nobrowser", &ROMlib_nobrowser, &bad_arg_p);
opt_int_val (common_db, "print", &ROMlib_print, &bad_arg_p);
#if defined (NEXT) || defined (LINUX)
opt_int_val (common_db, "nodotfiles", &ROMlib_no_dot_files, &bad_arg_p);
#endif
#if defined (NOMOUSE_COMMAND_LINE_OPTION)
opt_int_val (common_db, "nomouse", &ROMlib_no_mouse, &bad_arg_p);
#endif
#if 0
opt_int_val (common_db, "noclock", &ROMlib_noclock, &bad_arg_p);
#endif
{
int no_auto = FALSE;
opt_int_val (common_db, "noautorefresh", &no_auto, &bad_arg_p);
do_autorefresh_p = !no_auto;
}
opt_int_val (common_db, "refresh", &ROMlib_refresh, &bad_arg_p);
check_arg ("refresh", &ROMlib_refresh, 0, 60);
opt_int_val (common_db, "grayscale", &grayscale_p, &bad_arg_p);
#if defined (LINUX)
opt_int_val (common_db, "nodrivesearch", &nodrivesearch_p, &bad_arg_p);
#endif
{
char *str;
if (opt_val (common_db, "prvers", &str))
{
uint32 vers;
if (!ROMlib_parse_version (str, &vers))
bad_arg_p = TRUE;
else
ROMlib_PrDrvrVers = (vers >> 8) * 10 + ((vers >> 4) & 0xF);
}
}
#if defined (SUPPORT_LOG_ERR_TO_RAM)
{
int log;
log = 0;
opt_int_val (common_db, "ramlog", &log, &bad_arg_p);
log_err_to_ram_p = (log != 0);
}
#endif
if (opt_val (common_db, "info", NULL))
{
#if defined (MSDOS)
msdos_print_info ();
#else
print_info ();
#endif /* MSDOS */
exit (0);
}
#if defined (MSDOS)
{
int port;
if (opt_int_val (common_db, "modemport", &port, &bad_arg_p))
{
if (port < 1 || port > 4)
{
fprintf (stderr, "modemport must be 1, 2, 3 or 4.\n");
bad_arg_p = TRUE;
}
else
set_modem_port_mapping_to_pc_port (port);
}
if (opt_int_val (common_db, "printerport", &port, &bad_arg_p))
{
if (port < 1 || port > 4)
{
fprintf (stderr, "printerport must be 1, 2, 3 or 4.\n");
bad_arg_p = TRUE;
}
else
set_printer_port_mapping_to_pc_port (port);
}
}
#endif
/* If we failed to parse our arguments properly, exit now.
* I don't think we should call ExitToShell yet because the
* rest of the system isn't initialized.
*/
if (argc >= 2)
{
int a;
/* Only complain if we see something with a leading dash; anything
* else might be a file to launch.
*/
for (a = 1; a < argc; a++)
if (argv[a][0] == '-')
{
fprintf (stderr, "%s: unknown option `%s'\n",
program_name, argv[a]);
bad_arg_p = TRUE;
}
}
filltables ();
l = (long) ostraptable[0x0FC];
((unsigned char *) jmpl_to_ResourceStub)[2] = l >> 24;
((unsigned char *) jmpl_to_ResourceStub)[3] = l >> 16;
((unsigned char *) jmpl_to_ResourceStub)[4] = l >> 8;
((unsigned char *) jmpl_to_ResourceStub)[5] = l;
ostraptable[0xFC] = (void *) US_TO_SYN68K(jmpl_to_ResourceStub);
osstuff[0xFC].orig = (void *) US_TO_SYN68K(jmpl_to_ResourceStub);
saveSysZone = SysZone;
saveApplZone = ApplZone;
saveApplLimit = ApplLimit;
memset (&nilhandle, ~0, (char *) &lastlowglobal - (char *) &nilhandle);
setupsignals ();
Ticks_UL.u = 0;
nilhandle = 0; /* so nil dereferences "work" */
memset (&EventQueue, 0, sizeof (EventQueue));
memset (&VBLQueue, 0, sizeof (VBLQueue));
memset (&DrvQHdr, 0, sizeof (DrvQHdr));
memset (&VCBQHdr, 0, sizeof (VCBQHdr));
memset (&FSQHdr, 0, sizeof (FSQHdr));
TESysJust = 0;
SysZone = saveSysZone;
ApplZone = saveApplZone;
ApplLimit = saveApplLimit;
BootDrive = 0;
DefVCBPtr = 0;
CurMap = 0;
TopMapHndl = 0;
DSAlertTab = 0;
ResumeProc = 0;
SFSaveDisk = 0;
GZRootHnd = 0;
ANumber = 0;
ResErrProc = 0;
FractEnable = 0;
SEvtEnb = 0;
MenuList = 0;
MBarEnable = 0;
MenuFlash = 0;
TheMenu = 0;
MBarHook = 0;
MenuHook = 0;
MenuCInfo = NULL;
HeapEnd = 0;
ApplLimit = 0;
SoundActive = soundactiveoff;
PortBUse = 2; /* configured for Serial driver */
memset (KeyMap, 0, sizeof_KeyMap);
if (vdriver_grayscale_p || grayscale_p)
{
/* Choose a nice light gray hilite color. */
HiliteRGB.red = CWC (0xAAAA);
HiliteRGB.green = CWC (0xAAAA);
HiliteRGB.blue = CWC (0xAAAA);
}
else
{
/* how about a nice yellow hilite color? */
HiliteRGB.red = CWC (0xFFFF);
HiliteRGB.green = CWC (0xFFFF);
HiliteRGB.blue = CWC (0);
}
{
static uint16 ret = CWC (0x4E75);
JCrsrTask = (ProcPtr) RM (&ret);
}
SET_HILITE_BIT ();
TheGDevice = MainDevice = DeviceList = CLC_NULL;
OneOne = CLC (0x00010001);
Lo3Bytes = CLC (0xFFFFFF);
DragHook = 0;
TopMapHndl = 0;
SysMapHndl = 0;
MBDFHndl = 0;
MenuList = 0;
MBSaveLoc = 0;
SysVersion = CW (system_version);
FSFCBLen = CWC (94);
ScrapState = CWC (-1);
#if defined (MSDOS)
switch_to_non_moving_sbrk ();
#endif
#if defined (MSDOS)
/* Make sure FILES= is big enough. */
if (check_files_p && !msdos_test_max_files ())
exit (-39);
/* Make sure we have at least 0.75 megabytes of memory remaining, so
* that syn68k will have a big enough working area. This figure is
* somewhat arbitrary, but we really don't want the user cutting it
* particularly close and then complain about lousy performance and
* flaky behavior.
*/
if (!msdos_check_memory_remaining (768 * 1024))
{
vdriver_shutdown ();
print_mem_full_message ();
exit (-50);
}
#endif
#if defined(NEXT) && !defined(SYN68K)
ROMlib_install_ardi_mods ();
#endif /* NEXT && !SYN68K */
#if defined (NEXT)
ROMlib_determine040ness ();
ROMlib_checkadb ();
#endif /* NEXT */
TheZone = SysZone;
UTableBase =
(DCtlHandlePtr) (long) RM (NewPtr (sizeof (UTableBase[0].p) * NDEVICES));
memset (MR (UTableBase), 0, sizeof (UTableBase[0].p) * NDEVICES);
UnitNtryCnt = CW (NDEVICES);
TheZone = ApplZone;
if (graphics_p)
{
/* Set up the current graphics mode appropriately. */
if (!vdriver_set_mode (flag_width, flag_height, flag_bpp, grayscale_p))
illegal_mode ();
/* initialize the mac rgb_spec's */
make_rgb_spec (&mac_16bpp_rgb_spec,
16, TRUE, 0,
5, 10, 5, 5, 5, 0,
CL (GetCTSeed ()));
make_rgb_spec (&mac_32bpp_rgb_spec,
32, TRUE, 0,
8, 16, 8, 8, 8, 0,
CL (GetCTSeed ()));
gd_allocate_main_device ();
}
#if defined (DISPLAY_SPLASH_SCREEN)
if (graphics_p)
splash_screen_displayed_p = splash_screen_display (FALSE, "splash");
#endif
ROMlib_eventinit (graphics_p);
hle_init ();
ROMlib_fileinit ();
InitUtil ();
#if !defined (X) && !defined (SDL)
/* #warning "Hack so we don't smash mouse/keyboard m68k interrupt vectors." */
#if defined (M68K_EVENT_VECTOR)
save_trap_vectors[M68K_EVENT_VECTOR]
= *(syn68k_addr_t *)SYN68K_TO_US(M68K_EVENT_VECTOR * 4);
#endif
#if defined (M68K_MOUSE_MOVED_VECTOR)
save_trap_vectors[M68K_MOUSE_MOVED_VECTOR]
= *(syn68k_addr_t *)SYN68K_TO_US(M68K_MOUSE_MOVED_VECTOR * 4);
#endif
#endif
{
char *appearance_str;
if (opt_val (common_db, "appearance", &appearance_str))
bad_arg_p |= !ROMlib_parse_appearance (appearance_str);
}
InitResources ();
/* parse the `-system' option */
{
char *system_str;
if (opt_val (common_db, "system", &system_str))
bad_arg_p |= !parse_system_version (system_str);
else
ROMlib_set_system_version (system_version);
}
if (bad_arg_p)
{
fprintf (stderr,
"Type \"%s -help\" for a list of command-line options.\n",
program_name);
exit (-10);
}
{
boolean_t keyboard_set_failed;
if (opt_val (common_db, "keyboard", &arg))
{
keyboard_set_failed = !ROMlib_set_keyboard (arg);
if (keyboard_set_failed)
printf ("``%s'' is not an available keyboard\n", arg);
}
else
keyboard_set_failed = FALSE;
if (keyboard_set_failed || opt_val (common_db, "keyboards", NULL))
display_keyboard_choices ();
}
ROMlib_seginit (argc, argv);
InitFonts ();
#if !defined (NDEBUG)
dump_init (NULL);
#endif
/* see qColorutil.c */
ROMlib_color_init ();
wind_color_init ();
/* must be called after `ROMlib_color_init ()' */
image_inits ();
/* must be after `image_inits ()' */
sb_ctl_init ();
AE_init ();
{
INTEGER env = 0;
ROMlib_Fsetenv (&env, 0);
}
TEDoText = RM ((ProcPtr) P_ROMlib_dotext); /* where should this go ? */
#if defined (SYN68K)
{
LONGINT save58;
save58 = *(LONGINT *) SYN68K_TO_US(0x58);
/* Replace the trap vectors which got smashed during initialization. */
memcpy (SYN68K_TO_US (0), save_trap_vectors, sizeof save_trap_vectors);
*(LONGINT *) SYN68K_TO_US(0x58) = save58;
setup_trap_vectors ();
}
/* Set up timer interrupts. We need to do this after everything else
* has been initialized.
*/
if (!syncint_init ())
{
vdriver_shutdown ();
fputs ("Fatal error: unable to initialize timer.\n", stderr);
exit (-11);
}
#endif /* SYN68K */
sound_init ();
set_refresh_rate (ROMlib_refresh);
restore_virtual_ints (int_state);
WWExist = QDExist = EXIST_NO;
#if defined (CYGWIN32)
complain_if_no_ghostscript ();
#endif
executor_main ();
if (!ROMlib_no_windows)
ExitToShell ();
else
exit (0);
/* NOT REACHED */
return 0;
}