factor-shell: Initial commit.
parent
ffb42c913e
commit
7b7fd6a2e5
|
@ -0,0 +1 @@
|
|||
Doug Coleman
|
|
@ -0,0 +1,46 @@
|
|||
! Copyright (C) 2018 Doug Coleman.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: tools.test factor-shell ;
|
||||
IN: factor-shell.tests
|
||||
|
||||
! Absolute paths
|
||||
{ "/" } [ "/" canonicalize-path ] unit-test
|
||||
{ "/" } [ "/." canonicalize-path ] unit-test
|
||||
{ "/" } [ "/.." canonicalize-path ] unit-test
|
||||
{ "/" } [ "/Users/.." canonicalize-path ] unit-test
|
||||
{ "/" } [ "/Users/../" canonicalize-path ] unit-test
|
||||
{ "/" } [ "/Users/../." canonicalize-path ] unit-test
|
||||
{ "/" } [ "/Users/.././" canonicalize-path ] unit-test
|
||||
{ "/" } [ "/Users/.././././././" canonicalize-path ] unit-test
|
||||
{ "/" } [ "/Users/../././/////./././/././././//././././././." canonicalize-path ] unit-test
|
||||
{ "/" } [ "/Users/../../../..////.././././././/../" canonicalize-path ] unit-test
|
||||
{ "/Users" } [ "/Users/../../../Users" canonicalize-path ] unit-test
|
||||
|
||||
{ "/Users" } [ "/Users" canonicalize-path ] unit-test
|
||||
{ "/Users" } [ "/Users/." canonicalize-path ] unit-test
|
||||
{ "/Users/foo/bar" } [ "/Users/foo/bar" canonicalize-path ] unit-test
|
||||
|
||||
|
||||
! Relative paths
|
||||
{ "." } [ f canonicalize-path ] unit-test
|
||||
{ "." } [ "" canonicalize-path ] unit-test
|
||||
{ "." } [ "." canonicalize-path ] unit-test
|
||||
{ "." } [ "./" canonicalize-path ] unit-test
|
||||
{ "." } [ "./." canonicalize-path ] unit-test
|
||||
{ ".." } [ ".." canonicalize-path ] unit-test
|
||||
{ ".." } [ "../" canonicalize-path ] unit-test
|
||||
{ ".." } [ "../." canonicalize-path ] unit-test
|
||||
{ ".." } [ ".././././././//." canonicalize-path ] unit-test
|
||||
|
||||
{ "../.." } [ "../.." canonicalize-path ] unit-test
|
||||
{ "../.." } [ "../../" canonicalize-path ] unit-test
|
||||
{ "../.." } [ "../.././././/./././" canonicalize-path ] unit-test
|
||||
|
||||
|
||||
! Root paths
|
||||
{ "/" } [ "/" root-path ] unit-test
|
||||
{ "/" } [ "/Users" root-path ] unit-test
|
||||
{ "/" } [ "//" root-path ] unit-test
|
||||
{ "/" } [ "//Users" root-path ] unit-test
|
||||
{ "/" } [ "/Users/foo/bar////././." root-path ] unit-test
|
||||
{ "/" } [ "/Users/////" root-path ] unit-test
|
|
@ -0,0 +1,285 @@
|
|||
! Copyright (C) 2018 Doug Coleman.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: accessors arrays assocs classes.parser classes.tuple
|
||||
combinators combinators.extras combinators.short-circuit
|
||||
combinators.smart continuations fry io io.backend
|
||||
io.encodings.utf8 io.files io.files.info io.launcher
|
||||
io.pathnames io.standard-paths kernel lexer libc math math.order
|
||||
multiline multiline.private namespaces parser prettyprint
|
||||
sequences sequences.deep splitting strings system
|
||||
system-info.macosx tools.hexdump unicode unix.signals unix.users
|
||||
words ;
|
||||
IN: factor-shell
|
||||
|
||||
! General utils.
|
||||
: trim-blanks ( string -- string' ) [ blank? ] trim ; inline
|
||||
: trim-head-blanks ( string -- string' ) [ blank? ] trim-head ; inline
|
||||
: trim-tail-blanks ( string -- string' ) [ blank? ] trim-tail ; inline
|
||||
|
||||
GENERIC: find-command-path ( obj -- obj' )
|
||||
|
||||
M: sequence find-command-path
|
||||
0 over [ find-in-standard-login-path ] change-nth ;
|
||||
|
||||
M: process find-command-path
|
||||
[ find-command-path ] change-command ;
|
||||
|
||||
GENERIC#: run-process>string 1 ( command encoding -- string )
|
||||
|
||||
M: array run-process>string ( command encoding -- string )
|
||||
[ <process> swap >>command +new-session+ >>group ] dip
|
||||
|
||||
[ find-command-path ] dip
|
||||
[ contents ] with-process-reader ; inline
|
||||
|
||||
M: string run-process>string
|
||||
[ <process> swap >>command +new-session+ >>group ] dip
|
||||
|
||||
[ contents ] with-process-reader ; inline
|
||||
|
||||
: run-utf8-process>string ( command -- string )
|
||||
utf8 run-process>string ; inline
|
||||
|
||||
|
||||
! Shell things.
|
||||
SYMBOL: factsh-directory-stack
|
||||
factsh-directory-stack [ V{ } clone ] initialize
|
||||
|
||||
SYMBOL: previous-directory
|
||||
SYMBOL: pending-directory
|
||||
|
||||
|
||||
: pushd ( string -- )
|
||||
factsh-directory-stack get push ;
|
||||
|
||||
: popd ( -- string )
|
||||
factsh-directory-stack get pop ;
|
||||
<<
|
||||
: matching-delimiter ( ch -- ch' )
|
||||
H{
|
||||
{ CHAR: ( CHAR: ) }
|
||||
{ CHAR: [ CHAR: ] }
|
||||
{ CHAR: { CHAR: } }
|
||||
{ CHAR: < CHAR: > }
|
||||
{ CHAR: : CHAR: ; }
|
||||
} ?at drop ;
|
||||
|
||||
: matching-delimiter-string ( string -- string' )
|
||||
[ matching-delimiter ] map ;
|
||||
>>
|
||||
|
||||
<<
|
||||
|
||||
SYNTAX: STRING-SYNTAX:
|
||||
scan-token
|
||||
[ create-class-in dup tuple { "payload" } define-tuple-class ]
|
||||
[ ] bi
|
||||
{ "[[" "[=[" "[==[" "[===[" "[====[" } [
|
||||
[ append create-word-in dup reset-generic ]
|
||||
[ nip matching-delimiter-string ] 2bi
|
||||
rot
|
||||
'[ _ parse-multiline-string _ boa suffix! ] define-syntax
|
||||
] with with each ;
|
||||
|
||||
>>
|
||||
|
||||
<<
|
||||
! STRING-SYNTAX: FACTSH
|
||||
STRING-SYNTAX: FACTOR
|
||||
STRING-SYNTAX: PYTHON
|
||||
STRING-SYNTAX: RUBY
|
||||
>>
|
||||
|
||||
GENERIC: execute-string ( obj -- string )
|
||||
M: FACTOR execute-string
|
||||
payload>> "-e=" prepend vm-path swap 2array run-utf8-process>string ;
|
||||
M: PYTHON execute-string
|
||||
payload>> 1array { "python" "-c" } prepend run-utf8-process>string ;
|
||||
M: RUBY execute-string
|
||||
payload>> 1array { "ruby" "-e" } prepend run-utf8-process>string ;
|
||||
|
||||
ERROR: expected-len1 obj ;
|
||||
: len1 ( seq -- obj ) dup length 1 = [ first ] [ expected-len1 ] if ;
|
||||
ERROR: expected-len2 obj ;
|
||||
: len2 ( seq -- obj1 obj2 ) dup length 2 = [ first2 ] [ expected-len2 ] if ;
|
||||
|
||||
: find-binary-name ( string -- string/path ? )
|
||||
dup find-in-standard-login-path [ nip t ] [ f ] if* ;
|
||||
|
||||
: transfer ( var1 var2 -- )
|
||||
[ get ] dip set ; inline
|
||||
|
||||
: swap-vars ( var1 var2 -- )
|
||||
[ [ get ] bi@ ] 2keep [ set ] dip set ; inline
|
||||
|
||||
ERROR: builtin-failed command argument message ;
|
||||
: check-directory-exists ( to -- to )
|
||||
dup { [ exists? ] [ file-info directory? ] } 1&&
|
||||
[ "cd" swap "No such directory" builtin-failed ] unless ;
|
||||
|
||||
: root-path ( path -- path' )
|
||||
dup absolute-path? [
|
||||
dup [ path-separator? ] find
|
||||
drop 1 + head
|
||||
] when ;
|
||||
|
||||
: relative-path ( path -- relative-path )
|
||||
dup absolute-path? [
|
||||
dup [ path-separator? ] find
|
||||
drop 1 + tail
|
||||
] when ;
|
||||
|
||||
: canonicalize-path ( path -- path' )
|
||||
[
|
||||
relative-path
|
||||
[ path-separator? ] split-when
|
||||
[ { "." "" } member? ] reject
|
||||
V{ } clone [
|
||||
dup ".." = [
|
||||
over empty?
|
||||
[ over push ]
|
||||
[ over ?last ".." = [ over push ] [ drop dup pop* ] if ] if
|
||||
] [
|
||||
over push
|
||||
] if
|
||||
] reduce
|
||||
] keep dup absolute-path? [
|
||||
[
|
||||
[ ".." = ] trim-head
|
||||
path-separator join
|
||||
] dip root-path prepend-path
|
||||
] [
|
||||
drop path-separator join [ "." ] when-empty
|
||||
] if ;
|
||||
|
||||
: count-canonical-path-components ( path -- n )
|
||||
canonicalize-path [ CHAR: / = ] count 1 + ;
|
||||
|
||||
: mismatch-tail ( seq1 seq2 -- seq1 seq2 )
|
||||
2dup mismatch [ 2dup [ length ] bi@ min ] unless*
|
||||
'[ _ tail ] bi@ ;
|
||||
|
||||
! c:\ vs d:\ -- no relative path possible. also impossible on unix.
|
||||
: derive-both-absolute ( absolute-path1 absolute-path2 -- path1-relative-to-path2 )
|
||||
[ "/" split ] bi@ mismatch-tail [ "/" join ] bi@
|
||||
count-canonical-path-components
|
||||
[ ".." ] replicate "/" join prepend-path ;
|
||||
|
||||
: derive-relative-path ( path1 path2 -- path1-relative-to-path2 )
|
||||
[ canonicalize-path ] bi@
|
||||
{
|
||||
{ [ 2dup [ absolute-path ] both? ] [ derive-both-absolute ] }
|
||||
[ "unimplemented" throw ]
|
||||
} cond ; inline
|
||||
|
||||
: home-directory? ( path -- ? ) "~" head? ;
|
||||
: current-directory? ( path -- ? ) "./" head? ;
|
||||
|
||||
: fixup-home-directory ( path -- path' )
|
||||
[ path-separator? ] split-when
|
||||
dup first length 1 > [
|
||||
unclip [ "/" join ] [ rest home parent-directory prepend ] bi* append
|
||||
] [
|
||||
"/" join
|
||||
] if ;
|
||||
|
||||
: our-cd ( args -- string/f )
|
||||
'[
|
||||
_ [ trim-blanks ] map
|
||||
current-directory pending-directory transfer
|
||||
[
|
||||
current-directory '[ drop home ] change
|
||||
] [
|
||||
{
|
||||
{ [ dup { "-" } sequence= ] [ drop previous-directory get current-directory set ] }
|
||||
{ [ dup first home-directory? ] [ first fixup-home-directory current-directory set ] }
|
||||
! { { "-L" } [ ] }
|
||||
! { { "-P" } [ ] }
|
||||
[ len1 normalize-path canonicalize-path check-directory-exists current-directory set ]
|
||||
} cond
|
||||
] if-empty
|
||||
pending-directory previous-directory transfer f
|
||||
] [
|
||||
unparse
|
||||
] recover ;
|
||||
|
||||
SYMBOL: exit-shell?
|
||||
|
||||
ERROR: unknown-command cmd args ;
|
||||
: eval-factsh ( string -- string-output )
|
||||
[ blank? ] trim-head " " split harvest [
|
||||
f
|
||||
] [
|
||||
unclip {
|
||||
{ "cd" [ our-cd ] }
|
||||
{ "ls" [ { "ls" } prepend utf8 run-process>string ] }
|
||||
{ "pwd" [ drop current-directory get "\n" append ] }
|
||||
{ "exit" [ [ 0 exit-shell? set ] [ len1 exit-shell? set ] if-empty "logout\n" ] }
|
||||
! { "pushd" [ len1 current-directory get ] }
|
||||
! { "popd" [ current-directory get ] }
|
||||
[
|
||||
dup current-directory? [
|
||||
dup current-directory get prepend-path
|
||||
dup exists? [
|
||||
2nip run-utf8-process>string
|
||||
] [
|
||||
swap unknown-command
|
||||
] if
|
||||
] [
|
||||
find-binary-name [ prefix run-utf8-process>string ]
|
||||
[ swap unknown-command ] if
|
||||
] if
|
||||
]
|
||||
} case
|
||||
] if-empty ;
|
||||
|
||||
: computer-name ( -- string ) { 1 10 } sysctl-query-string "." split1 drop ;
|
||||
|
||||
: osx-bash-prompt ( -- string )
|
||||
[
|
||||
computer-name ":"
|
||||
current-directory get
|
||||
dup home = [ drop "~" ] [ file-name ] if
|
||||
" " real-user-name "$ >> "
|
||||
] "" append-outputs-as ;
|
||||
|
||||
: echo-prompt ( -- )
|
||||
osx-bash-prompt write flush ;
|
||||
|
||||
SYMBOL: shell-last-exit
|
||||
|
||||
GENERIC: handle-repl-error ( obj -- )
|
||||
M: unknown-command handle-repl-error
|
||||
cmd>> ": command not found" append print ;
|
||||
|
||||
M: process-failed handle-repl-error
|
||||
process>> status>> shell-last-exit set ;
|
||||
|
||||
: factsh-repl ( -- )
|
||||
! [ "hello SIGUSR1" print flush ] SIGUSR1 add-signal-handler
|
||||
[ "hello SIGINT" print flush ] SIGINT add-signal-handler
|
||||
[ "hello SIGSTOP" print flush ] SIGSTOP add-signal-handler
|
||||
[ "hello SIGQUIT" print flush ] SIGQUIT add-signal-handler
|
||||
[ "hello SIGHUP" print flush ] SIGHUP add-signal-handler
|
||||
[ "hello SIGTERM" print flush ] SIGTERM add-signal-handler
|
||||
[ "hello SIGHUP" print flush ] SIGHUP add-signal-handler
|
||||
|
||||
f exit-shell? [
|
||||
current-directory previous-directory transfer
|
||||
[
|
||||
[ echo-prompt readln [ eval-factsh [ write ] when* ] [ nl t exit-shell? set ] if* ]
|
||||
[ handle-repl-error ] recover
|
||||
exit-shell? get not
|
||||
] loop
|
||||
] with-variable ;
|
||||
|
||||
MAIN: factsh-repl
|
||||
! PYTHON[[ print("hi")]] execute-string
|
||||
! RUBY[[ printf('hello')]] execute-string
|
||||
! FACTOR[[ USE: io "hi" print ]] execute-string
|
||||
|
||||
|
||||
! stty -a
|
||||
! ps ax -O tpgid
|
||||
! http://unix.stackexchange.com/questions/149741/why-is-sigint-not-propagated-to-child-process-when-sent-to-its-parent-process
|
||||
|
Loading…
Reference in New Issue