Subject: MBEDIT unter Windows NT
Date: Sun, 6 Aug 2000 14:43:10 -0400
From: Raimund Spaeth <100527.1475@compuserve.com>
To: Michael Braun <braun-m@t-online.de>

An: braun-m@t-online.de

Hallo Herr Braun,

                  per Zufall habe ich beim Experimentieren
mit Suse Linux 6.4 Ihren Multi-Platform Editor "MBEDIT"
entdeckt.

Die Idee, auf unterschiedlichen Plattformen den gleichen
Texteditor - mit Calculator und Macro Recorder Funtionen -
zu haben, gefiel mir gut.

Da ich mit Linux leider nur in der Freizeit spielen kann,
und ansonsten gezwungen bin, mit Windows NT 4 zu arbeiten,
habe ich versucht, "MBEDIT" unter Windows NT zum Laufen
zu bringen.

Entweder habe ich einen grundlegenden Fehler gemacht,
oder der auf Ihrer Homepage abgelegte Quellstand
untersttzt Windows NT nicht direkt?

Wenn Sie mir zum Thema "MBEDIT" und Windows NT einen Tip
geben knnten, wrde ich mich freuen.

Ich habe einige Stunden herumexperimentiert, und eine
Bastel-"Lsung" gefunden, die luft; mir aber nicht
besonders gefllt. Wenn Sie etwas Zeit und Interesse
haben, knnen Sie mal in das angehngte ZIP Archiv schauen.
Am Anfang von "ansi_out.c" steht einiger Kommentar,
inclusive Hinweis auf einen trickreichen Bug, der wohl
bisher nur durch bestimmte Eigenschaften der von Ihnen
benutzen Betriebssystem/Compiler-Kombinationen verborgen
geblieben ist.

Herzlichen Dank an Sie und Ihre Mitstreiter fr die
Verffentlichung von "MBEDIT" unter GNU GPL - aus
meiner Sicht ist es ein recht ntzliches Werkzeug.

Mit freundlichen Gren
Raimund Spth

100526.1475@compuserve.com

P.S. Das Versenden von binren Anhngen an t-online
     geht nicht so, wie ich es mir dachte. Daher
     statt ZIP Archiv als Anhang alles als Text.
     Sorry, aber das Problem mit t-online verfolgt
     mich schon eine Weile, auch in der Firma:
     Lotus Notes will auch keine binren Anhnge
     an t-online schicken, der Himmel weiss, warum.

Datei 1: ansi_out.c

