/*
 *  4GS.EXE версии 6.7
 *  Stub for LE/LX EXE file.
 *  Search and call DOS4G, 4GWPRO, DOS4GW or DOS32A DOS Extenders
 *
 *  Компиляция( C-- v0.220+ ) :
 *                              C--.EXE 4GS
 *
 *  Last modify 19-02-2000
 *
 *  Размер после компиляции - 368/394 byte(s)
 *
 *  C-- compiler you can download from
 *      http://www.chat.ru/~sheker
 */

#use80386
#codesize
#DOSrequired 0x0300
#alignword FALSE
#stack 4096
#resize TRUE
#jumptomain NONE
#pragma option texe
#pragma option x
#compilerversion 0.220

/*
**  Определить, если не хотите видеть DOS4G banner
**  Закомментировать, если хотите видеть DOS4G banner
*/
//?define QUIET TRUE

/*
**  В каком сегменте у нас данные
*/
?define DATA_SEG CS

//
// строка 'DOS4GWPRO'
//
byte dos4g  = { 'D','O','S','4','G','W','P','R','O' };
//
// строка 'DOS32A'
//
byte dos32a = { 'D','O','S','3','2','A' };

#dosstring  TRUE
//
// строка CR,LF
//
byte crlf = "\n";
//
//  строка 'DOS 386+ need!$', затем заменяем '86' на '.0' ...
//
byte errDOS = "DOS 386+ need";
//
//  строка 'Cmdline too long!$'
//
byte errARG = "Cmdline too long";
//
//  строка 'DOSX not found!$'
//
byte errPRG = "DOSX not found";
//
//  строка '386+ need!'
//
?define errCPU #errDOS+4

#dosstring  FALSE

?ifdef QUIET
byte addenv = "DOS4G=QUIET\000";
?endif

byte arg_len;                   // длина коммандной строки
byte arg_str[255];              // коммандная строка, заканчивается 0Dh
byte prg_str[256];              // полный путь к программе


/******************************************************************
 *  Вызывается, когда найдена переменная окружения :)
 *  ES:DI = путь в переменной окружения
 ******************************************************************/
ADDPRG() {
////////////////////////////////////////////////////////////
//  Проверяем, последний символ '\\', если нет - добавляем
////////////////////////////////////////////////////////////
    DI--;                   // смотрим на последний символ
    AL = '\\';
    $scasb                  // сравниваем с '\\'
    IF ( NOTZEROFLAG )
        $stosb              // если нет - добавляем '\\'

/******************************************************************
 *  Вызывается для поиска в текущем каталоге
 *  ES:DI = prg_str
 ******************************************************************/
addprg1:
    $push DS                // сохраним DS & SI,
    $push SI                // указатель на Environ
    DS = DATA_SEG;          // DS = seg @data
    BP = #CHKPRG;           // BP = offset CHKPRG процедуры
    CL = 5;                 // CX == 5 = len('DOS4G'), CH = 0, thanks M. Шекер
    SI = #dos4g;            // проверяем 'DOS4G.EXE'
    $call BP                // call CHKPRG
    CX++;                   // CX = 6 = len('DOS4GW'), проверяем 'DOS4GW.EXE'
    $call BP                // call CHKPRG
    $lodsb                  // SI += 3, но короче, DS:SI = '4GWPRO'
    $lodsw                  // CX = 6 = len('4GWPRO'), проверяем '4GWPRO.EXE'
    $call BP                // call CHKPRG
    SI = #dos32a;           // DS:SI = 'DOS32A', CX = 6 = len('DOS32A')
    $call BP                // call CHKPRG
    $pop SI                 // востанавливаем DS & SI
    $pop DS
}


/******************************************************************
 *  Вызывается для добавления имени програмы и попытки зупуска
 *  ES:DI = prg_str с путем
 *  DS:SI = имя программы БЕЗ '.EXE'
 *  CX = длина имени
 ******************************************************************/
