1 /*        con_nt.cpp
2  *
3  *        Copyright (c) 1994-1996, Marko Macek
4  *
5  *        You may distribute under the terms of either the GNU General Public
6  *        License or the Artistic License, as specified in the README file.
7  *
8  */
9 
10 /* some functionality is now shared with g_text.cpp and g_draw.cpp */
11 /* new os/2 code needs to be merged with this */
12 /* the console APIs on win'95 seem to be really screwed up */
13 
14 /*
15  *      10/13/96 Jal:
16  *              Rebuilt for Windows NT, generic; no port/Watcom code should
17  *              be needed to compile (jal).
18  *              Removed most mouse handling code (unnecessary), added pipe handler by
19  *              porting the OS/2 version to NT.
20  *              Solved some bugs with regard to the TCell problem.
21  *
22  *      10/28/96 Jal:
23  *              Started to replace threaded pipe code with nonthreaded code, using
24  *              overlapped I/O.
25  *
26  *      Wed Jan 15 1997 (Jal):
27  *              -       The Grey-Alt-+ key and some other keys were not recognised. This
28  *                      was because NT didn't mark these as "enhanced" keys. Now the code
29  *                      translates some (scancode,ascii) pairs to the enhanced keyboard.
30  *                      The table was already present!
31  *              -       To solve the "flashing cursor" problem: now doesn't enter FTE
32  *                      mainloop when console returns empty action..
33  *
34  */
35 #define WIN32_LEAN_AND_MEAN
36 #include <windows.h>
37 #include <process.h>
38 #include <signal.h>
39 
40 #include <wincon.h>
41 
42 #include <ctype.h>
43 #include <stdio.h>
44 #include "sysdep.h"
45 #include "console.h"
46 #include "gui.h"
47 #include "s_files.h"
48 #include "s_string.h"
49 #include "c_config.h"
50 
51 #define True    1
52 #define False   0
53 
54 
55 static int Initialized = 0;
56 static int MousePresent = 0;
57 static int CursorVisible = 1; /* 1 means visible */
58 static int MouseVisible = 0; /* 0 means hidden */
59 static TEvent MouseEv = { evNone };
60 static TEvent EventBuf = { evNone };
61 //static TEventMask EventMask;
62 
63 static HANDLE ConOut;
64 static HANDLE ConIn;
65 static HANDLE OurConOut;
66 static DWORD OldConsoleMode;
67 
68 static int LastMouseX = 0;
69 static int      LastMouseY = 0;
70 //static int      isWin95 = 0;
71 
72 static char winTitle[256] = "FTE";
73 static char winSTitle[256] = "FTE";
74 
75 int codepage;
76 
77 #define dbm(x)          //printf(x), Sleep(3000)
78 
79 
80 #if 0
81 void dbg(const char* s, ...) /*FOLD00*/
82 {
83 }
84 #else
85 
dbg(const char * s,...)86 void dbg(const char* s, ...) /*FOLD00*/
87 {
88     char    buf[256];
89     va_list args;
90 
91     va_start(args, s);
92     vsprintf(buf, s, args);
93     va_end(args);
94     OutputDebugString(buf);
95 }
96 #endif
97 
98 
DrawCursor(int Show)99 static void DrawCursor(int Show) { /*FOLD00*/
100     CONSOLE_CURSOR_INFO cci;
101 
102     GetConsoleCursorInfo(OurConOut, &cci);
103     cci.bVisible = Show ? TRUE : FALSE;
104     SetConsoleCursorInfo(OurConOut, &cci);
105 }
106 
107 #define NUMITEMS(x)     (sizeof(x) / sizeof(x[0]))
108 
109 #if 1
110 /*
111  *      Translation table 1: translate (scan,asciicode) of the input event to a
112  *      valid FTE keystroke. This is used because NT sometimes "forgets" to flag
113  *      special keys as "enhanced" (grey) keys..
114  */
115 static struct {
116     USHORT CharScan;
117     TKeyCode KeyCode;
118 } TransCharScan[] = {
119     { 0x0100, kbEsc },                              { 0x011B, kbEsc },
120     { 0x1C0D, kbEnter },            { 0x1C0A, kbEnter },
121     { 0x1C00, kbEnter },            { 0xE00D, kbEnter | kfGray },
122     { 0xA600, kbEnter | kfGray },   { 0xE00A, kbEnter | kfGray },
123     { 0x0E08, kbBackSp },           { 0x0E7F, kbBackSp },
124     { 0x0E00, kbBackSp },           { 0x0F09, kbTab },
125     { 0x9400, kbTab },              { 0xA500, kbTab },
126     { 0x0F00, kbTab },              { 0x4E00, '+' | kfGray },
127     { 0x9000, '+' | kfGray },       { 0x4E2B, '+' | kfGray },
128     { 0x4A00, '-' | kfGray },       { 0x8E00, '-' | kfGray },
129     { 0x4A2D, '-' | kfGray },       { 0x3700, '*' | kfGray },
130     { 0x9600, '*' | kfGray },       { 0x372A, '*' | kfGray },
131     { 0xE02F, '/' | kfGray },       { 0xA400, '/' | kfGray },
132     { 0x9500, '/' | kfGray },       { 0x0300, 0 }
133 };
134 #endif
135 
136 #if 0
137 static struct {
138     int ScanCode;
139     TKeyCode KeyCode;
140 } TransScan[] = {
141     { 0x78, '1' }, { 0x79, '2' }, { 0x7A, '3' }, { 0x7B, '4' }, { 0x7C, '5' },
142     { 0x7D, '6' }, { 0x7E, '7' }, { 0x7F, '8' }, { 0x80, '9' }, { 0x81, '0' },
143 
144     { 0x10, 'Q' }, { 0x11, 'W' }, { 0x12, 'E' }, { 0x13, 'R' }, { 0x14, 'T' },
145     { 0x15, 'Y' }, { 0x16, 'U' }, { 0x17, 'I' }, { 0x18, 'O' }, { 0x19, 'P' },
146 
147     { 0x1E, 'A' }, { 0x1F, 'S' }, { 0x20, 'D' }, { 0x21, 'F' }, { 0x22, 'G' },
148     { 0x23, 'H' }, { 0x24, 'J' }, { 0x25, 'K' }, { 0x26, 'L' },
149 
150     { 0x2C, 'Z' }, { 0x2D, 'X' }, { 0x2E, 'C' }, { 0x2F, 'V' }, { 0x30, 'B' },
151     { 0x31, 'N' }, { 0x32, 'M' },
152 
153     { 0x29, '`' }, { 0x82, '-' }, { 0x83, '=' }, { 0x2B, '\\' }, { 0x1A, '[' },
154     { 0x1B, ']' }, { 0x27, ';' }, { 0x28, '\'' }, { 0x33, ',' }, { 0x34, '.' },
155     { 0x35, '/' }, { 0x37, '*' }, { 0x4E, '+' }, { 0x4A, '-' },
156 
157     { 0x3B, kbF1    },      { 0x3C, kbF2    },      { 0x3D, kbF3    },
158     { 0x3E, kbF4    },      { 0x3F, kbF5    },      { 0x40, kbF6    },
159     { 0x41, kbF7    },      { 0x42, kbF8    },      { 0x43, kbF9    },
160     { 0x44, kbF10   },      { 0x85, kbF11   },      { 0x86, kbF12   },
161 
162     { 0x54, kbF1    },      { 0x55, kbF2    },      { 0x56, kbF3    },
163     { 0x57, kbF4    },      { 0x58, kbF5    },      { 0x59, kbF6    },
164     { 0x5A, kbF7    },      { 0x5B, kbF8    },      { 0x5C, kbF9    },
165     { 0x5D, kbF10   },      { 0x87, kbF11   },      { 0x88, kbF12   },
166 
167     { 0x5E, kbF1    },      { 0x5F, kbF2    },      { 0x60, kbF3    },
168     { 0x61, kbF4    },      { 0x62, kbF5    },      { 0x63, kbF6    },
169     { 0x64, kbF7    },      { 0x65, kbF8    },      { 0x66, kbF9    },
170     { 0x67, kbF10   },      { 0x89, kbF11   },      { 0x8A, kbF12   },
171 
172     { 0x68, kbF1    },      { 0x69, kbF2    },      { 0x6A, kbF3    },
173     { 0x6B, kbF4    },      { 0x6C, kbF5    },      { 0x6D, kbF6    },
174     { 0x6E, kbF7    },      { 0x6F, kbF8    },      { 0x70, kbF9    },
175     { 0x71, kbF10   },      { 0x8B, kbF11   },      { 0x8C, kbF12   },
176 
177     { 0x47, kbHome  },      { 0x48, kbUp    },      { 0x49, kbPgUp  },
178     { 0x4B, kbLeft  },      { 0x4C, kbCenter},      { 0x4D, kbRight },
179     { 0x4F, kbEnd   },      { 0x50, kbDown  },      { 0x51, kbPgDn  },
180     { 0x52, kbIns   },      { 0x53, kbDel   },
181 
182     { 0x77, kbHome  },      { 0x8D, kbUp    },      { 0x84, kbPgUp  },
183     { 0x73, kbLeft  },                                              { 0x74, kbRight },
184     { 0x75, kbEnd   },      { 0x91, kbDown  },      { 0x76, kbPgDn  },
185     { 0x92, kbIns   },      { 0x93, kbDel   },
186 
187     { 0x97, kbHome  | kfGray },  { 0x98, kbUp        | kfGray },  { 0x99, kbPgUp  | kfGray },
188     { 0x9B, kbLeft  | kfGray },                                                       { 0x9D, kbRight | kfGray },
189     { 0x9F, kbEnd   | kfGray },  { 0xA0, kbDown  | kfGray },  { 0xA1, kbPgDn  | kfGray },
190     { 0xA2, kbIns   | kfGray },  { 0xA3, kbDel       | kfGray }
191 };
192 #endif
193 
194 
195 struct {
196     SHORT VirtCode;
197     unsigned long KeyCode;
198 } VirtTab[]  =
199 {
200     { 112, kbF1 },
201     { 113, kbF2 },
202     { 114, kbF3 },
203     { 115, kbF4 },
204     { 116, kbF5 },
205     { 117, kbF6 },
206     { 118, kbF7 },
207     { 119, kbF8 },
208     { 120, kbF9 },
209     { 121, kbF10 },
210     { 122, kbF11 },
211     { 123, kbF12 },
212 
213     { 35, kbEnd },
214     { 36, kbHome },
215     { 33, kbPgUp },
216     { 34, kbPgDn },
217     { 38, kbUp },
218     { 37, kbLeft },
219     { 39, kbRight },
220     { 40, kbDown },
221     { 45, kbIns },
222     { 46, kbDel },
223 
224     { 27, kbEsc },
225     { 13, kbEnter },
226     { 8, kbBackSp },
227     { 32, kbSpace },
228     { 9, kbTab },
229 
230     { 0, 0 }
231 };
232 
233 static const char shftwrng[]  = "~!@#$%^&*()_+{}|:\"<>?";
234 static const char shftright[] = "`1234567890-=[]\\;',./";
235 
ReadConsoleEvent(TEvent * E)236 int ReadConsoleEvent(TEvent *E) /*FOLD00*/
237 {
238     /*
239      *      Reads and interprets the console event. It is called when console input
240      *      handle is signalled. To prevent flashing cursors this routine returns
241      *      F if there's nothing to do; this causes the caller to loop without
242      *      returning to the FTE mainloop.
243      */
244     INPUT_RECORD inp;
245     DWORD           nread;
246     TKeyCode        Ch = 0;
247     TKeyCode        flg = 0;
248     ULONG           flags;
249     int             I, i;
250 
251     ReadConsoleInput(ConIn, &inp, 1, &nread);
252     if (nread != 1) return False;                           // Nothing read after signal??
253 
254     switch (inp.EventType)
255     {
256     case WINDOW_BUFFER_SIZE_EVENT:
257         //** Resized the window. Make FTE use the new size..
258         frames->Resize(inp.Event.WindowBufferSizeEvent.dwSize.X, inp.Event.WindowBufferSizeEvent.dwSize.Y);
259         frames->Repaint();
260         return True;
261 
262     case KEY_EVENT:
263         if (inp.Event.KeyEvent.bKeyDown) {
264             if ((inp.Event.KeyEvent.dwControlKeyState & CAPSLOCK_ON) &&
265                 inp.Event.KeyEvent.wVirtualKeyCode != 106 &&
266                 inp.Event.KeyEvent.wVirtualKeyCode != 109 &&
267                 inp.Event.KeyEvent.wVirtualKeyCode != 107)
268             {
269                 if (!(inp.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED)) {
270                     for (i=0; shftwrng[i]; i++) {
271                         if (inp.Event.KeyEvent.uChar.AsciiChar == shftwrng[i]) {
272                             inp.Event.KeyEvent.uChar.AsciiChar = shftright[i];
273                             break;
274                         }
275                     }
276                 }
277                 else {
278                     for (i=0; shftright[i]; i++) {
279                         if (inp.Event.KeyEvent.uChar.AsciiChar == shftright[i]) {
280                             inp.Event.KeyEvent.uChar.AsciiChar = shftwrng[i];
281                             break;
282                         }
283                     }
284                 }
285             }
286         }
287         //** Skip shift, control and alt key stuff.
288         switch(inp.Event.KeyEvent.wVirtualKeyCode)
289         {       case VK_SHIFT: case VK_CONTROL: case VK_MENU: case VK_PAUSE:
290         case VK_CAPITAL: case VK_LWIN:  case VK_RWIN: case VK_APPS:
291             return False;
292         }
293 
294         //** Distill FTE flags from the NT flags. This fails for some keys
295         //** because NT has an oddity with enhanced keys (Alt-Grey-+ etc).
296 
297         // from chaac: Please do not toutch RIGHT_ALT_PRESSED handling,
298         // in some keyboard ALT-GR is used for special characters.
299         flags = inp.Event.KeyEvent.dwControlKeyState;
300         if (flags & (/*RIGHT_ALT_PRESSED |*/ LEFT_ALT_PRESSED)) flg |= kfAlt;
301         if (flags & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) flg |= kfCtrl;
302         if (flags & (RIGHT_ALT_PRESSED)) flg &= ~kfCtrl;
303         if (flags & SHIFT_PRESSED) flg |= kfShift;
304         if (flags & ENHANCED_KEY) flg |= kfGray;
305 
306 #if 0
307         dbg("key1: %s, vk=%x, vscan=%x, flags=%x, rep=%d, ascii=%x (%c).\n",
308             inp.Event.KeyEvent.bKeyDown ? "down" : "up",
309             inp.Event.KeyEvent.wVirtualKeyCode,
310             inp.Event.KeyEvent.wVirtualScanCode,
311             flags,
312             inp.Event.KeyEvent.wRepeatCount,
313             inp.Event.KeyEvent.uChar.AsciiChar,
314             inp.Event.KeyEvent.uChar.AsciiChar);
315 #endif
316         Ch = 0;
317 
318         // handle special case when user with scandinavian keyboard presses
319         // alt-gr + special key and then spacebar
320         if (inp.Event.KeyEvent.bKeyDown) {
321             if ((inp.Event.KeyEvent.wVirtualKeyCode == 0x20) &&
322                 (inp.Event.KeyEvent.wVirtualScanCode == 0x39))
323             {
324                 switch(inp.Event.KeyEvent.uChar.AsciiChar)
325                 {
326                 case '~': Ch = '~'; break;
327                 case '^': Ch = '^'; break;
328                 case '`': Ch = '`'; break;
329                 case '�': Ch = '�'; break;
330                 }
331             }
332         }
333 
334         //** Translate VK codes to FTE codes,
335         if (Ch == 0)
336         {
337             for (I = 0; I < sizeof(VirtTab)/sizeof(VirtTab[0]); I++)
338                 if (VirtTab[I].VirtCode == inp.Event.KeyEvent.wVirtualKeyCode)
339                 {
340                     Ch = VirtTab[I].KeyCode;
341                     break;
342                 }
343         }
344 
345         //** Not a virtual key-> do charscan translation, if needed;
346         if(Ch == 0)
347         {
348             unsigned int     cc = ((inp.Event.KeyEvent.wVirtualScanCode << 8) | (unsigned char)inp.Event.KeyEvent.uChar.AsciiChar);
349             for(I = 0; I < NUMITEMS(TransCharScan); I++)
350             {
351                 if(cc == TransCharScan[I].CharScan)
352                 {
353                     Ch = TransCharScan[I].KeyCode;
354                     break;
355                 }
356             }
357         }
358         if (Ch == 0)
359         {
360             if ((Ch = (TKeyCode) (unsigned char)inp.Event.KeyEvent.uChar.AsciiChar) != 0)
361             {
362                 if (flg & kfAlt) Ch = toupper(Ch);
363             }
364         }
365 
366         if (Ch == 0)                            //** Odd: cannot distill keycode.
367             return False;
368 
369         if (flg & kfCtrl)
370             if (Ch < 32)
371                 Ch += 64;
372 
373         E->Key.Code = Ch | flg;         // Set FTE keycode,
374         E->What = inp.Event.KeyEvent.bKeyDown ? evKeyDown : evKeyUp;
375         return True;
376 
377     case MOUSE_EVENT:
378         LastMouseX = E->Mouse.X = inp.Event.MouseEvent.dwMousePosition.X;
379         LastMouseY = E->Mouse.Y = inp.Event.MouseEvent.dwMousePosition.Y;
380         flags = inp.Event.MouseEvent.dwControlKeyState;
381         if (flags & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) flg |= kfAlt;
382         if (flags & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) flg |= kfCtrl;
383         if (flags & SHIFT_PRESSED) flg |= kfShift;
384         E->Mouse.KeyMask = flg;
385         E->Mouse.Buttons = (unsigned short)inp.Event.MouseEvent.dwButtonState;
386         E->Mouse.Count = 1;
387         if (inp.Event.MouseEvent.dwEventFlags & DOUBLE_CLICK)
388             E->Mouse.Count = 2;
389 
390         if ((inp.Event.MouseEvent.dwEventFlags & MOUSE_WHEELED) == MOUSE_WHEELED)
391         {
392             E->What        = evCommand;
393             E->Msg.View    = 0;
394             E->Msg.Model   = 0;
395             E->Msg.Param2  = 0;
396 
397             // Scroll:
398             // (The SDK does not tell how to determine whether the wheel
399             // was scrolled up or down. Found an example on:
400             // http://www.adrianxw.dk/SoftwareSite/Consoles/Consoles5.html
401             if (inp.Event.MouseEvent.dwButtonState & 0xFF000000) {  // Wheel down
402                 if (flg & kfShift) { // Translate to horizontal scroll.
403                     E->Msg.Command = (flg & kfCtrl) ? cmHScrollPgRt : cmHScrollRight;
404                 } else { // Translate to vertical scroll.
405                     E->Msg.Command = (flg & kfCtrl) ? cmVScrollPgDn : cmVScrollDown;
406                 }
407             } else { // Wheel up
408                 if (flg & kfShift) { // Translate to horizontal scroll.
409                     E->Msg.Command = (flg & kfCtrl) ? cmHScrollPgLt : cmHScrollLeft;
410                 } else { // Translate to vertical scroll.
411                     E->Msg.Command = (flg & kfCtrl) ? cmVScrollPgUp : cmVScrollUp;
412                 }
413             }
414 
415             E->Msg.Param1 = (flg & kfCtrl) ? 1 : 3; // 1 page / 3 lines
416 
417             return True;
418         }
419 
420         if (inp.Event.MouseEvent.dwEventFlags == MOUSE_MOVED)
421         {
422             E->What = evMouseMove;
423             //puts("Move");
424         }
425         else
426         {
427             static unsigned short mb = 0;
428 
429             if (inp.Event.MouseEvent.dwButtonState & ~mb)
430             {
431                 E->What = evMouseDown;
432                 E->Mouse.Buttons = ((unsigned short)inp.Event.MouseEvent.dwButtonState) & ~mb;
433                 //puts("Down");
434             }
435             else
436             {
437                 E->What = evMouseUp;
438                 E->Mouse.Buttons = mb & ~((unsigned short)inp.Event.MouseEvent.dwButtonState);
439                 //puts("Up");
440             }
441             mb = (unsigned short)inp.Event.MouseEvent.dwButtonState;
442         }
443         return True;
444     }
445     return False;
446 }
447 
448 
ConInit(int,int)449 int ConInit(int /*XSize*/, int /*YSize*/) { /*FOLD00*/
450     if (Initialized) return 1;
451 
452     EventBuf.What = evNone;
453     MousePresent    = 0; //MOUSInit();
454 
455     //** Get NT/Win95 flag,
456     OSVERSIONINFO   oi;
457 
458     oi.dwOSVersionInfoSize = sizeof(oi);
459     GetVersionEx((LPOSVERSIONINFO) &oi);
460     //isWin95 = (oi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
461 
462     ConOut = GetStdHandle(STD_OUTPUT_HANDLE);
463     ConIn = GetStdHandle(STD_INPUT_HANDLE);
464     codepage = GetConsoleCP();
465     GetConsoleMode(ConIn, &OldConsoleMode);
466     SetConsoleMode(ConIn,
467                    ENABLE_WINDOW_INPUT |
468                    ENABLE_MOUSE_INPUT);
469     OurConOut = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
470                                           0, NULL,
471                                           CONSOLE_TEXTMODE_BUFFER, NULL);
472     ConContinue();
473 
474     Initialized = 1;
475 
476     return 1;
477 }
478 
ConDone()479 int ConDone() { /*FOLD00*/
480     ConSuspend();
481     CloseHandle(OurConOut);
482 
483     return 1;
484 }
485 
ConSuspend()486 int ConSuspend() { /*FOLD00*/
487     SetConsoleActiveScreenBuffer(ConOut);
488     SetConsoleMode(ConIn, OldConsoleMode);
489 
490     return 1;
491 }
492 
ConContinue()493 int ConContinue() { /*FOLD00*/
494     SetConsoleActiveScreenBuffer(OurConOut);
495     GetConsoleMode(ConIn, &OldConsoleMode);
496     SetConsoleMode(ConIn, ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
497 
498     SetConsoleOutputCP(codepage);
499     SetConsoleCP(codepage);
500 
501     return 1;
502 }
503 
ConClear()504 int ConClear() { /*FOLD00*/
505     int W, H;
506     TDrawBuffer B;
507 
508     MoveChar(B, 0, ConMaxCols, ' ', 0x07, 1);
509     if (!ConQuerySize(&W, &H)
510         || !ConSetBox(0, 0, W, H, B[0]))
511         return 0;
512 
513     return 1;
514 }
515 
516 
517 #if 0   // Mouse control not necessary when using console functions.
518 /*--------------------------------------------------------------------------*/
519 /*      CLASS:  tMouHelp is used to control mouse cursor visibility during              */
520 /*                      screen updates.                                                                                                 */
521 /*--------------------------------------------------------------------------*/
522 class tMouHelp
523 {
524 protected:
525     int     mh_x, mh_y;                     // Current mouse position / 0
526     int     mh_valid;
527     int     mh_disabled;                    // T if mouse should be re-enabled.
528 
529 public:
530     tMouHelp() : mh_x(0), mh_y(0), mh_valid(FALSE), mh_disabled(FALSE)      {}
531     ~tMouHelp()
532     {       if(MouseVisible && mh_disabled) DrawMouse(1);
533     }
534 
535     void    disIfLine(int x, int w, int y)
536     {
537         if(mh_disabled) return;
538         if(! mh_valid)
539         {
540             ConQueryMousePos(&mh_x, &mh_y);
541             mh_valid = TRUE;
542         }
543         if(y == mh_y && mh_x >= x && mh_x < x+y)
544         {
545             mh_disabled= TRUE;
546             DrawMouse(0);
547         }
548     }
549 };
550 #endif
551 
ConPutBox(int X,int Y,int W,int H,PCell Cell)552 int ConPutBox(int X, int Y, int W, int H, PCell Cell) /*FOLD00*/
553 {
554     int             I;
555     PCell           p = Cell;
556     COORD           corg, csize;
557     SMALL_RECT      rcl;
558     BOOL            rc;
559 
560     for (I = 0; I < H; I++)
561     {
562         corg.X  = corg.Y = 0;
563         csize.X = W;
564         csize.Y = 1;
565         rcl.Left= X;
566         rcl.Top = I + Y;
567         rcl.Bottom = I + Y;// + (isWin95 ? 1 : 0);
568         rcl.Right = X + W - 1;// + (isWin95 ? 1 : 0);
569 
570         rc = WriteConsoleOutput(OurConOut, (PCHAR_INFO)p, csize, corg, &rcl);
571         if (rc != TRUE) {
572             //("WriteConsoleOutput %d\n", rc);
573         }
574         p += W;
575     }
576     return 1;
577 }
578 
ConGetBox(int X,int Y,int W,int H,PCell Cell)579 int ConGetBox(int X, int Y, int W, int H, PCell Cell) /*FOLD00*/
580 {
581     int             I;
582     USHORT          WW = W << 1;
583     PCell           p = Cell;
584     COORD           corg, csize;
585     SMALL_RECT      rcl;
586 
587     for (I = 0; I < H; I++)
588     {
589         corg.X = corg.Y = 0;
590         csize.X = W;
591         csize.Y = 1;
592         rcl.Left = X;
593         rcl.Top = I + Y;
594         rcl.Bottom = I + Y;// + (isWin95 ? 1 : 0);
595         rcl.Right = X + W - 1;// + (isWin95 ? 1 : 0);
596 
597         ReadConsoleOutput(OurConOut, (PCHAR_INFO)p, csize, corg, &rcl);
598         p += W;
599     }
600     return 1;
601 }
602 
ConPutLine(int X,int Y,int W,int H,PCell Cell)603 int ConPutLine(int X, int Y, int W, int H, PCell Cell) /*FOLD00*/
604 {
605     int             I;
606     COORD           corg, csize;
607     SMALL_RECT      rcl;
608     BOOL rc;
609 
610     for (I = 0; I < H; I++)
611     {
612         corg.X = corg.Y = 0;
613         csize.X = W;
614         csize.Y = 1;
615         rcl.Left = X;
616         rcl.Top = I + Y;
617         rcl.Bottom = I + Y;// + (isWin95 ? 1 : 0);
618         rcl.Right = X + W - 1;// + (isWin95 ? 1 : 0);
619 
620         rc = WriteConsoleOutput(OurConOut, (PCHAR_INFO)Cell, csize, corg, &rcl);
621         if (rc != TRUE) {
622             //printf("WriteConsoleOutput %d\n", rc);
623         }
624     }
625 
626     return 1;
627 }
628 
ConSetBox(int X,int Y,int W,int H,TCell Cell)629 int ConSetBox(int X, int Y, int W, int H, TCell Cell) /*FOLD00*/
630 {
631     int             I;
632     COORD           corg, csize;
633     SMALL_RECT      rcl;
634     TDrawBuffer B;
635 
636     I = W;
637     while (I-- > 0) B[I] = Cell;
638 
639     for (I = 0; I < H; I++)
640     {
641         corg.X = corg.Y = 0;
642         csize.X = W;
643         csize.Y = 1;
644         rcl.Left = X;
645         rcl.Top = I + Y;
646         rcl.Bottom = I + Y;// - (isWin95 ? 1 : 0);
647         rcl.Right = X + W - 1;// - (isWin95 ? 1 : 0);
648 
649         WriteConsoleOutput(OurConOut, (PCHAR_INFO)B, csize, corg, &rcl);
650     }
651 
652     return 1;
653 }
654 
ConScroll(int Way,int X,int Y,int W,int H,TAttr Fill,int Count)655 int ConScroll(int Way, int X, int Y, int W, int H, TAttr Fill, int Count) /*FOLD00*/
656 {
657     TCell           FillCell;
658     SMALL_RECT      rect, clip;
659     COORD           dest;
660 
661     MoveCh(&FillCell, ' ', Fill, 1);
662 
663     clip.Left = X;
664     clip.Top = Y;
665     clip.Right = X + W - 1;
666     clip.Bottom = Y + H - 1;
667 
668     rect = clip;
669     dest.X = X;
670     dest.Y = Y;
671 
672     switch (Way) {
673     case csUp:
674         rect.Top += Count;
675         break;
676     case csDown:
677         rect.Bottom -= Count;
678         dest.Y += Count;
679         break;
680     case csLeft:
681         rect.Left += Count;
682         break;
683     case csRight:
684         rect.Right += Count;
685         dest.X += Count;
686         break;
687     }
688 
689     ScrollConsoleScreenBuffer(OurConOut, &rect, &clip, dest, (PCHAR_INFO)&FillCell);
690 
691     return 1;
692 }
693 
ConSetSize(int X,int Y)694 int ConSetSize(int X, int Y) { /*FOLD00*/
695     return 0;
696 }
697 
ConQuerySize(int * X,int * Y)698 int ConQuerySize(int *X, int *Y) { /*FOLD00*/
699     CONSOLE_SCREEN_BUFFER_INFO csbi;
700 
701     GetConsoleScreenBufferInfo(OurConOut, &csbi);
702     *X = csbi.dwSize.X;
703     *Y = csbi.dwSize.Y;
704 
705     dbg("Console size (%u,%u)\n", *X, *Y);
706 
707     return 1;
708 }
709 
ConSetCursorPos(int X,int Y)710 int ConSetCursorPos(int X, int Y) { /*FOLD00*/
711     COORD xy;
712 
713     xy.X = X;
714     xy.Y = Y;
715     SetConsoleCursorPosition(OurConOut, xy);
716 
717     return 1;
718 }
719 
ConQueryCursorPos(int * X,int * Y)720 int ConQueryCursorPos(int *X, int *Y) { /*FOLD00*/
721     CONSOLE_SCREEN_BUFFER_INFO csbi;
722 
723     GetConsoleScreenBufferInfo(OurConOut, &csbi);
724     *X = csbi.dwCursorPosition.X;
725     *Y = csbi.dwCursorPosition.Y;
726 
727     return 1;
728 }
729 
ConShowCursor()730 int ConShowCursor() { /*FOLD00*/
731     CursorVisible = 1;
732     DrawCursor(1);
733 
734     return 1;
735 }
736 
ConHideCursor()737 int ConHideCursor() { /*FOLD00*/
738     CursorVisible = 0;
739     DrawCursor(0);
740 
741     return 1;
742 }
743 
ConCursorVisible()744 int ConCursorVisible() { /*FOLD00*/
745     return (CursorVisible == 1);
746 }
747 
ConSetCursorSize(int Start,int End)748 int ConSetCursorSize(int Start, int End) { /*FOLD00*/
749     return 0;
750 }
751 
ConSetMousePos(int X,int Y)752 int ConSetMousePos(int X, int Y) { /*FOLD00*/
753     return 0;
754 }
755 
ConQueryMousePos(int * X,int * Y)756 int ConQueryMousePos(int *X, int *Y) { /*FOLD00*/
757     *X = LastMouseX;
758     *Y = LastMouseY;
759 
760     // NT does not have this ? (not needed anyway, but check mouse hiding above).
761     return 1;
762 }
763 
ConShowMouse()764 int ConShowMouse() { /*FOLD00*/
765     MouseVisible = 1;
766 
767     return (MousePresent != 0);
768 }
769 
ConHideMouse()770 int ConHideMouse() { /*FOLD00*/
771     MouseVisible = 0;
772 
773     return (MousePresent != 0);
774 }
775 
ConMouseVisible()776 int ConMouseVisible() { /*FOLD00*/
777     return (MouseVisible == 1);
778 }
779 
ConQueryMouseButtons(int * ButtonCount)780 int ConQueryMouseButtons(int *ButtonCount) { /*FOLD00*/
781     return 1;
782 }
783 
ConPutEvent(const TEvent & Event)784 int ConPutEvent(const TEvent& Event) { /*FOLD00*/
785     EventBuf = Event;
786 
787     return 1;
788 }
789 
ConFlush()790 int ConFlush() { /*FOLD00*/
791     return 1;
792 }
793 
ConGrabEvents(TEventMask EventMask)794 int ConGrabEvents(TEventMask EventMask) { /*FOLD00*/
795     return 1;
796 }
797 
798 
799 static PCell SavedScreen = 0;
800 static int SavedX, SavedY, SaveCursorPosX, SaveCursorPosY;
801 
SaveScreen()802 int SaveScreen() { /*FOLD00*/
803     if (SavedScreen)
804         free(SavedScreen);
805 
806     ConQuerySize(&SavedX, &SavedY);
807 
808     SavedScreen = (PCell) malloc(SavedX * SavedY * sizeof(TCell));
809 
810     if (SavedScreen)
811         ConGetBox(0, 0, SavedX, SavedY, SavedScreen);
812 
813     ConQueryCursorPos(&SaveCursorPosX, &SaveCursorPosY);
814 
815     return 1;
816 }
817 
RestoreScreen()818 int RestoreScreen() { /*FOLD00*/
819     if (SavedScreen) {
820         ConPutBox(0, 0, SavedX, SavedY, SavedScreen);
821         ConSetCursorPos(SaveCursorPosX, SaveCursorPosY);
822     }
823     return 1;
824 }
825 
826 
GUI(int & argc,char ** argv,int XSize,int YSize)827 GUI::GUI(int &argc, char **argv, int XSize, int YSize) { /*FOLD00*/
828     fArgc = argc;
829     fArgv = argv;
830     ::ConInit(-1, -1);
831     SaveScreen();
832     ::ConSetSize(XSize, YSize);
833     gui = this;
834 }
835 
~GUI()836 GUI::~GUI() { /*FOLD00*/
837     RestoreScreen();
838 
839     if (SavedScreen)
840         free(SavedScreen);
841 
842     ::ConDone();
843     gui = 0;
844 }
845 
ConSuspend()846 int GUI::ConSuspend() { /*FOLD00*/
847     RestoreScreen();
848     return ::ConSuspend();
849 }
850 
ConContinue()851 int GUI::ConContinue() { /*FOLD00*/
852     SaveScreen();
853     return ::ConContinue();
854 }
855 
ShowEntryScreen()856 int GUI::ShowEntryScreen() { /*FOLD00*/
857     TEvent E;
858 
859     ConHideMouse();
860     RestoreScreen();
861     SetConsoleActiveScreenBuffer(ConOut);
862     do { gui->ConGetEvent(evKeyDown, &E, -1, 1, 0); } while (E.What != evKeyDown);
863     SetConsoleActiveScreenBuffer(OurConOut);
864     ConShowMouse();
865     if (frames)
866         frames->Repaint();
867     return 1;
868 }
869 
ConGetDrawChar(unsigned int index)870 char ConGetDrawChar(unsigned int index) { /*FOLD00*/
871     static const char *tab=NULL;
872 
873     if (!tab) {
874         tab=GetGUICharacters ("WindowsNT","ڿ��ij�ô��\x1A����\x1B\x1A");
875     }
876     assert(index < strlen(tab));
877 
878     return tab[index];
879 }
880 
881 
RunProgram(int mode,char * Command)882 int GUI::RunProgram(int mode, char *Command) { /*FOLD00*/
883     int rc, W, H, W1, H1;
884 
885     ConQuerySize(&W, &H);
886     ConHideMouse();
887     ConSuspend();
888 
889     if (*Command == 0)      // empty string = shell
890         Command = getenv(
891                          "COMSPEC"
892                         );
893 
894     // we don't want ctrl-c or ctrl-break to exit our editor...
895     signal(SIGBREAK, SIG_IGN);
896     signal(SIGINT, SIG_IGN);
897 
898     rc = system(Command);
899 
900     // restore handlers back to default handlers
901     signal(SIGBREAK, SIG_DFL);
902     signal(SIGINT, SIG_DFL);
903 
904     ConContinue();
905     ConShowMouse();
906     ConQuerySize(&W1, &H1);
907 
908     if (W != W1 || H != H1) {
909         frames->Resize(W1, H1);
910     }
911     frames->Repaint();
912     return rc;
913 }
914 
ConSetTitle(const char * Title,const char * STitle)915 int ConSetTitle(const char *Title, const char *STitle) { /*FOLD00*/
916     char buf[sizeof(winTitle)] = {0};
917     JustFileName(Title, buf, sizeof(buf));
918     if (buf[0] == '\0') // if there is no filename, try the directory name.
919         JustLastDirectory(Title, buf, sizeof(buf));
920 
921     strlcpy(winTitle, "FTE - ", sizeof(winTitle));
922     if (buf[0] != 0) // if there is a file/dir name, stick it in here.
923     {
924         strlcat(winTitle, buf, sizeof(winTitle));
925         strlcat(winTitle, " - ", sizeof(winTitle));
926     }
927     strlcat(winTitle, Title, sizeof(winTitle));
928     strlcpy(winSTitle, STitle, sizeof(winSTitle));
929     SetConsoleTitle (winTitle);
930 
931     return 1;
932 }
933 
ConGetTitle(char * Title,size_t MaxLen,char * STitle,size_t SMaxLen)934 int ConGetTitle(char *Title, size_t MaxLen, char *STitle, size_t SMaxLen) { /*FOLD00*/
935     strlcpy(Title, winTitle, MaxLen);
936     strlcpy(STitle, winSTitle, SMaxLen);
937     return 1;
938 }
939 
940 
941 #if 0
942 /****************************************************************************/
943 /*                                                                                                                                                      */
944 /*      CODING: Pipe handler.                                                                                                   */
945 /*                                                                                                                                                      */
946 /****************************************************************************/
947 /*--------------------------------------------------------------------------*/
948 /*      STATIC GLOBALS.                                                                                                                 */
949 /*--------------------------------------------------------------------------*/
950 #define MAX_PIPES       4
951 #define PIPE_BUFLEN 4096
952 #define PIPEBUF_SZ      4096
953 
954 class NTHandle
955 {
956 protected:
957     HANDLE  nth_h;
958 
959 public:
960     operator HANDLE()
961     {       return nth_h;
962     }
963 
964     void    close()
965     {       if(nth_h != INVALID_HANDLE_VALUE)
966     {
967         CloseHandle(nth_h);
968         nth_h = INVALID_HANDLE_VALUE;
969     }
970     }
971 
972 
973     NTHandle()      {       nth_h = INVALID_HANDLE_VALUE;   }
974 
975     ~NTHandle()
976     {       close();
977     }
978 
979     NTHandle(const HANDLE& h) : nth_h(h)    {}
980     NTHandle(const NTHandle& nth);                          // UNDEFINED (no assgn)
981     NTHandle& operator =(const NTHandle& nth);      // UNDEFINED (no assgn)
982     NTHandle& operator =(const HANDLE nth)
983     {       close();
984         nth_h = nth;
985     return *this;
986     }
987 };
988 
989 
990 class GPipe
991 {
992 public:
993     int     p_used;
994     int     p_id;
995     char*   p_buffer;
996     int     p_buflen;
997     int     p_bufused;
998     int     p_bufpos;
999     EModel* p_notify;
1000     char*   p_command;
1001     int     p_retcode;
1002     int     p_doterm;
1003 
1004     //** NT specific.
1005     HANDLE  p_proc_h;                               // Handle of spawned process,
1006     HANDLE  pipeDataRead;
1007     HANDLE  pipeStartRead;
1008     HANDLE  pipeMutex;
1009     HANDLE  p_pipe_ph;                              // Input pipe (read by FTE)
1010     HANDLE  p_child_ph;                     // Client side's handle (written to by spawned)
1011     DWORD   p_read_len;                     // #bytes read in overlapped I/O
1012     int     p_io_posted;                    // T when overlapped I/O is posted,
1013     int     p_completed;                    // T when client process closed down.
1014     int     p_has_data;                     // T when OVERLAPPED completed.
1015 
1016     static GPipe    pipe_ar[MAX_PIPES];
1017 
1018 
1019 public:
1020     int     open(char *Command, EModel *notify);
1021     int     close();
1022     int     read(void *buffer, int len);
1023     int     getEvent(TEvent* event);
1024 
1025 
1026 protected:
1027     int     createPipe();
1028     void    releasePipe();
1029     int     runCommand();
1030     void    closeProc();
1031     int     handlePost();
1032     int     postRead();
1033 
1034 
1035 
1036 public:
1037     static GPipe*   getFreePipe();
1038     static GPipe*   getPipe(int id);
1039 
1040 };
1041 
1042 GPipe GPipe::pipe_ar[MAX_PIPES];
1043 
1044 
1045 /*
1046  *      getFreePipe() locates an unused GPipe structure. It also assigns it's ID.
1047  */
1048 GPipe* GPipe::getFreePipe() /*FOLD00*/
1049 {
1050     int     i;
1051 
1052     for(i = 0; i < MAX_PIPES; i++)
1053     {
1054         if(! pipe_ar[i].p_used)
1055         {
1056             pipe_ar[i].p_id = i;            // Set pipenr,
1057             return pipe_ar + i;
1058         }
1059     }
1060     return NULL;                                            // No free pipe
1061 }
1062 
1063 
1064 GPipe* GPipe::getPipe(int id) /*FOLD00*/
1065 {
1066     if (id < 0 || id > MAX_PIPES) return NULL;
1067     if(! pipe_ar[id].p_used) return NULL;
1068     return pipe_ar + id;
1069 }
1070 
1071 
1072 int GPipe::createPipe() /*FOLD00*/
1073 {
1074     /*
1075      *      Called from open() to create and open the server and the client pipes.
1076      */
1077     static int      PCount = 0;
1078     //HANDLE          hchild;
1079     char            pipename[50];
1080     int             ok;
1081     SECURITY_ATTRIBUTES sa;
1082 
1083     sa.nLength = sizeof(sa);                        // Security descriptor for INHERIT.
1084     sa.lpSecurityDescriptor = 0;
1085     sa.bInheritHandle       = 1;
1086 
1087 #if 1
1088     if (CreatePipe(&p_pipe_ph, &p_child_ph, &sa, 0) == FALSE)
1089         return FALSE;
1090 
1091     Pipes[i].tid = _beginthread(PipeThread,
1092                                 FAKE_BEGINTHREAD_NULL
1093                                 16384, &Pipes[i]);
1094 #else
1095 
1096     //** Create the named pipe, and handle the SERVER (edit)'s end...
1097     sprintf(pipename, "\\\\.\\pipe\\fte%d\\child%d", getpid(), PCount);
1098     p_pipe_ph = CreateNamedPipe(pipename,
1099                                 PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
1100                                 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
1101                                 1,                      // nMaxInstances,
1102                                 0, PIPEBUF_SZ,
1103                                 1000,
1104                                 0);
1105     if(p_pipe_ph == INVALID_HANDLE_VALUE)
1106         return FALSE;
1107     PCount++;
1108 
1109     /*
1110      *      Client side: get a connection to the server's pipe. Do this before the
1111      *      call to ConnectNamedPipe() to prevent it from blocking.
1112      */
1113 
1114 #if 1
1115     p_child_ph      = CreateFile(pipename, GENERIC_WRITE, 0, &sa,
1116                                  OPEN_EXISTING, 0, 0);
1117 #else
1118     p_child_ph      = CreateFile("_test", GENERIC_WRITE|GENERIC_READ, 0, &sa,
1119                                  CREATE_ALWAYS, 0, 0);
1120 #endif
1121     if(p_child_ph == INVALID_HANDLE_VALUE)
1122         dbm("CreateFile(client_side_pipe) has failed");
1123     else
1124     {
1125         //** Server side: aquire connection..
1126         ok      = TRUE;
1127         if(! ConnectNamedPipe(p_pipe_ph, 0))    // Get connect;
1128         {
1129             if(GetLastError() != ERROR_PIPE_CONNECTED)
1130                 ok = FALSE;
1131         }
1132 
1133         //** Connect worked?
1134         if(!ok)
1135             dbm("ConnectNmPipe() has failed");
1136         else
1137             return TRUE;                            // All opened & ready for action!
1138 
1139         //** Something went wrong.
1140         CloseHandle(p_child_ph);                // Close child: was inh.
1141         DisconnectNamedPipe(p_pipe_ph); // Force disconnection of client (-)
1142         CloseHandle(p_child_ph);
1143     }
1144     CloseHandle(p_pipe_ph);
1145 #endif
1146     return FALSE;                                                                   // Something has failed.
1147 }
1148 
1149 
1150 void GPipe::releasePipe() /*FOLD00*/
1151 {
1152     /*
1153      *      releasePipe() releases all that createPipe() allocates. It's usually
1154      *      called when an error causes the process to abort.
1155      */
1156     if(p_child_ph != INVALID_HANDLE_VALUE)
1157     {
1158         CloseHandle(p_child_ph);
1159         p_child_ph      = INVALID_HANDLE_VALUE;
1160     }
1161 
1162     if(p_pipe_ph != 0)
1163     {
1164         //DisconnectNamedPipe(p_pipe_ph);
1165         CloseHandle(p_pipe_ph);
1166         p_pipe_ph = INVALID_HANDLE_VALUE;
1167     }
1168 }
1169 
1170 
1171 int GPipe::runCommand() /*FOLD00*/
1172 {
1173     /*
1174      *      runCommand() takes the child pipe, dups it onto stdout and stderr while
1175      *      saving their old assignments, then it spawns
1176      */
1177     int                         ok;
1178     char*                       comspec, *args, tbuf[256];
1179     HANDLE                          errh;
1180     PROCESS_INFORMATION pi;
1181     STARTUPINFO             si;
1182     const char          nt4[] = "4nt.exe";
1183 
1184     ok              = FALSE;
1185     comspec = getenv("COMSPEC");
1186 
1187     /*
1188      *  BUG workaround: When using 4NT, it doesn't properly reassign stderr!
1189      *  This is a bug in 4NT, so if comspec *is* 4nt use cmd.exe instead...
1190      */
1191     if(comspec == 0) return -1;
1192     int l = strlen(comspec);
1193     if(strnicmp(comspec + (l- sizeof(nt4)+1), nt4, sizeof(nt4)-1) == 0)
1194     {
1195         //** It's 4DOS all right..
1196         args = getenv("SystemRoot");
1197         if(args== 0) return -1;
1198         strlcpy(tbuf, args, sizeof(tbuf));                 // Get to c:\winnt
1199         strlcat(tbuf, "\\system32\\cmd.exe", sizeof(tbuf));
1200         comspec = tbuf;
1201     }
1202 
1203     int argslen = strlen(comspec) + strlen(p_command) + 120;
1204     args    = (char *)malloc(argslen);
1205     if(args == 0)
1206         dbm("malloc() failed for command line..");
1207     else
1208     {
1209         //** Form a command line for the process;
1210         strlcpy(args, comspec, argslen);
1211         strlcat(args, " /c ", argslen);
1212         strlcat(args, p_command, argslen);
1213 
1214         //** Dup the child handle to get separate handles for stdout and err,
1215         if (DuplicateHandle(GetCurrentProcess(), p_child_ph, // Source,
1216                            GetCurrentProcess(), &errh,          // Destination,
1217                            0, True,                                             // Same access, inheritable
1218                            DUPLICATE_SAME_ACCESS));
1219 
1220         {
1221             /* Set up members of STARTUPINFO structure. */
1222             memset(&si, 0, sizeof(si));
1223             si.cb = sizeof(STARTUPINFO);
1224             si.lpReserved = NULL;
1225             si.lpReserved2 = NULL;
1226             si.cbReserved2 = 0;
1227             si.lpDesktop = NULL;
1228 /*            si.dwFlags = STARTF_USESTDHANDLES;
1229 #if 1
1230             si.hStdOutput = p_child_ph;
1231             si.hStdError    = errh;
1232             si.hStdInput    = INVALID_HANDLE_VALUE;
1233 #else
1234             si.hStdOutput = errh;
1235             si.hStdError    = p_child_ph;
1236             si.hStdInput    = INVALID_HANDLE_VALUE;
1237 #endif*/
1238             if(CreateProcess(NULL, args, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
1239             {
1240                 ok      = TRUE;
1241                 CloseHandle(pi.hThread);        // Thread handle not needed
1242                 p_proc_h        = pi.hProcess;  // Return process handle (to get RC)
1243             }
1244             CloseHandle(errh);                                      // Close error handle,
1245         }
1246         else
1247             dbm("DupHandle for stderr failed.");
1248 
1249         free(args);                                     // Release command line.
1250     }
1251 
1252     //        SetConsoleMode(horgout, ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
1253 
1254     //** And last but not least: close the child handle.
1255     CloseHandle(p_child_ph);
1256     p_child_ph = INVALID_HANDLE_VALUE;
1257     return ok;
1258 }
1259 
1260 
1261 void GPipe::closeProc() /*FOLD00*/
1262 {
1263     /*
1264      *      closeProc() gets called when a read fails. It assumes the process has
1265      *      ended, retrieves the process return code, then it closes all handles.
1266      *      The state is set to p_completed.
1267      */
1268     DWORD   ec;
1269 
1270     dbg("[closeProc] ");
1271 
1272     if(! GetExitCodeProcess(p_proc_h, &ec)) ec = 0xabcd;
1273     p_retcode       = ec;                                           // Save return code of process,
1274     if(p_proc_h != INVALID_HANDLE_VALUE)    // Close process,
1275     {
1276         CloseHandle(p_proc_h);
1277         p_proc_h = INVALID_HANDLE_VALUE;
1278     }
1279 
1280     //** Close the main pipe,
1281     if(p_pipe_ph != INVALID_HANDLE_VALUE)
1282     {
1283         CloseHandle(p_pipe_ph);
1284         p_pipe_ph = INVALID_HANDLE_VALUE;
1285     }
1286     p_completed = TRUE;
1287     p_has_data      = TRUE;
1288 }
1289 
1290 
1291 int GPipe::startRead() /*FOLD00*/
1292 {
1293     /*
1294      *      postRead() checks if an overlapped READ needs to be posted by checking
1295      *      the io_posted flag. If that's clear and no termination or closed flag
1296      *      is set then a new overlapped I/O request is issued.
1297      */
1298     p_has_data      = FALSE;
1299     dbg("[postRead ");
1300     if(p_io_posted || p_completed)
1301         dbg("no action: %s] ", p_io_posted ? "posted" : "complete");
1302     else
1303     {
1304         p_ovl.hEvent    = p_data_evh;           // Signal this when done,
1305         if(!ReadFile(p_pipe_ph, p_buffer, p_buflen, &p_read_len, NULL))
1306         {
1307             DWORD   ec = GetLastError();
1308             if(ec != ERROR_IO_PENDING)
1309             {
1310                 //** Something's wrong. Treat as closed pipe for now.
1311                 closeProc();                            // Close pipe, complete stuff...
1312                 dbg("postfail] ");
1313                 return FALSE;                           // And return failure.
1314             }
1315         }
1316         p_io_posted = TRUE;                             // Handle pending ioresult.
1317         dbg("posted] ");
1318     }
1319     return TRUE;
1320 }
1321 
1322 
1323 int GPipe::open(char* command, EModel* notify) /*FOLD00*/
1324 {
1325     memset(&p_ovl, 0, sizeof(p_ovl));               // Clear overlapped,
1326     p_bufused       = 0;
1327     p_bufpos        = 0;
1328     p_io_posted = FALSE;
1329     p_has_data      = FALSE;
1330     p_completed = FALSE;                                    // T if client closed.
1331     p_doterm        = FALSE;
1332     p_buflen        = PIPE_BUFLEN;
1333     p_notify        = notify;
1334     p_doterm        = FALSE;
1335 
1336     p_pipe_ph       = INVALID_HANDLE_VALUE;
1337     p_child_ph      = INVALID_HANDLE_VALUE;
1338     if( (p_command = strdup(command)) == 0)
1339         return -1;
1340 
1341     //** Allocate the read buffer;
1342     if ((p_buffer = (char*) malloc(p_buflen)) != 0) {
1343         if ((pipeDataRead = CreateEvent(0, 1, 0, 0)) == 0) {
1344             dbm("CreateEvent(data_evh) failed.");
1345             goto fail;
1346         }
1347         if ((pipeStartRead = CreateEvent(0, 1, 0, 0)) == 0) {
1348             dbm("CreateEvent(data_evh) failed.");
1349             goto fail;
1350         }
1351         if ((pipeMutex = CreateMutex(NULL, FALSE, NULL)) == NULL) {
1352             dbm("Failed pipe mutex");
1353             goto fail;
1354         }
1355 
1356 
1357         else
1358         {
1359             if(createPipe())                                // Create server & client pipe.
1360             {
1361                 if(! postRead())
1362                     dbm("postRead() initial failed.");
1363                 else
1364                 {
1365                     if(runCommand())
1366                     {
1367                         p_used  = TRUE;
1368                         return p_id;
1369                     }
1370                 }
1371                 releasePipe();                          // Release pipes,
1372             }
1373             CloseHandle(p_data_evh);
1374         }
1375         free(p_buffer);
1376     }
1377     free(p_command);
1378     return -1;
1379 }
1380 
1381 
1382 int GPipe::close() /*FOLD00*/
1383 {
1384     /*
1385      *      close() disconnects from the spawned task, closes the pipe and releases
1386      *      all stuff.
1387      */
1388     if(! p_used) return -1;
1389     if(! p_completed)                                               // Overlapped I/O not complete yet?
1390     {
1391         //** We *must* wait till the overlapped I/O completes,
1392         if(p_io_posted)
1393         {
1394             GetOverlappedResult(p_pipe_ph, &p_ovl, &p_read_len, TRUE);
1395             p_io_posted = FALSE;
1396         }
1397     }
1398     p_completed= TRUE;
1399 
1400     //** Now close all that might be pending,
1401     free(p_buffer);
1402     free(p_command);
1403 
1404     releasePipe();                                          // Close all pipe stuff,
1405     if(p_proc_h != INVALID_HANDLE_VALUE)
1406     {
1407         CloseHandle(p_proc_h);
1408         p_proc_h = INVALID_HANDLE_VALUE;
1409     }
1410 
1411     CloseHandle(pipeStartRead);
1412     CloseHandle(pipeDataRead);
1413     CloseHandle(pipeMutex);
1414 
1415     p_used = FALSE;
1416     return p_retcode;
1417 }
1418 
1419 
1420 int GPipe::read(void *buffer, int len) /*FOLD00*/
1421 {
1422     /*
1423      *      read() is called to get the current data from the pipe. It takes the
1424      *      #bytes read and returns them. It returns data till the buffer is
1425      *      exhausted. If the process is completed it returns -1; else it returns
1426      *      the #bytes read. It returns 0 if the buffer's empty.
1427      */
1428     dbg("[read ");
1429     if(p_has_data)
1430     {
1431         if(p_bufpos < p_read_len)                               // Data in buffer?
1432         {
1433             unsigned        l;
1434 
1435             l       = p_read_len - p_bufpos;                // Try to output all,
1436             if(l > len) l = len;
1437             memcpy(buffer, p_buffer+p_bufpos, l);   // Copy data from the buffer,
1438             p_bufpos        += l;
1439             dbg("%u data] ", l);
1440             return l;                                                       // Data returned,
1441         }
1442 
1443         //** There's nothing left in the buffer. Is the task complete?
1444         if(p_completed)
1445         {
1446             dbg("no data, complete] ");
1447             return -1;
1448         }
1449 
1450         if(! postRead())
1451         {
1452             dbg("post failed-> complete] ");
1453             return -1;
1454         }
1455 
1456         dbg("nodata, post] ");
1457         return 0;
1458     }
1459     else if(p_completed)
1460     {
1461         dbg("completed] ");
1462         return -1;
1463     }
1464 
1465     dbg("nothing] ");
1466     return 0;
1467 }
1468 
1469 
1470 int GPipe::getEvent(TEvent* event) /*FOLD00*/
1471 {
1472     dbg("[getpipeevent: ");
1473     event->What = evNone;
1474 
1475     if(! p_used || p_notify == 0) return 0;         // No data.
1476     if(! handlePost()) return 0;                            // Again: no data,
1477     //** This pipe has data!
1478     event->What             = evNotify;
1479     event->Msg.View         = 0;
1480     event->Msg.Model        = p_notify;
1481     event->Msg.Command      = cmPipeRead;
1482     event->Msg.Param1       = p_id;
1483     dbg("ok] ");
1484     return 1;
1485 }
1486 
1487 
1488 /*
1489  *      NT Pipe handler - overview
1490  *      ==========================
1491  *      The NT pipe handler uses overlapped I/O to read console events.
1492  *
1493  *      OpenPipe():
1494  *      When the pipe is opened, one of the pipe structures is allocated and set
1495  *      to used. Then an event semaphore (reset_manual) is created. This semaphore
1496  *      will be signalled when data is available on the input pipe which gathers
1497  *      the spawned tasks's output.
1498  *
1499  *      Then a pipe is created, opened for the client side and stdout and stderr
1500  *      are redirected therein. After that the client task is spawned.
1501  *
1502  *      If the spawn succeeds an overlapped READ is posted for the pipe; then the
1503  *      OpenPipe function returns.
1504  *
1505  *      ConGetEvent():
1506  *      The ConGetEvent() handler does a WaitForMultipleObjects() on the console
1507  *      handle and all pipe handles currently active. If a pipe has data the
1508  *      overlapped result is gotten, and the output is sent to the message window.
1509  *      Then, if the thread didn't finish, a new overlapped read is posted.
1510  *
1511  *
1512  */
1513 int GUI::OpenPipe(const char *Command, EModel *notify) /*FOLD00*/
1514 {
1515     GPipe*  gp;
1516 
1517     if( (gp = GPipe::getFreePipe()) == 0)
1518         return -1;                                                      // Out of pipes.
1519     return gp->open(Command, notify);               // And ask the pipe to init.
1520 }
1521 
1522 
1523 int GUI::SetPipeView(int id, EModel *notify) /*FOLD00*/
1524 {
1525     GPipe*  p;
1526 
1527     if( (p = GPipe::getPipe(id)) == 0) return -1;
1528     p->lock();
1529     p->p_notify = notify;
1530     p->unlock();
1531     return 0;
1532 }
1533 
1534 
1535 ssize_t GUI::ReadPipe(int id, void *buffer, size_t len) /*FOLD00*/
1536 {
1537     //int     l;
1538     GPipe*  p;
1539 
1540     if( (p = GPipe::getPipe(id)) == 0) return -1;
1541     return p->read(buffer, len);
1542 }
1543 
1544 
1545 int GUI::ClosePipe(int id) /*FOLD00*/
1546 {
1547     GPipe*  p;
1548 
1549     if( (p = GPipe::getPipe(id)) == 0) return -1;
1550     return p->close();
1551 }
1552 
1553 
1554 static int GetPipeEvent(int id, TEvent *Event) /*FOLD00*/
1555 {
1556     //int     i;
1557     GPipe*  p;
1558 
1559     if( (p = GPipe::getPipe(id)) == 0) return -1;
1560     return p->getEvent(Event);
1561 }
1562 
1563 #else
1564 
1565 #define MAX_PIPES 4
1566 #define PIPE_BUFLEN 4096
1567 
1568 typedef struct {
1569     int used;
1570     int id;
1571     int reading, stopped;
1572     HANDLE Thread;
1573     HANDLE Access;
1574     HANDLE ResumeRead;
1575     HANDLE NewData;
1576     char *buffer;
1577     int buflen;
1578     int bufused;
1579     int bufpos;
1580     EModel *notify;
1581     char *Command;
1582     DWORD RetCode;
1583     int DoTerm;
1584 } GPipe;
1585 
1586 static GPipe Pipes[MAX_PIPES] = {
1587     { 0 }, { 0 }, { 0 }, { 0 }
1588 };
1589 
CreatePipeChild(HANDLE & child,HANDLE & hPipe,char * Command)1590 static int CreatePipeChild(HANDLE &child, HANDLE &hPipe, char *Command) {
1591     //static int PCount = 0;
1592     int arglen = 0;
1593     HANDLE hChildPipe;
1594     BOOL rc;
1595 
1596     SECURITY_ATTRIBUTES sa;
1597 
1598     sa.nLength = sizeof(sa);                        // Security descriptor for INHERIT.
1599     sa.lpSecurityDescriptor = 0;
1600     sa.bInheritHandle       = 1;
1601 
1602     rc = CreatePipe(&hPipe, &hChildPipe, &sa, 0);
1603     if (rc != TRUE)
1604         return -1;
1605 
1606     int                         ok;
1607     char*                       comspec, *args, tbuf[256];
1608     //HANDLE                          errh;
1609     PROCESS_INFORMATION pi;
1610     STARTUPINFO             si;
1611     HANDLE hNul;
1612     const char          nt4[] = "4nt.exe";
1613 
1614     ok              = FALSE;
1615     comspec = getenv("COMSPEC");
1616 
1617     /*
1618      *  BUG workaround: When using 4NT, it doesn't properly reassign stderr!
1619      *  This is a bug in 4NT, so if comspec *is* 4nt use cmd.exe instead...
1620      */
1621     if (comspec == 0)
1622         return -1;
1623     int l = strlen(comspec);
1624     if (strnicmp(comspec + (l- sizeof(nt4)+1), nt4, sizeof(nt4)-1) == 0)
1625     {
1626         //** It's 4DOS all right..
1627         args = getenv("SystemRoot");
1628         if(args== 0) return -1;
1629         strlcpy(tbuf, args, sizeof(tbuf));                 // Get to c:\winnt
1630         strlcat(tbuf, "\\system32\\cmd.exe", sizeof(tbuf));
1631         comspec = tbuf;
1632     }
1633 
1634     int argslen = strlen(comspec) + strlen(Command) + 120;
1635     args    = (char *)malloc(argslen);
1636     if(args == 0)
1637         dbm("malloc() failed for command line..");
1638     else
1639     {
1640         //** Form a command line for the process;
1641         strlcpy(args, comspec, argslen);
1642         strlcat(args, " /c ", argslen);
1643         strlcat(args, Command, argslen);
1644 
1645         //** Dup the child handle to get separate handles for stdout and err,
1646         /*if (DuplicateHandle(GetCurrentProcess(), hChildPipe,
1647                            GetCurrentProcess(), &errh,
1648                            0, True, DUPLICATE_SAME_ACCESS))*/
1649         //fprintf(stderr, "open NUL\n");
1650 
1651         hNul = CreateFile("NUL",
1652                           GENERIC_READ | GENERIC_WRITE,
1653                           FILE_SHARE_READ | FILE_SHARE_WRITE,
1654                           NULL, OPEN_EXISTING,
1655                           0,
1656                           NULL);
1657         //fprintf(stderr, "starting %s\n", args);
1658 
1659         if (hNul != NULL) {
1660             /* Set up members of STARTUPINFO structure. */
1661             memset(&si, 0, sizeof(si));
1662             si.cb = sizeof(STARTUPINFO);
1663             si.lpReserved = NULL;
1664             si.lpReserved2 = NULL;
1665             si.cbReserved2 = 0;
1666             si.lpDesktop = NULL;
1667             si.dwFlags = STARTF_USESTDHANDLES;
1668 #if 1
1669             si.hStdOutput = hChildPipe;
1670             si.hStdError    = hChildPipe;
1671             si.hStdInput    = hNul;//INVALID_HANDLE_VALUE;
1672 #else
1673             si.hStdOutput = errh;
1674             si.hStdError    = hChildPipe;
1675             si.hStdInput    = INVALID_HANDLE_VALUE;
1676 #endif
1677             if (CreateProcess(NULL, args, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) == TRUE)
1678             {
1679                 ok      = TRUE;
1680                 CloseHandle(pi.hThread);        // Thread handle not needed
1681                 //fprintf(stderr, "create process success\n");
1682                 child = pi.hProcess;  // Return process handle (to get RC)
1683             } else
1684                 //fprintf(stderr, "create process failed %d\n" + GetLastError());
1685             CloseHandle(hNul);                                      // Close error handle,
1686         }
1687         else
1688             dbm("DupHandle for stderr failed.");
1689 
1690         free(args);
1691     }
1692 
1693     CloseHandle(hChildPipe);
1694     return 0;
1695 }
1696 
PipeThread(void * p)1697 static DWORD __stdcall PipeThread(void *p) {
1698     GPipe *pipe = (GPipe *)p;
1699     BOOL rc;
1700     DWORD used;
1701     HANDLE child;
1702     HANDLE hfPipe;
1703 
1704     rc = CreatePipeChild(child, hfPipe, pipe->Command);
1705 
1706     if (rc != 0) {
1707         //fprintf(stderr, "Failed CreatePipeChild\n");
1708         WaitForSingleObject(pipe->Access, INFINITE);
1709         pipe->reading = 0;
1710         SetEvent(pipe->NewData);
1711         ReleaseMutex(pipe->Access);
1712         return 0;
1713     }
1714 
1715     //fprintf(stderr, "Pipe: Begin: %d %s\n", pipe->id, pipe->Command);
1716     while (1) {
1717         //fprintf(stderr, "Waiting on pipe\n");
1718         rc = ReadFile(hfPipe, pipe->buffer, pipe->buflen, &used, NULL);
1719         if (rc != TRUE) {
1720             //fprintf(stderr, "ReadFile failed %d %ld", GetLastError(), used);
1721             used = 0;
1722         }
1723 
1724         WaitForSingleObject(pipe->Access, INFINITE);
1725         //fprintf(stderr, "Waiting on mutex\n");
1726         pipe->bufused = used;
1727         //fprintf(stderr, "Pipe: fread: %d %d\n", pipe->id, pipe->bufused);
1728         ResetEvent(pipe->ResumeRead);
1729         if (pipe->bufused == 0)
1730             break;
1731         if (pipe->notify) {
1732             SetEvent(pipe->NewData);
1733             pipe->stopped = 0;
1734         }
1735         ReleaseMutex(pipe->Access);
1736         if (pipe->DoTerm)
1737             break;
1738         //fprintf(stderr, "Waiting on sem\n");
1739         WaitForSingleObject(pipe->ResumeRead, INFINITE);
1740         //fprintf(stderr, "Read: Released mutex\n");
1741         if (pipe->DoTerm)
1742             break;
1743     }
1744     CloseHandle(hfPipe);
1745     //fprintf(stderr, "Pipe: pClose: %d\n", pipe->id);
1746     rc = WaitForSingleObject(child, INFINITE);
1747     GetExitCodeProcess(child, &pipe->RetCode);
1748     CloseHandle(child);
1749     pipe->reading = 0;
1750     SetEvent(pipe->NewData);
1751     ReleaseMutex(pipe->Access);
1752     //fprintf(stderr, "Read: Released mutex\n");
1753     return 1;
1754 }
1755 
OpenPipe(const char * Command,EModel * notify)1756 int GUI::OpenPipe(const char *Command, EModel *notify) {
1757     int i;
1758 
1759     for (i = 0; i < MAX_PIPES; i++) {
1760         if (Pipes[i].used == 0) {
1761             Pipes[i].reading = 1;
1762             Pipes[i].stopped = 1;
1763             Pipes[i].id = i;
1764             Pipes[i].bufused = 0;
1765             Pipes[i].bufpos = 0;
1766             Pipes[i].buflen = PIPE_BUFLEN;
1767             Pipes[i].Command = strdup(Command);
1768             Pipes[i].notify = notify;
1769             Pipes[i].DoTerm = 0;
1770             if ((Pipes[i].buffer = (char *)malloc(PIPE_BUFLEN)) == 0)
1771                 return -1;
1772 
1773             if ((Pipes[i].Access = CreateMutex(NULL, FALSE, NULL)) == NULL) {
1774                 free(Pipes[i].Command);
1775                 free(Pipes[i].buffer);
1776                 return -1;
1777             }
1778 
1779             if ((Pipes[i].ResumeRead = CreateEvent(0, 1, 0, 0)) == NULL) {
1780                 free(Pipes[i].Command);
1781                 free(Pipes[i].buffer);
1782                 CloseHandle(Pipes[i].Access);
1783                 return -1;
1784             }
1785 
1786             if ((Pipes[i].NewData = CreateEvent(0, 1, 0, 0)) == NULL) {
1787                 free(Pipes[i].Command);
1788                 free(Pipes[i].buffer);
1789                 CloseHandle(Pipes[i].ResumeRead);
1790                 CloseHandle(Pipes[i].Access);
1791                 return -1;
1792             }
1793 
1794             DWORD tid;
1795 
1796             if ((Pipes[i].Thread = CreateThread(NULL, 32768,
1797                                                 &PipeThread, &Pipes[i],
1798                                                 0, &tid)) == NULL)
1799             {
1800                 free(Pipes[i].Command);
1801                 free(Pipes[i].buffer);
1802                 CloseHandle(Pipes[i].ResumeRead);
1803                 CloseHandle(Pipes[i].Access);
1804                 CloseHandle(Pipes[i].NewData);
1805                 return -1;
1806             }
1807             Pipes[i].used = 1;
1808             //fprintf(stderr, "Pipe Open: %d\n", i);
1809             return i;
1810         }
1811     }
1812     return -1;
1813 }
1814 
SetPipeView(int id,EModel * notify)1815 int GUI::SetPipeView(int id, EModel *notify) {
1816     if (id < 0 || id > MAX_PIPES)
1817         return 0;
1818     if (Pipes[id].used == 0)
1819         return 0;
1820     WaitForSingleObject(Pipes[id].Access, INFINITE);
1821     //fprintf(stderr, "Pipe View: %d %08X\n", id, notify);
1822     Pipes[id].notify = notify;
1823     ReleaseMutex(Pipes[id].Access);
1824     return 1;
1825 }
1826 
ReadPipe(int id,void * buffer,size_t len)1827 ssize_t GUI::ReadPipe(int id, void *buffer, size_t len) {
1828     ssize_t l;
1829     //ULONG ulPostCount;
1830 
1831     if (id < 0 || id > MAX_PIPES || Pipes[id].used == 0)
1832         return -1;
1833 
1834     //fprintf(stderr, "Read: Waiting on mutex\n");
1835     //ConContinue();
1836     WaitForSingleObject(Pipes[id].Access, INFINITE);
1837     //fprintf(stderr, "Pipe Read: Get %d %d\n", id, len);
1838     if (Pipes[id].bufused - Pipes[id].bufpos > 0) {
1839         l = len;
1840         if (l > Pipes[id].bufused - Pipes[id].bufpos) {
1841             l = Pipes[id].bufused - Pipes[id].bufpos;
1842         }
1843         memcpy(buffer,
1844                Pipes[id].buffer + Pipes[id].bufpos,
1845                l);
1846         Pipes[id].bufpos += l;
1847         if (Pipes[id].bufpos == Pipes[id].bufused) {
1848             Pipes[id].bufused = 0;
1849             Pipes[id].bufpos = 0;
1850             //fprintf(stderr, "Pipe Resume Read: %d\n", id);
1851             Pipes[id].stopped = 1;
1852             //fprintf(stderr, "Read: posting sem\n");
1853             SetEvent(Pipes[id].ResumeRead);
1854         }
1855     } else if (Pipes[id].reading == 0)
1856         l = -1;
1857     else {
1858         l = 0;
1859 //        DosBeep(200, 200);
1860     }
1861     //fprintf(stderr, "Pipe Read: Got %d %d\n", id, l);
1862     ReleaseMutex(Pipes[id].Access);
1863     //fprintf(stderr, "Read: Released mutex\n");
1864     return l;
1865 }
1866 
ClosePipe(int id)1867 int GUI::ClosePipe(int id) {
1868     if (id < 0 || id > MAX_PIPES)
1869         return 0;
1870     if (Pipes[id].used == 0)
1871         return 0;
1872     if (Pipes[id].reading == 1) {
1873         Pipes[id].DoTerm = 1;
1874         SetEvent(Pipes[id].ResumeRead);
1875         WaitForSingleObject(&Pipes[id].Thread, INFINITE);
1876     }
1877     free(Pipes[id].buffer);
1878     free(Pipes[id].Command);
1879     CloseHandle(Pipes[id].NewData);
1880     CloseHandle(Pipes[id].ResumeRead);
1881     CloseHandle(Pipes[id].Access);
1882     CloseHandle(Pipes[id].Thread);
1883     //fprintf(stderr, "Pipe Close: %d\n", id);
1884     Pipes[id].used = 0;
1885     //ConContinue();
1886     return (Pipes[id].RetCode == 0);
1887 }
1888 
GetPipeEvent(int i,TEvent * Event)1889 int GetPipeEvent(int i, TEvent *Event) {
1890     Event->What = evNone;
1891     if (Pipes[i].used == 0) return 0;
1892     if (Pipes[i].notify == 0) return 0;
1893     ResetEvent(Pipes[i].NewData);
1894     //fprintf(stderr, "Pipe New Data: %d\n", i);
1895     Event->What = evNotify;
1896     Event->Msg.View = 0;
1897     Event->Msg.Model = Pipes[i].notify;
1898     Event->Msg.Command = cmPipeRead;
1899     Event->Msg.Param1 = i;
1900     return 1;
1901 }
1902 
1903 #endif
1904 
ConGetEvent(TEventMask EventMask,TEvent * Event,int WaitTime,int Delete)1905 int ConGetEvent(TEventMask EventMask, TEvent *Event, int WaitTime, int Delete) /*FOLD00*/
1906 {
1907     //** Any saved events left?
1908     if (EventBuf.What != evNone) {
1909         *Event = EventBuf;
1910         if (Delete) EventBuf.What = evNone;
1911         return 1;
1912     }
1913     if (MouseEv.What != evNone) {
1914         *Event = MouseEv;
1915         if (Delete) MouseEv.What = evNone;
1916         return 1;
1917     }
1918 
1919     //** Now block and wait for a new event on the console handle and all pipes,
1920     HANDLE  o_ar[1 + MAX_PIPES];
1921     DWORD   rc;
1922     int     i, nh;
1923 
1924     EventBuf.What = evNone;
1925     Event->What = evNone;
1926 
1927     //** Fill the handle array with all active handles for pipes && console,
1928     o_ar[0] = ConIn;
1929     for(i = 0, nh = 1; i < MAX_PIPES; i++) {    // For all possible pipes
1930         if (Pipes[i].used)
1931             o_ar[nh++] = Pipes[i].NewData;
1932     }
1933 
1934     for(;;) {
1935         rc = WaitForMultipleObjects(nh, o_ar, FALSE, WaitTime);
1936         if (rc != WAIT_FAILED && (rc >= WAIT_OBJECT_0 && rc < WAIT_OBJECT_0+nh)) {
1937             i = rc - WAIT_OBJECT_0;             // Get item that signalled new data
1938 	    if (i == 0) {                       // Was console?
1939                 if (ReadConsoleEvent(Event))    // Get console,
1940                     return 1;                   // And exit if valid,
1941             } else {
1942                 GetPipeEvent(i - 1, Event);     // Read data from pipe.
1943                 return 1;
1944             }
1945         }
1946         else
1947             return 0;                          // Something's wrong!
1948     }
1949 }
1950 
1951 #include "clip.h"
1952 
GetClipText(ClipData * cd)1953 int GetClipText(ClipData *cd) {
1954     int rc = 0;
1955     cd->fLen = 0;
1956     cd->fChar = NULL;
1957     if (OpenClipboard(NULL)) {
1958         HANDLE hmem;
1959 
1960         if ((hmem = GetClipboardData(CF_TEXT)) != NULL) {
1961             LPVOID data;
1962 
1963             if ((data = GlobalLock(hmem)) != NULL) {
1964                 int len = strlen((char *)data);
1965 
1966                 cd->fChar = (char *)malloc(len);
1967                 if (cd->fChar != NULL) {
1968                     cd->fLen = len;
1969                     memcpy(cd->fChar, data, len);
1970                     rc = 1;
1971                 }
1972                 GlobalUnlock(hmem);
1973             }
1974         }
1975         CloseClipboard();
1976     }
1977     return rc;
1978 }
1979 
PutClipText(ClipData * cd)1980 int PutClipText(ClipData *cd) {
1981     int rc = 0;
1982     if (OpenClipboard(NULL)) {
1983         if (EmptyClipboard()) {
1984             HGLOBAL hmem;
1985 
1986             if ((hmem = GlobalAlloc(GMEM_MOVEABLE, cd->fLen + 1)) != NULL) {
1987                 LPVOID data;
1988 
1989                 if ((data = GlobalLock(hmem)) != NULL) {
1990                     memcpy(data, cd->fChar, cd->fLen);
1991                     ((char *)data)[cd->fLen] = 0;
1992                     GlobalUnlock(hmem);
1993                     if (SetClipboardData(CF_TEXT, hmem))
1994                         rc = 1;
1995                 }
1996             }
1997         }
1998         CloseClipboard();
1999     }
2000     return rc;
2001 }
2002