UNIT AE5 ;

{$R-}
{$B-}
{$I-}
{$S+}
{$V-}

INTERFACE

USES Crt, Dos, Printer, AE0, AE1, AE2, AE3, AE4 ;

PROCEDURE Initialize ;
PROCEDURE ShutOff ;
PROCEDURE ExecKey (KeyNr : WORD) ;

IMPLEMENTATION

{-----------------------------------------------------------------------------}
{ Initializes all necessary variables, and loads the file specified on the    }
{ command line into the first workspace.                                      }
{-----------------------------------------------------------------------------}

PROCEDURE Initialize ;

VAR Reg : REGISTERS ;
    Counter : WORD ;
    ConfigFile : FILE OF ConfigBlock ;
    ConfigFilePath : PathStr ;
    AEDir : DirStr ;
    AEName : NameStr ;
    AEExt : ExtStr ;
    WorkFile : FILE OF WorkBlock ;
    Work : WorkBlock ;
    ValidConfig : boolean ;

BEGIN
CLRSCR ;
ProgramFinished := FALSE ;
{$IFDEF DEVELOP }
MinMemAvail := MEMAVAIL ;
InitMemAvail := MEMAVAIL ;
{$ENDIF }
{ check the presence of a color video adapter }
Reg.AH := 15 ;
INTR ($10, Reg) ;
ColorCard := Reg.AL <> Mono ;
{ set start address of screen memory }
IF ColorCard
   THEN BEGIN
        DisplayPtr := PTR ($B800, 0) ;
        StatusLinePtr := PTR ($B800, NrOfTextLines * ColsOnScreen * 2) ;
        END
   ELSE BEGIN
        DisplayPtr := PTR ($B000, 0) ;
        StatusLinePtr := PTR ($B000, NrOfTextLines * ColsOnScreen * 2) ;
        END ;
{ store screen settings }
OrigCursorType := GetCursor ;
OrigTextAttr := TextAttr ;
{ initialize setup }
Config.Setup := DefaultSetup ;
TextAttr := ScreenColorArray [Config.Setup.ScreenColors].NormAttr ;
{ initialize paste buffer, macro stack and several global variables }
NEW (PasteBuffer) ;
PasteBuffersize := 0 ;
MacroStackpointer := Inactive ;
MacroDefining := Inactive ;
LoadfileName := '*.*' ;
FindString := '' ;
ReplaceString := '' ;
SearchOptions := '' ;
SearchType := Find ;
{ clear keyboard buffer }
ClearKeyBuffer ;
EscPressed := FALSE ;
{ create empty histories }
CreateHistory (FileHist, 80) ;
CreateHistory (FindHist, 255) ;
CreateHistory (ReplaceHist, 255) ;
{ initialize workspaces }
CurrentWsnr := 1 ;
REPEAT
    NEW (Workspace [CurrentWsnr].Buffer) ;
    ClearCurrentWs ;
    {$IFDEF DEVELOP }
    IF CurrentWsnr = 1
       THEN BasicMemAvail := MEMAVAIL ;
    {$ENDIF }
    INC (CurrentWsnr) ;
UNTIL (CurrentWsnr > MaxNrOfWorkspaces) OR (MAXAVAIL < WsBufSize) ;
NrOfWorkspaces := CurrentWsnr - 1 ;
CurrentWsnr := 1 ;
CurrentWs := Workspace [CurrentWsnr] ;
{ initialize macros to default }
FOR Counter := 1 TO NrOfMacros DO
    Config.Macro.Length [Counter] := Inactive ;
{ try to find setup file }
ConfigFilePath := '' ;
IF Exists (ConfigFileName)
   THEN ConfigFilePath := FExpand (ConfigFileName)
   ELSE BEGIN
        IF LO (DosVersion) >= 3
           THEN BEGIN
                { look for setup file in directory where AE.EXE is }
                FSplit (PARAMSTR (0), AEDir, AEName, AEExt) ;
                IF Exists (AEDir + ConfigFileName)
                   THEN ConfigFilePath := AEDir + ConfigFileName ;
                END
        END ;
ValidConfig := TRUE ;
IF LENGTH (ConfigFilePath) > 0
   THEN BEGIN
        { load setup }
        ASSIGN (ConfigFile, ConfigFilePath) ;
        RESET (ConfigFile) ;
        READ (ConfigFile, Config) ;
        CLOSE (ConfigFile) ;
        CheckDiskError ;
        IF (DiskError <> 0) OR
           (Config.Setup.Version < UpCompatSetupVersion) OR
           (Config.Setup.Version > AEVersionNr)
           THEN BEGIN
                { error reading setup file or incompatible version:
                  reset to default }
                Config.Setup := DefaultSetup ;
                FOR Counter := 1 TO NrOfMacros DO
                    Config.Macro.Length [Counter] := Inactive ;
                ValidConfig := FALSE ;
                END ;
        END ;
Config.Setup.Version := AEVersionNr ;
{ set screen colors and cursor }
IF (NOT ColorCard) AND (Config.Setup.ScreenColors > 2)
   THEN { on monochrome card only color settings 1 and 2 are valid }
        Config.Setup.ScreenColors := 1 ;
