-
Notifications
You must be signed in to change notification settings - Fork 1
/
edit.c
6158 lines (5456 loc) · 148 KB
/
edit.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
edit.c - Enhanced command line editing for CMD.EXE.
Jason Hood, 24 October to 21 November, 2005 and 19 to 23 December, 2006.
API hooking derived from ANSI.xs by Jean-Louis Morel, from his Perl package
Win32::Console::ANSI. Keyboard hooking based on the hook tutorial sample
code by RattleSnake: Systemwide Windows Hooks without external DLL.
v1.02, 23 July, 2010:
- better handling of control characters;
- fixed alternative directory association in the root;
- LSTK was listing Shift+Control for Control keys;
- tweaks to completion;
+ read options from HKLM if they don't exist in HKCU.
v2.00, 22 July to 8 August, 2011:
* compile cleanly with GCC 4;
- fixed file name completion with leading/trailing dot;
+ new function Execute to prevent adding the line to history;
- ensure input is from the console (prevents "set/p <nul" from crashing);
- ignore input from non-commands (at least "set/p");
- find imports by name (fixes ANSICON issue);
* improved prompt handling for macros & multiple commands;
+ new function CopyFromPrev, similar to the original F3;
+ save history on exit, read on install, but only for the "primary" instance;
- macros will not take arguments after a pipe, ampersand or redirection;
+ new functions SwapWords & SwapArgs;
- piping internal commands will also use the pipe options;
- fixed colouring a redisplayed multiline prompt at the end of the buffer;
- ignore ^C generated by Ctrl+Break;
+ new function UnderToggle to change underscore behaviour;
+ new functions Select, Cut & Paste, to provide a mini-clipboard;
+ new functions EndWordLeft & EndWordRight, to move past the end of a word;
* show the file name and line number of a config file error;
* auto-recall continues after a single failure; DelLeft does not delete;
don't continue after DelRight;
+ new functions FindBack & FindForw do an incremental search;
+ custom configuration based on initial directory;
+ copy the history from the parent instance (if not loading it);
* config file will be read as UTF-8, if the BOM is present;
+ new functions Undo, Redo & Revert;
- read the options even if not installed (to preserve for the install);
* treat a hex keypad number < 256 as Unicode;
- fixed substituting empty macro argument;
* expand multiline macros using the command separator;
* LSTH uses ESCAPE to find numbers and search for the redirection symbols;
* delimit internal commands the same as a definition;
- fixed initial install.
16 September, 2011:
+ new functions UpdateEnter & UpdateErase to replace history lines.
15 November, 2011:
* go back to calling the normal import (explicitly require ANSICON to be
installed after CMDread).
v2.01, 8 December, 2011:
* copy CMD.EXE's imports into edit.dll (finally work with ANSICON).
14 & 15 June, 2012:
* if there's no parent, copy history from primary.
21 May, 2013:
- fix file name completion testing for directory on an empty name.
v2.10, 28 May to 21 June, 2013:
* set locale code page and use wprintf for slightly better output;
+ DBCS (double-width characters) support;
+ Windows 8 support (use API-MS-Win-Core-Console-* import library);
- 64-bit: fix copying history;
* use Unicode;
- use the code page for the custom history file from the config file;
- fixed bug with internal command redirection when redirection wasn't used;
* list completed file names using minimal lines;
- in testing DBCS double-width wrapping, just realised dispbeg was always 0!
- fixed redefining a keyboard macro;
- fixed listing a single-character keyboard macro;
+ new functions Hidden & HiddenEx to wipe the command and prompt, and remove
it from history; defk '-' prefix for HiddenEx shorthand;
* make use of the other buffer to determine the size of the prompt;
- fixed wiping a wrapped command;
- fixed user scrolling issues;
- fixed mixing environment variables with symbols;
+ expand %CD% and recognise the other CMD.EXE dynamic variables;
- recognise, but don't expand, environment variable substitution.
v2.11, 4 July, 2013:
* increase maximum file line length to 2046 (2048 - LF - NUL);
- prevent crash when reading an invalid file.
v2.12, 10 July, 2013:
* read options from CMDread, not here;
- fix saving to the history file specified from the command line.
*/
#include "CMDread.h"
#include "version.h"
#include <commdlg.h>
#include <shellapi.h>
#include <ImageHlp.h>
#ifndef ENABLE_INSERT_MODE
#define ENABLE_INSERT_MODE 0x20
#define ENABLE_QUICK_EDIT_MODE 0x40
#endif
// ========== Auxiliary debug function
#define MYDEBUG 0
#if (MYDEBUG > 0)
void DEBUGSTR( LPCWSTR szFormat, ... ) // sort of OutputDebugStringf
{
WCHAR szBuffer[1024];
va_list pArgList;
va_start( pArgList, szFormat );
_vsnwprintf( szBuffer, lenof(szBuffer), szFormat, pArgList );
va_end( pArgList );
OutputDebugString( szBuffer );
}
#elif defined(__GNUC__)
#define DEBUGSTR( ... )
#else
#define DEBUGSTR
#endif
// ========== Global variables and constants
#ifdef __GNUC__
#define SHARED __attribute__((dllexport, shared, section(".share")))
#else
#pragma data_seg(".share", "read,write,shared")
#pragma data_seg()
#define SHARED __declspec(dllexport allocate(".share"))
#endif
SHARED DWORD primary_id = 0;
SHARED DWORD parent_pid = 0;
SHARED Option option = {
{ 25, 50 }, // insert & overwrite cursor sizes
0, // default insert mode
0, // beep on errors
0, // don't auto-recall commands
0, // enable macro & symbol translation
0, // enable CMDread
0, // append backslash on completed dirs
0, // empty match remains at start
' ', // old prefix character to disable translation
1, // minimum line length to store in history
50, // number of commands to store in history
0, // enable colouring
31, // command line, bright white on blue
27, // recording, bright cyan on blue
27, // drive and colon, bright cyan on blue
30, // directory separator, bright yellow on blue
26, // directory, bright green on blue
30, // greater than sign, bright yellow on blue
26, // base directory, bright green on blue
121, // selection, bright blue on grey
1, // underscore is part of a word
' ', // prefix character to disable translation
'+', // prefix character to update history line
};
SHARED WCHAR cfgname[MAX_PATH] = { 0 }; // configuration file
SHARED WCHAR hstname[MAX_PATH] = { 0 }; // history file
SHARED BOOL cmd_history = FALSE; // read command line history file
// Structure to hold a line.
typedef struct
{
PWSTR txt; // text of the line
DWORD len; // length of the line
} Line, *PLine;
// Structure to search for functions, keys & internal commands.
typedef struct
{
PCWSTR name; // function/key/internal command name
int func; // function/VK/function ptr
} Cfg;
// Structure to store the character and function assigned to a key.
typedef struct
{
WCHAR ch; // character
char fn; // function
char filler; // ensure alignment as a single DWORD
} Key, *PKey;
// Type of keyboard macro.
enum { MAC_FUNC, MAC_LINE, MAC_HIDDEN };
// Structure for keyboard macros.
typedef struct macro_s
{
struct macro_s* next;
char* key; // pointer into appropriate key array
int type;
DWORD len;
union
{
PWSTR line; // for type == MAC_LINE or MAC_HIDDEN
PKey func; // for type == MAC_FUNC
Key chfn; // for len == 1
};
} Macro, *PMacro;
// Structure to hold lines for macros, symbols and associations.
typedef struct linelist_s
{
struct linelist_s* next;
DWORD len;
WCHAR line[0];
} LineList, *PLineList;
// Structure for a definition (macro, symbol or association).
typedef struct define_s
{
struct define_s* next;
PLineList line;
DWORD len;
WCHAR name[0];
} Define, *PDefine;
// Structure for the history and completed filenames.
typedef struct history_s
{
struct history_s* prev;
struct history_s* next;
union
{
DWORD len; // history line length
struct
{
WORD flen; // file name length
WORD dlen; // displayed length
};
};
WCHAR line[0];
} History, *PHistory;
// Function prototype for an internal command.
typedef void (*IntFunc)( DWORD );
// Structure for undo/redo.
typedef struct undo_s
{
struct undo_s* prev;
struct undo_s* next;
int type; // the operation to perform
int pos; // where to perform it
DWORD len; // how much to undo
DWORD max; // capacity of txt
PWSTR txt; // the text to restore
} UndoInfo, *PUndoInfo;
enum { UNDOGROUP, UNDOINSERT, UNDODELETE }; // how to undo
BOOL primary; // are we the primary instance?
BOOL save_history; // preserve history?
__declspec(dllexport)
Status local = {
PVERX,
TRUE, // we are active
{ 0 }, // history file
};
static DWORD conwr; // dummy variable for API functions
#define WriteCon( h, t, l ) WriteConsole( h, t, l, &conwr, NULL )
#define FillConChar( h, c, l, p ) FillConsoleOutputCharacter( h,c,l,p,&conwr )
#define FillConAttr( h, a, l, p ) FillConsoleOutputAttribute( h,a,l,p,&conwr )
HANDLE hConIn, hConOut; // handles to keyboard input and screen output
HANDLE hConWid, hConWid1; // output handles to determine character width
CONSOLE_SCREEN_BUFFER_INFO screen; // current screen info
Line prompt = { L"", 0 }; // pointer to the prompt
WORD p_attr[MAX_PATH+2]; // buffer to store prompt's attributes
DWORD p_attr_len; // length of above
BOOL show_prompt; // should we redisplay the prompt?
int erase_prompt; // remove the prompt for a multi-cmd?
BOOL hidden_cmd; // prevent writing newline for Hidden/HiddenEx
Line line; // line being edited
DWORD max; // maximum size of above
DWORD dispbeg, dispend; // beginning and ending position to display
DWORD cellend; // ending character cell position
Line selected; // the selection text
BOOL kbd; // is input from the keyboard?
BOOL def_macro; // defining a macro?
Line mcmd; // buffer for multiple commands
int lastm; // previous macro listed was multi-line
COORD lastc; // cursor position of previous command
BOOL found_quote; // true if get_string found a quote
FILE* file; // file containing commands
UINT filecp; // code page of the file
PCWSTR file_name; // name of the file for the error display
int line_no; // line number of current command
BOOL seen_error; // has an error already been shown?
int check_break; // Control+Break received?
BOOL trap_break; // pass it on?
Line envvar; // buffer for environment variable
#define ESCAPE '^' // character to treat next character literally
#define CMDSEP 19 // character used to separate multiple commands
#define VARIABLE '%' // character used for symbol and variable subst
#define FEXEC L".exe.com.bat.cmd" // default executable list
#define FIGNORE L".exe.com.dll.obj.o.bak" // default ignore list
#define INVALID_FNAME L"=,;+<|>&@" // "invalid" filename characters
#define QUOTE_FNAME L" &()[]{}^=;!%'+,`~" // quoted filename characters
// (same as CMD.EXE)
#define BRACE_TERM L" \t,;+" // brace expansion separators
#define BRACE_STOP L"<|>&" // brace expansion terminators
#define BRACE_ESCAPE L"{},^" // escaped brace exp. characters
#define DEF_TERM L" \t<|>&/" // definition delimiter
#define VAR_ESCAPE L"%^" // escaped variable characters
#define ARG_ESCAPE L"%*^" // escaped argument characters
// CMD.EXE dynamic variables - these are recognised, but not expanded (except
// for %CD%).
const LPCWSTR cmd_vars[] =
{
//L"CD", // handled separately
L"DATE",
L"TIME",
L"RANDOM",
L"ERRORLEVEL",
L"CMDEXTVERSION",
L"CMDCMDLINE",
L"HIGHESTNUMANODENUMBER",
};
// Map control characters using the CP437 glyphs as Unicode codepoints.
const WCHAR ControlChar[] = { ' ', // 0
L'\x263A', // 1 White Smiling Face
L'\x263B', // 2 Black Smiling Face
L'\x2665', // 3 Black Heart Suit
L'\x2666', // 4 Black Diamond Suit
L'\x2663', // 5 Black Club Suit
L'\x2660', // 6 Black Spade Suit
L'\x2022', // 7 Bullet
L'\x25D8', // 8 Inverse Bullet
L'\x25CB', // 9 White Circle
L'\x25D9', // 10 Inverse White Circle
L'\x2642', // 11 Male Sign
L'\x2640', // 12 Female Sign
L'\x266A', // 13 Eigth Note
L'\x266B', // 14 Beamed Eigth Notes
L'\x263C', // 15 White Sun With Rays
L'\x25BA', // 16 Black Right-Pointing Pointer
L'\x25C4', // 17 Black Left-Pointing Pointer
L'\x2195', // 18 Up Down Arrow
L'\x203C', // 19 Double Exclamation Mark
L'\x00B6', // 20 Pilcrow Sign
L'\x00A7', // 21 Section Sign
L'\x25AC', // 22 Black Rectangle
L'\x21A8', // 23 Up Down Arrow With Base
L'\x2191', // 24 Upwards Arrow
L'\x2193', // 25 Downwards Arrow
L'\x2192', // 26 Rightwards Arrow
L'\x2190', // 27 Leftwards Arrow
L'\x221F', // 28 Right Angle
L'\x2194', // 29 Left Right Arrow
L'\x25B2', // 30 Black Up-Pointing Pointer
L'\x25BC' // 31 Black Down-Pointing Pointer
};
// Functions
enum
{
Default, // just place the character in the buffer
Ignore, // totally ignore the key
Quote, // the next key will not be interpreted as a command
CharLeft, // cursor one character to the left
CharRight, // cursor one character to the right
WordLeft, // cursor to the start of the current/previous word
WordRight, // cursor to the start of the next word
EndWordLeft, // cursor to the end of the previous word
EndWordRight, // cursor to the end of the current/next word
StringLeft, // cursor to the start of the current/previous string
StringRight, // cursor to the start of the next string
BegLine, // cursor to the beginning of the line
EndLine, // cursor to the end of the line
Select, // select text
Cut, // cut selected text or current argument
Paste, // paste cut text
PrevLine, // previous command in history buffer
NextLine, // next command in history buffer
SearchBack, // search the history backwards
SearchForw, // search the history forwards
FindBack, // incrementally search the history backwards
FindForw, // incrementally search the history forwards
FirstLine, // first command in history
LastLine, // last command in history
CopyFromPrev, // copy remainder of line from the previous command
List, // filename completion list
ListDir, // directory completion list
Cycle, // filename completion cycle
CycleBack, // filename completion cycle backwards
CycleDir, // directory completion cycle
CycleDirBack, // directory completion cycle backwards
SelectFiles, // select files from the Open dialog
DelLeft, // delete character to the left of the cursor
DelRight, // delete character under the cursor
DelWordLeft, // delete word at left of cursor
DelWordRight, // delete word at right of cursor
DelArg, // delete argument at or left of cursor
DelBegLine, // delete to the beginning of the line
DelEndLine, // delete to the end of the line
DelEndExec, // delete to the end of the line and execute the line
Erase, // erase line
StoreErase, // erase the line but put it in the history
CmdSep, // command separator
Transpose, // transpose (swap characters)
SwapWords, // swap the current word with the previous
SwapArgs, // swap the current argument with the previous
AutoRecall, // toggle auto-recall
MacroToggle, // macro/symbol/brace toggling
UnderToggle, // change behaviour of underscore
VarSubst, // inline var. substitution/brace expansion/association
Enter, // accept line
Execute, // execute the line without adding it to the history
Wipe, // execute the line without history & remove its display
Hidden, // execute the line & remove it and the prompt
HiddenEx, // as above and don't add to history
InsOvr, // insert/overwrite toggle
Play, // play back a series of keys
Record, // record a series of keys
Undo, // undo previous function(s)
Redo, // undo the undo
Revert, // undo/redo everything
UpdateEnter, // accept line, replacing the existing one in history
UpdateErase, // erase line but replace the existing one in history
LastFunc
};
// Function names for lstk.
const LPCWSTR func_str[] =
{
L"Default", L"Ignore", L"Quote", L"CharLeft",
L"CharRight", L"WordLeft", L"WordRight", L"EndWordLeft",
L"EndWordRight", L"StringLeft", L"StringRight", L"BegLine",
L"EndLine", L"Select", L"Cut", L"Paste",
L"PrevLine", L"NextLine", L"SearchBack", L"SearchForw",
L"FindBack", L"FindForw", L"FirstLine", L"LastLine",
L"CopyFromPrev", L"List", L"ListDir", L"Cycle",
L"CycleBack", L"CycleDir", L"CycleDirBack", L"SelectFiles",
L"DelLeft", L"DelRight", L"DelWordLeft", L"DelWordRight",
L"DelArg", L"DelBegLine", L"DelEndLine", L"DelEndExec",
L"Erase", L"StoreErase", L"CmdSep", L"Transpose",
L"SwapWords", L"SwapArgs", L"AutoRecall", L"MacroToggle",
L"UnderToggle", L"VarSubst", L"Enter", L"Execute",
L"Wipe", L"Hidden", L"HiddenEx", L"InsOvr",
L"Play", L"Record", L"Undo", L"Redo",
L"Revert", L"UpdateEnter", L"UpdateErase"
};
#define f( name ) { L###name, name }, // simplify function config definitions
// Function strings
const Cfg cfg[] = {
f( AutoRecall )
f( BegLine )
f( CharLeft )
f( CharRight )
f( CmdSep )
f( CopyFromPrev )
f( Cut )
f( Cycle )
f( CycleBack )
f( CycleDir )
f( CycleDirBack )
f( Default )
f( DelArg )
f( DelBegLine )
f( DelEndExec )
f( DelEndLine )
f( DelLeft )
f( DelRight )
f( DelWordLeft )
f( DelWordRight )
f( EndLine )
f( EndWordLeft )
f( EndWordRight )
f( Enter )
f( Erase )
f( Execute )
f( FindBack )
f( FindForw )
f( FirstLine )
f( Hidden )
f( HiddenEx )
f( Ignore )
f( InsOvr )
f( LastLine )
f( List )
f( ListDir )
f( MacroToggle )
f( NextLine )
f( Paste )
f( Play )
f( PrevLine )
f( Quote )
f( Record )
f( Redo )
f( Revert )
f( SearchBack )
f( SearchForw )
f( Select )
f( SelectFiles )
f( StoreErase )
f( StringLeft )
f( StringRight )
f( SwapArgs )
f( SwapWords )
f( Transpose )
f( UnderToggle )
f( Undo )
f( UpdateEnter )
f( UpdateErase )
f( VarSubst )
f( Wipe )
f( WordLeft )
f( WordRight )
};
// Key strings
const Cfg cfgkey[] = {
{ L"Bksp", VK_DOWN+1 },
{ L"Del", VK_DELETE },
{ L"Down", VK_DOWN },
{ L"End", VK_END },
{ L"Enter", VK_DOWN+3 },
{ L"Esc", VK_DOWN+4 },
{ L"Home", VK_HOME },
{ L"Ins", VK_INSERT },
{ L"Left", VK_LEFT },
{ L"PgDn", VK_NEXT },
{ L"PgUp", VK_PRIOR },
{ L"Right", VK_RIGHT },
{ L"Tab", VK_DOWN+2 },
{ L"Up", VK_UP },
};
#define CFGKEYS (lenof(cfgkey))
// Key names for lstk.
const LPCWSTR key_str[] =
{
L"PgUp", L"PgDn", L"End", L"Home", L"Left", L"Up", L"Right",
L"Down", L"Bksp", L"Tab", L"Enter", L"Esc", L"Ins", L"Del",
};
// Keymaps
char key_table[][4] = { // VK_PRIOR to VK_DELETE
// plain shift control alt
{ FirstLine, Ignore, Ignore, Ignore, },// PageUp
{ LastLine, Ignore, Ignore, Ignore, },// PageDn
{ EndLine, Ignore, DelEndLine, Ignore, },// End
{ BegLine, Ignore, DelBegLine, Ignore, },// Home
{ CharLeft, EndWordLeft, WordLeft, StringLeft, },// Left
{ PrevLine, Ignore, FindBack, Ignore, },// Up
{ CharRight, EndWordRight, WordRight, StringRight, },// Right
{ NextLine, Ignore, FindForw, Ignore, },// Down
// plain shift control shift+control
{ DelLeft, DelLeft, DelWordLeft, DelArg, },// Backspace
{ Cycle, CycleBack, List, ListDir, },// Tab
{ Enter, UpdateEnter, Execute, Hidden, },// Enter
{ Erase, Erase, Ignore, Ignore, },// Escape
// plain shift control alt
{ InsOvr, Paste, Ignore, Ignore, },// Insert
{ DelRight, Cut, DelWordRight, Ignore, },// Delete
};
char fkey_table[][4] = { // VK_F1 to VK_F12
// plain shift control alt
{ Ignore, Ignore, Ignore, Ignore, },// F1
{ Ignore, Ignore, Ignore, Ignore, },// F2
{ CopyFromPrev, Ignore, Ignore, Ignore, },// F3
{ Ignore, Ignore, Ignore, Ignore, },// F4
{ Ignore, Ignore, Ignore, Ignore, },// F5
{ Ignore, Ignore, Ignore, Ignore, },// F6
{ Ignore, Ignore, Ignore, Ignore, },// F7
{ SearchBack, SearchForw, Ignore, Ignore, },// F8
{ Ignore, Ignore, Ignore, Ignore, },// F9
{ Ignore, Ignore, Ignore, Ignore, },// F10
{ Ignore, Ignore, Ignore, Ignore, },// F11
{ Record, Ignore, Ignore, Ignore, },// F12
};
char ctrl_key_table[][2] = {
// plain // shift
{ Ignore, Ignore, }, // ^@
{ BegLine, SwapArgs, }, // ^A
{ CharLeft, Ignore, }, // ^B
{ Cut, Ignore, }, // ^C
{ DelRight, ListDir, }, // ^D
{ EndLine, Ignore, }, // ^E
{ CharRight, List, }, // ^F
{ StoreErase, UpdateErase, }, // ^G
{ DelLeft, Ignore, }, // ^H
{ Cycle, CycleBack, }, // ^I
{ VarSubst, Ignore, }, // ^J
{ DelEndLine, Ignore, }, // ^K
{ DelWordLeft, Ignore, }, // ^L
{ Select, Ignore, }, // ^M
{ NextLine, Ignore, }, // ^N
{ DelEndExec, Ignore, }, // ^O
{ PrevLine, Paste, }, // ^P
{ Quote, Ignore, }, // ^Q
{ SearchBack, FindBack, }, // ^R
{ CmdSep, SelectFiles, }, // ^S
{ Transpose, SwapWords, }, // ^T
{ PrevLine, Revert, }, // ^U
{ SearchForw, FindForw, }, // ^V
{ DelWordRight, Ignore, }, // ^W
{ DelBegLine, Ignore, }, // ^X
{ AutoRecall, Ignore, }, // ^Y
{ Undo, Redo, }, // ^Z
{ Erase, Ignore, }, // ^[
{ CycleDir, CycleDirBack, }, // ^\ (can't end line w/ backslash)
{ CmdSep, Ignore, }, // ^]
{ Wipe, HiddenEx, }, // ^^
{ MacroToggle, UnderToggle, }, // ^_
};
// Keyboard macros
PMacro macro_head, macro_prev; // head of the list, previous macro
PMacro find_macro( char* ); // search macro list for key
PMacro add_macro( char* ); // add or clear macro for key
void end_macro( PMacro ); // finish off a macro
void del_macro( char* ); // delete the macro for key
// Internal commands
FILE* lstout; // list output file
BOOL lstpipe; // outputting to a pipe?
int old_mode; // current stdout file mode
BOOL redirect( DWORD ); // check for redirection
void end_redirect( void ); // close the file
void list_key( char* ); // show the assignment of a key
void execute_defa( DWORD );
void execute_defk( DWORD );
void execute_defm( DWORD );
void execute_defs( DWORD );
void execute_dela( DWORD );
void execute_delh( DWORD );
void execute_delk( DWORD );
void execute_delm( DWORD );
void execute_dels( DWORD );
void execute_lsta( DWORD );
void execute_lsth( DWORD );
void execute_lstk( DWORD );
void execute_lstm( DWORD );
void execute_lsts( DWORD );
void execute_rsta( DWORD );
void execute_rsth( DWORD );
void execute_rstm( DWORD );
void execute_rsts( DWORD );
#ifndef _WIN64
const Cfg internal[] = {
{ L"defa", (int)execute_defa }, // define association
{ L"defk", (int)execute_defk }, // define key
{ L"defm", (int)execute_defm }, // define macro
{ L"defs", (int)execute_defs }, // define symbol
{ L"dela", (int)execute_dela }, // delete association(s)
{ L"delh", (int)execute_delh }, // delete history line(s)
{ L"delk", (int)execute_delk }, // delete key(s)
{ L"delm", (int)execute_delm }, // delete macro(s)
{ L"dels", (int)execute_dels }, // delete symbol(s)
{ L"lsta", (int)execute_lsta }, // list associations
{ L"lsth", (int)execute_lsth }, // list history
{ L"lstk", (int)execute_lstk }, // list keys
{ L"lstm", (int)execute_lstm }, // list macros
{ L"lsts", (int)execute_lsts }, // list symbols
{ L"rsta", (int)execute_rsta }, // reset associations
{ L"rsth", (int)execute_rsth }, // reset history
{ L"rstm", (int)execute_rstm }, // reset macros
{ L"rsts", (int)execute_rsts }, // reset symbols
};
#else
const IntFunc internal_funcs[] = {
execute_defa,
execute_defk,
execute_defm,
execute_defs,
execute_dela,
execute_delh,
execute_delk,
execute_delm,
execute_dels,
execute_lsta,
execute_lsth,
execute_lstk,
execute_lstm,
execute_lsts,
execute_rsta,
execute_rsth,
execute_rstm,
execute_rsts,
};
const Cfg internal[] = {
{ L"defa", 0 },
{ L"defk", 1 },
{ L"defm", 2 },
{ L"defs", 3 },
{ L"dela", 4 },
{ L"delh", 5 },
{ L"delk", 6 },
{ L"delm", 7 },
{ L"dels", 8 },
{ L"lsta", 9 },
{ L"lsth", 10 },
{ L"lstk", 11 },
{ L"lstm", 12 },
{ L"lsts", 13 },
{ L"rsta", 14 },
{ L"rsth", 15 },
{ L"rstm", 16 },
{ L"rsts", 17 },
};
#endif
//#define MIN_CMD_LEN 4
//#define MAX_CMD_LEN 4
#define CMD_LEN 4
#define INTERNAL_CMDS (lenof(internal))
#define DEFM L"DEFM> " // prompt for defining a macro
#define DEFM_LEN 6
const WCHAR ENDM[] = L"endm"; // string used to end macro definition
#define ENDM_LEN 4
// Line manipulation
COORD line_to_scr( DWORD ); // convert line position to screen
void set_display_marks( DWORD, DWORD ); // indicate positions to display
void copy_chars( PCWSTR, DWORD ); // set line to string
void remove_chars( DWORD, DWORD ); // remove characters from line
DWORD insert_chars( DWORD, PCWSTR, DWORD ); // add string to line
DWORD replace_chars( DWORD, DWORD, PCWSTR, DWORD ); // replace string w/ another
char* get_key( PKey ); // read a key
WCHAR process_keypad( WORD ); // translate Alt+Keypad to character
void edit_line( void ); // read and edit line from the keyboard
void display_prompt( void ); // re-display the original prompt
void remove_prompt( DWORD ); // wipe out the prompt
// Undo
UndoInfo undo, redo; // fixed head of each list
PUndoInfo undoing; // pointer to the current list
void reset_undo( void ); // "erase" both lists
void add_to_undo( int, DWORD, DWORD ); // add an operation to the current list
BOOL undo_redo( PUndoInfo, int* ); // undo/redo a group
// Definitions (macro, symbol and association)
PDefine sym_head, mac_head, assoc_head; // heads of the various lists
PDefine macro_stk; // stack of executing macros
PDefine add_define( PDefine*, DWORD, DWORD ); // add definition to list
PDefine find_define( PDefine*, DWORD, DWORD ); // find definition in list
PDefine find_assoc( PCWSTR, DWORD ); // find extension in list
void del_define( PDefine* ); // delete definition from list
void delete_define( PDefine*, DWORD ); // delete list of definitions
void reset_define( PDefine* ); // wipe all definitions
void list_define( PDefine, WCHAR ); // display a definition
void list_defines( PDefine*, DWORD ); // display a list of definitions
PLineList add_line( DWORD ); // add a line to the line list
void free_linelist( PLineList ); // free memory used by line list
// Line input
BOOL read_cmdfile( PCWSTR ); // read the file
BOOL get_file_line( BOOL ); // read the line from file
void get_next_line( void ); // get next line of input
void get_macro_line( BOOL ); // input coming from a macro
void pop_macro( void ); // macro finished, remove it from stack
// Line output
void multi_cmd( void ); // separate multiple commands
void dosify( void ); // convert UNIX-style command to Windows
BOOL brace_expansion( void ); // expand the first set of braces
void expand_braces( void ); // perform brace expansion
BOOL associate( void ); // perform filename association
BOOL expand_symbol( void ); // expand a symbol to its definition
BOOL expand_macro( void ); // expand a macro to its definition
BOOL internal_cmd( void ); // process internal command
void expand_vars( BOOL ); // expand environment vars and symbols
// History
History history = { &history, &history, { 0 } }; // constant empty line
int histsize; // number of lines in history
#define HISTSIZE 1000 // restrict the file to this
PHistory new_history( PCWSTR, DWORD ); // allocate new history item
void remove_from_history( PHistory ); // remove item from history
void add_to_history( BOOL ); // add current line to history
PHistory search_history( PHistory, DWORD, BOOL ); // search history for match
PHistory find_history( PHistory, int*, DWORD, BOOL );
void copy_parent_history( void ); // initial history from parent
// Filename completion
PHistory fname; // list of found filenames and initial pattern
DWORD fname_pos, path_pos; // position in line of start of filename/path
WCHAR dirchar = '\\'; // character to use for directory indicator
int fname_max, fname_cnt; // longest name and number of names found
DWORD assoc_pos; // position of the association within the list
PWCHAR flist; // open dialog filenames
#define FLIST_LEN 2048 // size of flist
BOOL match_file( PCWSTR, PCWSTR, DWORD, BOOL, BOOL, PHANDLE, PWIN32_FIND_DATA );
// find a file, possibly matching its extension
int find_files( int*, int ); // find matching files and common prefix
void list_files( void ); // list all files
int calc_lines( void ); // determine number of lines for the listing
BOOL check_name_count( int ); // determine action to take for many files
BOOL quote_needed( PCWSTR, int ); // should filename be quoted?
PWSTR make_filter( BOOL ); // make the open dialog filter string
void make_relative( PWSTR, PWSTR ); // make an absolute path relative
// Utility
#define isword( ch ) (IsCharAlphaNumeric( ch ) || \
(ch == '_' && option.underscore))
#define isblank( ch ) (ch == ' ' || ch == '\t')
int search_cfg( PCWSTR, DWORD, const Cfg [], int ); // find config string
char* find_key( DWORD, DWORD ); // find the position of a key
PWSTR new_txt( PCWSTR, DWORD ); // make a copy of a string
BOOL make_line( DWORD ); // ensure line is a particular length
BOOL make_length( PWSTR*, DWORD*, DWORD );
void bell( void ); // audible alert
DWORD skip_blank( DWORD ); // skip over spaces and tabs
DWORD skip_nonblank( DWORD ); // skip over everything not space/tab
DWORD skip_nondelim( DWORD ); // skip over everything not a delimiter
BOOL is_quote( int ); // quote character?
DWORD get_string( DWORD, LPDWORD, BOOL ); // retrieve argument
void un_escape( PCWSTR ); // remove the escape character
BOOL match_ext( PCWSTR, DWORD, PCWSTR, DWORD ); // match extension in list
DWORD get_env_var( PCWSTR, PCWSTR ); // get environment variable
void show_error( PCWSTR, DWORD, DWORD ); // show an internal command error
void set_codepage( void ); // set output code page (for printf)
DWORD display_length( PCWSTR, DWORD, DWORD ); // get the display length for DBCS
UINT codepage; // the output code page
BOOL dbcs; // is it DBCS?
// ------------------------- Line Manipulation ---------------------------
// Convert line position to cursor position.
COORD line_to_scr( DWORD pos )
{
COORD c;
if (dbcs)
pos = display_length( line.txt, line.len, pos );
pos += screen.dwCursorPosition.X;
c.X = pos % screen.dwSize.X;
c.Y = pos / screen.dwSize.X + screen.dwCursorPosition.Y;
return c;
}
// Set the beginning and ending positions to be displayed; end is after the
// position (ie. end - beg == number of characters).
void set_display_marks( DWORD beg, DWORD end )
{
DWORD cell;
if (beg < dispbeg)
dispbeg = beg;
if (end > dispend)
dispend = end;
if (dbcs)
{
if (end > line.len) // for the recording prompt
cell = display_length( line.txt, line.len, beg ) + end - beg;
else
cell = display_length( line.txt, line.len, dispend );
if (cell > cellend)
cellend = cell;
}
}
// Set the line to str, which is cnt characters.
void copy_chars( PCWSTR str, DWORD cnt )
{
set_display_marks( 0, line.len );
if (cnt > max)
{
bell();
cnt = max;
}
memcpy( line.txt, str, WSZ(cnt) );
line.len = cnt;
set_display_marks( 0, line.len );
reset_undo();
}
// Remove cnt characters starting from pos.
void remove_chars( DWORD pos, DWORD cnt )
{
// Set the display mark for DBCS to the previous character, in order to
// handle the possible case of a wrapped double-width character.
set_display_marks( (dbcs && pos != 0) ? pos - 1 : pos, line.len );
add_to_undo( UNDOINSERT, pos, cnt );
memcpy( line.txt + pos, line.txt + pos + cnt, WSZ(line.len - pos - cnt) );
line.len -= cnt;
}
// Insert string of cnt characters at pos.
DWORD insert_chars( DWORD pos, PCWSTR str, DWORD cnt )
{
if (line.len + cnt > max)
{
bell();
cnt = max - line.len;
}
memmove( line.txt + pos + cnt, line.txt + pos, WSZ(line.len - pos) );
memcpy( line.txt + pos, str, WSZ(cnt) );
line.len += cnt;
set_display_marks( pos, line.len );
add_to_undo( UNDODELETE, pos, cnt );
return cnt;
}
// Replace old characters at pos with string of cnt characters.
DWORD replace_chars( DWORD pos, DWORD old, PCWSTR str, DWORD cnt )
{
if (old >= cnt)
{
set_display_marks( pos, pos + cnt );
if (old != cnt)
remove_chars( pos + cnt, old - cnt );