//----------------------------------------------------------------------------
// Bastel-"Lsung", um den von Michael Braun (e-mail: braun-m@t-online.de,
//   http://home.t-online.de/home/braun-m) verffentlichten Multi-Platform
//   Editor "MBEDIT" (FSF GPL >= V2) auch unter Windows NT nutzen zu knnen.
//
// Vielleicht ist ja alles nur ein Miverstndnis: Beim Experimentieren mit
//   Suse Linux 6.4 ist mir zufllig besagter "MBEDIT" aufgefallen.
//   Wegen der netten Kombination von Calculator und Macro Recorder fand
//   ich dann dieses "old-styled" Tool recht ntzlich und wollte es auch
//   unter Windows NT (4.0 SP5) mal ausprobieren.
//
// Da habe ich wohl etwas falsch gemacht: auf dem Bildschirm erschienen
//   Terminal Steuersequenzen statt lesbarem Text. Soweit ich Windows NT
//   kenne, werden <ESC> Sequenzen fr 32 bit Konsol-Anwendungen ja auch
//   nicht untersttzt. Die Variante 16 bit command.com mit mit ANSI.SYS
//   und DOS-Version von "MBEDIT" macht wegen der begrenzten Puffergre
//   in dieser Umgebung sicher keinen Sinn.
//
// Die Frage: Wie komme ich zu einer 32 bit Konsol-Version
//            von "MBEDIT" fr Windows NT?
//
// Experten, bitte nicht lachen: mir ist nichts anderes (als einfache
//   "Lsung") eingefallen, als alle "MBEDIT" Ausgaben abzufangen und
//   die Terminal Steuersequenzen (minimaler Umfang) in Win32 Konsol-
//   funktionen umzusetzen.
//
// DER SOURCECODE IST SCHNELL MAL ZUSAMMENGEFLICKT: Mir fehlte die Mue
//   fr Qualittsarbeit (das generelle Freeware-Problem?). Es fehlt
//   jegliche Fehlerbehandlung, fr maximale Windowsize sind willkrliche
//   Konstanten benutzt .........., die Mngelliste liee sich (fast)
//   endlos fortsetzen. Wenn mal eines Tages so richtig Zeit vom
//   Himmel fllt, wird alles anders .......... Genug, da ich mich
//   aufgerafft habe, diese Zeilen zu schreiben - fr den "MBEDIT"
//   Autor als Feedback.
//
// Die "MBEDIT.MAC" Datei mu natrlich zu dieser einfachen Bastelei
//   passen - siehe gefilterte ESC Sequenzen (was passiert bei SW != 0?
//   was schreibt WriteConsole wohl als "bell" auf den Bildschirm? .....
//   - ich habe es nicht ausprobiert, SW0 ist fr mich o.k. Aber es
//   lauern noch andere Fallgruben bei "unpassender" "MBEDIT.MAC" Datei!

//----------------------------------------------------------------------------
// Was habe ich mit den Original-Quellen von Michael Braun gemacht?
//   Nach dem letzen #include in allen .c Dateien noch ein
//   #include "ansi_out.h" eingefgt, die Dateien ansi_out.h und
//   ansi_out.c in den Trichter ..... UND EIN PASSENDE "MBEDIT.MAC"
//   DATEI NICHT VERGESSEN!
//
// Etwas Raten und Experimentieren zeigte, da alle Ausgaben ber
//   printf() und putchar() laufen. Damit werden durch
//
//     #define   printf       ansi_printf
//     #define   putchar(c)   ansi_putchar(c)
//
//   an passender Stelle alle Ausgaben "umgelenkt".
//
//                    +----------+
//                      MBEDIT  
//                    +----------+
//                             
//              +--------+     +---------+
//               printf       putchar 
//              +--------+     +---------+
//                             
//            +-------------+   
//             ansi_printf    
//            +-------------+   
//                             
//                  +--------------+
//                   ansi_putchar 
//                  +--------------+
//                             
//    +------------------+     +-----------------+
//     char ansi_buf[]        console_putchar 
//     process_ansi_buf                       
//                            WriteConsole    
//     Set..Cursor....       +-----------------+
//     Set..TextAttr..  
//    +------------------+
//
// Ach ja, und noch etwas: Die "MBEDIT" Version hatte ein global.h
//   mit "V8.17". Bei bersetzung mit Microsoft Visual Studio V6.0 SP3
//   macht sich ein Bug in disp_hnd.c bemerkbar,
//
//     [ Die Variable "comment_type" in "disp_1_line()" ist nicht
//       als static deklariert. ]
//
//   der natrich nicht auffllt, wenn Compiler und Betriebssystem
//   zusammen ein glckliches Hndchen haben, und die auto Variable
//   so im Stack liegt, da auch durch evtl. Interupt-Behandlung
//   diese zwischen zwei Aufrufen (es geht ja um die Erkennung von
//   mehrzeiligem Kommentar!) nicht berschrieben wird.
//
// Aber mit GNU (wie in Suse Liniux 6.4 bzw. mingw32 2.95.2-msvcrt)
//   passiert weder unter Suse Linux 6.4 noch unter
//   Windows NT 4.0 SP5 etwas!
//   Die alte Binsenweisheit, da Software manchmal eher zufllig
//   doch funktioniert, ist damit wieder einmal mehr besttigt.
//
// Zum Schlu herzlichen Dank allen, die zum Enstehen von "MBEDIT"
//   beigetragen haben. Ich find es recht praktisch, jetzt unter
//   Windows NT und Linux mit den gleichen Tasten zum gleichen
//   Ergebnis zu kommen - und zum Thema "vi" o.. hier kein weiterer
//   Kommentar!
//
// 8. August 2000, Raimund Spth, 100527.1475@compuserve.com
//
// Jeder Hinweis auf eine einfachere Methode, "MBEDIT" unter
//   Windows NT als 32 bit Applikation zum Laufen zu bringen,
//   ist natrlich herzlich willkommen!

