/*
** RSXOPT.C
**
** call : rsxopt [-i -b -a] name.exe [options]
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
#include <fcntl.h>
#include <ctype.h>

struct exe_hdr {
    unsigned short signatur;
    unsigned short low;
    unsigned short high;
    unsigned short reloc;
    unsigned short hdr_para;
    } ;

struct emx_hdr2 {
    char sig[18];
    char next_hdr[4];
    char option[64];
};

/* return first non-digit */
static char * asc2int(char *s, int *retv)
{
    char *str = s;
    *retv = 0;

    while (*str != 0) {
	if ((*str < '0') || (*str > '9'))
	    break;
	*retv *= 10;
	*retv += *str - '0';
	str++;
    }
    return (str);
}

char *scan_for_option(char *s)
{
    int temp;

    switch (*s) {

    case 'a':                   /* DOS features */
	for (++s; *s > ' '; ++s) {
	    if (*s == 'm')
		puts("- enable 'memaccess()'");
	    else if (*s == 'c')
		puts("- enable 'execute data'");
	    else if (*s == 'i')
		puts("- enable 'hardware i/o'");
	    else if (*s == 'w')
		puts("- enable 'write access'");
	    else
		return NULL;
	}
	if (*(--s) == 'a')
	    return NULL;
	break;

    case 'c':                   /* core */
	puts("- disable writing core files");
	break;

    case 'f':                   /* frame size (ignore) */
	++s;
	if (!isdigit(*s))
	    return NULL;
	s = asc2int(s, &temp);
	printf("- emx frame size = %d KB\n", temp);
	return s;

    case 's':                   /* frame size (ignore) */
	++s;
	if (!isdigit(*s))
	    return NULL;
	s = asc2int(s, &temp);
	printf("- emx stack size = %d KB\n", temp);
	return s;

    case 'h':                   /* max handles */
	++s;
	if (!isdigit(*s))
	    return NULL;
	s = asc2int(s, &temp);
	printf("- dos handles = %d\n", temp);
	return s;

    case 'p':                   /* don't use lower DOS mem */
	puts("- don't use DOS memory");
	break;

    case 'R':       /* special RSX options */
	for (++s; *s > ' '; ++s) {
	    if (*s == ',')
		++s;
	    switch (*s) {
		case 'a':                   /* ss = ds */
		    puts("- don't use expand-down stacksegment");
		    break;
		case 'e':                   /* copro */
		    temp = 0;
		    if (isdigit(*(s+1)))
			temp = *(++s) - '0' + 1;
		    if (temp)
			printf("- force copro state to %d\n", temp -1);
		    else
			puts("- disable fpu-emu");
		    break;
		case 'm':                   /* malloc for memaccess */
		    if (!isdigit(*(s+1)))
			break;
		    s = asc2int(++s, &temp) - 1;
		    printf("- prealloc memory = %d KB\n", temp);
		    break;
		case 's':                   /* stack frame */
		    ++s;
		    if (!isdigit(*s))
			return NULL;
		    s =  asc2int(s, &temp) - 1;
		    printf("- rsx stack size = %d KB\n", temp);
		    break;
		case 'x':                   /* enable schedule */
		    puts("- enable scheduler");
		    break;
		case 'z':                   /* zero heap */
		    puts("- zeor heap");
		    break;
		case '9':                   /* no dpmi10 calls */
		    break;
		case 'D':                   /* don't touch int3 */
		    break;
		case 'F':                   /* redirect output */
		    break;
		case 'I':                   /* print syscalls */
		    break;
		case 'K':                   /* kernel debug */
		    break;
		case 'P':                   /* print syscalls */
		    break;
		default:
		    return NULL;
	    }
	}
	if (*(--s) == 'R')
	    return NULL;
	break;

    default:
	return NULL;

    } /* switch (*s) */

    return s;
}

void display_options(char *s)
{
    char *t;

    if (! *s) {
	puts("File has no emxbind options");
	return;
    }

    printf("File contains options: '%s'\n", s);

    for (; *s != '\0'; ++s) {
	while (*s == ' ')
	    ++s;
	if (*s == '-') {
	    t = scan_for_option(++s);
	    if (t == NULL) {
		printf("** error in emx options : %s **\n", --s);
		return;
	    }
	} else
	    break;
	s = t;
    }
}

void usage(void )
{
    puts("usage: rsxopt [-a -b -i] file.exe [options]");
    puts("\t -a: append options");
    puts("\t -b: build new options");
    puts("\t -i: info about options");
}

int main(int argc, char **argv)
{
    int hexe;
    char option;
    struct exe_hdr exehdr;
    struct emx_hdr2 emxhdr;

    puts("RSXOPT - bind options in exefile ; (c) Rainer Schnitker");

    if (argc < 3) {
	usage();
	return 1;
    }

    if (strcmp(argv[1],"-i") == 0)
	option = 'i';
    else if (strcmp(argv[1],"-a") == 0)
	option = 'a';
    else if (strcmp(argv[1],"-b") == 0)
	option = 'b';
    else {
	usage();
	return 1;
    }

    if ((hexe = open(argv[2], O_RDWR | O_BINARY)) < 0) {
	perror("open");
	return 1;
    }

    read(hexe, &exehdr, sizeof(struct exe_hdr));

    if (exehdr.signatur != 0x5a4d) {
	puts("file has not exe header!");
	close(hexe);
	return 1;
    }

    lseek(hexe, (long)exehdr.hdr_para * 16L, SEEK_SET);
    read(hexe, &emxhdr, sizeof(emxhdr));

    if (memcmp(emxhdr.sig, "emx", 3) != 0) {
	puts("file has not emx file header!");
	close(hexe);
    }

    if (option == 'i')
	display_options(emxhdr.option);
    else {
	int i;
	char *s = emxhdr.option;
	if (option == 'a') {
	    s += strlen(s);
	    *s++ = ' ';
	}
	for (i = 3; argv[i]; i++) {
	    strcpy(s, argv[i]);
	    s += strlen(s);
	    *s++ = ' ';
	}
	*--s = '\0';
	lseek(hexe, (long)exehdr.hdr_para * 16L, SEEK_SET);
	write(hexe, &emxhdr, sizeof(emxhdr));
    }
    close(hexe);
    return 0;
}
