/*
** MKMSG [-h cfile] [-inc afile] [-asm srcfile [-min|-max]] txtfile
**
** take message file and produce assembler source file. lines in txtfile
** can be of 6 types:
**      1) "<<NMSG>>"   -- use near segment
**      2) "<<FMSG>>"   -- use far segment
**      3) "#anything"  -- comment line (ignore)
**      4) ""           -- blank line (ignore)
**      5) "handle<tab>number<tab>message_text"
**              -- message with number and symbolic handle
**      6) "<tab>number<tab>message_text"
**              -- message with number but no symbolic handle
**
** the -h file gets "#define handle number" for those messages with handles.
** the -inc file gets "handle = number" for the same messages. the -asm file
** gets standard segment definitions, then the messages are placed either in
** a near segment (MSG) or a far segment (FAR_MSG) depending on if they follow
** a <<NMSG>> or a <<FMSG>>. if neither is present, <<NMSG>> is assumed. if
** -min or -max is given with -asm, the minimum or maximum amount of 0-padding
** is calculated and placed in the .asm file. any combination of the options
** may be given, and if none are present then the input is only checked for
** syntactic validity. maximum and minimum amount of padding depends on the
** length of the individual messages, and is defined in the cp/dos spec
**
** If the -32 switch is supplied then the -asm file will be compatible
** with a 32 bit flat model operating system. In which case <<NMSG>> and
** <<FMSG>> cause the messages to be placed in two tables. The tables are
** named MSG_tbl and FAR_MSG_tbl respectively. These are within the 32 bit
** small model data segment.
**
** NOTE: This file is no longer used for NT MASM. Instead its output was
** converted to asmmsg.h and asmmsg2.h and slimed. This was the quick and
** dirty way to be able to compile masm for other processors. (Jeff Spencer)
** For more info read the header on asmmsg2.h.
**
** randy nevin, microsoft, 4/86
** (c)copyright microsoft corp, 1986
**
** Modified for 32 bit by Jeff Spencer 10/90
*/

#include <stdio.h>
#include <ctype.h>

void SetNear( void );
void SetFar( void );

char usage[] =
  "usage: MKMSG [-h cfile] [-inc afile] [-asm srcfile [-min|-max]] [-32] txtfile\n";
char ex[] = "expected escape sequence: %s\n";

char n1[] = "HDR segment byte public \'MSG\'\nHDR ends\n";
char n2[] = "MSG segment byte public \'MSG\'\nMSG ends\n";
char n3[] = "PAD segment byte public \'MSG\'\nPAD ends\n";
char n4[] = "EPAD segment byte common \'MSG\'\nEPAD ends\n";
char n5[] = "DGROUP group HDR,MSG,PAD,EPAD\n\n";

char f1[] = "FAR_HDR segment byte public \'FAR_MSG\'\nFAR_HDR ends\n";
char f2[] = "FAR_MSG segment byte public \'FAR_MSG\'\nFAR_MSG ends\n";
char f3[] = "FAR_PAD segment byte public \'FAR_MSG\'\nFAR_PAD ends\n";
char f4[] = "FAR_EPAD segment byte common \'FAR_MSG\'\nFAR_EPAD ends\n";
char f5[] = "FMGROUP group FAR_HDR,FAR_MSG,FAR_PAD,FAR_EPAD\n\n";

int f32Bit = 0;         /* -32?, produce 32bit flat model code */
char didnear = 0;
char didfar = 0;
FILE *fasm = NULL;      /* -asm stream */