//----------------------------------------------------------------------------
// Last BUT NOT LEAST: Alles gem FSF GPL >= V2!
//
// KEINE GEWHR fr ordnungsgeme Funktion --- EXPERIMENTELLE VERSION!

//----------------------------------------------------------------------------

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <windows.h>

#define   SCREEN_ROWS   40       // willkrlich gewhlt
#define   SCREEN_COLS   100      // willkrlich gewhlt

//----------------------------------------------------------------------------

static int  display_lines = 0;
static char msg[200000];         // Nur zum Testen, aber keine Angst vor
                                 // groen Zahlen in 32 bit Umgebung!

static HANDLE Hco = NULL;
static char ansi_buf[40];

//----------------------------------------------------------------------------
// Nur zum Testen (englisches Windows NT, die Caption des missbrauchten
//                 Notepad Fensters ist "Untitled - Notepad", fr deutsches
//                 Windows anpassen!).
//
// Fr GDB geplagte Zeitgenossen, die unter Windows arbeiten, aber partout
//   dem Luxus des Microsoft Visual Studio aus dem Weg gehen wollen:
//   Wenn ein Notepad Fenster mit der richtigen Caption gefunden wird,
//   landen alle printd() Ausgaben in diesem Notepad Fenster.
//   Wenn der Puffer voll ist, erscheinen die neuen Ausgaben zwar noch am
Ende,
//   aber der vorhergende Text wird abgehackt! (Daher der grosse msg
Buffer).
//   Nicht vergessen: Linefeed muss als "\r\n" angeliefert werden, alles
//   geht ungefiltert an das Notepad (und dieses kennt "\n" nur als
//   Graphikzeichen)!
//
// Fr Neugierige: Auch wenn in "MBEDIT.MAC" kein "<ESC>[7m" definiert ist,
//   beim Programmstart kommen einige dieser Sequenzen: Lt sich im Notepad
//   (mit der richtigen Caption!) beobachten!

int printd(char *fmt, ...)
{
    HWND      Nwnd, Cwnd;
    char      s[200];
    va_list   args;
    int       len;
    int       lines;
    char      s_display_lines[10];

    Nwnd = FindWindow(NULL, "Untitled - Notepad");

    if(Nwnd == NULL)
        return -1;

    Cwnd = GetWindow(Nwnd, GW_CHILD);   // Notepad hat genau ein
                                        // Edit Control als Child Window
    if(Cwnd == NULL)
        return -2;

    va_start(args, fmt);
    len = vsprintf(s, fmt, args);
    va_end(args);

    sprintf(s_display_lines, "<%-6d>  ", ++display_lines);

    (void) SendMessage(Cwnd, WM_GETTEXT, sizeof(msg)/sizeof(msg[0]) - 1000,
                       (LPARAM) msg);
    strcat(msg, s_display_lines);
    strcat(msg, s);
    (void) SendMessage(Cwnd, WM_SETTEXT, 0, (LPARAM) msg);

    lines = SendMessage(Cwnd, EM_GETLINECOUNT, 0, 0);

    if(lines > 5)
        (void) SendMessage(Cwnd, EM_LINESCROLL, 0, lines - 3);

    return len;
}