CHKPRG() {
    $pusha                  // сохранить AX-BP :)
    $rep $movsb             // добавляем имя программы
////////////////////////////////////////////////////////////
//  Добавляем '.EXE\0'
////////////////////////////////////////////////////////////
    AX = 'E.';
    $stosw
    AL = 'X';               // AH уже = 'E', thanks M. Шекер
    $stosw
    AX><CX;                 // AX = 0;
    $stosb
////////////////////////////////////////////////////////////
//  Готовимся к EXECy
////////////////////////////////////////////////////////////
    $push CS
    $push 6Ch               // FCB #2
    $push CS
    $push 5Ch               // FCB #1
    $push DS
    $push #arg_len          // командная строка
    $push AX                // == 0, использовать текущий Environ
    BX = SP;                // ES:BX = EXEC parm
    DX = #prg_str;          // имя файла
    AH = 4Bh;               // AX = 0, thanks M. Sheker
    $int 21h
    IF( NOTCARRYFLAG ) {
      AH = 4Dh;             // CF == 0, запустилось !!!
      GOTO quit;            // получаем ExitCode & выход ...
    }
    SP += 14;               // восстанавливаем стек
    $popa                   // и регистры AX-BP
}


/******************************************************************
 *  Точка входа : DS = ES = SS = seg PSP = seg @data = seg @code
 ******************************************************************/