__cdecl main( argc, argv )
        int argc;
        char **argv;
        {
        FILE *f;                /* the input file */
        char *h = NULL;         /* -h file name */
        FILE *fh = NULL;        /* -h stream */
        char *inc = NULL;       /* -inc file name */
        FILE *finc = NULL;      /* -inc stream */
        char *asm = NULL;       /* -asm file name */
        int min = 0;            /* -min? */
        int max = 0;            /* -max? */
        int asmstate = 0;       /* 0=nothing, 1=doing nmsg, 2=doing fmsg */
        int instring;           /* db "... */
        char buf[256];          /* line buffer */
        int ch;
        int i;
        int number;             /* index of message number in line */
        int msg;                /* index of message text in line */
        int npad = 0;           /* cumulative amount of near padding */
        int fpad = 0;           /* cumulative amount of far padding */
        int length;
        double factor;
        double result;

        argc--;  /* skip argv[0] */
        argv++;

        while (argc && **argv == '-')  /* process options */
                if (!strcmp( "-h", *argv )) {  /* create .h file */
                        argc--;
                        argv++;

                        if (!argc)
                                printf( "no -h file given\n" );
                        else if (h) {
                                printf( "extra -h file %s ignored\n", *argv );
                                argc--;
                                argv++;
                                }
                        else    {  /* remember -h file */
                                h = *argv;
                                argc--;
                                argv++;
                                }
                        }
                else if (!strcmp( "-inc", *argv )) {  /* create .inc file */
                        argc--;
                        argv++;

                        if (!argc)
                                printf( "no -inc file given\n" );
                        else if (inc) {
                                printf( "extra -inc file %s ignored\n", *argv );
                                argc--;
                                argv++;
                                }
                        else    {  /* remember -inc file */
                                inc = *argv;
                                argc--;
                                argv++;
                                }
                        }
                else if (!strcmp( "-asm", *argv )) {  /* create .asm file */
                        argc--;
                        argv++;

                        if (!argc)
                                printf( "no -asm file given\n" );
                        else if (asm) {
                                printf( "extra -asm file %s ignored\n", *argv );
                                argc--;
                                argv++;
                                }
                        else    {  /* remember -asm file */
                                asm = *argv;
                                argc--;
                                argv++;
                                }
                        }
                else if (!strcmp( "-min", *argv )) {  /* minimum padding */
                        argc--;
                        argv++;

                        if (min)
                                printf( "redundant -min\n" );

                        min = 1;
                        }
                else if (!strcmp( "-max", *argv )) {  /* maximum padding */
                        argc--;
                        argv++;

                        if (max)
                                printf( "redundant -max\n" );

                        max = 1;
                        }
                else if (!strcmp( "-32", *argv )) {  /* 32bit code */
                        argc--;
                        argv++;
                        f32Bit = 1;
                        }
                else    {
                        printf( "unknown option %s ignored\n", *argv );
                        argc--;
                        argv++;
                        }

        if ((min || max) && !asm) {
                printf( "-min/-max ignored; no -asm file\n" );
                min = max = 0;
                }

        if (min && max) {
                printf( "-min and -max are mutually exclusive; -min chosen\n" );
                max = 0;
                }

        if (!argc) {  /* no arguments */
                printf( usage );
                exit( -1 );
                }

        if (argc != 1)  /* extra arguments */
                printf( "ignoring extra arguments\n" );

        if (!(f = fopen( *argv, "rb" ))) {
                printf( "can't open txtfile %s for binary reading\n", *argv );
                exit( -1 );
                }

        if (asm && !(fasm = fopen( asm, "w" ))) {
                printf( "can't open -asm file %s for writing\n", asm );
                exit( -1 );
                }

        if (h && !(fh = fopen( h, "w" ))) {
                printf( "can't open -h file %s for writing\n", h );
                exit( -1 );
                }

        if (inc && !(finc = fopen( inc, "w" ))) {
                printf( "can't open -inc file %s for writing\n", inc );
                exit( -1 );
                }

        if( fasm && f32Bit ){
                fprintf( fasm, "\t.386\n" );
                fprintf( fasm, "\t.model small,c\n" );
                fprintf( fasm, "\t.data\n\n" );
                }

        while ((ch = getc( f )) != EOF)  /* process lines */
                if (ch == '<') {  /* <<NMSG>> or <<FMSG>> */
                        buf[0] = ch;
                        i = 1;

                        while ((ch = getc( f )) != EOF && ch != '\r'
                                        && ch != '\n')
                                if (i < 255)
                                        buf[i++] = ch;

                        buf[i] = '\0';

                        if (!strcmp( "<<NMSG>>", buf ))/*near msgs follow*/
                                if (asmstate == 0) {
                                        if (fasm) {
                                                SetNear();
                                                asmstate = 1;
                                                }
                                        }
                                else if (asmstate == 1)
                                        printf( "already in nmsg\n" );
                                else if (asmstate == 2) {
                                        if (fasm) {
                                                if( !f32Bit ){
                                                       fprintf( fasm, "FAR_MSG ends\n\n" );
                                                       }
                                                SetNear();
                                                asmstate = 1;
                                                }
                                        }
                                else    {
                                        printf( "internal error\n" );
                                        exit( -1 );
                                        }
                        else if (!strcmp( "<<FMSG>>", buf ))/*far msgs follow*/
                                if (asmstate == 0) {
                                        if (fasm) {
                                                SetFar();
                                                asmstate = 2;
                                                }
                                        }
                                else if (asmstate == 1) {
                                        if (fasm) {
                                                if( !f32Bit ){
                                                        fprintf( fasm, "MSG ends\n\n" );
                                                        }
                                                SetFar();
                                                asmstate = 2;
                                                }
                                        }
                                else if (asmstate == 2)
                                        printf( "already in fmsg\n" );
                                else    {
                                        printf( "internal error\n" );
                                        exit( -1 );
                                        }
                        else
                                printf( "ignoring bad line: %s\n", buf );
                        }
                else if (ch == '#')  /* comment line */
                        while ((ch = getc( f )) != EOF && ch != '\r'
                                && ch != '\n')
                                ;
                else if (ch != '\r' && ch != '\n') {  /* something to do */
                        buf[0] = ch;
                        i = 1;

                        while ((ch = getc( f )) != EOF && ch != '\r'
                                        && ch != '\n')
                                if (i < 255)
                                        buf[i++] = ch;

                        buf[i] = '\0';

                        if (buf[i = 0] != '\t')
                                while (buf[i] && buf[i] != '\t')
                                        i++;

                        if (!buf[i]) {
                                printf( "expected <TAB>: %s\n", buf );
                                continue;
                                }
                        else
                                i++;

                        if (!buf[i] || buf[i] == '\t') {
                                printf( "expected msgnum: %s\n", buf );
                                continue;
                                }

                        number = i;

                        while (buf[i] && buf[i] != '\t')
                                i++;

                        if (buf[i] != '\t') {
                                printf( "expected <TAB>: %s\n", buf );
                                continue;
                                }

                        msg = ++i;

                        if (buf[0] != '\t') {  /* possible -h and/or -inc */
                                if (h) {
                                        fprintf( fh, "#define\t" );

                                        for (i = 0; i < msg-1; i++)
                                                putc( buf[i], fh );

                                        putc( '\n', fh );
                                        }

                                if (inc) {
                                        for (i = 0; i < number; i++)
                                                putc( buf[i], finc );

                                        fprintf( finc, "=\t" );

                                        while (i < msg-1)
                                                putc( buf[i++], finc );

                                        putc( '\n', finc );
                                        }
                                }

                        if (fasm) {  /* write asmfile */
                                if (asmstate == 0) {
                                        SetNear();
                                        asmstate = 1;
                                        }

                                fprintf( fasm, "\tdw\t" );

                                for (i = number; i < msg-1; i++)
                                        putc( buf[i], fasm );

                                fprintf( fasm, "\n\tdb\t" );
                                instring = 0;

                                for (i = msg, length = 0; buf[i];
                                                i++, length++)
                                                /* allocate message */
                                        if (buf[i] == '\\')
                                                /* C escape sequence */
                                                switch (buf[++i]) {
                                                case 'r':
                                                case 'n':
                                                case 't':
                                                case 'f':
                                                case 'v':
                                                case 'b':
                                                case '\'':
                                                case '"':
                                                case '\\':
                                                        if (instring) {
                                                                putc( '"',
                                                                        fasm );
                                                                putc( ',',
                                                                        fasm );
                                                                instring = 0;
                                                                }

                                                        if (buf[i] == 'r')
                                                                fprintf( fasm,
                                                                        "13" );
                                                        else if (buf[i] == 'n')
                                                                fprintf( fasm,
                                                                        "10" );
                                                        else if (buf[i] == 't')
                                                                fprintf( fasm,
                                                                        "9" );
                                                        else if (buf[i] == 'f')
                                                                fprintf( fasm,
                                                                        "12" );
                                                        else if (buf[i] == 'v')
                                                                fprintf( fasm,
                                                                        "11" );
                                                        else if (buf[i] == 'b')
                                                                fprintf( fasm,
                                                                        "8" );
                                                        else if (buf[i] == '\'')
                                                                fprintf( fasm,
                                                                        "39" );
                                                        else if (buf[i] == '"')
                                                                fprintf( fasm,
                                                                        "34" );
                                                        else if (buf[i] == '\\')
                                                                fprintf( fasm,
                                                                        "92" );

                                                        putc( ',', fasm );
                                                        break;
                                                case '\0':
                                                        printf( ex, buf );
                                                        i--;
                                                        break;
                                                default:
                                                        if (!instring) {
                                                                putc( '"',
                                                                        fasm );
                                                                instring = 1;
                                                                }

                                                        putc( buf[i], fasm );
                                                        break;
                                                }
                                        else if (instring)
                                                /* keep building string */
                                                putc( buf[i], fasm );
                                        else    {  /* start building string */
                                                putc( '"', fasm );
                                                instring = 1;
                                                putc( buf[i], fasm );
                                                }

                                if (instring) {  /* close string */
                                        putc( '"', fasm );
                                        putc( ',', fasm );
                                        }

                                putc( '0', fasm );
                                putc( '\n', fasm );

                                /* calculate padding */
                                /* depends on msg length */

                                if (min || max) {
                                        if (min)
                                                if (length <= 10)
                                                        factor = 1.01;
                                                else if (length <= 20)
                                                        factor = 0.81;
                                                else if (length <= 30)
                                                        factor = 0.61;
                                                else if (length <= 50)
                                                        factor = 0.41;
                                                else if (length <= 70)
                                                        factor = 0.31;
                                                else
                                                        factor = 0.30;
                                        else if (length <= 10)
                                                factor = 2.00;
                                        else if (length <= 20)
                                                factor = 1.00;
                                        else if (length <= 30)
                                                factor = 0.80;
                                        else if (length <= 50)
                                                factor = 0.60;
                                        else if (length <= 70)
                                                factor = 0.40;
                                        else
                                                factor = 0.30;

                                        result = (double)length * factor;

                                        if (asmstate == 1) {
                                                npad += (int)result;

                                                if (result
                                                        > (float)((int)result))
                                                        npad++;
                                                }
                                        else if (asmstate == 2) {
                                                fpad += (int)result;

                                                if (result
                                                        > (float)((int)result))
                                                        fpad++;
                                                }
                                        }
                                }
                        }

        if (fasm) {  /* finish up asm file */
                if( !f32Bit ){
                        if (asmstate == 1)
                                fprintf( fasm, "MSG ends\n\n");
                        else if (asmstate == 2)
                                fprintf( fasm, "FAR_MSG ends\n\n");

                        if (npad) {  /* add near padding */
                                fprintf( fasm, "PAD segment\n\tdb\t%d dup(0)\n",
                                        npad );
                                fprintf( fasm, "PAD ends\n\n" );
                                }

                        if (fpad) {  /* add far padding */
                                fprintf( fasm, "FAR_PAD segment\n\tdb\t%d dup(0)\n",
                                        fpad );
                                fprintf( fasm, "FAR_PAD ends\n\n" );
                                }
                        }
                fprintf( fasm, "\tend\n" );
                fclose( fasm );
                }

        if (fh)
                fclose( fh );

        if (finc)
                fclose( finc );

        fclose( f );
        exit( 0 );
        }


