source: trunk/src/netlabs/macros/epmshell.e@ 3000

Last change on this file since 3000 was 3000, checked in by Andreas Schnellbacher, 7 years ago
  • Fixed finding of the first shell: Handle case when shell with number 1 was closed.
  • Property svn:keywords set to Date Revision Author HeadURL Id
File size: 66.5 KB
Line 
1/****************************** Module Header *******************************
2*
3* Module Name: epmshell.e
4*
5* Copyright (c) Netlabs EPM Distribution Project 2002
6*
7* $Id: epmshell.e 3000 2018-04-19 21:28:32Z aschn $
8*
9* ===========================================================================
10*
11* This file is part of the Netlabs EPM Distribution package and is free
12* software. You can redistribute it and/or modify it under the terms of the
13* GNU General Public License as published by the Free Software
14* Foundation, in version 2 as it comes in the "COPYING" file of the
15* Netlabs EPM Distribution. This library is distributed in the hope that it
16* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
17* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18* General Public License for more details.
19*
20****************************************************************************/
21
22; Todo:
23; defc Shell
24; Add an optional param <workdir> before <command>. Workdir must be fully
25; qualified or start with . or .. or \ to get recognized. Enable spaces
26; in workdir.
27
28compile if not defined( SMALL) -- If SMALL not defined, then being separately compiled
29define INCLUDING_FILE = 'FINDDEF.E'
30
31const
32include 'stdconst.e'
33EA_comment 'This defines the EPM shell buffer.'
34
35const
36 compile if not defined( NLS_LANGUAGE)
37 NLS_LANGUAGE = 'ENGLISH'
38 compile endif
39 include NLS_LANGUAGE'.e'
40
41compile endif
42
43const
44-- Specify a string to be written whenever a new EPM command shell buffer
45-- is opened. Normally a prompt command, but can be anything. If the
46-- string is one of the ones shown below, then the Enter key can be used
47-- to do a write-to-shell of the text following the prompt, and a listbox
48-- can be generated showing all the commands which were entered in the
49-- current shell buffer. If a different prompt is used, EPM won't know
50-- how to parse the line to distinguish between the prompt and the command
51-- that follows, so those features will be omitted.
52compile if not defined( EPM_SHELL_PROMPT)
53 EPM_SHELL_PROMPT = '@prompt epm: $p $g '
54; EPM_SHELL_PROMPT = '@prompt [epm: $p ] ' -- Also supported
55compile endif
56
57; ---------------------------------------------------------------------------
58; Some ShellKram macros added. See STDKEYS.E for key definitions.
59; SHELLKRAM.E was available from Joerg Tiemann's homepage some years ago:
60; http://home.foni.net/~tiemannj/epm/index.html
61; See his pages for documentation.
62
63; ---------------------------------------------------------------------------
64; This should always be used in preference to query if current file is a
65; shell. Return 0 (false) or 1 (true).
66defproc IsAShell
67 ret = 0
68 Filename = arg(1)
69
70 /*
71 if Filename = '' then
72 getfileid fid, Filename
73 else
74 Filename = .filename
75 getfileid fid -- This doesn't work during select
76 endif
77 */
78 -- Must search for Filename in the ring. At shell creation, the new shell
79 -- window may not be the active one yet, while .filename is already set.
80 if Filename = '' then
81 Filename = .filename
82 endif
83 getfileid fid, Filename
84
85 ShellNum = GetAVar( fid'.ShellNum')
86 if ShellNum <> '' then
87 ShellHandle = GetAVar( 'Shell_h'ShellNum)
88 if ShellHandle <> '' then
89 ret = 1
90 endif
91 endif
92 return ret
93
94; ---------------------------------------------------------------------------
95; Used by defproc GetMode only to determine the default mode for it.
96; Return 0 (false) or 1 (true).
97defproc IsAShellFilename
98 ret = 0
99 Filename = arg(1)
100 if Filename = '' then
101 Filename = .filename
102 endif
103 Name = StripPath( Filename)
104 if leftstr( Name, 15) = '.command_shell_' then
105 ret = 1
106 endif
107 return ret
108
109; ---------------------------------------------------------------------------
110; Reactivate a shell buffer, if no ShellHandle exists.
111; Process that at defselect rather than at defload to save file loading time.
112definit
113 'HookAdd select MaybeStartShell'
114
115; ---------------------------------------------------------------------------
116; Starts a new shell for the current file, if not already done and if Mode =
117; SHELL. Can be used to "reactivate" a shell, whose .command_shell_ output
118; was saved before and then gets reloaded.
119defc MaybeStartShell
120 universal shell_index
121
122 do once = 1 to 1
123 Mode = GetMode()
124 if Mode <> 'SHELL' then
125 leave
126 endif
127 if IsAShell() then
128 leave
129 endif
130
131 'monofont'
132 shell_index = shell_index + 1
133 ShellHandle = '????'
134 rcx = SUE_new( ShellHandle, shell_index)
135 if rcx then
136 sayerror ERROR__MSG rcx SHELL_ERROR1__MSG
137 leave
138 endif
139
140 KeyPath = '\NEPMD\User\Shell\Alias'
141 on = (QueryConfigKey( KeyPath) <> 0)
142 if on then
143 call ShellReadAliasFile()
144 endif
145
146 getfileid ShellFid
147 --sayerror 'Shell handle with number' shell_index 'created'
148 if not IsABackupFileName() then
149 .autosave = 0
150 endif
151 call SetAVar( 'Shell_f'shell_index, ShellFid)
152 call SetAVar( 'Shell_h'shell_index, ShellHandle)
153 call SetAVar( ShellFid'.ShellNum', shell_index)
154 call SetAVar( ShellFid'.StartLineNum', .last)
155
156 PromptCmd = EPM_SHELL_PROMPT
157 if PromptCmd <> '' then
158 'Shell_Write' shell_index PromptCmd
159 endif
160
161 KeyPath = '\NEPMD\User\Shell\InitCmd'
162 InitCmd = QueryConfigKey( KeyPath)
163 if InitCmd <> '' then
164 'Shell_Write' shell_index InitCmd
165 endif
166
167 -- Determine previous work dir
168 call psave_pos( save_pos)
169 display -3
170 .lineg = .last
171 endline
172 fFound = (ShellGotoNextPrompt( 'P') = 0)
173 Dir = ''
174 Cmd = ''
175 if fFound then
176 call ShellParsePromptLine( Dir, Cmd)
177 else
178 call prestore_pos( save_pos)
179 endif
180 display 3
181 if Dir <> '' then
182 CdCmd = 'cdd' Dir
183 'shell_write' shell_index CdCmd
184 endif
185 enddo
186
187; ---------------------------------------------------------------------------
188const
189-- Maybe make that behavior configurable via menu?
190compile if not defined( SHELL_SWITCH_BUFFERS)
191 SHELL_SWITCH_BUFFERS = 1
192compile endif
193
194; Starts a new shell object or switches between shell buffers and a
195; (starting) non-shell buffer. If args were specified, then the last shell
196; is reused and the args are executed in that shell. If the optional keyword
197; "new" is specified as the first word of the args string, a new shell
198; buffer is created.
199;
200; Syntax: shell [new] [<command>]
201;
202; shell_index is the number of the last created shell, <shellnum>.
203; The array var 'Shell_f'<shellnum> holds the fileid,
204; 'Shell_h'<shellnum> the handle.
205
206defc Shell
207 universal shell_index
208 universal ring_enabled
209 universal ShellStartFid
210
211 if not ring_enabled then
212 'ring_toggle'
213 endif
214
215 args = strip( arg(1))
216
217 -- fSwitch activates shell1 -> shell2 -> startfile -> shell 1 -> ...
218 fSwitch = 0
219 if args = '' then
220 if SHELL_SWITCH_BUFFERS = 1 then
221 fSwitch = 1
222 endif
223 endif
224
225 fCreateNew = 0
226 wp = wordpos( 'NEW', upcase( args))
227 if wp = 1 then
228 fCreateNew = 1
229 args = delword( args, wp, 1)
230 endif
231
232 Cmd = strip( args)
233
234 getfileid CurFid
235 ShellFid = ''
236 ShellNum = ''
237
238 fShellStartFidInRing = 0
239 -- Need to check if not empty
240 if ShellStartFid <> '' then
241 if validatefileid( ShellStartFid) then
242 fShellStartFidInRing = 1
243 endif
244 endif
245
246 if not fCreateNew then
247 if IsAShell() then
248 if fSwitch then
249 ShellNum = GetAVar( CurFid'.ShellNum') + 1
250
251 do forever
252 if ShellNum > shell_index then
253 if fShellStartFidInRing then
254 ShellFid = ShellStartFid
255 else
256 -- if only 1 buffer in the ring, create a new one
257 -- if only shell buffers in the ring, switch to first/next shell buffer
258 -- else switch to next non-shell buffer
259 f = 0
260
261 do forever
262 prevfile
263 getfileid Fid
264 f = f + 1
265 if Fid = CurFid then
266 if f = 1 then
267 fCreateNew = 1
268 else
269 ShellNum = 1
270 endif
271 leave
272 endif
273 if not IsAShell() then
274 ShellFid = Fid
275 leave
276 endif
277 enddo
278
279 endif
280 if fCreateNew or ShellFid <> '' then
281 leave
282 endif
283 endif
284 ShellFid = GetAVar( 'Shell_f'ShellNum)
285 if validatefileid( ShellFid) then
286 leave
287 endif
288 ShellNum = ShellNum + 1
289 enddo
290
291 else
292 ShellFid = CurFid
293 ShellNum = GetAVar( ShellFid'.ShellNum')
294 endif
295
296 else
297 ShellStartFid = CurFid
298 if fSwitch then
299 -- Find first shell buffer, handle already closed shells
300 do n = 1 to shell_index
301 if GetAVar( 'Shell_f'n) <> '' then
302 ShellNum = n
303 leave
304 endif
305 enddo
306 else
307 -- Find last shell buffer to execute a command there
308 ShellNum = shell_index
309 endif
310
311 do forever
312 if ShellNum < 1 then
313 fCreateNew = 1
314 leave
315 endif
316 ShellFid = GetAVar( 'Shell_f'ShellNum)
317 if ShellFid = '' then
318 fCreateNew = 1
319 leave
320 elseif validatefileid( ShellFid) then
321 -- Found
322 leave
323 endif
324 -- Try next or previous shell num
325 if fSwitch then
326 -- Args specified, find the next shell
327 ShellNum = ShellNum + 1
328 else
329 -- No args specified, find the previous shell
330 ShellNum = ShellNum - 1
331 endif
332 enddo
333
334 endif
335 endif
336
337 if fCreateNew then
338 KeyPath = '\NEPMD\User\Shell\Alias'
339 on = (QueryConfigKey( KeyPath) <> 0)
340 if on then
341 call ShellReadAliasFile()
342 endif
343
344 shell_index = shell_index + 1
345 ShellNum = shell_index
346 ShellHandle = '????'
347 retval = SUE_new( ShellHandle, ShellNum)
348 if retval then
349 sayerror ERROR__MSG retval SHELL_ERROR1__MSG
350 else
351 'xcom e /c .command_shell_'ShellNum
352 if rc <> sayerror( 'New file') then
353 sayerror ERROR__MSG rc SHELL_ERROR2__MSG
354 stop
355 endif
356 getfileid ShellFid
357 .filename = '.command_shell_'ShellNum
358 if not IsABackupFileName() then
359 .autosave = 0
360 endif
361 call SetAVar( 'Shell_f'ShellNum, ShellFid)
362 call SetAVar( 'Shell_h'ShellNum, ShellHandle)
363 call SetAVar( ShellFid'.ShellNum', ShellNum)
364 call SetAVar( ShellFid'.StartLineNum', 1)
365 'postme monofont'
366
367 PromptCmd = EPM_SHELL_PROMPT
368 if PromptCmd <> '' then
369 'Shell_Write' ShellNum PromptCmd
370 endif
371
372 KeyPath = '\NEPMD\User\Shell\InitCmd'
373 InitCmd = QueryConfigKey( KeyPath)
374 if InitCmd <> '' then
375 'Shell_Write' ShellNum InitCmd
376 endif
377 endif
378 --dprintf( 'shellhandle = 0x'ltoa( ShellHandle, 16)', newObject.retval = 'retval)
379 else
380 activatefile ShellFid
381 endif
382
383 if Cmd then
384 ShellAppWaiting = GetAVar( ShellFid'.ShellAppWaiting')
385 if words( ShellAppWaiting) < 2 then
386 'Shell_Write' ShellNum Cmd
387 else
388 sayerror 'Command canceled. Shell is waiting for user input.'
389 endif
390 endif
391
392; ---------------------------------------------------------------------------
393defc ShellSetInitCmd
394 KeyPath = '\NEPMD\User\Shell\InitCmd'
395 WriteConfigKey( KeyPath, strip( arg(1)))
396
397; ---------------------------------------------------------------------------
398defc ShellInitCmdDlg
399 KeyPath = '\NEPMD\User\Shell\InitCmd'
400 InitCmd = QueryConfigKey( KeyPath)
401
402 Title = 'Init command for shell windows'
403 Text = 'Enter new value:'
404 Text = Text''copies( ' ', max( 100 - length(Text), 0))
405 Entry = InitCmd
406
407 parse value entrybox( Title,
408 '',
409 Entry,
410 0,
411 240,
412 atoi(1) || atoi(0) || atol(0) ||
413 Text) with button 2 NewInitCmd \0
414 NewInitCmd = strip( NewInitCmd)
415 if button = \1 & NewInitCmd <> InitCmd then
416 WriteConfigKey( KeyPath, NewInitCmd)
417 endif
418
419; ---------------------------------------------------------------------------
420; Destroys a shell object.
421; Syntax: shell_kill [<shellnum>]
422; Bug: On closing an EPM window with a shell that has another command
423; processor called (like rexxtry), the CMD.EXE process is not closed.
424; It causes 100% CPU usage instead. Apparently Shell_Kill or SUE_free
425; is not called on closing EPM. In order to fix that, a PM hook has to
426; be installed that filters WM_QUIT messages.
427defc Shell_Kill, ShellKill
428 parse arg ShellNum .
429 if ShellNum = '' & IsAShell() then
430 getfileid ShellFid
431 ShellNum = GetAVar( ShellFid'.ShellNum')
432 endif
433 if ShellNum = '' then
434 sayerror NOT_IN_SHELL__MSG
435 return
436 endif
437 ShellFid = GetAVar( 'Shell_f'ShellNum)
438 ShellHandle = GetAVar( 'Shell_h'ShellNum)
439 if ShellHandle <> '' then
440 retval = SUE_free( ShellHandle)
441 if retval then
442 sayerror ERROR__MSG retval SHELL_ERROR3__MSG
443 endif
444 call SetAVar( 'Shell_h'ShellNum, '')
445 endif
446 if ShellFid <> '' then
447 getfileid curfid
448 activatefile ShellFid
449 'xcom quit'
450
451 call SetAVar( 'Shell_f'ShellNum, '')
452 call SetAVar( 'Shell_h'ShellNum, '')
453 call SetAVar( ShellFid'.ShellNum', '')
454 if curfid <> ShellFid then
455 activatefile curfid
456 endif
457 endif
458
459; ---------------------------------------------------------------------------
460; Write to current shell the text of current line, starting at cursor
461defc SendShell
462 if not IsAShell() then
463 sayerror NOT_IN_SHELL__MSG
464 return
465 endif
466 getfileid ShellFid
467 ShellNum = GetAVar( ShellFid'.ShellNum')
468 'Shell_Write' ShellNum substr( textline(.line), .col)
469
470; ---------------------------------------------------------------------------
471; Syntax: shell_write [<shellnum>] [<text>]
472; If first word is not a number, then last opened shell will be used as
473; <shellnum>.
474; If <text> is missing, the 'Write to shell' EntryBox opens.
475defc Shell_Write, ShellWrite
476 universal ShellHandle
477 universal Shell_lastwrite
478 parse arg ShellNum Text
479 --dprintf( 'Shell_Write: ShellNum Text = 'ShellNum Text)
480 if not isnum( ShellNum) & IsAShell() then
481 getfileid ShellFid
482 ShellNum = GetAVar( ShellFid'.ShellNum')
483 parse arg Text
484 endif
485 if ShellNum = '' then
486 sayerror NOT_IN_SHELL__MSG
487 return
488 endif
489 getfileid ShellFid
490 ShellAppWaiting = GetAVar( ShellFid'.ShellAppWaiting')
491 ShellHandle = GetAVar( 'Shell_h'ShellNum)
492 if ShellHandle <> '' then
493 if Text = '' & words( ShellAppWaiting) < 2 then -- disable this silly box for Return in a waiting shell
494 shell_title = strip( WRITE_SHELL_MENU__MSG, 'T', '.') -- '~Write to shell...'
495 tilde = pos( '~', shell_title)
496 if tilde then
497 shell_title = delstr( shell_title, tilde, 1)
498 endif
499 do forever
500 parse value entrybox( shell_title, -- Title,
501compile if EPM_SHELL_PROMPT = '@prompt epm: $p $g' | EPM_SHELL_PROMPT = '@prompt [epm: $p ]'
502 '/'OK__MSG'/'LIST__MSG'/'Cancel__MSG'/', -- Buttons
503compile else
504 '/'OK__MSG'/'Cancel__MSG'/', -- Buttons
505compile endif
506 Shell_lastwrite, -- entrytext
507 '', 254, -- cols, maxchars
508 atoi(1) || atoi(0000) || gethwndc(APP_HANDLE) ||
509 SHELL_PROMPT__MSG ShellNum) with button 2 text \0
510compile if EPM_SHELL_PROMPT = '@prompt epm: $p $g' | EPM_SHELL_PROMPT = '@prompt [epm: $p ]'
511 if button=\2 then -- User asked for a list
512 getfileid shell_fileid
513 call psave_pos(save_pos)
514 'xcom e /c cmdslist'
515 if rc <> -282 then -- -282 = sayerror("New file")
516 sayerror ERROR__MSG rc BAD_TMP_FILE__MSG sayerrortext(rc)
517 return
518 endif
519 browse_mode = browse() -- query current state
520 if browse_mode then call browse(0); endif
521 .autosave = 0
522 getfileid lb_fid
523 activatefile shell_fileid
524 display -2
525 getsearch oldsearch
526 0
527 compile if EPM_SHELL_PROMPT = '@prompt epm: $p $g'
528 'xcom l /^epm\: .*>:o./x'
529 compile else -- else EPM_SHELL_PROMPT = '@prompt [epm: $p ]'
530 'xcom l /^\[epm\: .*\]:o./x'
531 compile endif -- EPM_SHELL_PROMPT
532 do while rc = 0
533 compile if EPM_SHELL_PROMPT = '@prompt epm: $p $g'
534 parse value textline(.line) with '>' cmd
535 compile else
536 parse value textline(.line) with ']' cmd
537 compile endif -- EPM_SHELL_PROMPT
538 insertline strip( cmd, 'L'), lb_fid.last + 1, lb_fid
539 repeatfind
540 enddo
541 setsearch oldsearch
542 call prestore_pos(save_pos)
543 if browse_mode then call browse(1); endif -- restore browse state
544 activatefile lb_fid
545 display 2
546 if not .modify then -- Nothing added?
547 'xcom quit'
548 activatefile shell_fileid
549 sayerror -273 -- String not found
550 return
551 endif
552 if listbox_buffer_from_file( shell_fileid, bufhndl, noflines, usedsize) then return; endif
553 parse value listbox( shell_title,
554 \0 || atol(usedsize) || atoi(32) || atoi(bufhndl),
555 '/'OK__MSG'/'EDIT__MSG'/'Cancel__MSG,
556 0, 0, -- 1, 35,
557 min( noflines, 12), 0,
558 gethwndc(APP_HANDLE) || atoi(1) || atoi(1) ||
559 atoi(0000)) with button 2 Text \0
560 call buffer( FREEBUF, bufhndl)
561 if button = \2 then -- 'Edit' selected
562 Shell_lastwrite = Text
563 iterate
564 endif
565 endif
566compile endif
567 if button <> \1 then return; endif
568 leave
569 enddo
570 endif -- text = ''
571 if Text <> '' then
572 Shell_lastwrite = Text
573 endif
574 writebuf = Text\13\10
575 retval = SUE_write( ShellHandle, writebuf, bytesmoved)
576 if retval or bytesmoved <> length(writebuf) then
577 sayerror 'Shell_Write: rc =' retval', byteswritten =' bytesmoved 'of' length(writebuf)
578 'Shell_Break'
579 endif
580 endif
581 --
582 -- the above code is not really complete. It should also deal with situations
583 -- where only part of the data to be written is written. It needs to keep the
584 -- unwritten data around and put it out again during the "NowCanWriteShell"
585 -- comand processing. (todo)
586
587; ---------------------------------------------------------------------------
588; Shell object sends this command to inform the editor that there is
589; room for additional data to be written.
590; Still used?
591defc NowCanWriteShell
592 sayerror SHELL_OBJECT__MSG arg(1) SHELL_READY__MSG -- Use Shell_Write with argumentstring
593
594; ---------------------------------------------------------------------------
595; Shell object sends this command to inform the editor that there is
596; additional data to be read.
597; Fixed to handle LF-terminated lines correctly.
598; Recognize if an app is waiting for user input (then last line is not the EPM prompt).
599; Set ShellAppWaiting to 0 or to line and col.
600; Right margin setting of current shell is not respected.
601defc NowCanReadShell
602 parse arg shellnum .
603 if not isnum(shellnum) then
604 sayerror 'NowCanReadShell: 'INVALID_ARG__MSG '"'arg(1)'"'
605 return
606 endif
607
608 lastline = ''
609 ShellFid = GetAVar( 'Shell_f'shellnum)
610 ShellHandle = GetAVar( 'Shell_h'shellnum)
611
612 StartLineNum = GetAVar( ShellFid'.StartLineNum')
613 fFilterInitLines = 0
614 if IsNum( StartLineNum) then
615 if StartLineNum = .last then
616 -- This will be rest after first processing
617 fFilterInitLines = 1
618 endif
619 endif
620 --dprintf( 'NowCanReadShell: StartLineNum = 'StartLineNum', .last = '.last', fFilterInitLines = 'fFilterInitLines)
621
622 bytesmoved = 1
623 while bytesmoved do
624 ReadBuf = copies( ' ', MAXCOL)
625 retval = SUE_readln( ShellHandle, ReadBuf, bytesmoved)
626 ReadBuf = leftstr( ReadBuf, bytesmoved)
627 --dprintf( 'NowCanReadShell: ReadBuf 1 = 'translate( ReadBuf, \1\2'~', \10\13\20))
628 if ReadBuf = \13 then
629 iterate -- ignore CR
630 endif
631 -- SUE_readln doesn't handle LF as line end, received from the app.
632 -- It won't initiate a NowCanReadShell at Unix line ends.
633 -- Therefore it must be parsed here again.
634 -- BTW: MORE.COM has the same bug.
635 rest = ReadBuf
636 --dprintf( 'NowCanReadShell: ReadBuf 2 = 'translate( ReadBuf, \1\2'~', \10\13\20))
637 -- "do while rest <> ''" is too slow here. It has caused following issue:
638 -- The prompt after executing a start command (maybe "start epm config.sys")
639 -- changed to "epm:F:\>" instead of "epm: F:\ >". This should be fixed now.
640 -- But the main problem still remains (EPM bug):
641 -- After a "start epm" command, SUE_readln sends all data very slowly, sometimes
642 -- byte per byte. This can be checked by undoing the output of a "dir" command.
643 -- A new undo state is created for every line of the dir output then.
644
645 -- Filter out ANSI Esc sequences.
646 -- This is not safe, because the stream could arrive split in between
647 -- the start and end of a sequence. But it works well so far.
648 do forever
649 pEscStart = pos( \27'[', rest)
650 if pEscStart = 0 then
651 leave
652 else
653 pEscEnd = verify( rest, 'ABCDHJKnfRhlmpsu', 'M', pEscStart + 1)
654 if pEscEnd = 0 then
655 leave
656 endif
657 rest = delstr( rest, pEscStart, pEscEnd - pEscStart + 1)
658 endif
659 enddo
660
661 do forever
662 -- Search for further <LF> chars; <LF> at pos 1 is handled
663 -- by the original code itself
664 pLF = pos( \10, rest, 2)
665 if pLF > 0 then
666 next = leftstr( rest, pLF - 1)
667 rest = substr( rest, pLF)
668 else
669 next = rest
670 rest = ''
671 endif
672 if leftstr( next, 1) = \10 then -- LF is lineend
673 insertline substr( next, 2), shellfid.last+1, shellfid
674 else
675 getline oldline, shellfid.last, shellfid
676 if length(oldline) + length(next) > MAXCOL then
677 insertline next, shellfid.last+1, shellfid
678 else
679 replaceline oldline''next, shellfid.last, shellfid
680 endif
681 endif
682 if rest = '' then
683 getline lastline, shellfid.last, shellfid
684 shellfid.line = shellfid.last
685 shellfid.col = min( MAXCOL, length(lastline) + 1)
686 -- Following added because "do while rest <> ''" was too slow:
687 leave
688 endif
689 enddo
690 endwhile
691
692 -- Check if last written line was the EPM prompt
693 -- in order to accept input by a waiting application directly
694 -- in the shell buffer, not only in the Write to shell dialog.
695compile if EPM_SHELL_PROMPT = '@prompt epm: $p $g'
696 p1 = leftstr( lastline, 5) = 'epm: '
697 p2 = rightstr( strip( lastline, 't'), 1) = '>'
698compile else -- else EPM_SHELL_PROMPT = '@prompt [epm: $p ]'
699 p1 = leftstr( lastline, 6) = '[epm: '
700 p2 = rightstr( strip( lastline, 't'), 1) = ']'
701compile endif -- EPM_SHELL_PROMPT
702 -- set array var
703 if p1 > 0 & p2 > 0 then
704 -- set to 0
705 ShellAppWaiting = 0
706 -- dprintf( 'app terminated')
707 else
708 -- set to '.line .col' if app is waiting for user input
709 ShellAppWaiting = shellfid.line shellfid.col
710 -- dprintf( 'app waiting for input or further output will follow')
711 endif
712 getfileid fid
713 call SetAVar( fid'.ShellAppWaiting', ShellAppWaiting) -- save
714
715 if fFilterInitLines then
716 -- Delete all lines of the new ones:
717 --LastDeleteLineNum = .last - 1
718 -- Delete the first 2 lines of the new ones, keep init cmd:
719 LastDeleteLineNum = Min( StartLineNum + 1, .last - 1)
720 -- For a restored shell file, the previous last line is deleted.
721 do l = LastDeleteLineNum to StartLineNum by -1
722 --dprintf( 'l = 'l', deleteline 'l':'textline( l))
723 deleteline l
724 enddo
725 .last
726 -- Disable future line deletion
727 SetAVar( ShellFid'.StartLineNum', '')
728 endif
729
730; ---------------------------------------------------------------------------
731; Write user input to the shell if EPM prompt is in current line.
732; Enhanced for filename completion. Prepend 'cd ' to input if a directory.
733; Remove trailing \ from directories for 'cd'.
734; Works with spaces in filenames and surrounding "...".
735; This is the defproc called by the Enter key def.
736; Returns 0 on success; 1 when not on a EPM prompt line.
737; If 1 is returned, then ShellEnterWriteToApp() should be called by the
738; Enter key def.
739;
740; ECHO must be ON. That is the default setting in CMD.EXE, but not in 4OS2.EXE.
741; Otherwise no prompt is inserted after the command execution and further commands
742; won't work (CMD.EXE) or the command is deleted (4OS2.EXE).
743; Therefore ECHO ON must be executed _after_ every call of 4OS2.EXE.
744defproc ShellEnterWrite
745 ret = 1
746 getfileid ShellFid
747 ShellNum = GetAVar( ShellFid'.ShellNum')
748 x = ShellPromptPos()
749 if x then
750 getline line
751 Text = substr( line, x + 1)
752 Text = strip( Text, 'L')
753
754 -- Process alias in Text
755 KeyPath = '\NEPMD\User\Shell\Alias'
756 on = (QueryConfigKey( KeyPath) <> 0)
757 if on then
758 Text = ShellResolveAlias( Text)
759 endif
760
761 -- Parse Text into CmdWord and CmdArgs
762 if leftstr( Text, 1) = '"' then
763 parse value Text with '"'CmdWord'"' CmdArgs
764 else
765 parse value Text with CmdWord CmdArgs
766 endif
767
768 CmdName = upcase( StripExt( StripPath( CmdWord)))
769
770 -- Handle the silly M$ syntax extension for CD like "cd\", "cd.." etc.
771 if wordpos( leftstr( upcase( CmdWord), 3), 'CD\ CD.') then
772 CmdArgs = substr( CmdWord, 3)
773 CmdWord = 'cd'
774 CmdName = 'CD'
775 endif
776
777 -- Re-surround CmdWord with "..." if spaces
778 if pos( ' ', CmdWord) then
779 CmdWord = '"'CmdWord'"'
780 endif
781
782 -- Prepend "cd" if no CmdName given (true for a trailing '\')
783 if CmdName = '' & CmdWord <> '' & CmdArgs = '' then
784 CmdArgs = CmdWord
785 CmdWord = 'cd'
786 CmdName = 'CD'
787 endif
788
789 if CmdName = 'CD' then
790 -- Strip "..." from CmdArgs
791 if leftstr( CmdArgs, 1) = '"' then
792 parse value CmdArgs with '"'CmdArgs'"'
793 endif
794 -- Strip trailing \ from CmdArgs
795 if rightstr( CmdArgs, 1) = '\' &
796 CmdArgs <> '\' &
797 rightstr( CmdArgs, 2) <> ':\' then
798 CmdArgs = strip( CmdArgs, 'T', '\')
799 endif
800 -- Surround with "..." if CmdArgs contains spaces
801 if pos( ' ', CmdArgs) then
802 CmdArgs = '"'CmdArgs'"'
803 endif
804 endif
805
806 if CmdName = '4OS2' then
807 -- Insert "echo on" when 4os2 is called
808 if CmdArgs = '' then
809 CmdArgs = 'echo on'
810 else
811 CmdArgs = 'echo on&'CmdArgs
812 endif
813 endif
814
815 Text = CmdWord
816 if CmdArgs <> '' then
817 Text = Text CmdArgs
818 endif
819
820 if .line = .last then
821 --.col = x + 1
822 .col = x + 2 -- Prompt || Space
823 erase_end_line -- Delete the rest, because echo is on to avoid
824 -- doubled Cmd.
825 -- Echo off, executed in CMD.EXE, would suppress the
826 -- prompt as well.
827 else
828 -- The Undo statement doesn't restore the prompt line well (only
829 -- last change, depending on .modify). Therefore the line is
830 -- restored by an array var, set by the defproc for the Enter key.
831 'postme ShellRestoreOrgCmd' .line
832 endif
833 'shell_write' ShellNum Text
834 ret = 0
835 endif
836 return ret
837
838; ---------------------------------------------------------------------------
839; Restore line number = arg(1) to its state saved in ShellOrgCmd.
840defc ShellRestoreOrgCmd
841 getfileid fid
842 ShellOrgCmd = GetAVar( fid'.ShellOrgCmd')
843 l = arg(1)
844 parse value ShellOrgCmd with line cmd
845 if line = l then
846 saved_line = .line
847 .lineg = l
848 x = ShellPromptPos()
849 replaceline substr( textline( l), 1, x)' 'cmd, l -- Prompt || Space || Cmd
850 .lineg = saved_line
851 endif
852
853; ---------------------------------------------------------------------------
854; Write user input to a prompting (waiting) app.
855; For differing the output that comes from the app from the user input, the
856; array var "ShellAppWaiting" is used. It holds line and col from the last
857; write of the shell object to the EPM window, set in defc NowCanReadShell.
858; In case of a terminated app, the EPM prompt was the last output and
859; ShellAppWaiting holds the value 0.
860; Returns 0 on success; 1 when no app is waiting.
861defproc ShellEnterWriteToApp
862 ret = 1
863 getfileid ShellFid
864 ShellNum = GetAVar( ShellFid'.ShellNum')
865 ShellAppWaiting = GetAVar( ShellFid'.ShellAppWaiting')
866 if words( ShellAppWaiting) = 2 then
867 parse value ShellAppWaiting with lastl lastc
868 Text = ''
869 l = lastl
870 do while l <= .line
871 getline line, l
872 if l = lastl then
873 startc = lastc
874 else
875 startc = 1
876 endif
877 Text = Text''substr( line, startc)
878 if l = .last then
879 insertline '', .last + 1
880 leave
881 else
882 l = l + 1
883 endif
884 enddo
885 'shell_write' ShellNum Text
886 ret = 0
887 endif
888 return ret
889
890; ---------------------------------------------------------------------------
891const
892compile if not defined(ALIAS_ESCAPE_CHAR)
893 ALIAS_ESCAPE_CHAR = '*'
894compile endif
895compile if not defined(ALIAS_SEP_CHARS)
896 ALIAS_SEP_CHARS = ' |<>'
897compile endif
898
899; Resolves alias values for shell commands. Returns '' if no alias def found.
900defproc ShellResolveAlias
901
902 if not IsNum( GetAVar( 'alias.key.'0)) then
903 call ShellReadAliasFile()
904 endif
905
906 Rest = arg(1)
907
908 ResolvedString = ''
909 amax = GetAVar( 'alias.key.0')
910 if amax = '' then
911 rc = ShellReadAliasFile()
912 if rc <> 0 then
913 return Rest
914 endif
915 endif
916
917 do while Rest <> ''
918
919 -- Find string at p in alias keys
920 -- Start at the end of the array to match the longest string
921 UpRest = upcase( Rest)
922 Val = ''
923 do a = amax to 1 by -1
924 --dprintf( 'Alias: a = 'a', Key = 'GetAVar( 'alias.key.'a))
925 Key = GetAVar( 'alias.key.'a)
926 UpKey = upcase( Key)
927 if abbrev( UpRest, UpKey) = 1 then
928 -- Get surrounding chars to check for separators
929 PrevChar = rightstr( ResolvedString, 1)
930 NextChar = substr( Rest, length( Key) + 1, 1)
931 -- Handle EscapeChar
932 if PrevChar = ALIAS_ESCAPE_CHAR then
933 -- Remove it
934 ResolvedString = leftstr( ResolvedString, length( ResolvedString) - 1)
935 -- Keep found key
936 Val = Key
937 leave
938 elseif pos( PrevChar, ALIAS_SEP_CHARS) > 0 & pos( NextChar, ALIAS_SEP_CHARS) > 0 then
939 -- Replace key with value
940 Val = GetAVar( 'alias.value.'a)
941 leave
942 else
943 iterate
944 endif
945 endif
946 enddo
947
948 if Val <> '' then
949 -- Found, advance search pos by length of key
950 pDelta = length( Key)
951 else
952 -- Not found, advance search pos by 1
953 pDelta = 1
954 Val = leftstr( Rest, 1)
955 endif
956
957 ResolvedString = ResolvedString''Val
958 Rest = substr( Rest, pDelta + 1)
959
960 enddo
961 --dprintf( 'ResolvedString = 'ResolvedString)
962 rc = 0
963
964 return ResolvedString
965
966; ---------------------------------------------------------------------------
967defproc ShellReadAliasFile
968
969 ValidApplications = 'SHELL'
970
971 IniFile = arg(1)
972 if IniFile = '' then
973 IniFile = Get_Env( 'NEPMD_USERDIR')'\bin\alias.cfg'
974 endif
975 if not Exist( IniFile) then
976 rc = 2
977 return
978 endif
979
980 getfileid CurFid
981 'DisableLoad'
982 'DisableSelect'
983
984 -- Load ini
985 'xcom e /d' IniFile
986 a = 0
987 Application = ''
988 if rc = 0 then
989 getfileid IniFid
990 .visible = 0
991 .autosave = 0
992
993 -- Delete array
994 preva = GetAVar( 'alias.key.'0)
995 if IsNum( preva) then
996 do x = 1 to preva
997 call SetAVar( 'alias.key.'x, '')
998 call SetAVar( 'alias.value.'x, '')
999 enddo
1000 call SetAVar( 'alias.key.'0, 0)
1001 endif
1002
1003 -- Load temp file for sorting
1004 'xcom e /c /q tempfile'
1005 if rc <> -282 then -- sayerror('New file')
1006 sayerror ERROR__MSG rc BAD_TMP_FILE__MSG sayerrortext(rc)
1007 return
1008 endif
1009 getfileid TempFid
1010 .visible = 0
1011 .autosave = 0
1012 browse_mode = browse() -- query current state
1013 if browse_mode then
1014 call browse(0)
1015 endif
1016
1017 -- Read ini
1018 activatefile IniFid
1019 do l = 1 to .last
1020
1021 IniLine = textline(l)
1022 StrippedIniLine = strip( IniLine)
1023
1024 -- Ignore comments, lines starting with ';' at column 1 are comments
1025 if substr( IniLine, 1, 1) = ';' then
1026 iterate
1027 -- Ignore empty lines
1028 elseif StrippedIniLine = '' then
1029 iterate
1030 endif
1031
1032 -- '[' at column 1 followed by a ']' on the same line marks the start
1033 -- of an application
1034 col1 = pos( '[', IniLine, 1)
1035 col2 = pos( ']', IniLine, 2)
1036 if col1 = 1 & col2 > 1 then
1037 Application = substr( IniLine, col1 + 1, col2 - col1 - 1)
1038 iterate
1039 endif
1040
1041 -- The first '=' in the line marks keyword and expression.
1042 -- Spaces around '=' are allowed
1043 parse value StrippedIniLine with KeyWord '=' KeyValue -- KeyWord (without '=') is allowed
1044 KeyWord = strip( KeyWord)
1045 KeyValue = strip( KeyValue)
1046
1047 if wordpos( Application, ValidApplications) = 0 then
1048 iterate
1049 else
1050 -- Append to temp file
1051 -- Use \1 as seperator because of its low ASCII value,
1052 -- '=' wouldn't be a good choice for sorting.
1053 insertline KeyWord''\1''KeyValue, TempFid.last + 1, TempFid
1054 --dprintf( 'Read alias file: Key = 'KeyWord', Value = 'KeyValue)
1055 endif
1056
1057 enddo
1058 -- Quit ini
1059 activatefile IniFid
1060 .modify = 0
1061 'quit'
1062
1063 -- Sort temp file to allow for finding the longest matched key
1064 activatefile TempFid
1065 if .last > 2 then
1066 call sort( 2, .last, 1, 40, TempFid, 'I')
1067 endif
1068
1069 -- Add lines to array
1070 do l = 2 to .last
1071 parse value textline( l) with Keyword''\1''KeyValue
1072 a = a + 1
1073 call SetAVar( 'alias.key.'a, KeyWord)
1074 call SetAVar( 'alias.value.'a, KeyValue)
1075 --dprintf( 'Sort alias keys: a = 'a', Key = 'KeyWord', Value = 'KeyValue)
1076 enddo
1077 call SetAVar( 'alias.key.'0, a)
1078
1079 -- Quit temp file
1080 activatefile TempFid
1081 .modify = 0
1082 'xcom quit'
1083 else
1084 if rc = -282 then -- sayerror('New file')
1085 'xcom quit'
1086 endif
1087 sayerror 'Error reading ini file 'inifile
1088 rc = 30
1089 endif
1090
1091 'EnableLoad'
1092 'EnableSelect'
1093 activatefile CurFid
1094 return
1095
1096; ---------------------------------------------------------------------------
1097defc ShellReadAliasFile
1098 call ShellReadAliasFile()
1099
1100; ---------------------------------------------------------------------------
1101defc ShellNewLine
1102 StdNewLine = arg(1)
1103 fExecStdNewLine = 0
1104
1105compile if not (EPM_SHELL_PROMPT = '@prompt epm: $p $g' | EPM_SHELL_PROMPT = '@prompt [epm: $p ]')
1106 fExecStdNewLine = 1
1107compile endif
1108
1109 if IsAShell() then
1110 rc = ShellEnterWrite()
1111 if rc then
1112 rc = ShellEnterWriteToApp()
1113 endif
1114 if rc then
1115 fExecStdNewLine = 1
1116 endif
1117 else
1118 fExecStdNewLine = 1
1119 endif
1120
1121 if fExecStdNewLine then
1122 StdNewLine
1123 endif
1124
1125; ---------------------------------------------------------------------------
1126defc ShellTab
1127 universal prevkey
1128 parse value prevkey with PrevKeyName \1 .
1129 KeyPath = '\NEPMD\User\Shell\FilenameCompletion'
1130 on = (QueryConfigKey( KeyPath) <> 0)
1131 if on then
1132 if wordpos( PrevKeyName, 'tab s_backtab') = 0 then
1133 'ShellFncInit'
1134 endif
1135 'ShellFncComplete'
1136 else
1137 'Tab' -- standard definition, keep in sync with STDKEYS.E or
1138 endif -- additional keyset definitions
1139
1140; ---------------------------------------------------------------------------
1141defc ShellBackTab
1142 universal prevkey
1143 parse value prevkey with PrevKeyName \1 .
1144 KeyPath = '\NEPMD\User\Shell\FilenameCompletion'
1145 on = (QueryConfigKey( KeyPath) <> 0)
1146 if on then
1147 if wordpos( PrevKeyName, 'tab s_backtab') = 0 then
1148 'ShellFncInit'
1149 endif
1150 'ShellFncComplete -'
1151 else
1152 'BackTab' -- standard definition, keep in sync with STDKEYS.E or
1153 endif -- additional keyset definitions
1154
1155; ---------------------------------------------------------------------------
1156defc ShellGotoPrevPrompt
1157 executekey up
1158 'xcom l /^epm\: [^>]*>:o\c/x-'
1159 refresh
1160
1161defc ShellGotoNextPrompt
1162 executekey down
1163 'xcom l /^epm\: [^>]*>:o\c/x+'
1164 refresh
1165
1166; and now step for step explained
1167; /^epm\: [^>]*>:o\c/x+
1168;
1169; / begin of pattern
1170; ^ begin of line
1171; epm epm
1172; \: colon
1173; [^>] any key except ">"
1174; * none - many of the previous
1175; > >
1176; :o optional whitespace
1177; \c places cursor behind whitespace
1178; / end of pattern
1179; x extended grep
1180; + search forward
1181; - search backward
1182
1183; ---------------------------------------------------------------------------
1184; Called from Shell command
1185defproc SUE_new( var shell_handle, shellnum)
1186 thandle = '????'
1187;; sayerror "address=0x" || ltoa(taddr, 16) || " hwnd=0x"ltoa(hwnd, 16);
1188 result = dynalink32( ERES_DLL,
1189 'SUE_new',
1190 address(thandle) ||
1191 gethwndc(EPMINFO_EDITCLIENT) ||
1192 atol(shellnum))
1193 shell_handle = thandle
1194 return result
1195
1196; ---------------------------------------------------------------------------
1197; Called from Shell_Kill command
1198defproc SUE_free( var shell_handle)
1199 thandle = shell_handle
1200 result = dynalink32( ERES_DLL,
1201 'SUE_free',
1202 address(thandle))
1203 shell_handle = thandle
1204 return result
1205
1206; ---------------------------------------------------------------------------
1207; Called from NowCanReadShell cmd
1208defproc SUE_readln( shell_handle, var buffe, var bytesmoved)
1209 bufstring = buffe -- just to insure the same amount of space is available
1210 bm = "??"
1211 result = dynalink32( ERES_DLL,
1212 'SUE_readln',
1213 shell_handle ||
1214 address(bufstring) ||
1215 atol(length(bufstring)) ||
1216 address(bm))
1217 bytesmoved = itoa( bm, 10)
1218 buffe = bufstring
1219 return result
1220
1221; ---------------------------------------------------------------------------
1222; Called from Shell_Write command
1223defproc SUE_write( shell_handle, buffe, var bytesmoved)
1224 bm = "??"
1225 result = dynalink32( ERES_DLL,
1226 'SUE_write',
1227 shell_handle ||
1228 address(buffe) ||
1229 atol(length(buffe)) ||
1230 address(bm))
1231 bytesmoved = itoa( bm, 10)
1232 return result
1233
1234; ---------------------------------------------------------------------------
1235; Sends a Break to a shell object
1236defc Shell_Break, ShellBreak
1237 parse arg shellnum .
1238 if ShellNum = '' & IsAShell() then
1239 getfileid ShellFid
1240 ShellNum = GetAVar( ShellFid'.ShellNum')
1241 endif
1242 if ShellNum = '' then
1243 sayerror NOT_IN_SHELL__MSG
1244 return
1245 endif
1246 ShellHandle = GetAVar( 'Shell_h'ShellNum)
1247 if ShellHandle = '' then
1248 return
1249 endif
1250
1251 -- Confirm on a prompt line
1252 if ShellPromptPos() then
1253 refresh
1254 if MBID_OK <> winmessagebox( 'Sending a Break signal not required', -- title
1255 'Apparently you are on a prompt line and there is'\n ||
1256 'no action to send a Break to.'\n\n ||
1257 'Do you really want to send a Break signal?',
1258 MB_OKCANCEL + MB_QUERY + MB_DEFBUTTON1 + MB_MOVEABLE)
1259 then
1260 return
1261 endif
1262 endif
1263
1264 -- Send a break signal to the shell object
1265 retval = SUE_break( ShellHandle)
1266 if retval then
1267 if retval = 162 then
1268 sayerror 'A signal is already pending, rc = 'retval' from SUE_break'
1269 else
1270 -- rc = 184 here means either: No process to break or Break not possible.
1271 sayerror ERROR__MSG retval 'sending break to 'SHELL_OBJECT__MSG
1272 endif
1273
1274 -- Pop up a MsgBox and ask the user before killing the shell
1275 -- Sideeffect: the command is paused.
1276 refresh
1277 if MBID_OK <> winmessagebox( 'Error sending Break signal', -- title
1278 'The Break signal, sent to the shell object, was'\n ||
1279 'not successful, at least not immediately.'\n\n ||
1280 'Do you want to restart the shell object?'\n ||
1281 '(The path will be restored, but any special'\n ||
1282 'environment will be lost.)',
1283 MB_OKCANCEL + MB_QUERY + MB_DEFBUTTON1 + MB_MOVEABLE)
1284 then
1285 return
1286 endif
1287
1288 -- Kill the shell
1289 -- Sending the kill signal will force a kill immediately
1290 retval = SUE_free( ShellHandle)
1291 if retval then
1292 if retval = 162 then
1293 sayerror 'A signal is already pending, rc = 'retval' from SUE_free'
1294 elseif retval = 184 then
1295 sayerror 'Shell object is already killed, rc = 'retval' from SUE_free'
1296 else
1297 sayerror ERROR__MSG retval SHELL_ERROR3__MSG
1298 endif
1299 -- Ignore errors here, most likely the shell object will be killed delayed
1300 else
1301 sayerror 'Shell object was killed successfully'
1302 endif
1303
1304 -- Create a new shell object, keeping ShellNum
1305 ShellHandle = '????'
1306 retval = SUE_new( ShellHandle, ShellNum)
1307 if retval then
1308 sayerror ERROR__MSG retval SHELL_ERROR1__MSG
1309 else
1310 call SetAVar( 'Shell_h'ShellNum, ShellHandle)
1311 InitCmd = ''
1312compile if EPM_SHELL_PROMPT <> ''
1313 InitCmd = EPM_SHELL_PROMPT
1314compile endif
1315 if InitCmd <> '' then
1316 'shell_write' ShellNum InitCmd
1317 endif
1318
1319 -- Determine previous work dir
1320 call psave_pos( save_pos)
1321 display -3
1322 .lineg = .last
1323 endline
1324 fFound = (ShellGotoNextPrompt( 'P') = 0)
1325 Dir = ''
1326 Cmd = ''
1327 if fFound then
1328 call ShellParsePromptLine( Dir, Cmd)
1329 else
1330 call prestore_pos( save_pos)
1331 endif
1332 display 3
1333 if Dir <> '' then
1334 CdCmd = 'cdd' Dir
1335 'shell_write' ShellNum CdCmd
1336 endif
1337 endif
1338
1339 endif
1340
1341; ---------------------------------------------------------------------------
1342defproc SUE_break( shell_handle)
1343 return dynalink32( ERES_DLL,
1344 'SUE_break',
1345 shell_handle)
1346
1347; ---------------------------------------------------------------------------
1348; 1) Reset modified state to avoid the dialog on quit.
1349; 2) The shell window allows for editing a previous command.
1350; (Previous commands can be reached by Ctrl+Up and Ctrl+Down.)
1351; The original command is saved, if on a prompt line, and if not already
1352; saved before.
1353; On executing an edited command, the old command is restored by
1354; ShellRestoreOrgCmd, called by ShellEnterWrite.
1355defmodify
1356 if IsAShell() then
1357 -- 1) Avoid the dialog on quit only for newly created shell buffers,
1358 -- not for reactivated ones
1359 if leftstr( .filename, 1) = '.' then
1360 .modify = 0
1361 'ResetDateTimeModified'
1362 'refreshinfoline MODIFIED'
1363 endif
1364
1365 -- 2) Save the original command
1366 do once = 1 to 1
1367 if .line = .last then -- last line has only the prompt, never a Cmd
1368 leave
1369 endif
1370
1371 getfileid fid
1372 ShellOrgCmd = GetAVar( fid'.ShellOrgCmd')
1373 parse value ShellOrgCmd with line .
1374 if line = .line then
1375 leave
1376 endif
1377
1378 p = ShellPromptPos()
1379 if not p then
1380 leave
1381 endif
1382 -- Get OldCmd only if new text was entered
1383 NewCmd = substr( textline( .line), p + 1)
1384 if strip( NewCmd) = '' then
1385 leave
1386 endif
1387
1388 -- Use Undo to get previous command at the previous prompt.
1389 undoaction 1, junk
1390 undoaction 6, StateRange -- query range
1391 parse value StateRange with oldeststate neweststate
1392 prevstate = max( neweststate - 1, oldeststate)
1393 undoaction 7, prevstate
1394 OldCmd = strip( substr( textline( .line), p + 1), 'l')
1395 if OldCmd <> '' then
1396 ShellOrgCmd = .line OldCmd
1397 call SetAVar( fid'.ShellOrgCmd', ShellOrgCmd)
1398 endif
1399 undoaction 7, neweststate
1400 enddo
1401 endif
1402
1403; ---------------------------------------------------------------------------
1404defc ShellClose
1405 if leftstr( .filename, 1) = '.' then
1406 .modify = 0
1407 endif
1408 'ShellKill'
1409
1410; ---------------------------------------------------------------------------
1411; This command can be used as key command, maybe for Esc.
1412; Syntax: shell_commandline [<shellnum>] [<text>]
1413defc shell_commandline
1414 parse arg ShellNum text
1415 if not isnum( ShellNum) & IsAShell() then
1416 getfileid ShellFid
1417 ShellNum = GetAVar( ShellFid'.ShellNum')
1418 parse arg Text
1419 endif
1420 if ShellNum = '' then
1421 sayerror NOT_IN_SHELL__MSG
1422 return
1423 endif
1424 'commandline shell_write 'ShellNum' 'Text
1425
1426; ---------------------------------------------------------------------------
1427; Returns 0 if not a shell,
1428; otherwise the .col for the end of the prompt (> or ]).
1429defproc ShellPromptPos
1430 if not IsAShell() then
1431 return 0
1432 endif
1433 line = arg(1)
1434 if line = '' then
1435 getline line
1436 endif
1437compile if not (EPM_SHELL_PROMPT = '@prompt epm: $p $g' | EPM_SHELL_PROMPT = '@prompt [epm: $p ]')
1438 return 1
1439compile endif
1440compile if EPM_SHELL_PROMPT = '@prompt epm: $p $g'
1441 p = pos( '>',line)
1442compile else
1443 p = pos( ']',line)
1444compile endif
1445 text = substr( line, p + 1)
1446compile if EPM_SHELL_PROMPT = '@prompt epm: $p $g'
1447 if leftstr( line, 5)='epm: ' & p then
1448compile else
1449 if leftstr( line, 6)='[epm: ' & p then
1450compile endif
1451 return p
1452 else
1453 return 0
1454 endif
1455
1456; ---------------------------------------------------------------------------
1457; Move cursor to the start of the next prompt line. The prompt line must
1458; have a command behind the prompt.
1459; Optional arg is P (find previous prompt) to search backwards.
1460defproc ShellGotoNextPrompt
1461 if upcase( arg(1) = 'P') then
1462 direction = '-r'
1463 else
1464 direction = ''
1465 endif
1466compile if EPM_SHELL_PROMPT = '@prompt epm: $p $g'
1467 'xcom l /^epm\: .*>:o/x'direction
1468compile else -- else EPM_SHELL_PROMPT = '@prompt [epm: $p ]'
1469 'xcom l /^\[epm\: .*\]:o/x'direction
1470compile endif -- EPM_SHELL_PROMPT
1471 return rc
1472
1473; ---------------------------------------------------------------------------
1474; Parse current line and set Dir and Cmd via call by reference
1475defproc ShellParsePromptLine( var Dir, var Cmd)
1476compile if EPM_SHELL_PROMPT = '@prompt epm: $p $g'
1477 parse value textline(.line) with 'epm:' Dir '>' Cmd
1478compile else
1479 parse value textline(.line) with 'epm:' Dir ']' Cmd
1480compile endif -- EPM_SHELL_PROMPT
1481 Dir = strip( Dir)
1482 Cmd = strip( Cmd)
1483 return
1484
1485; ---------------------------------------------------------------------------
1486; Filename completion like in 4os2.
1487; Difference in sorting order: dirs come first and executables are sorted
1488; according to their appearance in EXE_MASK_LIST
1489const
1490compile if not defined( FNC_EXE_MASK_LIST)
1491 FNC_EXE_MASK_LIST = '*.cmd *.exe *.com *.bat'
1492compile endif
1493compile if not defined( FNC_DIR_ONLY_CMD_LIST)
1494 FNC_DIR_ONLY_CMD_LIST = 'CD'
1495compile endif
1496compile if not defined( FNC_FILE_ONLY_CMD_LIST)
1497 FNC_FILE_ONLY_CMD_LIST = ''
1498compile endif
1499
1500defc ShellFncInit
1501 if IsAShell() then
1502 getfileid ShellFid
1503 ShellNum = GetAVar( ShellFid'.ShellNum')
1504 else
1505 return
1506 endif
1507 p = ShellPromptPos()
1508 if not p then
1509 return
1510 endif
1511 getline Line
1512 Prompt = leftstr( Line, p)
1513 PromptChar = substr( Prompt, p, 1)
1514 parse value Prompt with 'epm:' ShellDir (PromptChar)
1515 ShellDir = strip( ShellDir)
1516
1517 -- Get the part of the line between prompt and cursor
1518 Text = substr( Line, p + 1, .col - 1 - p)
1519 -- Strip leading spaces only, because a trailing space identifies the word before
1520 -- to have ended:
1521 -- > dir | -> dir *
1522 -- > dir| -> dir* (this will search for names starting with dir)
1523 Text = strip( Text, 'L')
1524
1525 -- Todo:
1526 -- o Find expression starting with ':\' or '\\' (FilePart may be part of a parameter,
1527 -- e.g.: Text = -dd:\os2\apps or Text = -d:d:\os2\apps)
1528 -- o Make options with filenames, not followed by a space, work,
1529 -- e.g.: Text = app.exe -d* -> CmdPart = 'app.exe -d', FilePart = '*'
1530
1531 -- Parse Text into CmdPart and FilePart
1532 CmdPart = ''
1533 CmdWord = ''
1534 FilePart = ''
1535 if rightstr( Text, 1) == ' ' then
1536 -- No FilePart
1537 if words( Text) > 0 then
1538 CmdWord = word( Text, 1)
1539 CmdPart = Text
1540 endif
1541 elseif rightstr( Text, 1) = '"' then
1542 -- FilePart is last word in "..."
1543 next = leftstr( Text, length( Text) - 1) -- strip last "
1544 lp = lastpos( '"', next)
1545 --dprintf( 'TabComplete', 'Text = ['Text'], lp = 'lp)
1546 FilePart = substr( Text, lp + 1, length( Text) - lp - 1)
1547 if lp > 1 then
1548 CmdPart = leftstr( Text, lp - 1)
1549 if pos( ' ', CmdPart) then
1550 CmdWord = word( Text, 1)
1551 endif
1552 endif
1553 else
1554 -- FilePart is last word
1555 if words( Text) = 1 then
1556 -- No CmdWord
1557 FilePart = Text
1558 elseif words( Text) > 1 then
1559 CmdWord = word( Text, 1)
1560 FilePart = lastword( Text)
1561 lp = wordindex( Text, words( Text))
1562 CmdPart = leftstr( Text, lp - 1)
1563 endif
1564 endif
1565 --dprintf( 'FNC: CmdWord = ['CmdWord'], CmdPart = ['CmdPart'], FilePart = ['FilePart']')
1566
1567 -- Construct fully qualified dirname to avoid change of directories, that
1568 -- doesn't work for UNC names.
1569 do once = 1 to 1
1570 FileMask = FilePart
1571 PrepMask = ''
1572 if substr( FilePart, 2, 2) = ':\' then
1573 leave
1574 elseif leftstr( FilePart, 2) = '\\' then
1575 leave
1576 else
1577 if leftstr( FilePart, 1) = '\' then
1578 -- Prepend drive
1579 if substr( ShellDir, 2, 2) = ':\' then
1580 PrepMask = leftstr( ShellDir, 2)
1581 FileMask = PrepMask''FilePart
1582 -- -- Prepend host
1583 -- elseif leftstr( ShellDir, 2) = '\\' then -- not possible
1584 -- parse value ShellDir with '\\'Server'\'Resource
1585 -- if pos( '\', Resource) then
1586 -- parse value Resource with Resource'\'rest
1587 -- endif
1588 -- PrepMask = '\\'Server'\'Resource
1589 -- FileMask = PrepMask''FilePart
1590 endif
1591 else
1592 -- Prepend ShellDir
1593 PrepMask = strip( ShellDir, 't', '\')'\'
1594 FileMask = PrepMask''FilePart
1595 endif
1596 endif
1597 enddo
1598
1599 -- Resolve FileMask to valid names for DosFind*
1600 FileMask = NepmdQueryFullName( FileMask)
1601
1602 -- The here fully qualified filemask must be changed to a relative path later,
1603 -- if FilePart was relative before.
1604
1605 -- Append * to FileMask only, if no * or ? is present in last dir segment.
1606 -- Determine if ExeMasks should be appended to FileMask.
1607 fAppendExeMask = 0
1608 fAppendAllMask = 0
1609 UnAppendedFileMask = FileMask
1610 lp = lastpos( '\', FileMask)
1611 LastSegment = substr( FileMask, lp + 1)
1612 --dprintf( 'TabComplete', 'LastSegment = ['LastSegment']')
1613 if verify( LastSegment, '?*', 'M') then
1614 --dprintf( 'TabComplete', '1 (wildcards): FileMask = ['FileMask']')
1615 elseif CmdWord = '' then
1616 --dprintf( 'TabComplete', '2 (no CmdWord): AppendExeMask')
1617 fAppendExeMask = 1
1618 else
1619 fAppendAllMask = 1
1620 endif
1621 if fAppendExeMask | fAppendAllMask then
1622 FileMask = FileMask'*'
1623 --dprintf( 'TabComplete', '3 (no wildcard): FileMask = ['FileMask']')
1624 endif
1625
1626 -- Delete old array
1627 cTotal = GetAVar( 'FncFound.0')
1628 if cTotal <> '' then
1629 do i = 1 to cTotal
1630 call SetAVar( 'FncFound.'i, '')
1631 enddo
1632 endif
1633 call SetAVar( 'FncFound.0', '')
1634 call SetAVar( 'FncFound.last', '')
1635
1636 c = 0 -- number of found names
1637 -- Should dirs be found?
1638 fFindDirs = (wordpos( upcase( CmdWord), FNC_FILE_ONLY_CMD_LIST) = 0)
1639 -- Should files be found?
1640 fFindFiles = (wordpos( upcase( CmdWord), FNC_DIR_ONLY_CMD_LIST) = 0)
1641 --dprintf( 'FNC: fAppendExeMask = 'fAppendExeMask', fAppendAllMask = 'fAppendAllMask', fFindDirs = 'fFindDirs', fFindFiles = 'fFindFiles)
1642
1643 -- Find dirs
1644 Handle = ''
1645 Name = ''
1646 --dprintf( 'FNC: find dirs: FileMask = 'FileMask)
1647 do while fFindDirs & NepmdGetNextDir( FileMask, Handle, Name)
1648 -- Append \ for dirs
1649 Name = Name'\'
1650
1651 -- Remove maybe previously added PrepMask if FilePart was relative
1652 l = length( PrepMask)
1653 if l > 0 then
1654 if leftstr( upcase(Name), l) == upcase( PrepMask) then
1655 Name = substr( Name, l + 1)
1656 endif
1657 endif
1658
1659 -- Add it
1660 c = c + 1
1661 call SetAVar( 'FncFound.'c, Name)
1662 enddo
1663
1664 -- Find files
1665 if fAppendExeMask then
1666 mMax = words( FNC_EXE_MASK_LIST)
1667 else
1668 mMax = 1
1669 endif
1670 do m = 1 to mMax
1671
1672
1673 -- Append next ExeMask to FileMask
1674 if fAppendExeMask then
1675 NextExeMask = word( FNC_EXE_MASK_LIST, m)
1676 -- Reset FileMask
1677 FileMask = UnAppendedFileMask
1678 FileMask = FileMask''NextExeMask
1679 endif
1680
1681 Handle = ''
1682 Name = ''
1683 --dprintf( 'FNC: find files: FileMask = 'FileMask)
1684 do while fFindFiles & NepmdGetNextFile( FileMask, Handle, Name)
1685
1686 -- Remove maybe previously added PrepMask if FilePart was relative
1687 l = length( PrepMask)
1688 if l > 0 then
1689 if leftstr( upcase(Name), l) == upcase( PrepMask) then
1690 Name = substr( Name, l + 1)
1691 endif
1692 endif
1693
1694 -- Add it
1695 c = c + 1
1696 call SetAVar( 'FncFound.'c, Name)
1697 enddo
1698
1699 enddo
1700
1701 if c > 0 then
1702 call SetAVar( 'FncFound.0', c) -- number of found names
1703 call SetAVar( 'FncFound.last', '0') -- use 0 as initial number
1704 sayerror c 'dirs/files found.'
1705 else
1706 sayerror 'No match for "'FilePart'".'
1707 endif
1708 call SetAVar( 'FncShellNum', ShellNum)
1709 call SetAVar( 'FncPrompt', Prompt)
1710 call SetAVar( 'FncCmdPart', CmdPart)
1711
1712; ---------------------------------------------------------------------------
1713; Tab must not be defined as accelerator key, because otherwise
1714; lastkey(2) and lastkey(3) would return wrong values for Tab.
1715; lastkey() = lastkey(0) and lastkey(1) for Tab doesn't work in EPM!
1716; When Sh is pressed, lastkey() is set to Sh. While Sh is down and
1717; Tab is pressed additionally, lastkey is set to Sh+Tab and lastkey(2)
1718; is set to Sh. Therefore querying lastkey(2) to determine if Tab was
1719; pressed before doesn't work for any key combination!
1720;defc TabComplete
1721defc ShellFncComplete
1722 fForeward = ( arg(1) <> '-')
1723 -- Check shell
1724 ShellNum = ''
1725 if IsAShell() then
1726 getfileid ShellFid
1727 ShellNum = GetAVar( ShellFid'.ShellNum')
1728 endif
1729 next = GetAVar( 'FncShellNum')
1730 if ShellNum = '' | ShellNum <> next then
1731 return
1732 endif
1733 -- Query array
1734 Prompt = GetAVar( 'FncPrompt')
1735 CmdPart = GetAVar( 'FncCmdPart')
1736 Name = ''
1737 cLast = GetAVar( 'FncFound.last')
1738 if cLast <> '' then
1739 cTotal = GetAVar( 'FncFound.0')
1740 --sayerror cTotal 'files in array.'
1741 if fForeward then
1742 if cLast < cTotal then
1743 cLast = cLast + 1
1744 else
1745 cLast = 1
1746 endif
1747 else
1748 if cLast > 1 then
1749 cLast = cLast - 1
1750 else
1751 cLast = cTotal
1752 endif
1753 endif
1754 Name = GetAVar( 'FncFound.'cLast)
1755 call SetAVar( 'FncFound.last', cLast) -- save last used name number
1756 endif
1757
1758 if Name <> '' then
1759 if pos( ' ', Name) then
1760 Name = '"'Name'"'
1761 endif
1762; Todo:
1763; Make -dName possible
1764 if CmdPart <> '' then
1765 NewLine = Prompt strip( CmdPart) Name
1766 else
1767 NewLine = Prompt Name
1768 endif
1769 replaceline NewLine
1770 .col = length( NewLine) + 1 -- go to end
1771 endif
1772
1773; ---------------------------------------------------------------------------
1774; Following definitions are copied from Joerg Tiemann's SHELLKRAM.E package:
1775
1776/***************************************************************************/
1777/* Shell_History */
1778/* The following Shell_History command is a 100% subset of the shell_write */
1779/* command in epmshell.e. I have only changed the order of the lines and */
1780/* left out some of the "compile if" statements for EPM <5.6, because at */
1781/* some point it got too complicated for me. ;-) */
1782/* Oh, what does it do? It is the same as shell_write, except that this */
1783/* one starts with the history dialog. */
1784/***************************************************************************/
1785
1786defc Shell_History, ShellHistory
1787 universal Shell_lastwrite
1788
1789 parse arg ShellNum Text
1790 if ShellNum = '' & IsAShell() then
1791 getfileid ShellFid
1792 ShellNum = GetAVar( ShellFid'.ShellNum')
1793 endif
1794 if ShellNum = '' then
1795 sayerror NOT_IN_SHELL__MSG
1796 return
1797 endif
1798
1799 ShellHandle = GetAVar( 'Shell_h'ShellNum)
1800
1801 if ShellHandle <> '' then
1802 if Text <> '' then Shell_lastwrite = Text; endif
1803
1804 shell_title = strip( WRITE_SHELL_MENU__MSG, 'T', '.') -- '~Write to shell...'
1805 tilde = pos( '~', shell_title)
1806 if tilde then
1807 shell_title = delstr( shell_title, tilde, 1)
1808 endif
1809 do forever
1810
1811 getfileid shell_fileid
1812 call psave_pos(save_pos)
1813 'xcom e /c cmdslist'
1814 if rc <> -282 then -- -282 = sayerror("New file")
1815 sayerror ERROR__MSG rc BAD_TMP_FILE__MSG sayerrortext(rc)
1816 return
1817 endif
1818 browse_mode = browse() -- query current state
1819 if browse_mode then call browse(0); endif
1820 .autosave = 0
1821 getfileid lb_fid
1822 activatefile shell_fileid
1823 display -2
1824 getsearch oldsearch
1825 0
1826compile if EPM_SHELL_PROMPT = '@prompt epm: $p $g'
1827 'xcom l /^epm\: .*>:o./x'
1828compile else -- else EPM_SHELL_PROMPT = '@prompt [epm: $p ]'
1829 'xcom l /^\[epm\: .*\]:o./x'
1830compile endif -- EPM_SHELL_PROMPT
1831 do while rc = 0
1832compile if EPM_SHELL_PROMPT = '@prompt epm: $p $g'
1833 parse value textline(.line) with '>' cmd
1834compile else
1835 parse value textline(.line) with ']' cmd
1836compile endif -- EPM_SHELL_PROMPT
1837 insertline strip( cmd, 'L'), lb_fid.last+1, lb_fid
1838 repeatfind
1839 enddo
1840 setsearch oldsearch
1841 call prestore_pos(save_pos)
1842 if browse_mode then call browse(1); endif
1843 activatefile lb_fid
1844 display 2
1845 if not .modify then -- Nothing added?
1846 'xcom quit'
1847 activatefile shell_fileid
1848 sayerror -273 -- String not found - 4OS2???
1849 return
1850 endif
1851
1852 if listbox_buffer_from_file( shell_fileid, bufhndl, noflines, usedsize) then return; endif
1853 parse value listbox( shell_title,
1854 \0 || atol(usedsize) || atoi(32) || atoi(bufhndl),
1855 '/'OK__MSG'/'EDIT__MSG'/'Cancel__MSG,
1856 0, 0, -- 1, 35,
1857 min( 12, 18), 0,
1858 gethwndc(APP_HANDLE) ||
1859 atoi(1) || atoi(1) || atoi(0000)) with Button 2 Text \0
1860 call buffer( FREEBUF, bufhndl)
1861 if Button = \2 then -- 'Edit' selected
1862 Shell_lastwrite = Text
1863 parse value entrybox( shell_title, -- Title,
1864compile if EPM_SHELL_PROMPT = '@prompt epm: $p $g' | EPM_SHELL_PROMPT = '@prompt [epm: $p ]'
1865 '/'OK__MSG'/'LIST__MSG'/'Cancel__MSG'/', -- Buttons
1866compile else
1867 '/'OK__MSG'/'Cancel__MSG'/', -- Buttons
1868compile endif
1869 Shell_lastwrite, -- Entrytext
1870 '', 254, -- cols, maxchars
1871 atoi(1) || atoi(0000) || gethwndc(APP_HANDLE) ||
1872 SHELL_PROMPT__MSG ShellNum) with Button 2 Text \0
1873 if Button = \2 then -- User asked for a list
1874 iterate -- do forever
1875 endif -- button 2 - 'List' in Edit Menu
1876
1877 endif -- button 2 - 'Edit' in List menu
1878 if Button <> \1 then return; endif
1879
1880 leave -- do forever
1881 enddo -- do forever
1882
1883 writebuf = Text\13\10
1884 retval = SUE_write( ShellHandle, writebuf, bytesmoved)
1885 if retval or bytesmoved <> length(writebuf) then
1886 sayerror 'Shell_History: write.retval = 'retval', byteswritten = 'bytesmoved' of 'length( writebuf)
1887 endif
1888 endif
1889
1890/********************************************************************************/
1891/* Shell_Input */
1892/* This is a cut off Shell_Write. Initially the idea was to use it to be able */
1893/* to use 4OS2 as the command line interpreter. But that did not work. In the */
1894/* meantime I've come to the conclusion that it is a nice command for use in */
1895/* the toolbar "* Shell_Input somecommand parameters". */
1896/********************************************************************************/
1897
1898defc Shell_Input
1899 universal Shell_lastwrite
1900 parse arg Text
1901 ShellNum = ''
1902 if IsAShell() then
1903 getfileid ShellFid
1904 ShellNum = GetAVar( ShellFid'.ShellNum')
1905 endif
1906 if ShellNum = '' then
1907 sayerror NOT_IN_SHELL__MSG
1908 return
1909 endif
1910
1911 ShellHandle = GetAVar( 'Shell_h'ShellNum)
1912
1913 if ShellHandle <> '' then
1914 if Text <> '' then
1915 Shell_lastwrite = Text
1916 endif
1917 writebuf = Text\13\10 -- input text + CRLF
1918 retval = SUE_write( ShellHandle, writebuf, bytesmoved)
1919 if retval or bytesmoved <> length(writebuf) then
1920 sayerror 'Shell_Input: write.retval = 'retval', byteswritten = 'bytesmoved' of 'length(writebuf)
1921 endif
1922 endif
1923
1924/******************************************************************/
1925/* Shell_SendKey */
1926/* Now yet another variation. This time to send single keystrokes */
1927/* to the command line interpreter, for example the often needed */
1928/* 'y' and 'n'. */
1929/******************************************************************/
1930
1931defc Shell_SendKey
1932 parse arg Text
1933 ShellNum = ''
1934 if IsAShell() then
1935 getfileid ShellFid
1936 ShellNum = GetAVar( ShellFid'.ShellNum')
1937 endif
1938 if ShellNum = '' then
1939 sayerror NOT_IN_SHELL__MSG
1940 return
1941 endif
1942 if Text = '' then
1943 sayerror 'Shell_SendKey: no key to send to shell specified'
1944 return
1945 endif
1946
1947 ShellHandle = GetAVar( 'Shell_h'ShellNum)
1948
1949 if ShellHandle <> '' then
1950 writebuf = Text -- just the pure text w/o CRLF
1951 retval = SUE_write( ShellHandle, writebuf, bytesmoved)
1952 if retval or bytesmoved <> length(writebuf) then
1953 sayerror 'Shell_SendKey: write.retval = 'retval', byteswritten = 'bytesmoved 'of' length(writebuf)
1954 endif
1955 endif
1956
1957/*
1958defc esk_About=
1959 r = WinMessageBox( 'About EPM Shellkram',
1960 'EPM Shellkram v.0.88 beta'\10 ||
1961 'Wed, 3 Oct 2001'\10 ||
1962 'Copyright (c) 1998-2001 by Joerg Tiemann'\10 ||
1963 'Joerg Tiemann <tiemannj@gmx.de>', 16432)
1964*/
Note: See TracBrowser for help on using the repository browser.