//----------------------------------------------------------------------------
// Ausgabe eines einzelnen Zeichens in Windows NT Konsole.
// Sollte theoretisch auch unter Win9x gehen, aber Vorsicht:
//   Die Konsol-Funktionen in Win9x haben einige "Features"!

void console_putchar(int c)
{
    char    s[2];
    DWORD   num;

    s[0] = (char) c;
    s[1] = 0;

    if(Hco == NULL)
    {
        Hco = GetStdHandle(STD_OUTPUT_HANDLE);

        if(Hco == NULL)
            return;
    }

    (void) WriteConsole(Hco, s, 1, &num, NULL);
}

//----------------------------------------------------------------------------
// Hier werden alle <ESC> Sequenzen bearbeitet.
// Zwischenspeicherung und Erkennung von Anfang/Ende in ansi_putchar.

void process_ansi_buf()
{
    int                          x, y, n;
    char                         c, c2;
    COORD                        pos;
    DWORD                        num;
    CONSOLE_SCREEN_BUFFER_INFO   CbInfo;
    char                         clear_buf[SCREEN_COLS + 2];

    if(Hco == NULL)
    {
        Hco = GetStdHandle(STD_OUTPUT_HANDLE);

        if(Hco == NULL)
            return;
    }

    if((n = sscanf(ansi_buf, "\033[%d;%dH%c", &y, &x, &c)) == 2)
    {                                        // Cursor Position setzen
        pos.X = (SHORT) (x - 1);
        pos.Y = (SHORT) (y - 1);
        (void) SetConsoleCursorPosition(Hco, pos);
        return;
    }

    if(strcmp(ansi_buf, "\033[?25h") == 0)   // Cursor On
        return;

    if(strcmp(ansi_buf, "\033[?25l") == 0)   // Cursor Off
        return;

    if(strcmp(ansi_buf, "\033[?7h") == 0)    // Wrap On
        return;

    if(strcmp(ansi_buf, "\033[?7l") == 0)    // Wrap Off
        return;

    if(strcmp(ansi_buf, "\033(0") == 0)      // Grafik On VT100
        return;

    if(strcmp(ansi_buf, "\033(B") == 0)      // Grafik Off VT100
        return;

    if(strcmp(ansi_buf, "\033[2J") == 0)     // Clear Screen
    {
        for(n = 0; n < SCREEN_COLS; ++n)
            clear_buf[n] = ' ';
        clear_buf[n] = 0;

        pos.X = 0;

        for(n = 0; n < SCREEN_ROWS; ++n)
        {
            pos.Y = (SHORT) n;
            (void) SetConsoleCursorPosition(Hco, pos);
            (void) WriteConsole(Hco, clear_buf, SCREEN_COLS, &num, NULL);
        }

        return;
    }

    if((n = sscanf(ansi_buf, "\x1B[%d%c%c", &y, &c, &c2)) == 2 && c == 'm')
    {                                        // Attribute setzen
        switch(y)
        {
            // Normal Video
            case 0:    (void) SetConsoleTextAttribute(Hco,
                                  FOREGROUND_RED | FOREGROUND_GREEN |
                                  FOREGROUND_INTENSITY);
                       break;

            // Reverse Video
            case 1:    (void) SetConsoleTextAttribute(Hco,
                                  FOREGROUND_RED   | FOREGROUND_GREEN |
                                  FOREGROUND_INTENSITY |
                                  BACKGROUND_BLUE);
                       break;

            // Status Line 1
            case 2:    (void) SetConsoleTextAttribute(Hco,
                                  BACKGROUND_GREEN | BACKGROUND_BLUE |
                                  BACKGROUND_INTENSITY);
                       break;

            // Status Line 2
            case 3:    (void) SetConsoleTextAttribute(Hco,
                                  FOREGROUND_RED  | FOREGROUND_GREEN |
                                  FOREGROUND_BLUE | FOREGROUND_INTENSITY |
                                  BACKGROUND_RED);
                       break;

            // Comment
            case 4:    (void) SetConsoleTextAttribute(Hco,
                                  FOREGROUND_GREEN | FOREGROUND_BLUE);
                       break;

            // Keyword
            case 5:   (void) SetConsoleTextAttribute(Hco,
                                  FOREGROUND_GREEN | FOREGROUND_INTENSITY);
                       break;

            default:   printd("Hoppla, Attribut %d wird nicht bearbeitet!\r\n", y);
                       break;
        }

        return;
    }

    if((n = sscanf(ansi_buf, "\x1B[%c%c", &c, &c2)) == 1)
    {                                                    // Cursor Position +/- 1
        (void) GetConsoleScreenBufferInfo(Hco, &CbInfo);

        switch(c)
        {
            case 'A':   --(CbInfo.dwCursorPosition.Y);   // Up
                        break;

            case 'B':   ++(CbInfo.dwCursorPosition.Y);   // Down
                        break;

            case 'C':   ++(CbInfo.dwCursorPosition.X);   // Right
                        break;

            case 'D':   --(CbInfo.dwCursorPosition.X);   // Left
                        break;

            default:    printd("Hoppla, Cursor +-1 mit ubekannter Richtung!\r\n", c);
                        break;
        }

        (void) SetConsoleCursorPosition(Hco, CbInfo.dwCursorPosition);

        return;
    }

    if(strcmp(ansi_buf, "\033[0K") == 0)   // Clear to EOL
    {
        (void) GetConsoleScreenBufferInfo(Hco, &CbInfo);

        for(n = 0; CbInfo.dwCursorPosition.X + n < SCREEN_COLS; ++n)
            clear_buf[n] = ' ';
        clear_buf[n] = 0;

        (void) WriteConsole(Hco, clear_buf, (unsigned long) n, &num, NULL);
        return;
    }

    if(strcmp(ansi_buf, "\033[2K") == 0)   // Clear Line
    {
        for(n = 0; n < SCREEN_COLS; ++n)
            clear_buf[n] = ' ';
        clear_buf[n] = 0;

        (void) GetConsoleScreenBufferInfo(Hco, &CbInfo);
        CbInfo.dwCursorPosition.X = 0;
        (void) SetConsoleCursorPosition(Hco, CbInfo.dwCursorPosition);
        (void) WriteConsole(Hco, clear_buf, SCREEN_COLS, &num, NULL);
        return;
    }

    printd("Hoppla, Es gibt noch eine unbekannte <ESC> Sequenz!\r\n");
}