void SetNear()
{
        if( f32Bit ) {
                if( !didnear ){
                        fprintf( fasm, "MSG_tbl EQU $\n" );
                        fprintf( fasm, "\tpublic MSG_tbl\n" );
                        didnear++;
                        }
                else{
                        /* Rather than modify mkmsg to handle mixed NEAR / FAR */
                        /* I (Jeff Spencer) chose the quick route of limiting it's capabilities */
                        /* As this capability wasn't needed for MASM 5.1 */
                        printf( "error - 32 bit version doesn't support alternating NEAR and FAR messages\n" );
                        exit( -1 );
                        }
                }
        else{
                if (!didnear) {
                        didnear++;
                        fprintf( fasm, n1 );
                        fprintf( fasm, n2 );
                        fprintf( fasm, n3 );
                        fprintf( fasm, n4 );
                        fprintf( fasm, n5 );
                        }

                fprintf( fasm,
                        "MSG segment\n" );
                }
        }





void SetFar()
{

        if( f32Bit ){
                if( !didfar ){
                        fprintf( fasm, "FAR_MSG_tbl EQU $\n" );
                        fprintf( fasm, "\tpublic FAR_MSG_tbl\n" );
                        didfar++;
                        }
                else{
                        /* Rather than modify mkmsg to handle mixed NEAR / FAR */
                        /* I (Jeff Spencer) chose the quick route of limiting it's capabilities */
                        /* As this capability wasn't needed for MASM 5.1 */
                        printf( "error - 32 bit version doesn't support alternating NEAR and FAR messages\n" );
                        exit( -1 );
                        }
                }
        else{
                if (!didfar) {
                        didfar++;
                        fprintf( fasm, f1 );
                        fprintf( fasm, f2 );
                        fprintf( fasm, f3 );
                        fprintf( fasm, f4 );
                        fprintf( fasm, f5 );
                        }

                fprintf( fasm,
                        "FAR_MSG segment\n" );
                }
        }