void main() inline {
///////////////////////////////////////////////////////////
//  Проверяем, CPU 386+ ?
///////////////////////////////////////////////////////////
    AH = 70h;
    $pushf
    $push AX
    $popf
    $pushf
    $pop BX
    $popf
    DX = errCPU;
    BH&=AH;
    IF( ZEROFLAG ) {
////////////////////////////////////////////////////////////
//  Выход с сообщением, DX = OFFSET [message]
////////////////////////////////////////////////////////////
fatal:
        DS = DATA_SEG;      // устанавливаем DS
        AH = 9;
        $int 21h
        DX = #crlf;
////////////////////////////////////////////////////////////
//  Выход из програмы
////////////////////////////////////////////////////////////
quit:
        $int 21h
        AH = 4Ch;
        $int 21h            // Bye! :)

////////////////////////////////////////////////////////////
//  Сюда попадаем, если CPU 386+
////////////////////////////////////////////////////////////
     }
////////////////////////////////////////////////////////////
//  Проверяем, что версия DOS >= 3.0
////////////////////////////////////////////////////////////
    AX = 302Eh;             // thanks to M. Sheker, AX = '.0', AH = 30h !!!
    DSWORD[ errCPU+1 ] = AX;// записываем поверх '86', получаем 'DOS 3.0+...
    $int 21h
    DX = #errDOS;
    $cmp AL, 3
    $jb fatal               // версия DOS < 3.0 :-(, DS = seg @data

    DS = DSWORD[ 002Ch ];   // DS = seg Environ
    $push DS                // save EnvSeg
////////////////////////////////////////////////////////////
//  Ищем имя программы в Environ
////////////////////////////////////////////////////////////
    $cld                    // DF = 0, нормальное :-) направление пересылки
    SI = 0;
    do {
        $lodsw
        SI--;
    } while ( AX );         // после окончания AX = 0 !!!
    AX >< CX;               // CX = 0

? ifdef QUIET               // сохраняем конец Environ
    $push SI                // для добавления туда
?endif                      // строки 'DOS4G=QUIET'

    $lodsb                  // делаем SI += 3 :-), так короче
    $lodsw
    DI = #arg_str;          // DI = offset arg_str
    $push DI                // сохраняем OFFSET arg_str
    $push ES                // и seg PSP
////////////////////////////////////////////////////////////
//  Копируем имя програмы в arg_str
////////////////////////////////////////////////////////////
    do {
        $lodsb
        $stosb
    } while ( AL );         // копируем, пока не встретился '\0'

    DI--;                   // DI = offset на последний символ
    $pop DS                 // DS = seg PSP
    SI = 80h;               // DS:SI = начало коммандной строки
    $lodsw                  // AL = длина строки, AH = 1-й символ
    SI--;                   // SI = 0081h
////////////////////////////////////////////////////////////
//  Добавляем командную строку в arg_str
////////////////////////////////////////////////////////////
    CL |= AL;               // нулевая командная строка ?
    IF( NOTZEROFLAG ) {     // заодно получаем в CX длину командной строки

        AL = ' ';           // 1-й символ командной строки - пробел ?
        IF ( AL != AH )
          $stosb            // если нет - добавить в arg_str

        $rep $movsb         // копируем командную строку в arg_str
    }
    AL = 0Dh;               // добавляем последний CR
    $stosb                  // CX = 0 опять ...
    AX >< DI;               // AX = offset на конец командной строки
    $pop DI                 // DI = offset на начало командной строки
    AX -= DI;               // AX = длина командной строки
    DX = #errARG;
    $cmp AX, 126            // проверяем, если длина командной строки > 126,
    $ja fatal               // то ошибка ( DOS4G[W] вылетает :-( ), DS = seg @data

    DI--;                   // DI = offset на arg_len ( длину командной строки )
    AX--;                   // AX = длина командной строки без CR
    $stosb                  // сохранить для EXECa !

?ifdef QUIET                // если QUIET :-)
    $pop DI
    $pop ES                 // ES:DI = конец Environ
////////////////////////////////////////////////////////////
//  Добавляем в Environ строка 'DOS4G=QUIET\0\0'
////////////////////////////////////////////////////////////
    SI=#addenv;             // имя новой переменной
    CL=13;
    $rep $movsb	            // добавить
    DS><ES;
?else
    $pop DS                 // DS = seg Env, ES = seg @data
?endif

    DI = #prg_str;          // ищем в текущей директории
    addprg1();
////////////////////////////////////////////////////////////
//  Поиск переменных окружения
////////////////////////////////////////////////////////////
    SI = 0;                 // DS:SI = начало Environ
BEGIN:
    $push SI                // сохраним SI
    DI = #dos4g;            // DS:DI = 'DOS4GWPRO'
    $lodsw                  // загрузили 2 байта
    $scasw                  // проверили на 'DO' :)
    IF( ZEROFLAG ) {        // нет - идем на проверку '4GPATH'
        $cmpsb              // следущий символ 'S' ?
        $jne L_NEXT         // нет - переход на следущую переменную Environ
        $lodsw              // загружаем следущие 2 байта ...
    }
    IF(AX == 'G4')          // проверяем на '4G' ( DOS4GPATH or 4GPATH )
                            // нет - идем на проверку 'PATH'
      $lodsw                // загружаем следущие 2 байта ...

    IF ( AL != 'P' )        // очередная буква 'P' ? ( DOS4GPATH,4GPATH,PATH )
        GOTO L_NEXT;        // нет - переход на следущую переменную Environ

    SI--;
    $lodsd                  // загружаем следущие 4 байта ...
    IF( EAX != '=HTA')      // проверяем на 'ATH=' ( DOS4GPATH,4GPATH,PATH )
        GOTO L_NEXT;        // нет - переход на следущую переменную Environ

////////////////////////////////////////////////////////////
//  Нашли что-то типа ( DOS4GPATH,4GPATH,PATH ), сканируем до ';' or '\0'
////////////////////////////////////////////////////////////
BEG_SCAN:
    DI = #prg_str;          // ES:DI = prg_str
    for ( ; ; ){
        $lodsb
        IF ( AL == 0 ) {    // встретился '\0' => конец переменной окружения
            ADDPRG();       // добавляем имя программ и пытаемся запустить и
L_NEXT:                     // переход к следующей переменной окружения
            $pop SI
            do {            // ищем '\0'
                $lodsw
                SI--;
            } while( AL );  // AL = '\0', AH = следующий за ним символ

            IF ( AL != AH)  // если AL == AH == '\0'
                GOTO BEGIN; // конец Environ и выход с ошибкой :-(

            DX = #errPRG;   // все, нужных переменных окружения не найдено
            goto fatal;     // или не запущено ни одной нужной программы
        }
        IF ( AL == ';' ) {  // встретился ';'
            ADDPRG();       // добавляем имя программ, пытаемся запустить
            GOTO BEG_SCAN;  // и опять по циклу :-)
        }
        $stosb              // иначе, если не ';' & '\0', добавляем в prg_str
    }                       // и цикл заново ...
}