//----------------------------------------------------------------------------
// Hier kommen alle Zeichen im Gnsemarsch vorbei.
// Entweder direkte Ausgabe oder Zwischenspeicherung einer <ESC> Sequenz.

int ansi_putchar(int c)
{
    char   s[2];

    if(ansi_buf[0] == 0 && c != 0x1B)
    {
        console_putchar(c);
        return c;
    }

    if(ansi_buf[0] == 0)
    {
        ansi_buf[0] = (char) c;
        ansi_buf[1] = 0;
        return c;
    }

    if(strlen(ansi_buf) + 2 < sizeof(ansi_buf))
    {
        s[0] = (char) c;
        s[1] = 0;
        strcat(ansi_buf, s);

        // Ende einer <ESC> Sequenz erkennen
        // Fr den hier erwarteten Subset!

        if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
           (ansi_buf[1] == '(' && ansi_buf[2]))
        {
            process_ansi_buf();
            ansi_buf[0] = 0;
        }
    }
    else
        ansi_buf[0] = 0;

    return c;
}

//----------------------------------------------------------------------------

int ansi_printf(char *fmt, ...)
{
    char      buffer[2000];
    va_list   args;
    int       i, len;

    va_start(args, fmt);
    len = vsprintf(buffer, fmt, args);

    for(i = 0; buffer[i]; ++i)
        (void) ansi_putchar(buffer[i]);   // Alles mu durch's Nadelhr!

    va_end(args);

    return len;
}