TextAttr := ScreenColorArray [Config.Setup.ScreenColors].NormAttr ;
CLRSCR ;
SetCursor (Config.Setup.Cursortype) ;
IF NOT ValidConfig
   THEN ErrorMessage (19) ;
{ load file(s) to edit }
WorkFilePath := '' ;
IF LENGTH (PARAMSTR (1) ) = 0
   THEN BEGIN
        IF Config.Setup.SaveWork
           THEN BEGIN
                { look for work file }
                IF Exists (WorkFileName)
                   THEN WorkFilePath := FExpand (WorkFileName)
                   ELSE BEGIN
                        IF LO (DosVersion) >= 3
                           THEN BEGIN
                                { look in directory where AE.EXE is }
                                FSplit (PARAMSTR (0), AEDir, AEName, AEExt) ;
                                IF Exists (AEDir + WorkFileName)
                                   THEN WorkFilePath := AEDir + WorkFileName ;
                                END ;
                        END ;
                END ;
        IF WorkFilePath = ''
           THEN Message ('Another Editor, version ' + AEVersionNr +
                         '. Press F1 for help')
           ELSE BEGIN
                { load work file }
                ASSIGN (WorkFile, WorkFilePath) ;
                RESET (WorkFile) ;
                READ (WorkFile, Work) ;
                CLOSE (WorkFile) ;
                IF (DiskError <> 0) OR
                   (Work.Version < UpCompatWorkVersion) OR
                   (Work.Version > AEVersionNr)
                   THEN ErrorMessage (23)
                   ELSE BEGIN
                        IF Work.NrOfWorkspaces > NrOfWorkspaces
                           THEN Work.NrOfWorkspaces := NrOfWorkspaces ;
                        FOR CurrentWsnr := 1 TO Work.NrOfWorkspaces DO
                            BEGIN
                            LoadFile (Work.Filename [CurrentWsnr]) ;
                            { set cursor position }
                            FOR Counter := 1 TO
                                (Work.CursorPos [CurrentWsnr].Linenr - 1) DO
                                LineDown (CurrentWs.CurPos) ;
                            EndOfLine (CurrentWs.CurPos) ;
                            IF (Work.CursorPos [CurrentWsnr].Index <
                                CurrentWs.CurPos.Index)
                                THEN SkipUp (CurrentWs.CurPos,
                                             (CurrentWs.CurPos.Index -
                                          Work.CursorPos [CurrentWsnr].Index) ) ;
                            CurrentWs.FirstVisibleLine :=
                                Work.FirstVisibleLine [CurrentWsnr] ;
                            CurrentWs.FirstScreenCol :=
                                Work.FirstScreenCol [CurrentWsnr] ;
                            Workspace[CurrentWsnr] := CurrentWs ;
                            END ;
                        CurrentWsnr := Work.CurrentWsnr ;
                        CurrentWs := Workspace [CurrentWsnr] ;
                        END ;
                END ;
        END
   ELSE BEGIN
        LoadfileName := FExpand (PARAMSTR (1) ) ;
        AddToHistory (FileHist,UpperCase(PARAMSTR (1))) ;
        IF Wildcarded (Loadfilename)
           THEN BEGIN
                GetFileFromList (LoadfileName) ;
                IF NOT EscPressed
                   THEN LoadFile (LoadfileName) ;
                END
           ELSE LoadFile (LoadfileName) ;
        END ;
{$IFDEF DEVELOP }
StdMemAvail := MEMAVAIL ;
MinMemAvail := MEMAVAIL ;
{$ENDIF }
END ;

{-----------------------------------------------------------------------------}
{ Saves workspace and restores screen settings before exiting program.        }
{-----------------------------------------------------------------------------}

PROCEDURE ShutOff ;

VAR WorkFile : FILE OF WorkBlock ;
    Work : WorkBlock ;
    i : BYTE ;

BEGIN
IF Config.Setup.SaveWork
   THEN BEGIN
        {save work file }
        IF LENGTH (WorkFilePath) = 0
           THEN WorkFilePath := FExpand (WorkFileName) ;
        Work.Banner := WorkFileName ;
        Work.Version := AEVersionNr ;
        Work.NrOfWorkspaces := NrOfWorkspaces ;
        FOR i := 1 TO MaxNrOfWorkspaces DO
            BEGIN
            Work.Filename [i] := Workspace [i].Name ;
            Work.CursorPos [i] := Workspace [i].CurPos ;
            Work.FirstVisibleLine [i] := Workspace [i].FirstVisibleLine ;
            Work.FirstScreenCol [i] := Workspace [i].FirstScreenCol ;
            END ;
        Work.CurrentWsnr := CurrentWsnr ;
        ASSIGN (WorkFile, WorkFilePath) ;
        REWRITE (WorkFile) ;
        WRITE (WorkFile, Work) ;
        CLOSE (WorkFile) ;
        END ;
SetCursor (OrigCursorType) ;
TextAttr := OrigTextAttr ;
CLRSCR ;
{$IFDEF DEVELOP }
WRITELN ('HEAP MEMORY:') ;
WRITELN ('Available at startup        ',InitMemAvail:6, ' bytes') ;
WRITELN ('Minimal needs (1 workspace) ',(InitMemAvail-BasicMemAvail):6,
         ' bytes') ;
IF NrOfWorkspaces > 1
   THEN WRITELN ('Needed for ',NrOfWorkspaces,' workspaces     ',
                 (InitMemAvail-StdMemAvail):6,' bytes') ;
WRITELN ('Dynamically used            ',(StdMemAvail-MinMemAvail):6,
         ' bytes') ;
WRITELN ('Always available            ',MinMemAvail:6, ' bytes') ;
{$ENDIF }
END ;

{-----------------------------------------------------------------------------}
{ Executes the action corresponding to the key number given.                  }
{-----------------------------------------------------------------------------}

PROCEDURE ExecKey (KeyNr : WORD) ;

VAR Counter               : WORD ;
    OldCurPos             : Position ;
    MacroNumber           : WORD ;
    NextPos               : Position ;
    BlockStart, BlockStop : WORD ;
    TabSteps              : WORD ;
    LineLength, NewSpaces : WORD ;
    OldMark               : WORD ;

BEGIN
WITH CurrentWs DO
  BEGIN
  CASE KeyNr OF
    331 : { left }
          BEGIN
          IF (Buffer^ [CurPos.Index - 2] = CR) AND (Buffer^ [CurPos.Index - 1] = LF)
             THEN SkipUp (CurPos, 2)
             ELSE SkipUp (CurPos, 1) ;
          END ;
    333 : { right }
          BEGIN
          IF (Buffer^ [CurPos.Index] = CR) AND (Buffer^ [CurPos.Index + 1] = LF)
             THEN SkipDown (CurPos, 2)
             ELSE SkipDown (CurPos, 1) ;
          END ;
    328 : { up }
          BEGIN
          LineUp (CurPos) ;
          END ;
    336 : { down }
          BEGIN
          LineDown (CurPos) ;
          END ;
    327 : { Home }
          BEGIN
          Home (CurPos) ;
          END ;
    335 : { End }
          BEGIN
          EndOfLine (CurPos) ;
          END ;
    329 : { PgUp }
          BEGIN
          FOR Counter := 1 TO (NrOfTextLines - 1) DO
              LineUp (CurPos) ;
          END ;
    337 : { PgDn }
          BEGIN
          FOR Counter := 1 TO (NrOfTextLines - 1) DO
              LineDown (CurPos) ;
          END ;
    388 : { ^PgUp }
          BEGIN
          CurPos.Index := 1 ;
          CurPos.Linenr := 1 ;
          CurPos.Colnr := 1 ;
          END ;
    374 : { ^PgDn }
          BEGIN
          FOR Counter := CurPos.Index TO BufferSize DO
              IF Buffer^ [Counter] = LF THEN INC (CurPos.Linenr) ;
          CurPos.Index := BufferSize ;
          CalculateColnr (CurPos) ;
          END ;
    375 : { ^Home }
          BEGIN
          WHILE CurPos.Linenr > FirstVisibleLine.Linenr
                DO LineUp (CurPos) ;
          Home (CurPos) ;
          END ;
    373 : { ^End }
          BEGIN
          WHILE (CurPos.Linenr < (FirstVisibleLine.Linenr + NrOfTextLines - 1) ) AND
                (CurPos.Index < BufferSize)
                DO LineDown (CurPos) ;
          Home (CurPos) ;
          END ;
    371 : { ^left }
          BEGIN
          IF CurPos.Index > 1
             THEN BEGIN
                  REPEAT DEC (CurPos.Index) ;
                         IF Buffer^ [CurPos.Index] = LF
                            THEN DEC (CurPos.Linenr) ;
                  UNTIL ( (NOT (Buffer^ [CurPos.Index] IN WordSeparators) ) OR
                         (CurPos.Index = 1) ) ;
                  WHILE ( (NOT (Buffer^ [CurPos.Index] IN WordSeparators) ) AND
                         (CurPos.Index > 0) ) DO
                        DEC (CurPos.Index) ;
                  INC (CurPos.Index) ;
                  CalculateColnr (CurPos) ;
                  END ;
          END ;
    372 : { ^right }
          BEGIN
          WHILE NOT ( (Buffer^ [CurPos.Index] IN WordSeparators) OR
                     (CurPos.Index = BufferSize) ) DO
                INC (CurPos.Index) ;
          WHILE (Buffer^ [CurPos.Index] IN WordSeparators) AND
                (CurPos.Index < BufferSize) DO
                BEGIN
                IF Buffer^ [CurPos.Index] = LF
                   THEN INC (CurPos.Linenr) ;
                INC (CurPos.Index) ;
                END ;
          CalculateColnr (CurPos) ;
          END ;
    340 : { shift-F1 }
          BEGIN
          AlterSetup ;
          END ;
    316 : { F2 }
          BEGIN
          Workspace [CurrentWsnr] := CurrentWs ;
          SaveFile (CurrentWsnr) ;
          CurrentWs := Workspace [CurrentWsnr] ;
          END ;
    341 : { shift-F2 }
          BEGIN
          Workspace [CurrentWsnr] := CurrentWs ;
          EnterString (Name, NIL, 'New file name: ', 79, TRUE, TRUE) ;
          IF NOT EscPressed
             THEN BEGIN
                  Name := FExpand (Name) ;
                  SaveFile (CurrentWsnr) ;
                  END ;
          CurrentWs := Workspace [CurrentWsnr] ;
          END ;
    317 : { F3 }
          BEGIN
          LoadfileName := '*.*' ;
          EnterString (LoadfileName, FileHist, 'Load file: ', 79, TRUE, TRUE) ;
          IF Wildcarded (LoadfileName) AND (NOT EscPressed)
             THEN GetFileFromList (LoadfileName) ;
          IF NOT EscPressed
             THEN
               BEGIN
               IF ChangesMade
                  THEN
                    BEGIN
                    IF Answer ('File has been changed. Save?')
                       THEN SaveFile (CurrentWsnr) ;
                    END ;
               IF NOT EscPressed
                  THEN LoadFile (LoadfileName) ;
               END ;
          END ;
    342 : { shift-F3 }
          BEGIN
          LoadfileName := '*.*' ;
          EnterString (LoadfileName, FileHist, 'Insert file: ', 79, TRUE, TRUE) ;
          IF Wildcarded (LoadfileName) AND (NOT EscPressed)
             THEN GetFileFromList (LoadfileName) ;
          IF NOT EscPressed
             THEN InsertFile (LoadfileName, CurPos) ;
          END ;
    305 : { alt-N }
          BEGIN
          EscPressed := FALSE ;
          IF ChangesMade
             THEN
               BEGIN
               IF Answer ('File has been changed. Save?')
                  THEN SaveFile (CurrentWsnr) ;
               END ;
          IF NOT EscPressed
             THEN
               BEGIN
               ClearCurrentWs ;
               END ;
          END ;
    318, 343,
    275 : { F4,shift-F4,alt-R }
          BEGIN
          IF KeyNr <> 275
             THEN
               BEGIN
               { if size of block <= 255: copy contents to FindString }
               IF Mark <> Inactive
                  THEN BEGIN
                       IF Mark < CurPos.Index
                          THEN BEGIN
                               BlockStart := Mark ;
                               BlockStop := Curpos.Index ;
                               END
                          ELSE BEGIN
                               BlockStart := Curpos.Index ;
                               BlockStop := Mark ;
                               END ;
                       IF (BlockStop - BlockStart) <= 255
                          THEN BEGIN
                               MOVE (Buffer^ [BlockStart], FindString [1],
                                     BlockStop - BlockStart) ;
                               FindString [0] := CHR (BlockStop - BlockStart) ;
                               END ;
                       END
                  ELSE FindString := '' ;
               { enter new search parameters }
               EnterString (FindString, FindHist, 'Find: ', 255, FALSE, FALSE) ;
               IF (KeyNr = 343) AND (NOT EscPressed)
                  THEN EnterString (ReplaceString, ReplaceHist, 'Replace with: ',
                                    255, FALSE, FALSE) ;
               IF NOT EscPressed
                  THEN
                    BEGIN
                    IF KeyNr = 318
                       THEN BEGIN
                            EnterString (SearchOptions, NIL,
                                         'Options (I,R,W): ',
                                         6, TRUE, TRUE) ;
                            SearchType := Find ;
                            END
                       ELSE BEGIN
                            EnterString (SearchOptions, NIL,
                                         'Options (I,N,R,W): ',
                                         6, TRUE, TRUE) ;
                            SearchType := FindAndReplace ;
                            END ;
                    END ;
               END ;
          IgnoreCase := POS ('I', SearchOptions) <> 0 ;
          ReverseSearch := POS ('R', SearchOptions) <> 0 ;
          NoQuery := POS ('N', SearchOptions) <> 0 ;
          WholeWords := POS ('W', SearchOptions) <> 0 ;
          IF (NOT EscPressed) OR (KeyNr = 275)
             THEN
               BEGIN
               { start search }
               OldCurPos := CurPos ;
               SearchString (FindString, CurPos) ;
               IF NOT Found
                  THEN
                    BEGIN
                    CurPos := OldCurPos ;
                    ErrorMessage (15) ;
                    END ;
               IF Found AND (SearchType = FindAndReplace)
                  THEN
                    BEGIN
                    { Counter will contain number of replacements made }
                    Counter := 0 ;
                    REPEAT
                      { show found string as block (if queried replace) }
                      OldMark := Mark ;
                      MARK := CurPos.Index + LENGTH (FindString) ;
                      IF NOT NoQuery
                         THEN RedrawScreen ;
                      { restore block mark }
                      Mark := OldMark ;
                      { determine if string must be replaced }
                      IF NoQuery OR Answer ('Replace?')
                         THEN
                           BEGIN
                           { OldCurPos will point to last replaced string }
                           OldCurPos := CurPos ;
                           { replace FindString with ReplaceString }
                           IF LENGTH (FindString) >= LENGTH (ReplaceString)
                              THEN
                                BEGIN
                                { adapt buffer size }
                                Shrink (CurPos.Index, LENGTH (FindString) -
                                                     LENGTH (ReplaceString) ) ;
                                IF LENGTH (ReplaceString) > 0
                                   THEN
                                     BEGIN
                                     { write ReplaceString }
                                     MOVE (ReplaceString [1], 
                                           Buffer^ [CurPos.Index],
                                           LENGTH (ReplaceString) ) ;
                                     IF NOT ReverseSearch
                                        THEN
                                          BEGIN
                                          { resume search after ReplaceString }
                                          SkipDown (CurPos,
                                                   LENGTH (ReplaceString) - 1) ;
                                          END ;
                                END ;
                                INC (Counter) ;
                                END
                              ELSE
                                BEGIN
                                IF Grow (CurPos.Index,
                                         LENGTH (ReplaceString) -
                                         LENGTH (FindString) )
                                   THEN
                                     BEGIN
                                     { write ReplaceString }
                                     MOVE (ReplaceString [1],
                                           Buffer^ [CurPos.Index],
                                           LENGTH (ReplaceString) ) ;
                                     IF NOT ReverseSearch
                                        THEN
                                          BEGIN
                                          { resume search after ReplaceString }
                                          SkipDown (CurPos,
                                                    LENGTH (ReplaceString) ) ;
                                          END ;
                                     INC (Counter) ;
                                     END
                                   ELSE
                                     { no room for replace: stop search }
                                     EscPressed := TRUE ;
                                END ;
                           { show replacement counter }
                           Message (WordToString (Counter, 0) +
                                    ' replacement(s) made') ;
                           END ;
                      IF NOT EscPressed
                         THEN CheckEsc ;
                      IF NOT EscPressed
                         THEN SearchString (FindString, CurPos) ;
                    UNTIL (NOT Found) OR EscPressed ;
                    { return to last replaced string }
                    CurPos := OldCurPos ;
                    END ;
               END ;
          END ;
    319 : { F5 }
          BEGIN
          MARK := CurPos.Index ;
          END ;
    344 : { shift-F5 }
          BEGIN
          MARK := Inactive ;
          END ;
    320 : { F6 }
          BEGIN
          IF CopyBlock
             THEN BEGIN
                  DeleteBlock ;
                  END ;
          END ;
    345 : { shift-F6 }
          BEGIN
          DeleteBlock ;
          END ;
    321 : { F7 }
          BEGIN
          IF CopyBlock
             THEN BEGIN
                  MARK := Inactive ;
                  Message ('Block copied into paste buffer') ;
                  END ;
          END ;
    346 : { shift-F7 }
          BEGIN
          IF MARK = Inactive
             THEN ErrorMessage (5)
             ELSE BEGIN
                  IF MARK < CurPos.Index
                     THEN BEGIN
                          BlockStart := MARK ;
                          BlockStop := CurPos.Index ;
                          END
                     ELSE BEGIN
                          BlockStart := CurPos.Index ;
                          BlockStop := MARK ;
                          END ;
                  Counter := BlockStart ;
                  WHILE (Buffer^ [Counter] =
                         PasteBuffer^ [Counter - BlockStart + 1]) AND
                        (Counter < BlockStop) DO
                        INC (Counter) ;
                  IF (Counter >= BlockStop) AND
                     ( (BlockStop - BlockStart) = PasteBufferSize)
                     THEN Message ('Block is equal to paste buffer')
                     ELSE Message ('Block differs from paste buffer at' +
                                   ' character '+
                                   WordToString(Counter-BlockStart+1,0)) ;
                  END ;
          END ;
    322 : { F8 }
          BEGIN
          IF InsertBlock THEN
             SkipDown (Curpos, PasteBufferSize) ;
          END ;
    347 : { shift-F8 }
          BEGIN
          IF MARK = Inactive
             THEN ErrorMessage (5)
             ELSE BEGIN
                  IF MARK < CurPos.Index
                     THEN PrintBlock (Buffer, MARK, CurPos.Index - 1)
                     ELSE PrintBlock (Buffer, CurPos.Index, MARK - 1) ;
                  END ;
          END ;
    281 : { alt-P }
          BEGIN
          IF Answer ('Print entire file buffer?')
             THEN PrintBlock (Buffer, 1, BufferSize - 1) ;
          END ;
    323 : { F9 }
          BEGIN
          Workspace [CurrentWsnr] := CurrentWs ;
          IF CurrentWsnr = NrOfWorkspaces
             THEN CurrentWsnr := 1
             ELSE INC (CurrentWsnr) ;
          CurrentWs := Workspace [CurrentWsnr] ;
          END ;
    348 : { shift-F9 }
          BEGIN
          Workspace [CurrentWsnr] := CurrentWs ;
          IF CurrentWsnr = 1
             THEN CurrentWsnr := NrOfWorkspaces
             ELSE DEC (CurrentWsnr) ;
          CurrentWs := Workspace [CurrentWsnr] ;
          END ;
    324 : { F10 }
          BEGIN
          { restore screen settings }
          TextAttr := OrigTextAttr ;
          SetCursor (OrigCursorType) ;
          CLRSCR ;
          WRITELN ('Type EXIT to return to AE ...') ;
          SwapVectors ;
          EXEC (GetEnv ('COMSPEC'), '') ;
          SwapVectors ;
          IF DosError <> 0
             THEN ErrorMessage (14) ;
          { reset screen settings }
          TextAttr := ScreenColorArray [Config.Setup.ScreenColors].NormAttr ;
          SetCursor (Config.Setup.CursorType) ;
          END ;
    274 : { alt-E }
          BEGIN
          WRITE (Lst, FF) ;
          CheckDiskError ;
          END ;
    286 : { alt-A }
          BEGIN
          Workspace [CurrentWsnr] := CurrentWs ;
          CurrentWsnr := 1 ;
          CurrentWs := Workspace [CurrentWsnr] ;
          END ;
    376..
    385 : { alt-1 .. alt-0 }
          BEGIN
          MacroNumber := KeyNr - 375 ;
          IF MacroDefining = MacroNumber
             THEN BEGIN
                  DEC (Config.Macro.LENGTH [MacroDefining]) ;
                  ErrorMessage (9) ;
                  END
             ELSE BEGIN
                  IF Config.Macro.LENGTH [MacroNumber] > 0
                     THEN BEGIN
                          IF MacroStackpointer = MacroStackDepth
                             THEN BEGIN
                                  MacroStackpointer := Inactive ;
                                  ErrorMessage (10) ;
                                  END
                             ELSE BEGIN
                                  { push macro onto MacroStack }
                                  INC (MacroStackpointer) ;
                                  WITH MacroStack [MacroStackpointer] DO
                                       BEGIN
                                       Macronr := MacroNumber ;
                                       Index := 1 ;
                                       END ;
                                  END ;
                          END ;
                  END ;
          END ;
    288 : { alt-D }
          BEGIN
          IF MacroDefining = Inactive
             THEN BEGIN
                  { Start define mode }
                  MacroNumber := 1 ;
                  EnterWord (MacroNumber,
                             'Define keyboard Macro nr. (1-10): ', 1, 10) ;
                  IF NOT EscPressed
                     THEN BEGIN
                          { reset old macro }
                          Config.Macro.LENGTH [MacroNumber] := 0 ;
                          MacroDefining := MacroNumber ;
                          END ;
                  END
             ELSE { end define mode }
                  MacroDefining := Inactive ;
          END ;
    289 : { alt-F }
          BEGIN
          IF Config.Setup.WordWrapLength = Inactive
             THEN ErrorMessage (11)
             ELSE BEGIN
                  FormatParagraph (CurPos) ;
                  WordDown (CurPos) ;
                  END ;
          END ;