//----------------------------------------------------------------------------

Datei 2: ansi_out.h

//----------------------------------------------------------------------------

#if defined(WIN_NT_BASTELVERSION)

int ansi_printf(char *, ...);
int ansi_putchar(int);
int printd(char *, ...);

//----------------------------------------------------------------------------

#define   printf       ansi_printf

#undef    putchar
#define   putchar(c)   ansi_putchar(c)

#endif

//----------------------------------------------------------------------------

Datei 3: mbedit.mac

\*-----------------------------------------------------------*\
\* Start mit Batch-File: (Window Size 102x41; Pos 36,20;     *\
\*                        Font Lucida Console 12)            *\
\* set HOME=..........                                       *\
\* start ..........\mbedit.exe %1 %2 %3 %4 %5                *\
\*                                                           *\
AV= 40;       \* NUMBER OF LINES                             *\
AC=100;       \* NUMBER OF COLUMNS                           *\
AG=  0;       \* SEMI-GRAFIK aus,   fr NT Bastelversion     *\
AM=  0;       \* Kein MOUSE DRIVER, fr NT Bastelversion     *\
AS=  1;       \* Automatic Shift of Screen with Cursor       *\
AE=200;       \* wait time for <esc> sequences in msec       *\
\*                                                           *\
AFNV=" [0m"   \* NORMAL  VIDEO   fr NT Bastelversion        *\
AFRV=" [1m"   \* REVERSE VIDEO   fr NT Bastelversion        *\
AF1V=" [2m"   \* STATUS  LINE 1  fr NT Bastelversion        *\
AF2V=" [3m"   \* STATUS  LINE 2  fr NT Bastelversion        *\
AFCV=" [4m"   \* COMMENTS        fr NT Bastelversion        *\
AFKV=" [5m"   \* KEYWORDS        fr NT Bastelversion        *\
\*-----------------------------------------------------------*\
\*                                                           *\
SHY        \* 8 bit Anzeige (Umlaute und Sonderzeichen)      *\
SIY        \* Auto Indent                                    *\
SBN        \* Keine .BAK Dateien aufheben                    *\
ST8        \* Tabstops alle 8 Zeichen                        *\
SNN        \* TAB nicht durch Blanks ersetzen                *\
SDN        \* Kein Screen Update whrend Macro Ausfhrung    *\
\*            sonst werden Macros u.U. sehr langsam !!!      *\
SW0        \* Keine Pieptne                                 *\
SM8,8,75   \* Rnder fr Paragraph Funktionen                *\
\*                                                           *\
MINIT\BRSY3\MM;   \* Syntax Highlight per INIT Macro,        *\
\*                   geht nicht (wie beschrieben) per SY3!   *\
\*                                                           *\
M\0A0\BR\XHDA\EM;    \* SHIFT-F1 -> +                        *\
M\0A1\BR\XHC4\EM;    \* SHIFT-F2 -> -                        *\
M\0A2\BR\XHC2\EM;    \* SHIFT-F3 -> -                        *\
M\0A3\BR\XHBF\EM;    \* SHIFT-F4 -> +                        *\
M\0A4\BR\XHB3\EM;    \* SHIFT-F5 ->                         *\
M\0A5\BR\XHC3\EM;    \* SHIFT-F6 -> +                        *\
M\0B0\BR\XHC5\EM;    \* CTRL-F1  -> +                        *\
M\0B1\BR\XHB4\EM;    \* CTRL-F2  ->                         *\
M\0B2\BR\XHC0\EM;    \* CTRL-F3  -> +                        *\
M\0B3\BR\XHC1\EM;    \* CTRL-F4  -> -                        *\
M\0B4\BR\XHD9\EM;    \* CTRL-F5  -> +                        *\
\*-----------------------------------------------------------*\