292, 302 : { alt-J, alt-C }
          BEGIN
          IF Config.Setup.WordWrapLength = Inactive
             THEN ErrorMessage (11)
             ELSE BEGIN
                  { measure line length }
                  EndOfLine (CurPos) ;
                  { remove trailing spaces }
                  Counter := 1 ;
                  WHILE (Buffer^ [CurPos.Index - Counter] = ' ') AND
                        (Counter < CurPos.Colnr) DO
                        INC (Counter) ;
                  DEC (CurPos.Index, Counter - 1) ;
                  DEC (CurPos.Colnr, Counter - 1) ;
                  Shrink (CurPos.Index, Counter - 1) ;
                  LineLength := CurPos.Colnr - 1 ;
                  { remove leading spaces }
                  Home (CurPos) ;
                  Counter := 0 ;
                  WHILE (Buffer^ [CurPos.Index + Counter] = ' ') AND
                        (Counter <= LineLength) DO
                        INC (Counter) ;
                  Shrink (CurPos.Index, Counter) ;
                  DEC (LineLength, Counter) ;
                  NewSpaces := (Config.Setup.WordWrapLength - LineLength) ;
                  IF KeyNr = 302
                     THEN NewSpaces := NewSpaces DIV 2 ;
                  IF NewSpaces > 0
                     THEN InsertSpaces (CurPos, NewSpaces) ;
                  END
          END ;
    306 : { alt-M }
          BEGIN
          OldCurPos := CurPos ;
          Found := TRUE ;
          CASE Buffer^ [CurPos.Index] OF
               '{' : MatchBracketsDown ('{', '}', CurPos) ;
               '}' : MatchBracketsUp ('{', '}', CurPos) ;
               '(' : MatchBracketsDown ('(', ')', CurPos) ;
               ')' : MatchBracketsUp ('(', ')', CurPos) ;
               '[' : MatchBracketsDown ('[', ']', CurPos) ;
               ']' : MatchBracketsUp ('[', ']', CurPos) ;
               '<' : MatchBracketsDown ('<', '>', CurPos) ;
               '>' : MatchBracketsUp ('<', '>', CurPos) ;
               ELSE Message ('Cursor must be on bracket ({[<>]})') ;
               END ; { of case }
          IF NOT Found
             THEN BEGIN
                  Message ('No matching bracket found') ;
                  CurPos := OldCurPos ;
                  END ;
          END ;
    287 : { alt-S }
          BEGIN
          IF PosStackpointer = PosStackDepth
             THEN ErrorMessage (12)
             ELSE BEGIN
                  INC (PosStackpointer) ;
                  PosStack [PosStackpointer] := CurPos.Index ;
                  END ;
           END ;
    290 : { alt-G }
          BEGIN
          IF PosStackpointer = Inactive
             THEN ErrorMessage (13)
             ELSE BEGIN
                  IF CurPos.Index < PosStack [PosStackpointer]
                     THEN SkipDown (CurPos,
                                    PosStack [PosStackpointer] - CurPos.Index)
                     ELSE SkipUp (CurPos,
                                  CurPos.Index - PosStack [PosStackpointer]) ;
                  DEC (PosStackpointer) ;
                  END ;
          END ;
    276, 278 : { alt-T, alt-U }
          BEGIN
          IF MARK = Inactive
             THEN BEGIN
                  IF KeyNr = 276
                     THEN BEGIN
                          IF Buffer^ [CurPos.Index] IN ['A'..'Z', 'a'..'z']
                             THEN BEGIN
                                  Buffer^ [CurPos.Index] :=
                                     CHR (ORD (Buffer^ [CurPos.Index]) XOR $20) ;
                                  ChangesMade := TRUE ;
                                  END ;
                          SkipDown (CurPos, 1) ;
                          END
                     ELSE BEGIN
                          IF Buffer^ [CurPos.Index] IN ['a'..'z']
                             THEN BEGIN
                                  Buffer^ [CurPos.Index] :=
                                     UPCASE (Buffer^ [CurPos.Index]) ;
                                  ChangesMade := TRUE ;
                                  END ;
                          SkipDown (CurPos, 1) ;
                          END
                  END
             ELSE BEGIN
                  IF MARK < CurPos.Index
                     THEN BEGIN
                          BlockStart := MARK ;
                          BlockStop := CurPos.Index ;
                          END
                     ELSE BEGIN
                          BlockStart := CurPos.Index  ;
                          BlockStop := MARK ;
                          END ;
                  IF KeyNr = 276
                     THEN BEGIN
                          { toggle case }
                          FOR Counter := BlockStart TO (BlockStop - 1) DO
                              IF Buffer^ [Counter] IN ['A'..'Z', 'a'..'z']
                                 THEN BEGIN
                                      Buffer^ [Counter] :=
                                         CHR (ORD (Buffer^ [Counter]) XOR $20) ;
                                      ChangesMade := TRUE ;
                                      END ;
                          END
                     ELSE BEGIN
                          { convert to upper case }
                          FOR Counter := BlockStart TO (BlockStop - 1) DO
                              IF Buffer^ [Counter] IN ['a'..'z']
                                 THEN BEGIN
                                      Buffer^ [Counter] :=
                                         UPCASE (Buffer^ [Counter]) ;
                                      ChangesMade := TRUE ;
                                      END ;
                          END ;
                  END ;
          END ;
 0..255 : { character-keys }
          BEGIN
          IF KeyNr = 26
             THEN Message ('Warning: Inserting end-of-file character') ;
          IF (NOT Config.Setup.Insertmode) AND
             (NOT ( (Buffer^ [CurPos.Index] = CR) AND
                   (Buffer^ [CurPos.Index + 1] = LF) ) ) AND
             (Buffer^ [CurPos.Index] <> LF) AND
             (CurPos.Index < Buffersize)
             THEN BEGIN
                  { if in overwrite mode and not at }
                  { end of line or buffer: overwrite character }
                  Buffer^ [CurPos.Index] := CHR (KeyNr) ;
                  INC (CurPos.Index) ;
                  IF KeyNr = 13
                     THEN BEGIN
                          INC (Curpos.Linenr) ;
                          CurPos.Colnr := 1 ;
                          END
                     ELSE INC (CurPos.Colnr) ;
                  ChangesMade := TRUE ;
                  END
             ELSE BEGIN
                  IF Grow (CurPos.Index, 1)
                     THEN BEGIN
                          { insert character }
                          Buffer^ [CurPos.Index] := CHR (KeyNr) ;
                          INC (CurPos.Index) ;
                          IF KeyNr = 13
                             THEN BEGIN
                                  INC (Curpos.Linenr) ;
                                  CurPos.Colnr := 1 ;
                                  END
                             ELSE INC (CurPos.Colnr) ;
                          END ;
                  END ;
          { check if line must be broken }
          IF (Config.Setup.WordWrapLength <> Inactive)
             THEN BEGIN
                  IF (CurPos.Colnr > Config.Setup.WordWrapLength)
                     THEN BEGIN
                          OldCurPos := CurPos ;
                          { find position for line break }
                          REPEAT WordUp (CurPos) ;
                          UNTIL (CurPos.Colnr <= Config.Setup.WordWrapLength)
                                OR (CurPos.Linenr < OldCurPos.Linenr) ;
                          { use NextPos to store index value of CurPos }
                          NextPos := CurPos ;
                          IF (CurPos.Linenr = OldCurPos.Linenr)
                             THEN BEGIN
                                  { remove spaces at end of line }
                                  Counter := 1 ;
                                  WHILE (Buffer^[Curpos.Index-Counter] = ' ')
                                        AND (Counter < Curpos.Colnr) DO
                                        INC (Counter) ;
                                  { only break line if not at left margin }
                                  IF Counter < CurPos.Colnr
                                     THEN BEGIN
                                          DEC (Counter) ;
                                          Shrink (CurPos.Index-Counter,Counter) ;
                                          DEC (CurPos.Index,Counter) ;
                                          DEC (CurPos.Colnr,Counter) ;
                                          InsertCRLF (CurPos) ;
                                          END ;
                                  END ;
                          { restore position }
                          SkipDown (CurPos,OldCurPos.Index - NextPos.Index) ;
                          FirstScreenCol := 1 ;
                          END ;
                  END ;
          END ;
266, 269 : { Enter,^Enter }
          BEGIN
          InsertCRLF (CurPos) ;
          END ;
    265 : { Tab }
          BEGIN
          IF Config.Setup.TabSpacing = 0
             THEN BEGIN
                  { find nearest beginning of word in previous line }
                  OldCurPos := CurPos ;
                  LineUp (Curpos) ;
                  WHILE (CurPos.Colnr <= OldCurPos.Colnr) AND
                        (CurPos.Linenr < OldCurpos.Linenr) DO
                        WordDown (CurPos) ;
                  IF CurPos.Linenr < OldCurpos.Linenr
                     THEN TabSteps := Curpos.Colnr - OldCurpos.Colnr
                     ELSE TabSteps := 0 ;
                  CurPos := OldCurPos ;
                  END
             ELSE BEGIN
                  { fixed-distance tabs }
                  TabSteps := Config.Setup.TabSpacing -
                              ( (CurPos.Colnr - 1) MOD Config.Setup.TabSpacing) ;
                  END ;
          IF (NOT Config.Setup.Insertmode)
             THEN BEGIN
                  { overwrite mode: skip tabsteps until eoln or eof }
                  WHILE (TabSteps > 0) AND
                        (Buffer^ [CurPos.Index] <> LF) AND
                        (CurPos.Index < Buffersize) DO
                        BEGIN
                        INC (CurPos.Index) ;
                        INC (CurPos.Colnr) ;
                        DEC (TabSteps) ;
                        END ;
                  { rest will be inserted as spaces }
                  END ; { of if }
          IF TabSteps > 0
             THEN InsertSpaces (CurPos, TabSteps) ;
          END ;
    271 : { shift-Tab }
          BEGIN
          IF Config.Setup.TabSpacing = 0
             THEN BEGIN
                  OldCurPos := CurPos ;
                  LineUp (CurPos) ;
                  EndOfLine (CurPos) ;
                  IF CurPos.ColNr > OldCurPos.Colnr
                     THEN BEGIN
                          DEC (CurPos.Index, CurPos.Colnr - OldCurPos.Colnr) ;
                          CurPos.Colnr := OldCurpos.Colnr ;
                          END ;
                  WordUp (CurPos) ;
                  IF CurPos.Linenr = (OldCurpos.Linenr - 1)
                     THEN TabSteps := OldCurpos.Colnr - Curpos.Colnr
                     ELSE TabSteps := OldCurPos.Colnr - 1 ;
                  CurPos := OldCurPos ;
                  END
             ELSE IF CurPos.Colnr > Config.Setup.TabSpacing
                     THEN BEGIN
                          TabSteps := (CurPos.Colnr - 1) MOD
                                      Config.Setup.TabSpacing ;
                          IF TabSteps = 0
                             THEN TabSteps := Config.Setup.TabSpacing ;
                          END
                     ELSE TabSteps := CurPos.Colnr - 1 ;
          DEC (CurPos.Index, TabSteps) ;
          DEC (CurPos.Colnr, TabSteps) ;
          END ;
    338 : { Ins }
          BEGIN
          Config.Setup.Insertmode := NOT Config.Setup.Insertmode ;
          END ;
    339 : { Del }
          BEGIN
          IF CurPos.Index < Buffersize
             THEN BEGIN
                  IF (Buffer^ [CurPos.Index] = CR) AND
                     (Buffer^ [CurPos.Index + 1] = LF)
                     THEN Shrink (CurPos.Index, 2)
                     ELSE Shrink (CurPos.Index, 1) ;
                  END ;
          END ;
    264 : { Backspace }
          BEGIN
          IF CurPos.Index > 1
             THEN BEGIN
                  IF (Buffer^ [CurPos.Index - 1] = LF) AND
                     (Buffer^ [CurPos.Index - 2] = CR)
                     THEN BEGIN
                          SkipUp (CurPos, 2) ;
                          Shrink (CurPos.Index, 2) ;
                          END
                     ELSE BEGIN
                          SkipUp (CurPos, 1) ;
                          Shrink (CurPos.Index, 1) ;
                          END ;
                  END ;
          END ;
    273 : { alt-W }
          BEGIN
          NextPos := CurPos ;
          { if at end of line: skip line separator }
          IF (Buffer^ [NextPos.Index] = LF) OR
             ((Buffer^ [NextPos.Index] = CR) AND
              (Buffer^ [NextPos.Index+1] = LF))
             THEN LineDown (NextPos)
             ELSE BEGIN
                  { skip to end of word }
                  WHILE (Buffer^ [NextPos.Index] IN
                         ['0'..'9','A'..'Z','a'..'z']) AND
                        (NextPos.Index < BufferSize) DO
                        BEGIN Inc (NextPos.Index) ;
                              Inc (NextPos.Colnr) ;
                        END ;
                  IF (Buffer^ [NextPos.Index] <> LF) AND
                     (NOT ((Buffer^ [NextPos.Index] = CR) AND
                           (Buffer^ [NextPos.Index+1] = LF)))
                     THEN SkipDown (Nextpos,1) ;
                  { skip all subsequent spaces }
                  WHILE (Buffer^ [NextPos.Index] = ' ') AND
                        (NextPos.Index < BufferSize)
                        DO BEGIN
                           INC (NextPos.Index) ;
                           INC (NextPos.Colnr) ;
                           END ;
                  END ;
          Shrink (CurPos.Index, NextPos.Index - CurPos.Index) ;
          END ;
    294 : { alt-L }
          BEGIN
          Home (CurPos) ;
          NextPos := CurPos ;
          LineDown (NextPos) ;
          Shrink (CurPos.Index, NextPos.Index - CurPos.Index) ;
          END ;
    301 : { alt-X }
          BEGIN
          Workspace [CurrentWsnr] := CurrentWs ;
          Counter := 1 ;
          EscPressed := FALSE ;
          WHILE (Counter <= NrOfWorkspaces) AND (NOT EscPressed) DO
                BEGIN
                WITH Workspace [Counter] DO
                     IF ChangesMade
                        THEN BEGIN
                             IF Config.Setup.SaveOnExit
                                THEN SaveFile (Counter)
                                ELSE IF Answer ('File in ' + CHR (64 + Counter) +
                                               ' has been changed. Save?')
                                        THEN BEGIN
                                             SaveFile (Counter) ;
                                             { if save unsuccessful: }
                                             { stop exit procedure }
                                             IF ChangesMade
                                                THEN EscPressed := TRUE ;
                                             END ;
                             END ;
                INC (Counter) ;
                END ; { of while }
          { do not exit from program if Escape was pressed }
          ProgramFinished := NOT EscPressed ;
          END ;
    272 : { alt-Q }
          BEGIN
          Workspace [CurrentWsnr] := CurrentWs ;
          DisplayInfo ;
          END ;
    300 : { alt-Z }
          BEGIN
          Message ('Another Editor by Dick Alstein.  Version '
                   + AEVersionNr + ', ' + AEVersionDate + '.') ;
          END ;
    283 : { Escape }
          BEGIN
          MARK := Inactive ;
          END ;
    ELSE  BEGIN
          WarningBeep ;
          Message ('This key has no function') ;
          END ;
    END  ; { of case }
  CASE KeyNr OF
    0..255, 264, 265, 266, 269, 273, 320, 322, 339, 345 :
    { char-keys, Backspace, Tab, Enter, ^Enter, Alt-W, F6, F8, Del, Shift-F6 }
                      IF Config.Setup.AutoWrap
                         THEN BEGIN
                              { do auto-wrap }
                              OldCurPos := CurPos ;
                              FormatParagraph (CurPos) ;
                              CurPos := OldCurpos ;
                              END ;
    END ;
  CASE KeyNr OF
    328, 336, 329, 337, 375, 373 : { up,down,PgUp,PgDn,^Home, ^End }
                      BEGIN
                      { when moving vertically through the buffer: }
                      { try to make Colnr equal to VirtualColnr }
                      { (i.e. the value that it "should" have) }
                      WHILE (CurPos.Colnr < VirtualColnr) AND
                            (Buffer^ [CurPos.Index] <> LF) AND
                            (CurPos.Index < BufferSize) DO
                            BEGIN
                            INC (CurPos.Index) ;
                            INC (CurPos.Colnr) ;
                            END ;
                      IF (Buffer^ [CurPos.Index] = LF) AND
                         (Buffer^ [CurPos.Index - 1] = CR)
                         THEN BEGIN
                              DEC (CurPos.Index) ;
                              DEC (CurPos.Colnr) ;
                              END ;
                      END ;
    ELSE              { all other keys }
                      VirtualColnr := Curpos.Colnr ;
    END  ; { of case }
  END ; { of with }
END ; { of procedure }

{-----------------------------------------------------------------------------}

BEGIN
END.
