球面ROFL

社長:今日はROFLを球体に貼り付けてみたいですね。

開発:球体にテクスチャを貼るには… ああ、地球をX3DOMで回している人がいますね。まんまHTMLを拝借して画像を貼り付けてみます…

社長、基盤:おおーっ。

開発:題して、球面デスクトップ。

社長:地図を貼り付けて回すとどうなるでしょうね?

開発:オープンストリートマップから頂いてきて、貼ってみます…

開発:昔の世界地図ではこう。

開発:Google Map を貼って星の王子さま的天体。

社長:Google Earth を私の行動半径で貼って見るとこんな感じですね。

基盤:Earthでみると、うちの屋上は不思議な構造というか素材になってるんですね。

社長:しかしこれ、3Dで大きな建物の四方の側面がちゃんと見えるって、どういう撮影方法なんでしょう?

開発:二機を並べて飛ばしてるんでしょうかね。

基盤:さすがに偵察衛星から撮ってるとかでは無いですよね。

社長:わたしの実家とその周辺もきっちり映ってるんです。子供の頃にこういうものを見られたら、世界観が変わったかもしれません。

開発:いまの子どもたちはこういうのを見てるんですねー。

基盤:何十年も昔の画像があったら良かったのにですね。

開発:何万年前とか何億年前のなら逆に気楽に合成できるかもですね。

基盤:松見公園のせんぬきは Earth で見ても異彩を放ってますね。

社長:高崎の観音様もばっちり映ってます。

開発:ああ、それでROFLなんですけど。直接球体に描く方法はまだわからないですが、まずはこういう感じで、普通に二次元画像データを球体に貼り付ければOKだということです。

基盤:極のほうまで描くにはメルカトル風に拡大してやらないといけないですね。

社長:あと、Rolling on the floor なんで、その場の自転だけじゃなくてころげ回らないといけないですね。

基盤:いま検索したら、Unicode に Rofl って割り当てられてるんですね。2016年だそうです。HTMLエンティティ的には 🤣

社長、開発:し、知りませんでした…

社長:どれどれ…

🤣 🤣 🤣 🤣

開発:なるほど。さすがメタフォントですね。

社長:拡大するとひとみ的なものがあるのがちょっとガッカリです。

開発:ロードする時に一瞬別の、赤っぽい字体が見えるのはなんなんでしょう?

基盤:フォントはブラウザによらず、OSによらず、同じですね。

開発:font-family を変えてみるとどうでしょうかね…

基盤:変わらないですね。

社長:機械的に斜体にされてるのが面白いですね。

* * *

開発:さて、出発点に戻りまして。球体に絵を描く、という。必要なのはこれだけです。

<script src="/gshell/x3dom-1.8.2-dev/x3dom.js"></script>
<link rel="stylesheet" href="/gshell/x3dom-1.8.2-dev/x3dom.css">

<x3d width="200px" height="200px">
  <scene><shape>
   <appearance><ImageTexture url="/gshell/WD-WallPaper03.png" /></appearance>
   <sphere DEF="obj" radius="3.3" />
  </shape></scene>
</x3d>

開発:これで、urlで指定した画像で覆われた、radiusで指定したサイズの球体が作られ、描画される。その球体は、マウスのドラッグで回転させることができ、ホイールで拡大縮小ができる。↓

社長:script と css の明示的なロードは、x3dom が真に標準化したらいらなくなるのでしょうね。

開発:X3DOMは1Mバイト近く、そこそこでかいので、ブラウザにプレインストールされてると良いですね。

社長:でもJavaScriptのソースで1MBって十分小さいですよね。

開発:圧縮すると200KB以下になりますね。

社長:配布元のサーバからダウンロードするとかなり重いですね。こういう標準活動を、Google が GoogleDrive とかでサポートしてあげるといいんじゃないでしょうか。

開発:W3Cのサイトとか、唖然とするほど遅いですよね。

基盤:SkypeのROFLを貼るとどうなるでしょう?

開発:球面全体に引き伸ばして描いちゃうと悲惨なことになるでしょうね…

全員:やはりw

基盤:タコかイカの目みたいですね。

開発:小さめに描いた図を貼り付けてみます… … … 思ったほどは小さくなりません。どういう座標を想定しているのやら…

開発:まあ一応顔全体が半球に収まりました。ついでに球面ディスプレイとも合体させてみました。

社長、基盤:(笑)

社長:表情を変えるROFLはまだちょっと難しいので、3DのXeyesとかやってみると良いかもですね。

-- 2020-1112 SatoxITS

GShell 0.8.1 − ITS more ROFL

社長:ということで、我社の3D-ROFLに取り組みましょう。

開発:これもまた初歩の初歩からスタートですね…

* * *

開発:いやはや、X3DOMって、ドキュメントがよくわからないですね。指定する数値の単位とか… なんとか箱が書けましたが…

基盤:おーっ、なんか飛び出してくる感じがします。

社長:さすが3D。

* * *

開発:canvasに描いたスマイルマークを箱に張ってみました。

社長:おーっと、透明の画像でも箱の内側が見えないんですね。

開発:スケスケ地球儀とか出来たらカッコいいんですけどね…

基盤:canvas を直接は貼れないんでしょうか?

開発:うーん。入門コースから見た雰囲気では、ダメなんじゃないかという感じがしますね…もっともx3dom.js のソースがありますから、拡張することはできると思いますが…

社長:裏面を別途作って下の層に重ねればいいんじゃないでしょうかね。

開発:かも知れません。

-- 2020-1111 SatoxITS

/* GShell-0.8.1 by SatoxITS
GShell version 0.8.1 // 2020-11-11 // SatoxITS
*/ // // /*
Topbar

Topbar

*/ //
// // // /*
Indexer

Indexer

*/ //
// /*

GShell // a General purpose Shell built on the top of Golang

It is a shell for myself, by myself, of myself. --SatoxITS(^-^) prev.

Edit Save Load Vers 0 Fork Stop Unfold Digest Source
*/ /*
Statement

Fun to create a shell

For a programmer, it must be far easy and fun to create his own simple shell rightly fitting to his favor and necessities, than learning existing shells with complex full features that he never use. I, as one of programmers, am writing this tiny shell for my own real needs, totally from scratch, with fun.

For a programmer, it is fun to learn new computer languages. For long years before writing this software, I had been specialized to C and early HTML2 :-). Now writing this software, I'm learning Go language, HTML5, JavaScript and CSS on demand as a novice of these, with fun.

This single file "gsh.go", that is executable by Go, contains all of the code written in Go. Also it can be displayed as "gsh.go.html" by browsers. It is a standalone HTML file that works as the viewer of the code of itself, and as the "home page" of this software.

Because this HTML file is a Go program, you may run it as a real shell program on your computer. But you must be aware that this program is written under situation like above. Needless to say, there is no warranty for this program in any means.

Aug 2020, SatoxITS (sato@its-more.jp)
*/ /*
Features

Cross-browser communication

... to be written ...

Vi compatible command line editor

The command line of GShell can be edited with commands compatible with vi. As in vi, you can enter command mode by ESC key, then move around in the history by j k / ? n N, or within the current line by l h f w b 0 $ % or so.

*/ /*
Index
Documents Command summary Go lang part Package structures import struct Main functions str-expansion // macro processor finder // builtin find + du grep // builtin grep + wc + cksum + ... plugin // plugin commands system // external commands builtin // builtin commands network // socket handler remote-sh // remote shell redirect // StdIn/Out redireciton history // command history rusage // resouce usage encode // encode / decode IME // command line IME getline // line editor scanf // string decomposer interpreter // command interpreter main JavaScript part Source Builtin data CSS part Source References Internal External Whole parts Source Download Dump
*/ //
//Go Source
// gsh - Go lang based Shell // (c) 2020 ITS more Co., Ltd. // 2020-0807 created by SatoxITS (sato@its-more.jp) package main // gsh main // Imported packages // Packages import ( "fmt" // fmt "errors" "strings" // strings "strconv" // strconv "sort" // sort "time" // time "bufio" // bufio "io/ioutil" // ioutil "os" // os "syscall" // syscall "plugin" // plugin "net" // net "net/http" // http //"html" // html "path/filepath" // filepath "go/types" // types "go/token" // token "encoding/base64" // base64 "unicode/utf8" // utf8 //"gshdata" // gshell's logo and source code "hash/crc32" // crc32 "golang.org/x/net/websocket" "runtime" ) /* #include // to be closed as HTML tag :-p #ifdef _WIN32 #include // // 2020-1022 added -- terminal mode on Windows // https://docs.microsoft.com/en-us/windows/console/setconsolemode // https://docs.microsoft.com/en-us/windows/win32/inputdev/using-keyboard-input int setTermRaw(){ HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); DWORD tmode = 0; if( GetConsoleMode(hStdin,&tmode) ){ DWORD xmode = tmode; xmode &= ~ENABLE_ECHO_INPUT; xmode &= ~ENABLE_LINE_INPUT; xmode |= ENABLE_PROCESSED_INPUT; // Control+C for SIGINT if( SetConsoleMode(hStdin,xmode) ){ return tmode; } } return 0; } int setTermMode(int tmode){ HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); SetConsoleMode(hStdin,tmode); return 0; } #else int setTermRaw(){ return -1; } int setTermMode(int tmode){ return 0; } #endif */ import "C" /* // // 2020-0906 added, // // CGo // #include "poll.h" // // to be closed as HTML tag :-p // typedef struct { struct pollfd fdv[8]; } pollFdv; // int pollx(pollFdv *fdv, int nfds, int timeout){ // return poll(fdv->fdv,nfds,timeout); // } import "C" // 2020-1021 replaced poll() with channel/select // // 2020-0906 added, func CFpollIn1(fp*os.File, timeoutUs int)(ready uintptr){ var fdv = C.pollFdv{} var nfds = 1 var timeout = timeoutUs/1000 fdv.fdv[0].fd = C.int(fp.Fd()) fdv.fdv[0].events = C.POLLIN if( 0 < EventRecvFd ){ fdv.fdv[1].fd = C.int(EventRecvFd) fdv.fdv[1].events = C.POLLIN nfds += 1 } r := C.pollx(&fdv,C.int(nfds),C.int(timeout)) if( r <= 0 ){ return 0 } if (int(fdv.fdv[1].revents) & int(C.POLLIN)) != 0 { //fprintf(stderr,"--De-- got Event\n"); return uintptr(EventFdOffset + fdv.fdv[1].fd) } if (int(fdv.fdv[0].revents) & int(C.POLLIN)) != 0 { return uintptr(NormalFdOffset + fdv.fdv[0].fd) } return 0 } */ const ( NAME = "gsh" VERSION = "0.8.1" DATE = "2020-11-11" AUTHOR = "SatoxITS(^-^)//" ) var ( GSH_HOME = ".gsh" // under home directory GSH_PORT = 9999 MaxStreamSize = int64(128*1024*1024*1024) // 128GiB is too large? PROMPT = "> " LINESIZE = (8*1024) PATHSEP = ":" // should be ";" in Windows DIRSEP = "/" // canbe \ in Windows OnWindows = false; ) func initGshEnv(){ if( runtime.GOOS == "windows" ){ PATHSEP = ";"; DIRSEP = "\\"; OnWindows = true; }else{ } } // -xX logging control // --A-- all // --I-- info. // --D-- debug // --T-- time and resource usage // --W-- warning // --E-- error // --F-- fatal error // --Xn- network // Structures // 2020-1022 Unix/Windows // ---------------------- //type aStat_t syscall.Stat_t; //type aStat_t struct { syscall.Stat_t } type aStat_t struct { Size int64 Mode os.FileMode Rdev int64 Blocks int64 Nlink int64 } func aLstat(path string, astat *aStat_t)(error){ /* sstat := syscall.Stat_t{}; err := syscall.Lstat(path,&sstat); *astat = aStat_t(sstat); */ fi,err := os.Stat(path); if( err == nil ){ astat.Mode = fi.Mode(); astat.Size = fi.Size(); } return err; } func aFstat(fd int, astat *aStat_t)(error){ /* sstat := syscall.Stat_t{}; err := syscall.Fstat(fd,&sstat); *astat = aStat_t(sstat); */ err := errors.New("NotImplemented-Fstat"); //fmt.Printf("---E-- fstat(%v)(%v)\n",fd,err); return err; } func aAccess(path string, mode uint32)(error){ //err := syscall.Access(path,mode); //err := errors.New("NotImplemented-Access"); fi,err := os.Stat(path) //fmt.Printf("-- Access(%v,%v)\n(%v)\n",path,mode,err); if( err == nil ){ fmode := fi.Mode(); if( fmode.IsRegular() ){ perm := fmode.Perm(); if( (uint32(perm) & mode) != 0 ){ return nil; } return errors.New("NotAccessible"); } return errors.New("NotRegularFile"); } return err; } // 2020-1022 Unix/Windows // ---------------------- type aRusage struct { syscall.Rusage Utime time.Duration Stime time.Duration //Sys interface{} } /* const aRUSAGE_SELF = syscall.RUSAGE_SELF const aRUSAGE_CHILDREN = syscall.RUSAGE_CHILDREN */ const aRUSAGE_SELF = 0 const aRUSAGE_CHILDREN = 1 func aGetrusage(sel int, ru *aRusage){ /* sysru := syscall.Rusage{}; syscall.Getrusage(sel,&sysru); ru.Utime = time.Duration(int64(sysru.Utime.Sec)*1000000000+int64(sysru.Utime.Usec)*1000); ru.Stime = time.Duration(int64(sysru.Stime.Sec)*1000000000+int64(sysru.Stime.Usec)*1000); */ } func aSetrusage(ru *aRusage, ps *os.ProcessState){ ru.Utime = ps.UserTime(); ru.Stime = ps.SystemTime(); } func showRusage(what string,argv []string, ru *aRusage){ fmt.Printf("%s: ",what); //fmt.Printf("Usr=%d.%06ds",ru.Utime.Sec,ru.Utime.Usec) //fmt.Printf(" Sys=%d.%06ds",ru.Stime.Sec,ru.Stime.Usec) fmt.Printf("Usr=%d.%06ds ",ru.Utime/1000000000,(ru.Utime/1000)%1000000); fmt.Printf(" Sys=%d.%06ds ",ru.Stime/1000000000,(ru.Stime/1000)%1000000); /* fmt.Printf(" Rss=%vB",ru.Maxrss) if isin("-l",argv) { fmt.Printf(" MinFlt=%v",ru.Minflt) fmt.Printf(" MajFlt=%v",ru.Majflt) fmt.Printf(" IxRSS=%vB",ru.Ixrss) fmt.Printf(" IdRSS=%vB",ru.Idrss) fmt.Printf(" Nswap=%vB",ru.Nswap) fmt.Printf(" Read=%v",ru.Inblock) fmt.Printf(" Write=%v",ru.Oublock) } fmt.Printf(" Snd=%v",ru.Msgsnd) fmt.Printf(" Rcv=%v",ru.Msgrcv) //if isin("-l",argv) { fmt.Printf(" Sig=%v",ru.Nsignals) //} */ fmt.Printf("\n"); } type GCommandHistory struct { StartAt time.Time // command line execution started at EndAt time.Time // command line execution ended at ResCode int // exit code of (external command) CmdError error // error string OutData *os.File // output of the command FoundFile []string // output - result of ufind Rusagev [2]aRusage // Resource consumption, CPU time or so CmdId int // maybe with identified with arguments or impact // redireciton commands should not be the CmdId WorkDir string // working directory at start WorkDirX int // index in ChdirHistory CmdLine string // command line } type GChdirHistory struct { Dir string MovedAt time.Time CmdIndex int } type CmdMode struct { BackGround bool } type Event struct { when time.Time event int evarg int64 CmdIndex int } var CmdIndex int var Events []Event type PluginInfo struct { Spec *plugin.Plugin Addr plugin.Symbol Name string // maybe relative Path string // this is in Plugin but hidden } type GServer struct { host string port string } // Digest const ( // SumType SUM_ITEMS = 0x000001 // items count SUM_SIZE = 0x000002 // data length (simplly added) SUM_SIZEHASH = 0x000004 // data length (hashed sequence) SUM_DATEHASH = 0x000008 // date of data (hashed sequence) // also envelope attributes like time stamp can be a part of digest // hashed value of sizes or mod-date of files will be useful to detect changes SUM_WORDS = 0x000010 // word count is a kind of digest SUM_LINES = 0x000020 // line count is a kind of digest SUM_SUM64 = 0x000040 // simple add of bytes, useful for human too SUM_SUM32_BITS = 0x000100 // the number of true bits SUM_SUM32_2BYTE = 0x000200 // 16bits words SUM_SUM32_4BYTE = 0x000400 // 32bits words SUM_SUM32_8BYTE = 0x000800 // 64bits words SUM_SUM16_BSD = 0x001000 // UNIXsum -sum -bsd SUM_SUM16_SYSV = 0x002000 // UNIXsum -sum -sysv SUM_UNIXFILE = 0x004000 SUM_CRCIEEE = 0x008000 ) type CheckSum struct { Files int64 // the number of files (or data) Size int64 // content size Words int64 // word count Lines int64 // line count SumType int Sum64 uint64 Crc32Table crc32.Table Crc32Val uint32 Sum16 int Ctime time.Time Atime time.Time Mtime time.Time Start time.Time Done time.Time RusgAtStart [2]aRusage RusgAtEnd [2]aRusage } type ValueStack [][]string type GshContext struct { StartDir string // the current directory at the start GetLine string // gsh-getline command as a input line editor ChdirHistory []GChdirHistory // the 1st entry is wd at the start //gshPA syscall.ProcAttr gshPA os.ProcAttr CommandHistory []GCommandHistory CmdCurrent GCommandHistory BackGround bool BackGroundJobs []os.ProcessState; //[]int LastRusage aRusage GshHomeDir string TerminalId int CmdTrace bool // should be [map] CmdTime bool // should be [map] PluginFuncs []PluginInfo iValues []string iDelimiter string // field sepearater of print out iFormat string // default print format (of integer) iValStack ValueStack LastServer GServer RSERV string // [gsh://]host[:port] RWD string // remote (target, there) working directory lastCheckSum CheckSum } func nsleep(ns time.Duration){ time.Sleep(ns) } func usleep(ns time.Duration){ nsleep(ns*1000) } func msleep(ns time.Duration){ nsleep(ns*1000000) } func sleep(ns time.Duration){ nsleep(ns*1000000000) } func strBegins(str, pat string)(bool){ if len(pat) <= len(str){ yes := str[0:len(pat)] == pat //fmt.Printf("--D-- strBegins(%v,%v)=%v\n",str,pat,yes) return yes } //fmt.Printf("--D-- strBegins(%v,%v)=%v\n",str,pat,false) return false } func isin(what string, list []string) bool { for _, v := range list { if v == what { return true } } return false } func isinX(what string,list[]string)(int){ for i,v := range list { if v == what { return i } } return -1 } func env(opts []string) { env := os.Environ() if isin("-s", opts){ sort.Slice(env, func(i,j int) bool { return env[i] < env[j] }) } for _, v := range env { fmt.Printf("%v\n",v) } } // - rewriting should be context dependent // - should postpone until the real point of evaluation // - should rewrite only known notation of symobl func scanInt(str string)(val int,leng int){ leng = -1 for i,ch := range str { if '0' <= ch && ch <= '9' { leng = i+1 }else{ break } } if 0 < leng { ival,_ := strconv.Atoi(str[0:leng]) return ival,leng }else{ return 0,0 } } func substHistory(gshCtx *GshContext,str string,i int,rstr string)(leng int,rst string){ if len(str[i+1:]) == 0 { return 0,rstr } hi := 0 histlen := len(gshCtx.CommandHistory) if str[i+1] == '!' { hi = histlen - 1 leng = 1 }else{ hi,leng = scanInt(str[i+1:]) if leng == 0 { return 0,rstr } if hi < 0 { hi = histlen + hi } } if 0 <= hi && hi < histlen { var ext byte if 1 < len(str[i+leng:]) { ext = str[i+leng:][1] } //fmt.Printf("--D-- %v(%c)\n",str[i+leng:],str[i+leng]) if ext == 'f' { leng += 1 xlist := []string{} list := gshCtx.CommandHistory[hi].FoundFile for _,v := range list { //list[i] = escapeWhiteSP(v) xlist = append(xlist,escapeWhiteSP(v)) } //rstr += strings.Join(list," ") rstr += strings.Join(xlist," ") }else if ext == '@' || ext == 'd' { // !N@ .. workdir at the start of the command leng += 1 rstr += gshCtx.CommandHistory[hi].WorkDir }else{ rstr += gshCtx.CommandHistory[hi].CmdLine } }else{ leng = 0 } return leng,rstr } func escapeWhiteSP(str string)(string){ if len(str) == 0 { return "\\z" // empty, to be ignored } rstr := "" for _,ch := range str { switch ch { case '\\': rstr += "\\\\" case ' ': rstr += "\\s" case '\t': rstr += "\\t" case '\r': rstr += "\\r" case '\n': rstr += "\\n" default: rstr += string(ch) } } return rstr } func unescapeWhiteSP(str string)(string){ // strip original escapes rstr := "" for i := 0; i < len(str); i++ { ch := str[i] if ch == '\\' { if i+1 < len(str) { switch str[i+1] { case 'z': continue; } } } rstr += string(ch) } return rstr } func unescapeWhiteSPV(strv []string)([]string){ // strip original escapes ustrv := []string{} for _,v := range strv { ustrv = append(ustrv,unescapeWhiteSP(v)) } return ustrv } // str-expansion // - this should be a macro processor func strsubst(gshCtx *GshContext,str string,histonly bool) string { rbuff := []byte{} if false { //@@U Unicode should be cared as a character return str } //rstr := "" inEsc := 0 // escape characer mode for i := 0; i < len(str); i++ { //fmt.Printf("--D--Subst %v:%v\n",i,str[i:]) ch := str[i] if inEsc == 0 { if ch == '!' { //leng,xrstr := substHistory(gshCtx,str,i,rstr) leng,rs := substHistory(gshCtx,str,i,"") if 0 < leng { //_,rs := substHistory(gshCtx,str,i,"") rbuff = append(rbuff,[]byte(rs)...) i += leng //rstr = xrstr continue } } switch ch { case '\\': inEsc = '\\'; continue //case '%': inEsc = '%'; continue case '$': } } switch inEsc { case '\\': switch ch { case '\\': ch = '\\' case 's': ch = ' ' case 't': ch = '\t' case 'r': ch = '\r' case 'n': ch = '\n' case 'z': inEsc = 0; continue // empty, to be ignored } inEsc = 0 case '%': switch { case ch == '%': ch = '%' case ch == 'T': //rstr = rstr + time.Now().Format(time.Stamp) rs := time.Now().Format(time.Stamp) rbuff = append(rbuff,[]byte(rs)...) inEsc = 0 continue; default: // postpone the interpretation //rstr = rstr + "%" + string(ch) rbuff = append(rbuff,ch) inEsc = 0 continue; } inEsc = 0 } //rstr = rstr + string(ch) rbuff = append(rbuff,ch) } //fmt.Printf("--D--subst(%s)(%s)\n",str,string(rbuff)) return string(rbuff) //return rstr } func showFileInfo(path string, opts []string) { if isin("-l",opts) || isin("-ls",opts) { fi, err := os.Stat(path) if err != nil { fmt.Printf("---------- ((%v))",err) }else{ mod := fi.ModTime() date := mod.Format(time.Stamp) fmt.Printf("%v %8v %s ",fi.Mode(),fi.Size(),date) } } fmt.Printf("%s",path) if isin("-sp",opts) { fmt.Printf(" ") }else if ! isin("-n",opts) { fmt.Printf("\n") } } func userHomeDir()(string,bool){ /* homedir,_ = os.UserHomeDir() // not implemented in older Golang */ homedir,found := os.LookupEnv("HOME") //fmt.Printf("--I-- HOME=%v(%v)\n",homedir,found) if !found { return "/tmp",found } return homedir,found } func toFullpath(path string) (fullpath string) { if path[0] == '/' { return path } pathv := strings.Split(path,DIRSEP) switch { case pathv[0] == ".": pathv[0], _ = os.Getwd() case pathv[0] == "..": // all ones should be interpreted cwd, _ := os.Getwd() ppathv := strings.Split(cwd,DIRSEP) pathv[0] = strings.Join(ppathv,DIRSEP) case pathv[0] == "~": pathv[0],_ = userHomeDir() default: cwd, _ := os.Getwd() pathv[0] = cwd + DIRSEP + pathv[0] } return strings.Join(pathv,DIRSEP) } func IsRegFile(path string)(bool){ fi, err := os.Stat(path) if err == nil { fm := fi.Mode() return fm.IsRegular(); } return false } // Encode / Decode // Encoder func (gshCtx *GshContext)Enc(argv[]string){ file := os.Stdin buff := make([]byte,LINESIZE) li := 0 encoder := base64.NewEncoder(base64.StdEncoding,os.Stdout) for li = 0; ; li++ { count, err := file.Read(buff) if count <= 0 { break } if err != nil { break } encoder.Write(buff[0:count]) } encoder.Close() } func (gshCtx *GshContext)Dec(argv[]string){ decoder := base64.NewDecoder(base64.StdEncoding,os.Stdin) li := 0 buff := make([]byte,LINESIZE) for li = 0; ; li++ { count, err := decoder.Read(buff) if count <= 0 { break } if err != nil { break } os.Stdout.Write(buff[0:count]) } } // lnsp [N] [-crlf][-C \\] func (gshCtx *GshContext)SplitLine(argv[]string){ strRep := isin("-str",argv) // "..."+ reader := bufio.NewReaderSize(os.Stdin,64*1024) ni := 0 toi := 0 for ni = 0; ; ni++ { line, err := reader.ReadString('\n') if len(line) <= 0 { if err != nil { fmt.Fprintf(os.Stderr,"--I-- lnsp %d to %d (%v)\n",ni,toi,err) break } } off := 0 ilen := len(line) remlen := len(line) if strRep { os.Stdout.Write([]byte("\"")) } for oi := 0; 0 < remlen; oi++ { olen := remlen addnl := false if 72 < olen { olen = 72 addnl = true } fmt.Fprintf(os.Stderr,"--D-- write %d [%d.%d] %d %d/%d/%d\n", toi,ni,oi,off,olen,remlen,ilen) toi += 1 os.Stdout.Write([]byte(line[0:olen])) if addnl { if strRep { os.Stdout.Write([]byte("\"+\n\"")) }else{ //os.Stdout.Write([]byte("\r\n")) os.Stdout.Write([]byte("\\")) os.Stdout.Write([]byte("\n")) } } line = line[olen:] off += olen remlen -= olen } if strRep { os.Stdout.Write([]byte("\"\n")) } } fmt.Fprintf(os.Stderr,"--I-- lnsp %d to %d\n",ni,toi) } // CRC32 crc32 // 1 0000 0100 1100 0001 0001 1101 1011 0111 var CRC32UNIX uint32 = uint32(0x04C11DB7) // Unix cksum var CRC32IEEE uint32 = uint32(0xEDB88320) func byteCRC32add(crc uint32,str[]byte,len uint64)(uint32){ var oi uint64 for oi = 0; oi < len; oi++ { var oct = str[oi] for bi := 0; bi < 8; bi++ { //fprintf(stderr,"--CRC32 %d %X (%d.%d)\n",crc,oct,oi,bi) ovf1 := (crc & 0x80000000) != 0 ovf2 := (oct & 0x80) != 0 ovf := (ovf1 && !ovf2) || (!ovf1 && ovf2) oct <<= 1 crc <<= 1 if ovf { crc ^= CRC32UNIX } } } //fprintf(stderr,"--CRC32 return %d %d\n",crc,len) return crc; } func byteCRC32end(crc uint32, len uint64)(uint32){ var slen = make([]byte,4) var li = 0 for li = 0; li < 4; { slen[li] = byte(len) li += 1 len >>= 8 if( len == 0 ){ break } } crc = byteCRC32add(crc,slen,uint64(li)) crc ^= 0xFFFFFFFF return crc } func strCRC32(str string,len uint64)(crc uint32){ crc = byteCRC32add(0,[]byte(str),len) crc = byteCRC32end(crc,len) //fprintf(stderr,"--CRC32 %d %d\n",crc,len) return crc } func CRC32Finish(crc uint32, table *crc32.Table, len uint64)(uint32){ var slen = make([]byte,4) var li = 0 for li = 0; li < 4; { slen[li] = byte(len & 0xFF) li += 1 len >>= 8 if( len == 0 ){ break } } crc = crc32.Update(crc,table,slen) crc ^= 0xFFFFFFFF return crc } func (gsh*GshContext)xCksum(path string,argv[]string, sum*CheckSum)(int64){ if isin("-type/f",argv) && !IsRegFile(path){ return 0 } if isin("-type/d",argv) && IsRegFile(path){ return 0 } file, err := os.OpenFile(path,os.O_RDONLY,0) if err != nil { fmt.Printf("--E-- cksum %v (%v)\n",path,err) return -1 } defer file.Close() if gsh.CmdTrace { fmt.Printf("--I-- cksum %v %v\n",path,argv) } bi := 0 var buff = make([]byte,32*1024) var total int64 = 0 var initTime = time.Time{} if sum.Start == initTime { sum.Start = time.Now() } for bi = 0; ; bi++ { count,err := file.Read(buff) if count <= 0 || err != nil { break } if (sum.SumType & SUM_SUM64) != 0 { s := sum.Sum64 for _,c := range buff[0:count] { s += uint64(c) } sum.Sum64 = s } if (sum.SumType & SUM_UNIXFILE) != 0 { sum.Crc32Val = byteCRC32add(sum.Crc32Val,buff,uint64(count)) } if (sum.SumType & SUM_CRCIEEE) != 0 { sum.Crc32Val = crc32.Update(sum.Crc32Val,&sum.Crc32Table,buff[0:count]) } // BSD checksum if (sum.SumType & SUM_SUM16_BSD) != 0 { s := sum.Sum16 for _,c := range buff[0:count] { s = (s >> 1) + ((s & 1) << 15) s += int(c) s &= 0xFFFF //fmt.Printf("BSDsum: %d[%d] %d\n",sum.Size+int64(i),i,s) } sum.Sum16 = s } if (sum.SumType & SUM_SUM16_SYSV) != 0 { for bj := 0; bj < count; bj++ { sum.Sum16 += int(buff[bj]) } } total += int64(count) } sum.Done = time.Now() sum.Files += 1 sum.Size += total if !isin("-s",argv) { fmt.Printf("%v ",total) } return 0 } // grep // "lines", "lin" or "lnp" for "(text) line processor" or "scanner" // a*,!ab,c, ... sequentioal combination of patterns // what "LINE" is should be definable // generic line-by-line processing // grep [-v] // cat -n -v // uniq [-c] // tail -f // sed s/x/y/ or awk // grep with line count like wc // rewrite contents if specified func (gsh*GshContext)xGrep(path string,rexpv[]string)(int){ file, err := os.OpenFile(path,os.O_RDONLY,0) if err != nil { fmt.Printf("--E-- grep %v (%v)\n",path,err) return -1 } defer file.Close() if gsh.CmdTrace { fmt.Printf("--I-- grep %v %v\n",path,rexpv) } //reader := bufio.NewReaderSize(file,LINESIZE) reader := bufio.NewReaderSize(file,80) li := 0 found := 0 for li = 0; ; li++ { line, err := reader.ReadString('\n') if len(line) <= 0 { break } if 150 < len(line) { // maybe binary break; } if err != nil { break } if 0 <= strings.Index(string(line),rexpv[0]) { found += 1 fmt.Printf("%s:%d: %s",path,li,line) } } //fmt.Printf("total %d lines %s\n",li,path) //if( 0 < found ){ fmt.Printf("((found %d lines %s))\n",found,path); } return found } // Finder // finding files with it name and contents // file names are ORed // show the content with %x fmt list // ls -R // tar command by adding output type fileSum struct { Err int64 // access error or so Size int64 // content size DupSize int64 // content size from hard links Blocks int64 // number of blocks (of 512 bytes) DupBlocks int64 // Blocks pointed from hard links HLinks int64 // hard links Words int64 Lines int64 Files int64 Dirs int64 // the num. of directories SymLink int64 Flats int64 // the num. of flat files MaxDepth int64 MaxNamlen int64 // max. name length nextRepo time.Time } func showFusage(dir string,fusage *fileSum){ bsume := float64(((fusage.Blocks-fusage.DupBlocks)/2)*1024)/1000000.0 //bsumdup := float64((fusage.Blocks/2)*1024)/1000000.0 fmt.Printf("%v: %v files (%vd %vs %vh) %.6f MB (%.2f MBK)\n", dir, fusage.Files, fusage.Dirs, fusage.SymLink, fusage.HLinks, float64(fusage.Size)/1000000.0,bsume); } const ( S_IFMT = 0170000 S_IFCHR = 0020000 S_IFDIR = 0040000 S_IFREG = 0100000 S_IFLNK = 0120000 S_IFSOCK = 0140000 ) func cumFinfo(fsum *fileSum, path string, staterr error, fstat aStat_t, argv[]string,verb bool)(*fileSum){ now := time.Now() if time.Second <= now.Sub(fsum.nextRepo) { if !fsum.nextRepo.IsZero(){ tstmp := now.Format(time.Stamp) showFusage(tstmp,fsum) } fsum.nextRepo = now.Add(time.Second) } if staterr != nil { fsum.Err += 1 return fsum } fsum.Files += 1 if 1 < fstat.Nlink { // must count only once... // at least ignore ones in the same directory //if finfo.Mode().IsRegular() { if (fstat.Mode & S_IFMT) == S_IFREG { fsum.HLinks += 1 fsum.DupBlocks += int64(fstat.Blocks) //fmt.Printf("---Dup HardLink %v %s\n",fstat.Nlink,path) } } //fsum.Size += finfo.Size() fsum.Size += fstat.Size fsum.Blocks += int64(fstat.Blocks) //if verb { fmt.Printf("(%8dBlk) %s",fstat.Blocks/2,path) } if isin("-ls",argv){ //if verb { fmt.Printf("%4d %8d ",fstat.Blksize,fstat.Blocks) } // fmt.Printf("%d\t",fstat.Blocks/2) } //if finfo.IsDir() if (fstat.Mode & S_IFMT) == S_IFDIR { fsum.Dirs += 1 } //if (finfo.Mode() & os.ModeSymlink) != 0 if (fstat.Mode & S_IFMT) == S_IFLNK { //if verb { fmt.Printf("symlink(%v,%s)\n",fstat.Mode,finfo.Name()) } //{ fmt.Printf("symlink(%o,%s)\n",fstat.Mode,finfo.Name()) } fsum.SymLink += 1 } return fsum } func (gsh*GshContext)xxFindEntv(depth int,total *fileSum,dir string, dstat aStat_t, ei int, entv []string,npatv[]string,argv[]string)(*fileSum){ nols := isin("-grep",argv) // sort entv /* if isin("-t",argv){ sort.Slice(filev, func(i,j int) bool { return 0 < filev[i].ModTime().Sub(filev[j].ModTime()) }) } */ /* if isin("-u",argv){ sort.Slice(filev, func(i,j int) bool { return 0 < filev[i].AccTime().Sub(filev[j].AccTime()) }) } if isin("-U",argv){ sort.Slice(filev, func(i,j int) bool { return 0 < filev[i].CreatTime().Sub(filev[j].CreatTime()) }) } */ /* if isin("-S",argv){ sort.Slice(filev, func(i,j int) bool { return filev[j].Size() < filev[i].Size() }) } */ for _,filename := range entv { for _,npat := range npatv { match := true if npat == "*" { match = true }else{ match, _ = filepath.Match(npat,filename) } path := dir + DIRSEP + filename if !match { continue } var fstat aStat_t staterr := aLstat(path,&fstat) if staterr != nil { if !isin("-w",argv){fmt.Printf("ufind: %v\n",staterr) } continue; } if isin("-du",argv) && (fstat.Mode & S_IFMT) == S_IFDIR { // should not show size of directory in "-du" mode ... }else if !nols && !isin("-s",argv) && (!isin("-du",argv) || isin("-a",argv)) { if isin("-du",argv) { fmt.Printf("%d\t",fstat.Blocks/2) } showFileInfo(path,argv) } if true { // && isin("-du",argv) total = cumFinfo(total,path,staterr,fstat,argv,false) } /* if isin("-wc",argv) { } */ if gsh.lastCheckSum.SumType != 0 { gsh.xCksum(path,argv,&gsh.lastCheckSum); } x := isinX("-grep",argv); // -grep will be convenient like -ls if 0 <= x && x+1 <= len(argv) { // -grep will be convenient like -ls if IsRegFile(path){ found := gsh.xGrep(path,argv[x+1:]) if 0 < found { foundv := gsh.CmdCurrent.FoundFile if len(foundv) < 10 { gsh.CmdCurrent.FoundFile = append(gsh.CmdCurrent.FoundFile,path) } } } } if !isin("-r0",argv) { // -d 0 in du, -depth n in find //total.Depth += 1 if (fstat.Mode & S_IFMT) == S_IFLNK { continue } if dstat.Rdev != fstat.Rdev { fmt.Printf("--I-- don't follow differnet device %v(%v) %v(%v)\n", dir,dstat.Rdev,path,fstat.Rdev) } if (fstat.Mode & S_IFMT) == S_IFDIR { total = gsh.xxFind(depth+1,total,path,npatv,argv) } } } } return total } func (gsh*GshContext)xxFind(depth int,total *fileSum,dir string,npatv[]string,argv[]string)(*fileSum){ nols := isin("-grep",argv) dirfile,oerr := os.OpenFile(dir,os.O_RDONLY,0) if oerr == nil { //fmt.Printf("--I-- %v(%v)[%d]\n",dir,dirfile,dirfile.Fd()) defer dirfile.Close() }else{ } prev := *total var dstat aStat_t staterr := aLstat(dir,&dstat) // should be flstat if staterr != nil { if !isin("-w",argv){ fmt.Printf("ufind: %v\n",staterr) } return total } //filev,err := ioutil.ReadDir(dir) //_,err := ioutil.ReadDir(dir) // ReadDir() heavy and bad for huge directory /* if err != nil { if !isin("-w",argv){ fmt.Printf("ufind: %v\n",err) } return total } */ if depth == 0 { total = cumFinfo(total,dir,staterr,dstat,argv,true) if !nols && !isin("-s",argv) && (!isin("-du",argv) || isin("-a",argv)) { showFileInfo(dir,argv) } } // it it is not a directory, just scan it and finish for ei := 0; ; ei++ { entv,rderr := dirfile.Readdirnames(8*1024) if len(entv) == 0 || rderr != nil { //if rderr != nil { fmt.Printf("[%d] len=%d (%v)\n",ei,len(entv),rderr) } break } if 0 < ei { fmt.Printf("--I-- xxFind[%d] %d large-dir: %s\n",ei,len(entv),dir) } total = gsh.xxFindEntv(depth,total,dir,dstat,ei,entv,npatv,argv) } if isin("-du",argv) { // if in "du" mode fmt.Printf("%d\t%s\n",(total.Blocks-prev.Blocks)/2,dir) } return total } // {ufind|fu|ls} [Files] [// Names] [-- Expressions] // Files is "." by default // Names is "*" by default // Expressions is "-print" by default for "ufind", or -du for "fu" command func (gsh*GshContext)xFind(argv[]string){ if 0 < len(argv) && strBegins(argv[0],"?"){ showFound(gsh,argv) return } if isin("-cksum",argv) || isin("-sum",argv) { gsh.lastCheckSum = CheckSum{} if isin("-sum",argv) && isin("-add",argv) { gsh.lastCheckSum.SumType |= SUM_SUM64 }else if isin("-sum",argv) && isin("-size",argv) { gsh.lastCheckSum.SumType |= SUM_SIZE }else if isin("-sum",argv) && isin("-bsd",argv) { gsh.lastCheckSum.SumType |= SUM_SUM16_BSD }else if isin("-sum",argv) && isin("-sysv",argv) { gsh.lastCheckSum.SumType |= SUM_SUM16_SYSV }else if isin("-sum",argv) { gsh.lastCheckSum.SumType |= SUM_SUM64 } if isin("-unix",argv) { gsh.lastCheckSum.SumType |= SUM_UNIXFILE gsh.lastCheckSum.Crc32Table = *crc32.MakeTable(CRC32UNIX) } if isin("-ieee",argv){ gsh.lastCheckSum.SumType |= SUM_CRCIEEE gsh.lastCheckSum.Crc32Table = *crc32.MakeTable(CRC32IEEE) } gsh.lastCheckSum.RusgAtStart = Getrusagev() } var total = fileSum{} npats := []string{} for _,v := range argv { if 0 < len(v) && v[0] != '-' { npats = append(npats,v) } if v == "//" { break } if v == "--" { break } if v == "-grep" { break } if v == "-ls" { break } } if len(npats) == 0 { npats = []string{"*"} } cwd := "." // if to be fullpath ::: cwd, _ := os.Getwd() if len(npats) == 0 { npats = []string{"*"} } fusage := gsh.xxFind(0,&total,cwd,npats,argv) if gsh.lastCheckSum.SumType != 0 { var sumi uint64 = 0 sum := &gsh.lastCheckSum if (sum.SumType & SUM_SIZE) != 0 { sumi = uint64(sum.Size) } if (sum.SumType & SUM_SUM64) != 0 { sumi = sum.Sum64 } if (sum.SumType & SUM_SUM16_SYSV) != 0 { s := uint32(sum.Sum16) r := (s & 0xFFFF) + ((s & 0xFFFFFFFF) >> 16) s = (r & 0xFFFF) + (r >> 16) sum.Crc32Val = uint32(s) sumi = uint64(s) } if (sum.SumType & SUM_SUM16_BSD) != 0 { sum.Crc32Val = uint32(sum.Sum16) sumi = uint64(sum.Sum16) } if (sum.SumType & SUM_UNIXFILE) != 0 { sum.Crc32Val = byteCRC32end(sum.Crc32Val,uint64(sum.Size)) sumi = uint64(byteCRC32end(sum.Crc32Val,uint64(sum.Size))) } if 1 < sum.Files { fmt.Printf("%v %v // %v / %v files, %v/file\r\n", sumi,sum.Size, abssize(sum.Size),sum.Files, abssize(sum.Size/sum.Files)) }else{ fmt.Printf("%v %v %v\n", sumi,sum.Size,npats[0]) } } if !isin("-grep",argv) { showFusage("total",fusage) } if !isin("-s",argv){ hits := len(gsh.CmdCurrent.FoundFile) if 0 < hits { fmt.Printf("--I-- %d files hits // can be refered with !%df\n", hits,len(gsh.CommandHistory)) } } if gsh.lastCheckSum.SumType != 0 { if isin("-ru",argv) { sum := &gsh.lastCheckSum sum.Done = time.Now() gsh.lastCheckSum.RusgAtEnd = Getrusagev() elps := sum.Done.Sub(sum.Start) fmt.Printf("--cksum-size: %v (%v) / %v files, %v/file\r\n", sum.Size,abssize(sum.Size),sum.Files,abssize(sum.Size/sum.Files)) nanos := int64(elps) dnanos := time.Duration(nanos); fmt.Printf("--cksum-time: %v/total, %v/file, %.1f files/s, %v\r\n", abbtime(dnanos), abbtime(time.Duration(nanos/sum.Files)), (float64(sum.Files)*1000000000.0)/float64(nanos), abbspeed(sum.Size,nanos)) diff := RusageSubv(sum.RusgAtEnd,sum.RusgAtStart) fmt.Printf("--cksum-rusg: %v\n",sRusagef("",argv,diff)) } } return } func showFiles(files[]string){ sp := "" for i,file := range files { if 0 < i { sp = " " } else { sp = "" } fmt.Printf(sp+"%s",escapeWhiteSP(file)) } } func showFound(gshCtx *GshContext, argv[]string){ for i,v := range gshCtx.CommandHistory { if 0 < len(v.FoundFile) { fmt.Printf("!%d (%d) ",i,len(v.FoundFile)) if isin("-ls",argv){ fmt.Printf("\n") for _,file := range v.FoundFile { fmt.Printf("") //sub number? showFileInfo(file,argv) } }else{ showFiles(v.FoundFile) fmt.Printf("\n") } } } } func showMatchFile(filev []os.FileInfo, npat,dir string, argv[]string)(string,bool){ fname := "" found := false for _,v := range filev { match, _ := filepath.Match(npat,(v.Name())) if match { fname = v.Name() found = true //fmt.Printf("[%d] %s\n",i,v.Name()) showIfExecutable(fname,dir,argv) } } return fname,found } func showIfExecutable(name,dir string,argv[]string)(ffullpath string,ffound bool){ var fullpath string if strBegins(name,DIRSEP){ fullpath = name }else if( len(dir) == 0 ){ fullpath = name; }else{ fullpath = dir + DIRSEP + name } fi, err := os.Stat(fullpath) //fmt.Printf("--Dp-- \"%v\"\n-- %v\n",fullpath,err); if err != nil { fullpath += ".exe"; fi, err = os.Stat(fullpath) } if err != nil { fullpath = dir + DIRSEP + name + ".go" fi, err = os.Stat(fullpath) } if err == nil { fm := fi.Mode() if fm.IsRegular() { // R_OK=4, W_OK=2, X_OK=1, F_OK=0 if aAccess(fullpath,5) == nil { ffullpath = fullpath ffound = true if ! isin("-s", argv) { showFileInfo(fullpath,argv) } } } } return ffullpath, ffound } func which(list string, argv []string) (fullpathv []string, itis bool){ if len(argv) <= 1 { fmt.Printf("Usage: which comand [-s] [-a] [-ls]\n") return []string{""}, false } path := argv[1] if strBegins(path,"/") { // should check if excecutable? _,exOK := showIfExecutable(path,"/",argv) fmt.Printf("--D-- %v exOK=%v\n",path,exOK) return []string{path},exOK } pathenv, efound := os.LookupEnv(list) if ! efound { fmt.Printf("--E-- which: no \"%s\" environment\n",list) return []string{""}, false } //fmt.Printf("PATH=%v\n",pathenv); showall := isin("-a",argv) || 0 <= strings.Index(path,"*") dirv := strings.Split(pathenv,PATHSEP) ffound := false ffullpath := path for _, dir := range dirv { if 0 <= strings.Index(path,"*") { // by wild-card list,_ := ioutil.ReadDir(dir) ffullpath, ffound = showMatchFile(list,path,dir,argv) }else{ ffullpath, ffound = showIfExecutable(path,dir,argv) } //if ffound && !isin("-a", argv) { if ffound && !showall { break; } } return []string{ffullpath}, ffound } func stripLeadingWSParg(argv[]string)([]string){ for ; 0 < len(argv); { if len(argv[0]) == 0 { argv = argv[1:] }else{ break } } return argv } func xEval(argv []string, nlend bool){ argv = stripLeadingWSParg(argv) if len(argv) == 0 { fmt.Printf("eval [%%format] [Go-expression]\n") return } pfmt := "%v" if argv[0][0] == '%' { pfmt = argv[0] argv = argv[1:] } if len(argv) == 0 { return } gocode := strings.Join(argv," "); //fmt.Printf("eval [%v] [%v]\n",pfmt,gocode) fset := token.NewFileSet() rval, _ := types.Eval(fset,nil,token.NoPos,gocode) fmt.Printf(pfmt,rval.Value) if nlend { fmt.Printf("\n") } } func getval(name string) (found bool, val int) { /* should expand the name here */ if name == "gsh.pid" { return true, os.Getpid() }else if name == "gsh.ppid" { return true, os.Getppid() } return false, 0 } func echo(argv []string, nlend bool){ for ai := 1; ai < len(argv); ai++ { if 1 < ai { fmt.Printf(" "); } arg := argv[ai] found, val := getval(arg) if found { fmt.Printf("%d",val) }else{ fmt.Printf("%s",arg) } } if nlend { fmt.Printf("\n"); } } func resfile() string { return "gsh.tmp" } //var resF *File func resmap() { //_ , err := os.OpenFile(resfile(), os.O_RDWR|os.O_CREATE, os.ModeAppend) // https://developpaper.com/solution-to-golang-bad-file-descriptor-problem/ _ , err := os.OpenFile(resfile(), os.O_RDWR|os.O_CREATE, 0600) if err != nil { fmt.Printf("refF could not open: %s\n",err) }else{ fmt.Printf("refF opened\n") } } // @@2020-0821 func gshScanArg(str string,strip int)(argv []string){ var si = 0 var sb = 0 var inBracket = 0 var arg1 = make([]byte,LINESIZE) var ax = 0 debug := false for ; si < len(str); si++ { if str[si] != ' ' { break } } sb = si for ; si < len(str); si++ { if sb <= si { if debug { fmt.Printf("--Da- +%d %2d-%2d %s ... %s\n", inBracket,sb,si,arg1[0:ax],str[si:]) } } ch := str[si] if ch == '{' { inBracket += 1 if 0 < strip && inBracket <= strip { //fmt.Printf("stripLEV %d <= %d?\n",inBracket,strip) continue } } if 0 < inBracket { if ch == '}' { inBracket -= 1 if 0 < strip && inBracket < strip { //fmt.Printf("stripLEV %d < %d?\n",inBracket,strip) continue } } arg1[ax] = ch ax += 1 continue } if str[si] == ' ' { argv = append(argv,string(arg1[0:ax])) if debug { fmt.Printf("--Da- [%v][%v-%v] %s ... %s\n", -1+len(argv),sb,si,str[sb:si],string(str[si:])) } sb = si+1 ax = 0 continue } arg1[ax] = ch ax += 1 } if sb < si { argv = append(argv,string(arg1[0:ax])) if debug { fmt.Printf("--Da- [%v][%v-%v] %s ... %s\n", -1+len(argv),sb,si,string(arg1[0:ax]),string(str[si:])) } } if debug { fmt.Printf("--Da- %d [%s] => [%d]%v\n",strip,str,len(argv),argv) } return argv } // should get stderr (into tmpfile ?) and return func (gsh*GshContext)Popen(name,mode string)(pin*os.File,pout*os.File,err bool){ //var pv = []int{-1,-1} //syscall.Pipe(pv) xarg := gshScanArg(name,1) name = strings.Join(xarg," ") //pin = os.NewFile(uintptr(pv[0]),"StdoutOf-{"+name+"}") //pout = os.NewFile(uintptr(pv[1]),"StdinOf-{"+name+"}") pin,pout,_ = os.Pipe(); fdix := 0 dir := "?" if mode == "r" { dir = "<" fdix = 1 // read from the stdout of the process }else{ dir = ">" fdix = 0 // write to the stdin of the process } gshPA := gsh.gshPA savfd := gshPA.Files[fdix] var fd uintptr = 0 if mode == "r" { //fd = pout.Fd() //gshPA.Files[fdix] = pout.Fd() gshPA.Files[fdix] = pout; }else{ //fd = pin.Fd() //gshPA.Files[fdix] = pin.Fd() gshPA.Files[fdix] = pin; } // should do this by Goroutine? if false { fmt.Printf("--Ip- Opened fd[%v] %s %v\n",fd,dir,name) fmt.Printf("--RED1 [%d,%d,%d]->[%d,%d,%d]\n", os.Stdin.Fd(),os.Stdout.Fd(),os.Stderr.Fd(), pin.Fd(),pout.Fd(),pout.Fd()) } savi := os.Stdin savo := os.Stdout save := os.Stderr os.Stdin = pin os.Stdout = pout os.Stderr = pout gsh.BackGround = true gsh.gshelllh(name) gsh.BackGround = false os.Stdin = savi os.Stdout = savo os.Stderr = save gshPA.Files[fdix] = savfd return pin,pout,false } // External commands func (gsh*GshContext)excommand(exec bool, argv []string) (notf bool,exit bool) { if gsh.CmdTrace { fmt.Printf("--I-- excommand[%v](%v)\n",exec,argv) } gshPA := gsh.gshPA fullpathv, itis := which("PATH",[]string{"which",argv[0],"-s"}) if itis == false { return true,false } fullpath := fullpathv[0] argv = unescapeWhiteSPV(argv) if 0 < strings.Index(fullpath,".go") { nargv := argv // []string{} gofullpathv, itis := which("PATH",[]string{"which","go","-s"}) if itis == false { fmt.Printf("--F-- Go not found\n") return false,true } gofullpath := gofullpathv[0] nargv = []string{ gofullpath, "run", fullpath } fmt.Printf("--I-- %s {%s %s %s}\n",gofullpath, nargv[0],nargv[1],nargv[2]) if exec { syscall.Exec(gofullpath,nargv,os.Environ()) }else{ //pid, _ := syscall.ForkExec(gofullpath,nargv,&gshPA) proc,_ := os.StartProcess(gofullpath,nargv,&gshPA); pstat,_ := proc.Wait(); pid := pstat.Pid(); if gsh.BackGround { fmt.Fprintf(stderr,"--Ip- in Background pid[%d]%d(%v)\n",pid,len(argv),nargv) //gsh.BackGroundJobs = append(gsh.BackGroundJobs,pid) gsh.BackGroundJobs = append(gsh.BackGroundJobs,*pstat) }else{ /* rusage := aRusage {} // syscall.Wait4(pid,nil,0,&rusage) gsh.LastRusage = rusage gsh.CmdCurrent.Rusagev[1] = rusage */ /* gsh.LastRusage = *pstat.SysUsage().(*aRusage); gsh.CmdCurrent.Rusagev[1] = *pstat.SysUsage().(*aRusage); */ aSetrusage(&gsh.LastRusage,pstat); gsh.CmdCurrent.Rusagev[1] = gsh.LastRusage; } } }else{ if exec { syscall.Exec(fullpath,argv,os.Environ()) }else{ //pid, _ := syscall.ForkExec(fullpath,argv,&gshPA) proc,_ := os.StartProcess(fullpath,argv,&gshPA); pstat,_ := proc.Wait(); pid := pstat.Pid(); //fmt.Printf("[%d]\n",pid); // '&' to be background if( false ){ fmt.Printf("Sys=%v\n",gshPA.Sys); if( gshPA.Sys != nil ){ //fmt.Printf("inFG=%v\n",gshPA.Sys.Foreground); } } if gsh.BackGround { fmt.Fprintf(stderr,"--Ip- in Background pid[%d]%d(%v)\n",pid,len(argv),argv) //gsh.BackGroundJobs = append(gsh.BackGroundJobs,pid) gsh.BackGroundJobs = append(gsh.BackGroundJobs,*pstat) }else{ /* rusage := aRusage {} // syscall.Wait4(pid,nil,0,&rusage); gsh.LastRusage = rusage gsh.CmdCurrent.Rusagev[1] = rusage */ /* gsh.LastRusage = *pstat.SysUsage().(*aRusage); gsh.CmdCurrent.Rusagev[1] = *pstat.SysUsage().(*aRusage); */ aSetrusage(&gsh.LastRusage,pstat); gsh.CmdCurrent.Rusagev[1] = gsh.LastRusage; } } } return false,false } // Builtin Commands func (gshCtx *GshContext) sleep(argv []string) { if len(argv) < 2 { fmt.Printf("Sleep 100ms, 100us, 100ns, ...\n") return } duration := argv[1]; d, err := time.ParseDuration(duration) if err != nil { d, err = time.ParseDuration(duration+"s") if err != nil { fmt.Printf("duration ? %s (%s)\n",duration,err) return } } //fmt.Printf("Sleep %v\n",duration) time.Sleep(d) if 0 < len(argv[2:]) { gshCtx.gshellv(argv[2:]) } } func (gshCtx *GshContext)repeat(argv []string) { if len(argv) < 2 { return } start0 := time.Now() for ri,_ := strconv.Atoi(argv[1]); 0 < ri; ri-- { if 0 < len(argv[2:]) { //start := time.Now() gshCtx.gshellv(argv[2:]) end := time.Now() elps := end.Sub(start0); if( 1000000000 < elps ){ fmt.Printf("(repeat#%d %v)\n",ri,elps); } } } } func (gshCtx *GshContext)gen(argv []string) { gshPA := gshCtx.gshPA if len(argv) < 2 { fmt.Printf("Usage: %s N\n",argv[0]) return } // should br repeated by "repeat" command count, _ := strconv.Atoi(argv[1]) //fd := gshPA.Files[1] // Stdout //file := os.NewFile(fd,"internalStdOut") file := gshPA.Files[1]; // Stdout fmt.Printf("--I-- Gen. Count=%d to [%d]\n",count,file.Fd()) //buf := []byte{} outdata := "0123 5678 0123 5678 0123 5678 0123 5678\r" for gi := 0; gi < count; gi++ { file.WriteString(outdata) } //file.WriteString("\n") fmt.Printf("\n(%d B)\n",count*len(outdata)); //file.Close() } // Remote Execution // 2020-0820 func Elapsed(from time.Time)(string){ elps := time.Now().Sub(from) if 1000000000 < elps { return fmt.Sprintf("[%5d.%02ds]",elps/1000000000,(elps%1000000000)/10000000) }else if 1000000 < elps { return fmt.Sprintf("[%3d.%03dms]",elps/1000000,(elps%1000000)/1000) }else{ return fmt.Sprintf("[%3d.%03dus]",elps/1000,(elps%1000)) } } //func abbtime(nanos int64)(string){ func abbtime(nanos time.Duration)(string){ if 1000000000 < nanos { return fmt.Sprintf("%d.%02ds",nanos/1000000000,(nanos%1000000000)/10000000) }else if 1000000 < nanos { return fmt.Sprintf("%d.%03dms",nanos/1000000,(nanos%1000000)/1000) }else{ return fmt.Sprintf("%d.%03dus",nanos/1000,(nanos%1000)) } } func abssize(size int64)(string){ fsize := float64(size) if 1024*1024*1024 < size { return fmt.Sprintf("%.2fGiB",fsize/(1024*1024*1024)) }else if 1024*1024 < size { return fmt.Sprintf("%.3fMiB",fsize/(1024*1024)) }else{ return fmt.Sprintf("%.3fKiB",fsize/1024) } } func absize(size int64)(string){ fsize := float64(size) if 1024*1024*1024 < size { return fmt.Sprintf("%8.2fGiB",fsize/(1024*1024*1024)) }else if 1024*1024 < size { return fmt.Sprintf("%8.3fMiB",fsize/(1024*1024)) }else{ return fmt.Sprintf("%8.3fKiB",fsize/1024) } } func abbspeed(totalB int64,ns int64)(string){ MBs := (float64(totalB)/1000000) / (float64(ns)/1000000000) if 1000 <= MBs { return fmt.Sprintf("%6.3fGB/s",MBs/1000) } if 1 <= MBs { return fmt.Sprintf("%6.3fMB/s",MBs) }else{ return fmt.Sprintf("%6.3fKB/s",MBs*1000) } } func abspeed(totalB int64,ns time.Duration)(string){ MBs := (float64(totalB)/1000000) / (float64(ns)/1000000000) if 1000 <= MBs { return fmt.Sprintf("%6.3fGBps",MBs/1000) } if 1 <= MBs { return fmt.Sprintf("%6.3fMBps",MBs) }else{ return fmt.Sprintf("%6.3fKBps",MBs*1000) } } func fileRelay(what string,in*os.File,out*os.File,size int64,bsiz int)(wcount int64){ Start := time.Now() buff := make([]byte,bsiz) var total int64 = 0 var rem int64 = size nio := 0 Prev := time.Now() var PrevSize int64 = 0 fmt.Printf(Elapsed(Start)+"--In- X: %s (%v/%v/%v) START\n", what,absize(total),size,nio) for i:= 0; ; i++ { var len = bsiz if int(rem) < len { len = int(rem) } Now := time.Now() Elps := Now.Sub(Prev); if 1000000000 < Now.Sub(Prev) { fmt.Printf(Elapsed(Start)+"--In- X: %s (%v/%v/%v) %s\n", what,absize(total),size,nio, abspeed((total-PrevSize),Elps)) Prev = Now; PrevSize = total } rlen := len if in != nil { // should watch the disconnection of out rcc,err := in.Read(buff[0:rlen]) if err != nil { fmt.Printf(Elapsed(Start)+"--En- X: %s read(%v,%v)<%v\n", what,rcc,err,in.Name()) break } rlen = rcc if string(buff[0:10]) == "((SoftEOF " { var ecc int64 = 0 fmt.Sscanf(string(buff),"((SoftEOF %v",&ecc) fmt.Printf(Elapsed(Start)+"--En- X: %s Recv ((SoftEOF %v))/%v\n", what,ecc,total) if ecc == total { break } } } wlen := rlen if out != nil { wcc,err := out.Write(buff[0:rlen]) if err != nil { fmt.Printf(Elapsed(Start)+"-En-- X: %s write(%v,%v)>%v\n", what,wcc,err,out.Name()) break } wlen = wcc } if wlen < rlen { fmt.Printf(Elapsed(Start)+"--En- X: %s incomplete write (%v/%v)\n", what,wlen,rlen) break; } nio += 1 total += int64(rlen) rem -= int64(rlen) if rem <= 0 { break } } Done := time.Now() Elps := float64(Done.Sub(Start))/1000000000 //Seconds TotalMB := float64(total)/1000000 //MB MBps := TotalMB / Elps fmt.Printf(Elapsed(Start)+"--In- X: %s (%v/%v/%v) %v %.3fMB/s\n", what,total,size,nio,absize(total),MBps) return total } func tcpPush(clnt *os.File){ // shrink socket buffer and recover usleep(100); } func (gsh*GshContext)RexecServer(argv[]string){ debug := true Start0 := time.Now() Start := Start0 // if local == ":" { local = "0.0.0.0:9999" } local := "0.0.0.0:9999" if 0 < len(argv) { if argv[0] == "-s" { debug = false argv = argv[1:] } } if 0 < len(argv) { argv = argv[1:] } port, err := net.ResolveTCPAddr("tcp",local); if err != nil { fmt.Printf("--En- S: Address error: %s (%s)\n",local,err) return } fmt.Printf(Elapsed(Start)+"--In- S: Listening at %s...\n",local); sconn, err := net.ListenTCP("tcp", port) if err != nil { fmt.Printf(Elapsed(Start)+"--En- S: Listen error: %s (%s)\n",local,err) return } reqbuf := make([]byte,LINESIZE) res := "" for { fmt.Printf(Elapsed(Start0)+"--In- S: Listening at %s...\n",local); aconn, err := sconn.AcceptTCP() Start = time.Now() if err != nil { fmt.Printf(Elapsed(Start)+"--En- S: Accept error: %s (%s)\n",local,err) return } clnt, _ := aconn.File() fd := clnt.Fd() ar := aconn.RemoteAddr() if debug { fmt.Printf(Elapsed(Start0)+"--In- S: Accepted TCP at %s [%d] <- %v\n", local,fd,ar) } res = fmt.Sprintf("220 GShell/%s Server\r\n",VERSION) fmt.Fprintf(clnt,"%s",res) if debug { fmt.Printf(Elapsed(Start)+"--In- S: %s",res) } count, err := clnt.Read(reqbuf) if err != nil { fmt.Printf(Elapsed(Start)+"--En- C: (%v %v) %v", count,err,string(reqbuf)) } req := string(reqbuf[:count]) if debug { fmt.Printf(Elapsed(Start)+"--In- C: %v",string(req)) } reqv := strings.Split(string(req),"\r") cmdv := gshScanArg(reqv[0],0) //cmdv := strings.Split(reqv[0]," ") switch cmdv[0] { case "HELO": res = fmt.Sprintf("250 %v",req) case "GET": // download {remotefile|-zN} [localfile] var dsize int64 = 32*1024*1024 var bsize int = 64*1024 var fname string = "" var in *os.File = nil var pseudoEOF = false if 1 < len(cmdv) { fname = cmdv[1] if strBegins(fname,"-z") { fmt.Sscanf(fname[2:],"%d",&dsize) }else if strBegins(fname,"{") { xin,xout,err := gsh.Popen(fname,"r") if err { }else{ xout.Close() defer xin.Close() in = xin dsize = MaxStreamSize pseudoEOF = true } }else{ xin,err := os.Open(fname) if err != nil { fmt.Printf("--En- GET (%v)\n",err) }else{ defer xin.Close() in = xin fi,_ := xin.Stat() dsize = fi.Size() } } } //fmt.Printf(Elapsed(Start)+"--In- GET %v:%v\n",dsize,bsize) res = fmt.Sprintf("200 %v\r\n",dsize) fmt.Fprintf(clnt,"%v",res) tcpPush(clnt); // should be separated as line in receiver fmt.Printf(Elapsed(Start)+"--In- S: %v",res) wcount := fileRelay("SendGET",in,clnt,dsize,bsize) if pseudoEOF { in.Close() // pipe from the command // show end of stream data (its size) by OOB? SoftEOF := fmt.Sprintf("((SoftEOF %v))",wcount) fmt.Printf(Elapsed(Start)+"--In- S: Send %v\n",SoftEOF) tcpPush(clnt); // to let SoftEOF data apper at the top of recevied data fmt.Fprintf(clnt,"%v\r\n",SoftEOF) tcpPush(clnt); // to let SoftEOF alone in a packet (separate with 200 OK) // with client generated random? //fmt.Printf("--In- L: close %v (%v)\n",in.Fd(),in.Name()) } res = fmt.Sprintf("200 GET done\r\n") case "PUT": // upload {srcfile|-zN} [dstfile] var dsize int64 = 32*1024*1024 var bsize int = 64*1024 var fname string = "" var out *os.File = nil if 1 < len(cmdv) { // localfile fmt.Sscanf(cmdv[1],"%d",&dsize) } if 2 < len(cmdv) { fname = cmdv[2] if fname == "-" { // nul dev }else if strBegins(fname,"{") { xin,xout,err := gsh.Popen(fname,"w") if err { }else{ xin.Close() defer xout.Close() out = xout } }else{ // should write to temporary file // should suppress ^C on tty xout,err := os.OpenFile(fname,os.O_CREATE|os.O_RDWR|os.O_TRUNC,0600) //fmt.Printf("--In- S: open(%v) out(%v) err(%v)\n",fname,xout,err) if err != nil { fmt.Printf("--En- PUT (%v)\n",err) }else{ out = xout } } fmt.Printf(Elapsed(Start)+"--In- L: open(%v,w) %v (%v)\n", fname,local,err) } fmt.Printf(Elapsed(Start)+"--In- PUT %v (/%v)\n",dsize,bsize) fmt.Printf(Elapsed(Start)+"--In- S: 200 %v OK\r\n",dsize) fmt.Fprintf(clnt,"200 %v OK\r\n",dsize) fileRelay("RecvPUT",clnt,out,dsize,bsize) res = fmt.Sprintf("200 PUT done\r\n") default: res = fmt.Sprintf("400 What? %v",req) } swcc,serr := clnt.Write([]byte(res)) if serr != nil { fmt.Printf(Elapsed(Start)+"--In- S: (wc=%v er=%v) %v",swcc,serr,res) }else{ fmt.Printf(Elapsed(Start)+"--In- S: %v",res) } aconn.Close(); clnt.Close(); } sconn.Close(); } func (gsh*GshContext)RexecClient(argv[]string)(int,string){ debug := true Start := time.Now() if len(argv) == 1 { return -1,"EmptyARG" } argv = argv[1:] if argv[0] == "-serv" { gsh.RexecServer(argv[1:]) return 0,"Server" } remote := "0.0.0.0:9999" if argv[0][0] == '@' { remote = argv[0][1:] argv = argv[1:] } if argv[0] == "-s" { debug = false argv = argv[1:] } dport, err := net.ResolveTCPAddr("tcp",remote); if err != nil { fmt.Printf(Elapsed(Start)+"Address error: %s (%s)\n",remote,err) return -1,"AddressError" } fmt.Printf(Elapsed(Start)+"--In- C: Connecting to %s\n",remote) serv, err := net.DialTCP("tcp",nil,dport) if err != nil { fmt.Printf(Elapsed(Start)+"Connection error: %s (%s)\n",remote,err) return -1,"CannotConnect" } if debug { al := serv.LocalAddr() fmt.Printf(Elapsed(Start)+"--In- C: Connected to %v <- %v\n",remote,al) } req := "" res := make([]byte,LINESIZE) count,err := serv.Read(res) if err != nil { fmt.Printf("--En- S: (%3d,%v) %v",count,err,string(res)) } if debug { fmt.Printf(Elapsed(Start)+"--In- S: %v",string(res)) } if argv[0] == "GET" { savPA := gsh.gshPA var bsize int = 64*1024 req = fmt.Sprintf("%v\r\n",strings.Join(argv," ")) fmt.Printf(Elapsed(Start)+"--In- C: %v",req) fmt.Fprintf(serv,req) count,err = serv.Read(res) if err != nil { }else{ var dsize int64 = 0 var out *os.File = nil var out_tobeclosed *os.File = nil var fname string = "" var rcode int = 0 var pid int = -1 fmt.Sscanf(string(res),"%d %d",&rcode,&dsize) fmt.Printf(Elapsed(Start)+"--In- S: %v",string(res[0:count])) if 3 <= len(argv) { fname = argv[2] if strBegins(fname,"{") { xin,xout,err := gsh.Popen(fname,"w") if err { }else{ xin.Close() defer xout.Close() out = xout out_tobeclosed = xout pid = 0 // should be its pid } }else{ // should write to temporary file // should suppress ^C on tty xout,err := os.OpenFile(fname,os.O_CREATE|os.O_RDWR|os.O_TRUNC,0600) if err != nil { fmt.Print("--En- %v\n",err) } out = xout //fmt.Printf("--In-- %d > %s\n",out.Fd(),fname) } } in,_ := serv.File() fileRelay("RecvGET",in,out,dsize,bsize) if 0 <= pid { gsh.gshPA = savPA // recovery of Fd(), and more? fmt.Printf(Elapsed(Start)+"--In- L: close Pipe > %v\n",fname) out_tobeclosed.Close() //syscall.Wait4(pid,nil,0,nil) //@@ } } }else if argv[0] == "PUT" { remote, _ := serv.File() var local *os.File = nil var dsize int64 = 32*1024*1024 var bsize int = 64*1024 var ofile string = "-" //fmt.Printf("--I-- Rex %v\n",argv) if 1 < len(argv) { fname := argv[1] if strBegins(fname,"-z") { fmt.Sscanf(fname[2:],"%d",&dsize) }else if strBegins(fname,"{") { xin,xout,err := gsh.Popen(fname,"r") if err { }else{ xout.Close() defer xin.Close() //in = xin local = xin fmt.Printf("--In- [%d] < Upload output of %v\n", local.Fd(),fname) ofile = "-from."+fname dsize = MaxStreamSize } }else{ xlocal,err := os.Open(fname) if err != nil { fmt.Printf("--En- (%s)\n",err) local = nil }else{ local = xlocal fi,_ := local.Stat() dsize = fi.Size() defer local.Close() //fmt.Printf("--I-- Rex in(%v / %v)\n",ofile,dsize) } ofile = fname fmt.Printf(Elapsed(Start)+"--In- L: open(%v,r)=%v %v (%v)\n", fname,dsize,local,err) } } if 2 < len(argv) && argv[2] != "" { ofile = argv[2] //fmt.Printf("(%d)%v B.ofile=%v\n",len(argv),argv,ofile) } //fmt.Printf(Elapsed(Start)+"--I-- Rex out(%v)\n",ofile) fmt.Printf(Elapsed(Start)+"--In- PUT %v (/%v)\n",dsize,bsize) req = fmt.Sprintf("PUT %v %v \r\n",dsize,ofile) if debug { fmt.Printf(Elapsed(Start)+"--In- C: %v",req) } fmt.Fprintf(serv,"%v",req) count,err = serv.Read(res) if debug { fmt.Printf(Elapsed(Start)+"--In- S: %v",string(res[0:count])) } fileRelay("SendPUT",local,remote,dsize,bsize) }else{ req = fmt.Sprintf("%v\r\n",strings.Join(argv," ")) if debug { fmt.Printf(Elapsed(Start)+"--In- C: %v",req) } fmt.Fprintf(serv,"%v",req) //fmt.Printf("--In- sending RexRequest(%v)\n",len(req)) } //fmt.Printf(Elapsed(Start)+"--In- waiting RexResponse...\n") count,err = serv.Read(res) ress := "" if count == 0 { ress = "(nil)\r\n" }else{ ress = string(res[:count]) } if err != nil { fmt.Printf(Elapsed(Start)+"--En- S: (%d,%v) %v",count,err,ress) }else{ fmt.Printf(Elapsed(Start)+"--In- S: %v",ress) } serv.Close() //conn.Close() var stat string var rcode int fmt.Sscanf(ress,"%d %s",&rcode,&stat) //fmt.Printf("--D-- Client: %v (%v)",rcode,stat) return rcode,ress } // Remote Shell // gcp file [...] { [host]:[port:][dir] | dir } // -p | -no-p func (gsh*GshContext)FileCopy(argv[]string){ var host = "" var port = "" var upload = false var download = false var xargv = []string{"rex-gcp"} var srcv = []string{} var dstv = []string{} argv = argv[1:] for _,v := range argv { /* if v[0] == '-' { // might be a pseudo file (generated date) continue } */ obj := strings.Split(v,":") //fmt.Printf("%d %v %v\n",len(obj),v,obj) if 1 < len(obj) { host = obj[0] file := "" if 0 < len(host) { gsh.LastServer.host = host }else{ host = gsh.LastServer.host port = gsh.LastServer.port } if 2 < len(obj) { port = obj[1] if 0 < len(port) { gsh.LastServer.port = port }else{ port = gsh.LastServer.port } file = obj[2] }else{ file = obj[1] } if len(srcv) == 0 { download = true srcv = append(srcv,file) continue } upload = true dstv = append(dstv,file) continue } /* idx := strings.Index(v,":") if 0 <= idx { remote = v[0:idx] if len(srcv) == 0 { download = true srcv = append(srcv,v[idx+1:]) continue } upload = true dstv = append(dstv,v[idx+1:]) continue } */ if download { dstv = append(dstv,v) }else{ srcv = append(srcv,v) } } hostport := "@" + host + ":" + port if upload { if host != "" { xargv = append(xargv,hostport) } xargv = append(xargv,"PUT") xargv = append(xargv,srcv[0:]...) xargv = append(xargv,dstv[0:]...) //fmt.Printf("--I-- FileCopy PUT gsh://%s/%v < %v // %v\n",hostport,dstv,srcv,xargv) fmt.Printf("--I-- FileCopy PUT gsh://%s/%v < %v\n",hostport,dstv,srcv) gsh.RexecClient(xargv) }else if download { if host != "" { xargv = append(xargv,hostport) } xargv = append(xargv,"GET") xargv = append(xargv,srcv[0:]...) xargv = append(xargv,dstv[0:]...) //fmt.Printf("--I-- FileCopy GET gsh://%v/%v > %v // %v\n",hostport,srcv,dstv,xargv) fmt.Printf("--I-- FileCopy GET gsh://%v/%v > %v\n",hostport,srcv,dstv) gsh.RexecClient(xargv) }else{ } } // target func (gsh*GshContext)Trelpath(rloc string)(string){ cwd, _ := os.Getwd() os.Chdir(gsh.RWD) os.Chdir(rloc) twd, _ := os.Getwd() os.Chdir(cwd) tpath := twd + "/" + rloc return tpath } // join to rmote GShell - [user@]host[:port] or cd host:[port]:path func (gsh*GshContext)Rjoin(argv[]string){ if len(argv) <= 1 { fmt.Printf("--I-- current server = %v\n",gsh.RSERV) return } serv := argv[1] servv := strings.Split(serv,":") if 1 <= len(servv) { if servv[0] == "lo" { servv[0] = "localhost" } } switch len(servv) { case 1: //if strings.Index(serv,":") < 0 { serv = servv[0] + ":" + fmt.Sprintf("%d",GSH_PORT) //} case 2: // host:port serv = strings.Join(servv,":") } xargv := []string{"rex-join","@"+serv,"HELO"} rcode,stat := gsh.RexecClient(xargv) if (rcode / 100) == 2 { fmt.Printf("--I-- OK Joined (%v) %v\n",rcode,stat) gsh.RSERV = serv }else{ fmt.Printf("--I-- NG, could not joined (%v) %v\n",rcode,stat) } } func (gsh*GshContext)Rexec(argv[]string){ if len(argv) <= 1 { fmt.Printf("--I-- rexec command [ | {file || {command} ]\n",gsh.RSERV) return } /* nargv := gshScanArg(strings.Join(argv," "),0) fmt.Printf("--D-- nargc=%d [%v]\n",len(nargv),nargv) if nargv[1][0] != '{' { nargv[1] = "{" + nargv[1] + "}" fmt.Printf("--D-- nargc=%d [%v]\n",len(nargv),nargv) } argv = nargv */ nargv := []string{} nargv = append(nargv,"{"+strings.Join(argv[1:]," ")+"}") fmt.Printf("--D-- nargc=%d %v\n",len(nargv),nargv) argv = nargv xargv := []string{"rex-exec","@"+gsh.RSERV,"GET"} xargv = append(xargv,argv...) xargv = append(xargv,"/dev/tty") rcode,stat := gsh.RexecClient(xargv) if (rcode / 100) == 2 { fmt.Printf("--I-- OK Rexec (%v) %v\n",rcode,stat) }else{ fmt.Printf("--I-- NG Rexec (%v) %v\n",rcode,stat) } } func (gsh*GshContext)Rchdir(argv[]string){ if len(argv) <= 1 { return } cwd, _ := os.Getwd() os.Chdir(gsh.RWD) os.Chdir(argv[1]) twd, _ := os.Getwd() gsh.RWD = twd fmt.Printf("--I-- JWD=%v\n",twd) os.Chdir(cwd) } func (gsh*GshContext)Rpwd(argv[]string){ fmt.Printf("%v\n",gsh.RWD) } func (gsh*GshContext)Rls(argv[]string){ cwd, _ := os.Getwd() os.Chdir(gsh.RWD) argv[0] = "-ls" gsh.xFind(argv) os.Chdir(cwd) } func (gsh*GshContext)Rput(argv[]string){ var local string = "" var remote string = "" if 1 < len(argv) { local = argv[1] remote = local // base name } if 2 < len(argv) { remote = argv[2] } fmt.Printf("--I-- jput from=%v to=%v\n",local,gsh.Trelpath(remote)) } func (gsh*GshContext)Rget(argv[]string){ var remote string = "" var local string = "" if 1 < len(argv) { remote = argv[1] local = remote // base name } if 2 < len(argv) { local = argv[2] } fmt.Printf("--I-- jget from=%v to=%v\n",gsh.Trelpath(remote),local) } // network // -s, -si, -so // bi-directional, source, sync (maybe socket) func (gshCtx*GshContext)sconnect(inTCP bool, argv []string) { gshPA := gshCtx.gshPA if len(argv) < 2 { fmt.Printf("Usage: -s [host]:[port[.udp]]\n") return } remote := argv[1] if remote == ":" { remote = "0.0.0.0:9999" } if inTCP { // TCP dport, err := net.ResolveTCPAddr("tcp",remote); if err != nil { fmt.Printf("Address error: %s (%s)\n",remote,err) return } conn, err := net.DialTCP("tcp",nil,dport) if err != nil { fmt.Printf("Connection error: %s (%s)\n",remote,err) return } file, _ := conn.File(); //fd := file.Fd() //fmt.Printf("Socket: connected to %s, socket[%d]\n",remote,fd) fmt.Printf("Socket: connected to %s, socket[%d]\n",remote,file.Fd()) savfd := gshPA.Files[1] //gshPA.Files[1] = fd; gshPA.Files[1] = file; gshCtx.gshellv(argv[2:]) gshPA.Files[1] = savfd file.Close() conn.Close() }else{ //dport, err := net.ResolveUDPAddr("udp4",remote); dport, err := net.ResolveUDPAddr("udp",remote); if err != nil { fmt.Printf("Address error: %s (%s)\n",remote,err) return } //conn, err := net.DialUDP("udp4",nil,dport) conn, err := net.DialUDP("udp",nil,dport) if err != nil { fmt.Printf("Connection error: %s (%s)\n",remote,err) return } file, _ := conn.File(); //fd := file.Fd() ar := conn.RemoteAddr() //al := conn.LocalAddr() fmt.Printf("Socket: connected to %s [%s], socket[%d]\n", //remote,ar.String(),fd) remote,ar.String(),file.Fd()) savfd := gshPA.Files[1] //gshPA.Files[1] = fd; gshPA.Files[1] = file; gshCtx.gshellv(argv[2:]) gshPA.Files[1] = savfd file.Close() conn.Close() } } func (gshCtx*GshContext)saccept(inTCP bool, argv []string) { gshPA := gshCtx.gshPA if len(argv) < 2 { fmt.Printf("Usage: -ac [host]:[port[.udp]]\n") return } local := argv[1] if local == ":" { local = "0.0.0.0:9999" } if inTCP { // TCP port, err := net.ResolveTCPAddr("tcp",local); if err != nil { fmt.Printf("Address error: %s (%s)\n",local,err) return } //fmt.Printf("Listen at %s...\n",local); sconn, err := net.ListenTCP("tcp", port) if err != nil { fmt.Printf("Listen error: %s (%s)\n",local,err) return } //fmt.Printf("Accepting at %s...\n",local); aconn, err := sconn.AcceptTCP() if err != nil { fmt.Printf("Accept error: %s (%s)\n",local,err) return } file, _ := aconn.File() //fd := file.Fd() //fmt.Printf("Accepted TCP at %s [%d]\n",local,fd) fmt.Printf("Accepted TCP at %s [%d]\n",local,file.Fd()) savfd := gshPA.Files[0] //gshPA.Files[0] = fd; gshPA.Files[0] = file; gshCtx.gshellv(argv[2:]) gshPA.Files[0] = savfd sconn.Close(); aconn.Close(); file.Close(); }else{ //port, err := net.ResolveUDPAddr("udp4",local); port, err := net.ResolveUDPAddr("udp",local); if err != nil { fmt.Printf("Address error: %s (%s)\n",local,err) return } fmt.Printf("Listen UDP at %s...\n",local); //uconn, err := net.ListenUDP("udp4", port) uconn, err := net.ListenUDP("udp", port) if err != nil { fmt.Printf("Listen error: %s (%s)\n",local,err) return } file, _ := uconn.File() //fd := file.Fd() ar := uconn.RemoteAddr() remote := "" if ar != nil { remote = ar.String() } if remote == "" { remote = "?" } // not yet received //fmt.Printf("Accepted at %s [%d] <- %s\n",local,fd,"") savfd := gshPA.Files[0] //gshPA.Files[0] = fd; gshPA.Files[0] = file; savenv := gshPA.Env gshPA.Env = append(savenv, "REMOTE_HOST="+remote) gshCtx.gshellv(argv[2:]) gshPA.Env = savenv gshPA.Files[0] = savfd uconn.Close(); file.Close(); } } // empty line command func (gshCtx*GshContext)xPwd(argv[]string){ // execute context command, pwd + date // context notation, representation scheme, to be resumed at re-login cwd, _ := os.Getwd() switch { case isin("-a",argv): gshCtx.ShowChdirHistory(argv) case isin("-ls",argv): showFileInfo(cwd,argv) default: fmt.Printf("%s\n",cwd) case isin("-v",argv): // obsolete emtpy command t := time.Now() date := t.Format(time.UnixDate) exe, _ := os.Executable() host, _ := os.Hostname() fmt.Printf("{PWD=\"%s\"",cwd) fmt.Printf(" HOST=\"%s\"",host) fmt.Printf(" DATE=\"%s\"",date) fmt.Printf(" TIME=\"%s\"",t.String()) fmt.Printf(" PID=\"%d\"",os.Getpid()) fmt.Printf(" EXE=\"%s\"",exe) fmt.Printf("}\n") } } // History // these should be browsed and edited by HTTP browser // show the time of command with -t and direcotry with -ls // openfile-history, sort by -a -m -c // sort by elapsed time by -t -s // search by "more" like interface // edit history // sort history, and wc or uniq // CPU and other resource consumptions // limit showing range (by time or so) // export / import history func (gshCtx *GshContext)xHistory(argv []string){ atWorkDirX := -1 if 1 < len(argv) && strBegins(argv[1],"@") { atWorkDirX,_ = strconv.Atoi(argv[1][1:]) } //fmt.Printf("--D-- showHistory(%v)\n",argv) for i, v := range gshCtx.CommandHistory { // exclude commands not to be listed by default // internal commands may be suppressed by default if v.CmdLine == "" && !isin("-a",argv) { continue; } if 0 <= atWorkDirX { if v.WorkDirX != atWorkDirX { continue } } if !isin("-n",argv){ // like "fc" fmt.Printf("!%-2d ",i) } if isin("-v",argv){ fmt.Println(v) // should be with it date }else{ if isin("-l",argv) || isin("-l0",argv) { elps := v.EndAt.Sub(v.StartAt); start := v.StartAt.Format(time.Stamp) fmt.Printf("@%d ",v.WorkDirX) fmt.Printf("[%v] %11v/t ",start,elps) } if isin("-l",argv) && !isin("-l0",argv){ fmt.Printf("%v",Rusagef("%t %u\t// %s",argv,v.Rusagev)) } if isin("-at",argv) { // isin("-ls",argv){ dhi := v.WorkDirX // workdir history index fmt.Printf("@%d %s\t",dhi,v.WorkDir) // show the FileInfo of the output command?? } fmt.Printf("%s",v.CmdLine) fmt.Printf("\n") } } } // !n - history index func searchHistory(gshCtx GshContext, gline string) (string, bool, bool){ if gline[0] == '!' { hix, err := strconv.Atoi(gline[1:]) if err != nil { fmt.Printf("--E-- (%s : range)\n",hix) return "", false, true } if hix < 0 || len(gshCtx.CommandHistory) <= hix { fmt.Printf("--E-- (%d : out of range)\n",hix) return "", false, true } return gshCtx.CommandHistory[hix].CmdLine, false, false } // search //for i, v := range gshCtx.CommandHistory { //} return gline, false, false } func (gsh*GshContext)cmdStringInHistory(hix int)(cmd string, ok bool){ if 0 <= hix && hix < len(gsh.CommandHistory) { return gsh.CommandHistory[hix].CmdLine,true } return "",false } // temporary adding to PATH environment // cd name -lib for LD_LIBRARY_PATH // chdir with directory history (date + full-path) // -s for sort option (by visit date or so) func (gsh*GshContext)ShowChdirHistory1(i int,v GChdirHistory, argv []string){ fmt.Printf("!%-2d ",v.CmdIndex) // the first command at this WorkDir fmt.Printf("@%d ",i) fmt.Printf("[%v] ",v.MovedAt.Format(time.Stamp)) showFileInfo(v.Dir,argv) } func (gsh*GshContext)ShowChdirHistory(argv []string){ for i, v := range gsh.ChdirHistory { gsh.ShowChdirHistory1(i,v,argv) } } func skipOpts(argv[]string)(int){ for i,v := range argv { if strBegins(v,"-") { }else{ return i } } return -1 } func (gshCtx*GshContext)xChdir(argv []string){ cdhist := gshCtx.ChdirHistory if isin("?",argv ) || isin("-t",argv) || isin("-a",argv) { gshCtx.ShowChdirHistory(argv) return } pwd, _ := os.Getwd() dir := "" if len(argv) <= 1 { dir = toFullpath("~") }else{ i := skipOpts(argv[1:]) if i < 0 { dir = toFullpath("~") }else{ dir = argv[1+i] } } if strBegins(dir,"@") { if dir == "@0" { // obsolete dir = gshCtx.StartDir }else if dir == "@!" { index := len(cdhist) - 1 if 0 < index { index -= 1 } dir = cdhist[index].Dir }else{ index, err := strconv.Atoi(dir[1:]) if err != nil { fmt.Printf("--E-- xChdir(%v)\n",err) dir = "?" }else if len(gshCtx.ChdirHistory) <= index { fmt.Printf("--E-- xChdir(history range error)\n") dir = "?" }else{ dir = cdhist[index].Dir } } } if dir != "?" { err := os.Chdir(dir) if err != nil { fmt.Printf("--E-- xChdir(%s)(%v)\n",argv[1],err) }else{ cwd, _ := os.Getwd() if cwd != pwd { hist1 := GChdirHistory { } hist1.Dir = cwd hist1.MovedAt = time.Now() hist1.CmdIndex = len(gshCtx.CommandHistory)+1 gshCtx.ChdirHistory = append(cdhist,hist1) if !isin("-s",argv){ //cwd, _ := os.Getwd() //fmt.Printf("%s\n",cwd) ix := len(gshCtx.ChdirHistory)-1 gshCtx.ShowChdirHistory1(ix,hist1,argv) } } } } if isin("-ls",argv){ cwd, _ := os.Getwd() showFileInfo(cwd,argv); } } func TimeValSub(tv1 *time.Duration, tv2 *time.Duration){ //*tv1 = syscall.NsecToTimeval(tv1.Nano() - tv2.Nano()) *tv1 -= *tv2; } func RusageSubv(ru1, ru2 [2]aRusage)([2]aRusage){ TimeValSub(&ru1[0].Utime,&ru2[0].Utime) TimeValSub(&ru1[0].Stime,&ru2[0].Stime) TimeValSub(&ru1[1].Utime,&ru2[1].Utime) TimeValSub(&ru1[1].Stime,&ru2[1].Stime) return ru1 } func TimeValAdd(tv1 time.Duration, tv2 time.Duration)(time.Duration){ //tvs := syscall.NsecToTimeval(tv1.Nano() + tv2.Nano()) tvs := tv1 + tv2; return tvs; } /* func RusageAddv(ru1, ru2 [2]aRusage)([2]aRusage){ TimeValAdd(ru1[0].Utime,ru2[0].Utime) TimeValAdd(ru1[0].Stime,ru2[0].Stime) TimeValAdd(ru1[1].Utime,ru2[1].Utime) TimeValAdd(ru1[1].Stime,ru2[1].Stime) return ru1 } */ // Resource Usage func sRusagef(fmtspec string, argv []string, ru [2]aRusage)(string){ // ru[0] self , ru[1] children ut := TimeValAdd(ru[0].Utime,ru[1].Utime) st := TimeValAdd(ru[0].Stime,ru[1].Stime) //uu := (int64(ut.Sec)*1000000 + int64(ut.Usec)) * 1000 //su := (int64(st.Sec)*1000000 + int64(st.Usec)) * 1000 uu := ut; // in nano sec su := st; // in nano sec tu := uu + su ret := fmt.Sprintf("%v/sum",abbtime(tu)) ret += fmt.Sprintf(", %v/usr",abbtime(uu)) ret += fmt.Sprintf(", %v/sys",abbtime(su)) return ret } func Rusagef(fmtspec string, argv []string, ru [2]aRusage)(string){ ut := TimeValAdd(ru[0].Utime,ru[1].Utime) st := TimeValAdd(ru[0].Stime,ru[1].Stime) //fmt.Printf("%d.%06ds/u ",ut.Sec,ut.Usec) //ru[1].Utime.Sec,ru[1].Utime.Usec) //fmt.Printf("%d.%06ds/s ",st.Sec,st.Usec) //ru[1].Stime.Sec,ru[1].Stime.Usec) fmt.Printf("%d.%06ds/u ",ut/1000000000,(ut/1000)%1000000); fmt.Printf("%d.%06ds/s ",st/1000000000,(st/1000)%1000000); return "" } func Getrusagev()([2]aRusage){ var ruv = [2]aRusage{} aGetrusage(aRUSAGE_SELF,&ruv[0]) aGetrusage(aRUSAGE_CHILDREN,&ruv[1]) return ruv } func (gshCtx *GshContext)xTime(argv[]string)(bool){ if 2 <= len(argv){ gshCtx.LastRusage = aRusage{} rusagev1 := Getrusagev() fin := gshCtx.gshellv(argv[1:]) rusagev2 := Getrusagev() showRusage(argv[1],argv,&gshCtx.LastRusage) rusagev := RusageSubv(rusagev2,rusagev1) showRusage("self",argv,&rusagev[0]) showRusage("chld",argv,&rusagev[1]) return fin }else{ rusage:= aRusage {} aGetrusage(aRUSAGE_SELF,&rusage) showRusage("self",argv, &rusage) aGetrusage(aRUSAGE_CHILDREN,&rusage) showRusage("chld",argv, &rusage) return false } } func (gshCtx *GshContext)xJobs(argv[]string){ fmt.Printf("%d Jobs\n",len(gshCtx.BackGroundJobs)) for ji, pid := range gshCtx.BackGroundJobs { //wstat := syscall.WaitStatus {0} rusage := aRusage {} //wpid, err := syscall.Wait4(pid,&wstat,syscall.WNOHANG,&rusage); //wpid, err := syscall.Wait4(pid,nil,syscall.WNOHANG,&rusage); wpid := pid.Pid(); err := errors.New("stab_NoError"); if err != nil { fmt.Printf("--E-- %%%d [%d] (%v)\n",ji,wpid,err) }else{ fmt.Printf("%%%d[%d]\n",ji,wpid) showRusage("chld",argv,&rusage) } } } func (gsh*GshContext)inBackground(argv[]string)(bool){ if gsh.CmdTrace { fmt.Printf("--I-- inBackground(%v)\n",argv) } gsh.BackGround = true // set background option xfin := false xfin = gsh.gshellv(argv) gsh.BackGround = false return xfin } // -o file without command means just opening it and refer by #N // should be listed by "files" comnmand func (gshCtx*GshContext)xOpen(argv[]string){ //var pv = []int{-1,-1} //err := syscall.Pipe(pv) //fmt.Printf("--I-- pipe()=[#%d,#%d](%v)\n",pv[0],pv[1],err) pin,pout,err := os.Pipe(); fmt.Printf("--I-- pipe()=[#%d,#%d](%v)\n",pin.Fd(),pout.Fd(),err) } func (gshCtx*GshContext)fromPipe(argv[]string){ } func (gshCtx*GshContext)xClose(argv[]string){ } // redirect func (gshCtx*GshContext)redirect(argv[]string)(bool){ if len(argv) < 2 { return false } cmd := argv[0] fname := argv[1] var file *os.File = nil fdix := 0 mode := os.O_RDONLY switch { case cmd == "-i" || cmd == "<": fdix = 0 mode = os.O_RDONLY case cmd == "-o" || cmd == ">": fdix = 1 mode = os.O_RDWR | os.O_CREATE case cmd == "-a" || cmd == ">>": fdix = 1 mode = os.O_RDWR | os.O_CREATE | os.O_APPEND } if fname[0] == '#' { fd, err := strconv.Atoi(fname[1:]) if err != nil { fmt.Printf("--E-- (%v)\n",err) return false } file = os.NewFile(uintptr(fd),"MaybePipe") }else{ xfile, err := os.OpenFile(argv[1], mode, 0600) if err != nil { fmt.Printf("--E-- (%s)\n",err) return false } file = xfile } gshPA := gshCtx.gshPA savfd := gshPA.Files[fdix] //gshPA.Files[fdix] = file.Fd() gshPA.Files[fdix] = file; fmt.Printf("--I-- Opened [%d] %s\n",file.Fd(),argv[1]) gshCtx.gshellv(argv[2:]) gshPA.Files[fdix] = savfd return false } //fmt.Fprintf(res, "GShell Status: %q", html.EscapeString(req.URL.Path)) func httpHandler(res http.ResponseWriter, req *http.Request){ path := req.URL.Path fmt.Printf("--I-- Got HTTP Request(%s)\n",path) { gshCtxBuf, _ := setupGshContext() gshCtx := &gshCtxBuf fmt.Printf("--I-- %s\n",path[1:]) gshCtx.tgshelll(path[1:]) } fmt.Fprintf(res, "Hello(^-^)//\n%s\n",path) } func (gshCtx *GshContext) httpServer(argv []string){ http.HandleFunc("/", httpHandler) accport := "localhost:9999" fmt.Printf("--I-- HTTP Server Start at [%s]\n",accport) http.ListenAndServe(accport,nil) } func (gshCtx *GshContext)xGo(argv[]string){ go gshCtx.gshellv(argv[1:]); } func (gshCtx *GshContext) xPs(argv[]string)(){ } // Plugin // plugin [-ls [names]] to list plugins // Reference: plugin source code func (gshCtx *GshContext) whichPlugin(name string,argv[]string)(pi *PluginInfo){ pi = nil for _,p := range gshCtx.PluginFuncs { if p.Name == name && pi == nil { pi = &p } if !isin("-s",argv){ //fmt.Printf("%v %v ",i,p) if isin("-ls",argv){ showFileInfo(p.Path,argv) }else{ fmt.Printf("%s\n",p.Name) } } } return pi } func (gshCtx *GshContext) xPlugin(argv[]string) (error) { if len(argv) == 0 || argv[0] == "-ls" { gshCtx.whichPlugin("",argv) return nil } name := argv[0] Pin := gshCtx.whichPlugin(name,[]string{"-s"}) if Pin != nil { os.Args = argv // should be recovered? Pin.Addr.(func())() return nil } sofile := toFullpath(argv[0] + ".so") // or find it by which($PATH) p, err := plugin.Open(sofile) if err != nil { fmt.Printf("--E-- plugin.Open(%s)(%v)\n",sofile,err) return err } fname := "Main" f, err := p.Lookup(fname) if( err != nil ){ fmt.Printf("--E-- plugin.Lookup(%s)(%v)\n",fname,err) return err } pin := PluginInfo {p,f,name,sofile} gshCtx.PluginFuncs = append(gshCtx.PluginFuncs,pin) fmt.Printf("--I-- added (%d)\n",len(gshCtx.PluginFuncs)) //fmt.Printf("--I-- first call(%s:%s)%v\n",sofile,fname,argv) os.Args = argv f.(func())() return err } func (gshCtx*GshContext)Args(argv[]string){ for i,v := range os.Args { fmt.Printf("[%v] %v\n",i,v) } } func (gshCtx *GshContext) showVersion(argv[]string){ if isin("-l",argv) { fmt.Printf("%v/%v (%v)",NAME,VERSION,DATE); }else{ fmt.Printf("%v",VERSION); } if isin("-a",argv) { fmt.Printf(" %s",AUTHOR) } if !isin("-n",argv) { fmt.Printf("\n") } } // Scanf // string decomposer // scanf [format] [input] func scanv(sstr string)(strv[]string){ strv = strings.Split(sstr," ") return strv } func scanUntil(src,end string)(rstr string,leng int){ idx := strings.Index(src,end) if 0 <= idx { rstr = src[0:idx] return rstr,idx+len(end) } return src,0 } // -bn -- display base-name part only // can be in some %fmt, for sed rewriting func (gsh*GshContext)printVal(fmts string, vstr string, optv[]string){ //vint,err := strconv.Atoi(vstr) var ival int64 = 0 n := 0 err := error(nil) if strBegins(vstr,"_") { vx,_ := strconv.Atoi(vstr[1:]) if vx < len(gsh.iValues) { vstr = gsh.iValues[vx] }else{ } } // should use Eval() if strBegins(vstr,"0x") { n,err = fmt.Sscanf(vstr[2:],"%x",&ival) }else{ n,err = fmt.Sscanf(vstr,"%d",&ival) //fmt.Printf("--D-- n=%d err=(%v) {%s}=%v\n",n,err,vstr, ival) } if n == 1 && err == nil { //fmt.Printf("--D-- formatn(%v) ival(%v)\n",fmts,ival) fmt.Printf("%"+fmts,ival) }else{ if isin("-bn",optv){ fmt.Printf("%"+fmts,filepath.Base(vstr)) }else{ fmt.Printf("%"+fmts,vstr) } } } func (gsh*GshContext)printfv(fmts,div string,argv[]string,optv[]string,list[]string){ //fmt.Printf("{%d}",len(list)) //curfmt := "v" outlen := 0 curfmt := gsh.iFormat if 0 < len(fmts) { for xi := 0; xi < len(fmts); xi++ { fch := fmts[xi] if fch == '%' { if xi+1 < len(fmts) { curfmt = string(fmts[xi+1]) gsh.iFormat = curfmt xi += 1 if xi+1 < len(fmts) && fmts[xi+1] == '(' { vals,leng := scanUntil(fmts[xi+2:],")") //fmt.Printf("--D-- show fmt(%v) val(%v) next(%v)\n",curfmt,vals,leng) gsh.printVal(curfmt,vals,optv) xi += 2+leng-1 outlen += 1 } continue } } if fch == '_' { hi,leng := scanInt(fmts[xi+1:]) if 0 < leng { if hi < len(gsh.iValues) { gsh.printVal(curfmt,gsh.iValues[hi],optv) outlen += 1 // should be the real length }else{ fmt.Printf("((out-range))") } xi += leng continue; } } fmt.Printf("%c",fch) outlen += 1 } }else{ //fmt.Printf("--D-- print {%s}\n") for i,v := range list { if 0 < i { fmt.Printf(div) } gsh.printVal(curfmt,v,optv) outlen += 1 } } if 0 < outlen { fmt.Printf("\n") } } func (gsh*GshContext)Scanv(argv[]string){ //fmt.Printf("--D-- Scanv(%v)\n",argv) if len(argv) == 1 { return } argv = argv[1:] fmts := "" if strBegins(argv[0],"-F") { fmts = argv[0] gsh.iDelimiter = fmts argv = argv[1:] } input := strings.Join(argv," ") if fmts == "" { // simple decomposition v := scanv(input) gsh.iValues = v //fmt.Printf("%v\n",strings.Join(v,",")) }else{ v := make([]string,8) n,err := fmt.Sscanf(input,fmts,&v[0],&v[1],&v[2],&v[3]) fmt.Printf("--D-- Scanf ->(%v) n=%d err=(%v)\n",v,n,err) gsh.iValues = v } } func (gsh*GshContext)Printv(argv[]string){ if false { //@@U fmt.Printf("%v\n",strings.Join(argv[1:]," ")) return } //fmt.Printf("--D-- Printv(%v)\n",argv) //fmt.Printf("%v\n",strings.Join(gsh.iValues,",")) div := gsh.iDelimiter fmts := "" argv = argv[1:] if 0 < len(argv) { if strBegins(argv[0],"-F") { div = argv[0][2:] argv = argv[1:] } } optv := []string{} for _,v := range argv { if strBegins(v,"-"){ optv = append(optv,v) argv = argv[1:] }else{ break; } } if 0 < len(argv) { fmts = strings.Join(argv," ") } gsh.printfv(fmts,div,argv,optv,gsh.iValues) } func (gsh*GshContext)Basename(argv[]string){ for i,v := range gsh.iValues { gsh.iValues[i] = filepath.Base(v) } } func (gsh*GshContext)Sortv(argv[]string){ sv := gsh.iValues sort.Slice(sv , func(i,j int) bool { return sv[i] < sv[j] }) } func (gsh*GshContext)Shiftv(argv[]string){ vi := len(gsh.iValues) if 0 < vi { if isin("-r",argv) { top := gsh.iValues[0] gsh.iValues = append(gsh.iValues[1:],top) }else{ gsh.iValues = gsh.iValues[1:] } } } func (gsh*GshContext)Enq(argv[]string){ } func (gsh*GshContext)Deq(argv[]string){ } func (gsh*GshContext)Push(argv[]string){ gsh.iValStack = append(gsh.iValStack,argv[1:]) fmt.Printf("depth=%d\n",len(gsh.iValStack)) } func (gsh*GshContext)Dump(argv[]string){ for i,v := range gsh.iValStack { fmt.Printf("%d %v\n",i,v) } } func (gsh*GshContext)Pop(argv[]string){ depth := len(gsh.iValStack) if 0 < depth { v := gsh.iValStack[depth-1] if isin("-cat",argv){ gsh.iValues = append(gsh.iValues,v...) }else{ gsh.iValues = v } gsh.iValStack = gsh.iValStack[0:depth-1] fmt.Printf("depth=%d %s\n",len(gsh.iValStack),gsh.iValues) }else{ fmt.Printf("depth=%d\n",depth) } } // Command Interpreter func (gshCtx*GshContext)gshellv(argv []string) (fin bool) { fin = false if gshCtx.CmdTrace { fmt.Fprintf(os.Stderr,"--I-- gshellv((%d))\n",len(argv)) } if len(argv) <= 0 { return false } xargv := []string{} for ai := 0; ai < len(argv); ai++ { xargv = append(xargv,strsubst(gshCtx,argv[ai],false)) } argv = xargv if false { for ai := 0; ai < len(argv); ai++ { fmt.Printf("[%d] %s [%d]%T\n", ai,argv[ai],len(argv[ai]),argv[ai]) } } cmd := argv[0] if gshCtx.CmdTrace { fmt.Fprintf(os.Stderr,"--I-- gshellv(%d)%v\n",len(argv),argv) } switch { // https://tour.golang.org/flowcontrol/11 case cmd == "": gshCtx.xPwd([]string{}); // emtpy command case cmd == "-x": gshCtx.CmdTrace = ! gshCtx.CmdTrace case cmd == "-xt": gshCtx.CmdTime = ! gshCtx.CmdTime case cmd == "-ot": gshCtx.sconnect(true, argv) case cmd == "-ou": gshCtx.sconnect(false, argv) case cmd == "-it": gshCtx.saccept(true , argv) case cmd == "-iu": gshCtx.saccept(false, argv) case cmd == "-i" || cmd == "<" || cmd == "-o" || cmd == ">" || cmd == "-a" || cmd == ">>" || cmd == "-s" || cmd == "><": gshCtx.redirect(argv) case cmd == "|": gshCtx.fromPipe(argv) case cmd == "args": gshCtx.Args(argv) case cmd == "bg" || cmd == "-bg": rfin := gshCtx.inBackground(argv[1:]) return rfin case cmd == "-bn": gshCtx.Basename(argv) case cmd == "call": _,_ = gshCtx.excommand(false,argv[1:]) case cmd == "cd" || cmd == "chdir": gshCtx.xChdir(argv); case cmd == "-cksum": gshCtx.xFind(argv) case cmd == "-sum": gshCtx.xFind(argv) case cmd == "-sumtest": str := "" if 1 < len(argv) { str = argv[1] } crc := strCRC32(str,uint64(len(str))) fprintf(stderr,"%v %v\n",crc,len(str)) case cmd == "close": gshCtx.xClose(argv) case cmd == "gcp": gshCtx.FileCopy(argv) case cmd == "dec" || cmd == "decode": gshCtx.Dec(argv) case cmd == "#define": case cmd == "dic" || cmd == "d": xDic(argv) case cmd == "dump": gshCtx.Dump(argv) case cmd == "echo" || cmd == "e": echo(argv,true) case cmd == "enc" || cmd == "encode": gshCtx.Enc(argv) case cmd == "env": env(argv) case cmd == "eval": xEval(argv[1:],true) case cmd == "ev" || cmd == "events": dumpEvents(argv) case cmd == "exec": _,_ = gshCtx.excommand(true,argv[1:]) // should not return here case cmd == "exit" || cmd == "quit": // write Result code EXIT to 3> return true case cmd == "fdls": // dump the attributes of fds (of other process) case cmd == "-find" || cmd == "fin" || cmd == "ufind" || cmd == "uf": gshCtx.xFind(argv[1:]) case cmd == "fu": gshCtx.xFind(argv[1:]) case cmd == "fork": // mainly for a server case cmd == "-gen": gshCtx.gen(argv) case cmd == "-go": gshCtx.xGo(argv) case cmd == "-grep": gshCtx.xFind(argv) case cmd == "gdeq": gshCtx.Deq(argv) case cmd == "genq": gshCtx.Enq(argv) case cmd == "gpop": gshCtx.Pop(argv) case cmd == "gpush": gshCtx.Push(argv) case cmd == "history" || cmd == "hi": // hi should be alias gshCtx.xHistory(argv) case cmd == "jobs": gshCtx.xJobs(argv) case cmd == "lnsp" || cmd == "nlsp": gshCtx.SplitLine(argv) case cmd == "-ls": gshCtx.xFind(argv) case cmd == "nop": // do nothing case cmd == "pipe": gshCtx.xOpen(argv) case cmd == "plug" || cmd == "plugin" || cmd == "pin": gshCtx.xPlugin(argv[1:]) case cmd == "print" || cmd == "-pr": // output internal slice // also sprintf should be gshCtx.Printv(argv) case cmd == "ps": gshCtx.xPs(argv) case cmd == "pstitle": // to be gsh.title case cmd == "rexecd" || cmd == "rexd": gshCtx.RexecServer(argv) case cmd == "rexec" || cmd == "rex": gshCtx.RexecClient(argv) case cmd == "repeat" || cmd == "rep": // repeat cond command gshCtx.repeat(argv) case cmd == "replay": gshCtx.xReplay(argv) case cmd == "scan": // scan input (or so in fscanf) to internal slice (like Files or map) gshCtx.Scanv(argv) case cmd == "set": // set name ... case cmd == "serv": gshCtx.httpServer(argv) case cmd == "shift": gshCtx.Shiftv(argv) case cmd == "sleep": gshCtx.sleep(argv) case cmd == "-sort": gshCtx.Sortv(argv) case cmd == "j" || cmd == "join": gshCtx.Rjoin(argv) case cmd == "a" || cmd == "alpa": gshCtx.Rexec(argv) case cmd == "jcd" || cmd == "jchdir": gshCtx.Rchdir(argv) case cmd == "jget": gshCtx.Rget(argv) case cmd == "jls": gshCtx.Rls(argv) case cmd == "jput": gshCtx.Rput(argv) case cmd == "jpwd": gshCtx.Rpwd(argv) case cmd == "time": fin = gshCtx.xTime(argv) case cmd == "ungets": if 1 < len(argv) { ungets(argv[1]+"\n") }else{ } case cmd == "pwd": gshCtx.xPwd(argv); case cmd == "ver" || cmd == "-ver" || cmd == "version": gshCtx.showVersion(argv) case cmd == "where": // data file or so? case cmd == "which": which("PATH",argv); case cmd == "gj" && 1 < len(argv) && argv[1] == "listen": go gj_server(argv[1:]); case cmd == "gj" && 1 < len(argv) && argv[1] == "serve": go gj_server(argv[1:]); case cmd == "gj" && 1 < len(argv) && argv[1] == "join": go gj_client(argv[1:]); case cmd == "gj": jsend(argv); case cmd == "jsend": jsend(argv); default: if gshCtx.whichPlugin(cmd,[]string{"-s"}) != nil { gshCtx.xPlugin(argv) }else{ notfound,_ := gshCtx.excommand(false,argv) if notfound { fmt.Printf("--E-- command not found (%v)\n",cmd) } } } return fin } func (gsh*GshContext)gshelll(gline string) (rfin bool) { argv := strings.Split(string(gline)," ") fin := gsh.gshellv(argv) return fin } func (gsh*GshContext)tgshelll(gline string)(xfin bool){ start := time.Now() fin := gsh.gshelll(gline) end := time.Now() elps := end.Sub(start); if gsh.CmdTime { fmt.Printf("--T-- " + time.Now().Format(time.Stamp) + "(%d.%09ds)\n", elps/1000000000,elps%1000000000) } return fin } func Ttyid() (int) { fi, err := os.Stdin.Stat() if err != nil { return 0; } //fmt.Printf("Stdin: %v Dev=%d\n", // fi.Mode(),fi.Mode()&os.ModeDevice) if (fi.Mode() & os.ModeDevice) != 0 { stat := aStat_t{}; err := aFstat(0,&stat) if err != nil { //fmt.Printf("--I-- Stdin: (%v)\n",err) }else{ //fmt.Printf("--I-- Stdin: rdev=%d %d\n", // stat.Rdev&0xFF,stat.Rdev); //fmt.Printf("--I-- Stdin: tty%d\n",stat.Rdev&0xFF); return int(stat.Rdev & 0xFF) } } return 0 } func (gshCtx *GshContext) ttyfile() string { //fmt.Printf("--I-- GSH_HOME=%s\n",gshCtx.GshHomeDir) ttyfile := gshCtx.GshHomeDir + "/" + "gsh-tty" + fmt.Sprintf("%02d",gshCtx.TerminalId) //strconv.Itoa(gshCtx.TerminalId) //fmt.Printf("--I-- ttyfile=%s\n",ttyfile) return ttyfile } func (gshCtx *GshContext) ttyline()(*os.File){ file, err := os.OpenFile(gshCtx.ttyfile(),os.O_RDWR|os.O_CREATE|os.O_TRUNC,0600) if err != nil { fmt.Printf("--F-- cannot open %s (%s)\n",gshCtx.ttyfile(),err) return file; } return file } func (gshCtx *GshContext)getline(hix int, skipping bool, prevline string) (string) { if( skipping ){ reader := bufio.NewReaderSize(os.Stdin,LINESIZE) line, _, _ := reader.ReadLine() return string(line) }else if true { return xgetline(hix,prevline,gshCtx) } /* else if( with_exgetline && gshCtx.GetLine != "" ){ //var xhix int64 = int64(hix); // cast newenv := os.Environ() newenv = append(newenv, "GSH_LINENO="+strconv.FormatInt(int64(hix),10) ) tty := gshCtx.ttyline() tty.WriteString(prevline) Pa := os.ProcAttr { "", // start dir newenv, //os.Environ(), []*os.File{os.Stdin,os.Stdout,os.Stderr,tty}, nil, } //fmt.Printf("--I-- getline=%s // %s\n",gsh_getlinev[0],gshCtx.GetLine) proc, err := os.StartProcess(gsh_getlinev[0],[]string{"getline","getline"},&Pa) if err != nil { fmt.Printf("--F-- getline process error (%v)\n",err) // for ; ; { } return "exit (getline program failed)" } //stat, err := proc.Wait() proc.Wait() buff := make([]byte,LINESIZE) count, err := tty.Read(buff) //_, err = tty.Read(buff) //fmt.Printf("--D-- getline (%d)\n",count) if err != nil { if ! (count == 0) { // && err.String() == "EOF" ) { fmt.Printf("--E-- getline error (%s)\n",err) } }else{ //fmt.Printf("--I-- getline OK \"%s\"\n",buff) } tty.Close() gline := string(buff[0:count]) return gline }else */ { // if isatty { fmt.Printf("!%d",hix) fmt.Print(PROMPT) // } reader := bufio.NewReaderSize(os.Stdin,LINESIZE) line, _, _ := reader.ReadLine() return string(line) } } //== begin ======================================================= getline /* * getline.c * 2020-0819 extracted from dog.c * getline.go * 2020-0822 ported to Go */ /* package main // getline main import ( "fmt" // fmt "strings" // strings "os" // os "syscall" // syscall //"bytes" // os //"os/exec" // os ) */ // C language compatibility functions var errno = 0 var stdin *os.File = os.Stdin var stdout *os.File = os.Stdout var stderr *os.File = os.Stderr var EOF = -1 var NULL = 0 type FILE os.File type StrBuff []byte var NULL_FP *os.File = nil var NULLSP = 0 //var LINESIZE = 1024 func system(cmdstr string)(int){ //PA := syscall.ProcAttr { PA := os.ProcAttr { "", // the starting directory os.Environ(), //[]uintptr{os.Stdin.Fd(),os.Stdout.Fd(),os.Stderr.Fd()}, []*os.File{os.Stdin,os.Stdout,os.Stderr}, nil, } argv := strings.Split(cmdstr," ") //pid,err := syscall.ForkExec(argv[0],argv,&PA) proc,err := os.StartProcess(argv[0],argv,&PA); if( err != nil ){ //fmt.Printf("--Es-- system(%v)\n(%v)\n",cmdstr,err); return -1; } pstat,_ := proc.Wait(); pid := pstat.Pid(); if( err != nil ){ fmt.Printf("--E-- pid=%v syscall(%v) err(%v)\n",pid,cmdstr,err) } //syscall.Wait4(pid,nil,0,nil) //fmt.Printf("====E== pid=%d exit=%v stat=%v\n",pid,pstat.Exited(),pstat.ExitCode()); /* argv := strings.Split(cmdstr," ") fmt.Fprintf(os.Stderr,"--I-- system(%v)\n",argv) //cmd := exec.Command(argv[0:]...) cmd := exec.Command(argv[0],argv[1],argv[2]) cmd.Stdin = strings.NewReader("output of system") var out bytes.Buffer cmd.Stdout = &out var serr bytes.Buffer cmd.Stderr = &serr err := cmd.Run() if err != nil { fmt.Fprintf(os.Stderr,"--E-- system(%v)err(%v)\n",argv,err) fmt.Printf("ERR:%s\n",serr.String()) }else{ fmt.Printf("%s",out.String()) } */ return 0 } func atoi(str string)(ret int){ ret,err := fmt.Sscanf(str,"%d",ret) if err == nil { return ret }else{ // should set errno return 0 } } func getenv(name string)(string){ val,got := os.LookupEnv(name) if got { return val }else{ return "?" } } func strcpy(dst StrBuff, src string){ var i int srcb := []byte(src) for i = 0; i < len(src) && srcb[i] != 0; i++ { dst[i] = srcb[i] } dst[i] = 0 } func xstrcpy(dst StrBuff, src StrBuff){ dst = src } func strcat(dst StrBuff, src StrBuff){ dst = append(dst,src...) } func strdup(str StrBuff)(string){ return string(str[0:strlen(str)]) } func sstrlen(str string)(int){ return len(str) } func strlen(str StrBuff)(int){ var i int for i = 0; i < len(str) && str[i] != 0; i++ { } return i } func sizeof(data StrBuff)(int){ return len(data) } func isatty(fd int)(ret int){ return 1 } func fopen(file string,mode string)(fp*os.File){ if mode == "r" { fp,err := os.Open(file) if( err != nil ){ fmt.Printf("--E-- fopen(%s,%s)=(%v)\n",file,mode,err) return NULL_FP; } return fp; }else{ fp,err := os.OpenFile(file,os.O_RDWR|os.O_CREATE|os.O_TRUNC,0600) if( err != nil ){ return NULL_FP; } return fp; } } func fclose(fp*os.File){ fp.Close() } func fflush(fp *os.File)(int){ return 0 } func fgetc(fp*os.File)(int){ var buf [1]byte _,err := fp.Read(buf[0:1]) if( err != nil ){ return EOF; }else{ return int(buf[0]) } } func sfgets(str*string, size int, fp*os.File)(int){ buf := make(StrBuff,size) var ch int var i int for i = 0; i < len(buf)-1; i++ { ch = fgetc(fp) //fprintf(stderr,"--fgets %d/%d %X\n",i,len(buf),ch) if( ch == EOF ){ break; } buf[i] = byte(ch); if( ch == '\n' ){ break; } } buf[i] = 0 //fprintf(stderr,"--fgets %d/%d (%s)\n",i,len(buf),buf[0:i]) return i } func fgets(buf StrBuff, size int, fp*os.File)(int){ var ch int var i int for i = 0; i < len(buf)-1; i++ { ch = fgetc(fp) //fprintf(stderr,"--fgets %d/%d %X\n",i,len(buf),ch) if( ch == EOF ){ break; } buf[i] = byte(ch); if( ch == '\n' ){ break; } } buf[i] = 0 //fprintf(stderr,"--fgets %d/%d (%s)\n",i,len(buf),buf[0:i]) return i } func fputc(ch int , fp*os.File)(int){ var buf [1]byte buf[0] = byte(ch) fp.Write(buf[0:1]) return 0 } func fputs(buf StrBuff, fp*os.File)(int){ fp.Write(buf) return 0 } func xfputss(str string, fp*os.File)(int){ return fputs([]byte(str),fp) } func sscanf(str StrBuff,fmts string, params ...interface{})(int){ fmt.Sscanf(string(str[0:strlen(str)]),fmts,params...) return 0 } func fprintf(fp*os.File,fmts string, params ...interface{})(int){ fmt.Fprintf(fp,fmts,params...) return 0 } // Command Line IME //----------------------------------------------------------------------- MyIME var MyIMEVER = "MyIME/0.0.2"; type RomKana struct { dic string // dictionaly ID pat string // input pattern out string // output pattern hit int64 // count of hit and used } var dicents = 0 var romkana [1024]RomKana var Romkan []RomKana func isinDic(str string)(int){ for i,v := range Romkan { if v.pat == str { return i } } return -1 } const ( DIC_COM_LOAD = "im" DIC_COM_DUMP = "s" DIC_COM_LIST = "ls" DIC_COM_ENA = "en" DIC_COM_DIS = "di" ) func helpDic(argv []string){ out := stderr cmd := "" if 0 < len(argv) { cmd = argv[0] } fprintf(out,"--- %v Usage\n",cmd) fprintf(out,"... Commands\n") fprintf(out,"... %v %-3v [dicName] [dicURL ] -- Import dictionary\n",cmd,DIC_COM_LOAD) fprintf(out,"... %v %-3v [pattern] -- Search in dictionary\n",cmd,DIC_COM_DUMP) fprintf(out,"... %v %-3v [dicName] -- List dictionaries\n",cmd,DIC_COM_LIST) fprintf(out,"... %v %-3v [dicName] -- Disable dictionaries\n",cmd,DIC_COM_DIS) fprintf(out,"... %v %-3v [dicName] -- Enable dictionaries\n",cmd,DIC_COM_ENA) fprintf(out,"... Keys ... %v\n","ESC can be used for '\\'") fprintf(out,"... \\c -- Reverse the case of the last character\n",) fprintf(out,"... \\i -- Replace input with translated text\n",) fprintf(out,"... \\j -- On/Off translation mode\n",) fprintf(out,"... \\l -- Force Lower Case\n",) fprintf(out,"... \\u -- Force Upper Case (software CapsLock)\n",) fprintf(out,"... \\v -- Show translation actions\n",) fprintf(out,"... \\x -- Replace the last input character with it Hexa-Decimal\n",) } func xDic(argv[]string){ if len(argv) <= 1 { helpDic(argv) return } argv = argv[1:] var debug = false var info = false var silent = false var dump = false var builtin = false cmd := argv[0] argv = argv[1:] opt := "" arg := "" if 0 < len(argv) { arg1 := argv[0] if arg1[0] == '-' { switch arg1 { default: fmt.Printf("--Ed-- Unknown option(%v)\n",arg1) return case "-b": builtin = true case "-d": debug = true case "-s": silent = true case "-v": info = true } opt = arg1 argv = argv[1:] } } dicName := "" dicURL := "" if 0 < len(argv) { arg = argv[0] dicName = arg argv = argv[1:] } if 0 < len(argv) { dicURL = argv[0] argv = argv[1:] } if false { fprintf(stderr,"--Dd-- com(%v) opt(%v) arg(%v)\n",cmd,opt,arg) } if cmd == DIC_COM_LOAD { //dicType := "" dicBody := "" if !builtin && dicName != "" && dicURL == "" { f,err := os.Open(dicName) if err == nil { dicURL = dicName }else{ f,err = os.Open(dicName+".html") if err == nil { dicURL = dicName+".html" }else{ f,err = os.Open("gshdic-"+dicName+".html") if err == nil { dicURL = "gshdic-"+dicName+".html" } } } if err == nil { var buf = make([]byte,128*1024) count,err := f.Read(buf) f.Close() if info { fprintf(stderr,"--Id-- ReadDic(%v,%v)\n",count,err) } dicBody = string(buf[0:count]) } } if dicBody == "" { switch arg { default: dicName = "WorldDic" dicURL = WorldDic if info { fprintf(stderr,"--Id-- default dictionary \"%v\"\n", dicName); } case "wnn": dicName = "WnnDic" dicURL = WnnDic case "sumomo": dicName = "SumomoDic" dicURL = SumomoDic case "sijimi": dicName = "SijimiDic" dicURL = SijimiDic case "jkl": dicName = "JKLJaDic" dicURL = JA_JKLDic } if debug { fprintf(stderr,"--Id-- %v URL=%v\n\n",dicName,dicURL); } dicv := strings.Split(dicURL,",") if debug { fprintf(stderr,"--Id-- %v encoded data...\n",dicName) fprintf(stderr,"Type: %v\n",dicv[0]) fprintf(stderr,"Body: %v\n",dicv[1]) fprintf(stderr,"\n") } body,_ := base64.StdEncoding.DecodeString(dicv[1]) dicBody = string(body) } if info { fmt.Printf("--Id-- %v %v\n",dicName,dicURL) fmt.Printf("%s\n",dicBody) } if debug { fprintf(stderr,"--Id-- dicName %v text...\n",dicName) fprintf(stderr,"%v\n",string(dicBody)) } entv := strings.Split(dicBody,"\n"); if info { fprintf(stderr,"--Id-- %v scan...\n",dicName); } var added int = 0 var dup int = 0 for i,v := range entv { var pat string var out string fmt.Sscanf(v,"%s %s",&pat,&out) if len(pat) <= 0 { }else{ if 0 <= isinDic(pat) { dup += 1 continue } romkana[dicents] = RomKana{dicName,pat,out,0} dicents += 1 added += 1 Romkan = append(Romkan,RomKana{dicName,pat,out,0}) if debug { fmt.Printf("[%3v]:[%2v]%-8v [%2v]%v\n", i,len(pat),pat,len(out),out) } } } if !silent { url := dicURL if strBegins(url,"data:") { url = "builtin" } fprintf(stderr,"--Id-- %v scan... %v added, %v dup. / %v total (%v)\n", dicName,added,dup,len(Romkan),url); } // should sort by pattern length for conclete match, for performance if debug { arg = "" // search pattern dump = true } } if cmd == DIC_COM_DUMP || dump { fprintf(stderr,"--Id-- %v dump... %v entries:\n",dicName,len(Romkan)); var match = 0 for i := 0; i < len(Romkan); i++ { dic := Romkan[i].dic pat := Romkan[i].pat out := Romkan[i].out if arg == "" || 0 <= strings.Index(pat,arg)||0 <= strings.Index(out,arg) { fmt.Printf("\\\\%v\t%v [%2v]%-8v [%2v]%v\n", i,dic,len(pat),pat,len(out),out) match += 1 } } fprintf(stderr,"--Id-- %v matched %v / %v entries:\n",arg,match,len(Romkan)); } } func loadDefaultDic(dic int){ if( 0 < len(Romkan) ){ return } //fprintf(stderr,"\r\n") xDic([]string{"dic",DIC_COM_LOAD}); var info = false if info { fprintf(stderr,"--Id-- Conguraturations!! WorldDic is now activated.\r\n") fprintf(stderr,"--Id-- enter \"dic\" command for help.\r\n") } } func readDic()(int){ /* var rk *os.File; var dic = "MyIME-dic.txt"; //rk = fopen("romkana.txt","r"); //rk = fopen("JK-JA-morse-dic.txt","r"); rk = fopen(dic,"r"); if( rk == NULL_FP ){ if( true ){ fprintf(stderr,"--%s-- Could not load %s\n",MyIMEVER,dic); } return -1; } if( true ){ var di int; var line = make(StrBuff,1024); var pat string var out string for di = 0; di < 1024; di++ { if( fgets(line,sizeof(line),rk) == NULLSP ){ break; } fmt.Sscanf(string(line[0:strlen(line)]),"%s %s",&pat,&out); //sscanf(line,"%s %[^\r\n]",&pat,&out); romkana[di].pat = pat; romkana[di].out = out; //fprintf(stderr,"--Dd- %-10s %s\n",pat,out) } dicents += di if( false ){ fprintf(stderr,"--%s-- loaded romkana.txt [%d]\n",MyIMEVER,di); for di = 0; di < dicents; di++ { fprintf(stderr, "%s %s\n",romkana[di].pat,romkana[di].out); } } } fclose(rk); //romkana[dicents].pat = "//ddump" //romkana[dicents].pat = "//ddump" // dump the dic. and clean the command input */ return 0; } func matchlen(stri string, pati string)(int){ if strBegins(stri,pati) { return len(pati) }else{ return 0 } } func convs(src string)(string){ var si int; var sx = len(src); var di int; var mi int; var dstb []byte for si = 0; si < sx; { // search max. match from the position if strBegins(src[si:],"%x/") { // %x/integer/ // s/a/b/ ix := strings.Index(src[si+3:],"/") if 0 < ix { var iv int = 0 //fmt.Sscanf(src[si+3:si+3+ix],"%d",&iv) fmt.Sscanf(src[si+3:si+3+ix],"%v",&iv) sval := fmt.Sprintf("%x",iv) bval := []byte(sval) dstb = append(dstb,bval...) si = si+3+ix+1 continue } } if strBegins(src[si:],"%d/") { // %d/integer/ // s/a/b/ ix := strings.Index(src[si+3:],"/") if 0 < ix { var iv int = 0 fmt.Sscanf(src[si+3:si+3+ix],"%v",&iv) sval := fmt.Sprintf("%d",iv) bval := []byte(sval) dstb = append(dstb,bval...) si = si+3+ix+1 continue } } if strBegins(src[si:],"%t") { now := time.Now() if true { date := now.Format(time.Stamp) dstb = append(dstb,[]byte(date)...) si = si+3 } continue } var maxlen int = 0; var len int; mi = -1; for di = 0; di < dicents; di++ { len = matchlen(src[si:],romkana[di].pat); if( maxlen < len ){ maxlen = len; mi = di; } } if( 0 < maxlen ){ out := romkana[mi].out; dstb = append(dstb,[]byte(out)...); si += maxlen; }else{ dstb = append(dstb,src[si]) si += 1; } } return string(dstb) } func trans(src string)(int){ dst := convs(src); xfputss(dst,stderr); return 0; } //------------------------------------------------------------- LINEEDIT // "?" at the top of the line means searching history // should be compatilbe with Telnet const ( EV_MODE = 255 EV_IDLE = 254 EV_TIMEOUT = 253 GO_UP = 252 // k GO_DOWN = 251 // j GO_RIGHT = 250 // l GO_LEFT = 249 // h DEL_RIGHT = 248 // x GO_TOPL = 'A'-0x40 // 0 GO_ENDL = 'E'-0x40 // $ GO_TOPW = 239 // b GO_ENDW = 238 // e GO_NEXTW = 237 // w GO_FORWCH = 229 // f GO_PAIRCH = 228 // % GO_DEL = 219 // d HI_SRCH_FW = 209 // / HI_SRCH_BK = 208 // ? HI_SRCH_RFW = 207 // n HI_SRCH_RBK = 206 // N ) // should return number of octets ready to be read immediately //fprintf(stderr,"\n--Select(%v %v)\n",err,r.Bits[0]) var EventRecvFd = -1 // file descriptor var EventSendFd = -1 const EventFdOffset = 1000000 const NormalFdOffset = 100 /* 2020-1021 replaced poll() with channel/select func putKeyinEvent(event int, evarg int){ if true { if EventRecvFd < 0 { var pv = []int{-1,-1} // syscall.Pipe(pv) EventRecvFd = pv[0] EventSendFd = pv[1] //fmt.Printf("--De-- EventPipe created[%v,%v]\n",EventRecvFd,EventSendFd) } }else{ if EventRecvFd < 0 { // the document differs from this spec // https://golang.org/src/syscall/syscall_unix.go?s=8096:8158#L340 sv,err := syscall.Socketpair(syscall.AF_UNIX,syscall.SOCK_STREAM,0) EventRecvFd = sv[0] EventSendFd = sv[1] if err != nil { fmt.Printf("--De-- EventSock created[%v,%v](%v)\n", EventRecvFd,EventSendFd,err) } } } var buf = []byte{ byte(event)} n,err := syscall.Write(EventSendFd,buf) if err != nil { fmt.Printf("--De-- putEvent[%v](%3v)(%v %v)\n",EventSendFd,event,n,err) } } */ func ungets(str string){ for _,ch := range str { putKeyinEvent(int(ch),0) } } func (gsh*GshContext)xReplay(argv[]string){ hix := 0 tempo := 1.0 xtempo := 1.0 repeat := 1 for _,a := range argv { // tempo if strBegins(a,"x") { fmt.Sscanf(a[1:],"%f",&xtempo) tempo = 1 / xtempo //fprintf(stderr,"--Dr-- tempo=[%v]%v\n",a[2:],tempo); }else if strBegins(a,"r") { // repeat fmt.Sscanf(a[1:],"%v",&repeat) }else if strBegins(a,"!") { fmt.Sscanf(a[1:],"%d",&hix) }else{ fmt.Sscanf(a,"%d",&hix) } } if hix == 0 || len(argv) <= 1 { hix = len(gsh.CommandHistory)-1 } fmt.Printf("--Ir-- Replay(!%v x%v r%v)\n",hix,xtempo,repeat) //dumpEvents(hix) //gsh.xScanReplay(hix,false,repeat,tempo,argv) go gsh.xScanReplay(hix,true,repeat,tempo,argv) runtime.Gosched(); // wait xScanReplay is launched //fmt.Printf("--Ir-- Replay set\n"); } // syscall.Select // 2020-0827 GShell-0.2.3 /* func FpollIn1(fp *os.File,usec int)(uintptr){ nfd := 1 rdv := syscall.FdSet {} fd1 := fp.Fd() bank1 := fd1/32 mask1 := int32(1 << fd1) rdv.Bits[bank1] = mask1 fd2 := -1 bank2 := -1 var mask2 int32 = 0 if 0 <= EventRecvFd { fd2 = EventRecvFd nfd = fd2 + 1 bank2 = fd2/32 mask2 = int32(1 << fd2) rdv.Bits[bank2] |= mask2 //fmt.Printf("--De-- EventPoll mask added [%d][%v][%v]\n",fd2,bank2,mask2) } tout := syscall.NsecToTimeval(int64(usec*1000)) //n,err := syscall.Select(nfd,&rdv,nil,nil,&tout) // spec. mismatch err := syscall.Select(nfd,&rdv,nil,nil,&tout) if err != nil { //fmt.Printf("--De-- select() err(%v)\n",err) } if err == nil { if 0 <= fd2 && (rdv.Bits[bank2] & mask2) != 0 { if false { fmt.Printf("--De-- got Event\n") } return uintptr(EventFdOffset + fd2) }else if (rdv.Bits[bank1] & mask1) != 0 { return uintptr(NormalFdOffset + fd1) }else{ return 1 } }else{ return 0 } } */ /* func fgetcTimeout1(fp *os.File,usec int)(int){ READ1: //readyFd := FpollIn1(fp,usec) readyFd := CFpollIn1(fp,usec) if readyFd < 100 { return EV_TIMEOUT } var buf [1]byte if EventFdOffset <= readyFd { fd := int(readyFd-EventFdOffset) _,err := syscall.Read(fd,buf[0:1]) if( err != nil ){ return EOF; }else{ if buf[0] == EV_MODE { recvKeyEvent(fd) goto READ1 } return int(buf[0]) } } _,err := fp.Read(buf[0:1]) if( err != nil ){ return EOF; }else{ return int(buf[0]) } } */ func visibleChar(ch int)(string){ switch { case '!' <= ch && ch <= '~': return string(ch) } switch ch { case ' ': return "\\s" case '\n': return "\\n" case '\r': return "\\r" case '\t': return "\\t" } switch ch { case 0x00: return "NUL" case 0x07: return "BEL" case 0x08: return "BS" case 0x0E: return "SO" case 0x0F: return "SI" case 0x1B: return "ESC" case 0x7F: return "DEL" } switch ch { case EV_IDLE: return fmt.Sprintf("IDLE") case EV_MODE: return fmt.Sprintf("MODE") } return fmt.Sprintf("%X",ch) } /* func recvKeyEvent(fd int){ var buf = make([]byte,1) _,_ = syscall.Read(fd,buf[0:1]) if( buf[0] != 0 ){ romkanmode = true }else{ romkanmode = false } } */ func (gsh*GshContext)xScanReplay(hix int,replay bool,repeat int,tempo float64,argv[]string){ var Start time.Time var events = []Event{} for _,e := range Events { if hix == 0 || e.CmdIndex == hix { events = append(events,e) } } elen := len(events) if 0 < elen { if events[elen-1].event == EV_IDLE { events = events[0:elen-1] } } for r := 0; r < repeat; r++ { for i,e := range events { nano := e.when.Nanosecond() micro := nano / 1000 if Start.Second() == 0 { Start = time.Now() } diff := time.Now().Sub(Start) if replay { if e.event != EV_IDLE { //fmt.Printf("--replay %v / %v event=%X\n",i,len(events),e.event); putKeyinEvent(e.event,0) if e.event == EV_MODE { // event with arg putKeyinEvent(int(e.evarg),0) } }else{ //fmt.Printf("--replay %v / %v idle=%X\n",i,len(events),e.event); } }else{ fmt.Printf("%7.3fms #%-3v !%-3v [%v.%06d] %3v %02X %-4v %10.3fms\n", float64(diff)/1000000.0, i, e.CmdIndex, e.when.Format(time.Stamp),micro, e.event,e.event,visibleChar(e.event), float64(e.evarg)/1000000.0) } if e.event == EV_IDLE { //fmt.Printf("--replay %v / %v delay\n",i,len(events)); d := time.Duration(float64(time.Duration(e.evarg)) * tempo) //nsleep(time.Duration(e.evarg)) nsleep(d) } } } } func dumpEvents(arg[]string){ hix := 0 if 1 < len(arg) { fmt.Sscanf(arg[1],"%d",&hix) } for i,e := range Events { nano := e.when.Nanosecond() micro := nano / 1000 //if e.event != EV_TIMEOUT { if hix == 0 || e.CmdIndex == hix { fmt.Printf("#%-3v !%-3v [%v.%06d] %3v %02X %-4v %10.3fms\n",i, e.CmdIndex, e.when.Format(time.Stamp),micro, e.event,e.event,visibleChar(e.event),float64(e.evarg)/1000000.0) } //} } } /* func fgetcTimeout(fp *os.File,usec int)(int){ ch := fgetcTimeout1(fp,usec) if ch != EV_TIMEOUT { now := time.Now() if 0 < len(Events) { last := Events[len(Events)-1] dura := int64(now.Sub(last.when)) Events = append(Events,Event{last.when,EV_IDLE,dura,last.CmdIndex}) } Events = append(Events,Event{time.Now(),ch,0,CmdIndex}) } return ch } */ // 2020-1021 replaced poll() with channel/select var Kbd = make(chan int); var Kbinit = false; var evQ = make(chan int); /* func keyInput(kbd chan int, fp *os.File){ for { ch := C.getc(C.stdin); if( ch == C.EOF ){ break; } kbd <- int(ch); } } */ // https://godoc.org/golang.org/x/crypto/ssh/terminal // https://stackoverflow.com/questions/14094190/function-similar-to-getchar func keyInput(kbd chan int, tty *os.File){ tmode := C.setTermRaw(); defer func(){ C.setTermMode(tmode); }(); if( !OnWindows ){ system("/bin/stty -echo -icanon"); defer func(){ system("/bin/stty echo sane"); }(); } for { var rbuf []byte = make([]byte,1); if( OnWindows ){ C.setTermRaw(); } _,rerr := tty.Read(rbuf); if( rerr != nil ){ break; } //fmt.Printf("++KBD[%X]\n",rbuf[0]); kbd <- int(rbuf[0]); } if( !OnWindows ){ system("/bin/stty echo sane"); } } func fgetcTimeout(fp *os.File,usec int)(int){ if( !Kbinit ){ Kbinit = true; go keyInput(Kbd,fp); } for { select { case <- time.After(time.Duration(usec*1000)): //fmt.Printf("--Timeout %v us\n",usec); return EV_TIMEOUT; case ch := <- Kbd: //fmt.Printf("--KBD[%X]\n",ch); // record a Keyin(ch) Event { now := time.Now() if 0 < len(Events) { last := Events[len(Events)-1] dura := int64(now.Sub(last.when)) Events = append(Events,Event{last.when,EV_IDLE,dura,last.CmdIndex}) } Events = append(Events,Event{time.Now(),ch,0,CmdIndex}) } return ch; case ch := <- evQ: if( ch == EV_MODE ){ recvKeyEvent() }else{ return ch; } } } } func putKeyinEvent(event int, evarg int){ evQ <- event; } func recvKeyEvent(){ ch := <- evQ; if( ch != 0 ){ romkanmode = true }else{ romkanmode = false } } var AtConsoleLineTop = true var TtyMaxCol = 72 // to be obtained by ioctl? var EscTimeout = (100*1000) var ( MODE_VicMode bool // vi compatible command mode MODE_ShowMode bool romkanmode bool // shown translation mode, the mode to be retained MODE_Recursive bool // recursive translation MODE_CapsLock bool // software CapsLock MODE_LowerLock bool // force lower-case character lock MODE_ViInsert int // visible insert mode, should be like "I" icon in X Window MODE_ViTrace bool // output newline before translation ) type IInput struct { lno int lastlno int pch []int // input queue prompt string line string right string inJmode bool pinJmode bool waitingMeta string // waiting meta character LastCmd string } func (iin*IInput)Getc(timeoutUs int)(int){ ch1 := EOF ch2 := EOF ch3 := EOF if( 0 < len(iin.pch) ){ // deQ ch1 = iin.pch[0] iin.pch = iin.pch[1:] }else{ ch1 = fgetcTimeout(stdin,timeoutUs); } if( ch1 == 033 ){ /// escape sequence ch2 = fgetcTimeout(stdin,EscTimeout); if( ch2 == EV_TIMEOUT ){ }else{ ch3 = fgetcTimeout(stdin,EscTimeout); if( ch3 == EV_TIMEOUT ){ iin.pch = append(iin.pch,ch2) // enQ }else{ switch( ch2 ){ default: iin.pch = append(iin.pch,ch2) // enQ iin.pch = append(iin.pch,ch3) // enQ case '[': switch( ch3 ){ case 'A': ch1 = GO_UP; // ^ case 'B': ch1 = GO_DOWN; // v case 'C': ch1 = GO_RIGHT; // > case 'D': ch1 = GO_LEFT; // < case '3': ch4 := fgetcTimeout(stdin,EscTimeout); if( ch4 == '~' ){ //fprintf(stderr,"x[%02X %02X %02X %02X]\n",ch1,ch2,ch3,ch4); ch1 = DEL_RIGHT } } case '\\': //ch4 := fgetcTimeout(stdin,EscTimeout); //fprintf(stderr,"y[%02X %02X %02X %02X]\n",ch1,ch2,ch3,ch4); switch( ch3 ){ case '~': ch1 = DEL_RIGHT } } } } } return ch1 } func (inn*IInput)clearline(){ var i int fprintf(stderr,"\r"); // should be ANSI ESC sequence for i = 0; i < TtyMaxCol; i++ { // to the max. position in this input action fputc(' ',os.Stderr); } fprintf(stderr,"\r"); } func (iin*IInput)Redraw(){ redraw(iin,iin.lno,iin.line,iin.right) } func redraw(iin *IInput,lno int,line string,right string){ inMeta := false showMode := "" showMeta := "" // visible Meta mode on the cursor position showLino := fmt.Sprintf("!%d! ",lno) InsertMark := "" // in visible insert mode if MODE_VicMode { }else if 0 < len(iin.right) { InsertMark = " " } if( 0 < len(iin.waitingMeta) ){ inMeta = true if iin.waitingMeta[0] != 033 { showMeta = iin.waitingMeta } } if( romkanmode ){ //romkanmark = " *"; }else{ //romkanmark = ""; } if MODE_ShowMode { romkan := "--" inmeta := "-" inveri := "" if MODE_CapsLock { inmeta = "A" } if MODE_LowerLock { inmeta = "a" } if MODE_ViTrace { inveri = "v" } if MODE_VicMode { inveri = ":" } if romkanmode { romkan = "\343\201\202" if MODE_CapsLock { inmeta = "R" }else{ inmeta = "r" } } if inMeta { inmeta = "\\" } showMode = "["+romkan+inmeta+inveri+"]"; } Pre := "\r" + showMode + showLino Output := "" Left := "" Right := "" if romkanmode { Left = convs(line) Right = InsertMark+convs(right) }else{ Left = line Right = InsertMark+right } Output = Pre+Left if MODE_ViTrace { Output += iin.LastCmd } Output += showMeta+Right for len(Output) < TtyMaxCol { // to the max. position that may be dirty Output += " " // should be ANSI ESC sequence // not necessary just after newline } Output += Pre+Left+showMeta // to set the cursor to the current input position fprintf(stderr,"%s",Output) if MODE_ViTrace { if 0 < len(iin.LastCmd) { iin.LastCmd = "" fprintf(stderr,"\r\n") } } AtConsoleLineTop = false //fmt.Printf("(Redraw(%v)(%v))\n",len(line),len(right)); } // utf8 func delHeadChar(str string)(rline string,head string){ _,clen := utf8.DecodeRune([]byte(str)) head = string(str[0:clen]) return str[clen:],head } func delTailChar(str string)(rline string, last string){ var i = 0 var clen = 0 for { _,siz := utf8.DecodeRune([]byte(str)[i:]) if siz <= 0 { break } clen = siz i += siz } last = str[len(str)-clen:] return str[0:len(str)-clen],last } // 3> for output and history // 4> for keylog? // Command Line Editor func xgetline(lno int, prevline string, gsh*GshContext)(string){ var iin IInput iin.lastlno = lno iin.lno = lno CmdIndex = len(gsh.CommandHistory) if( isatty(0) == 0 ){ if( sfgets(&iin.line,LINESIZE,stdin) == NULL ){ iin.line = "exit\n"; }else{ } return iin.line } if( true ){ //var pts string; //pts = ptsname(0); //pts = ttyname(0); //fprintf(stderr,"--pts[0] = %s\n",pts?pts:"?"); } if( false ){ fprintf(stderr,"! "); fflush(stderr); sfgets(&iin.line,LINESIZE,stdin); return iin.line } if( !OnWindows ){ system("/bin/stty -echo -icanon"); } xline := iin.xgetline1(prevline,gsh) if( !OnWindows ){system("/bin/stty echo sane"); } return xline } func (iin*IInput)Translate(cmdch int){ romkanmode = !romkanmode; if MODE_ViTrace { fprintf(stderr,"%v\r\n",string(cmdch)); }else if( cmdch == 'J' ){ fprintf(stderr,"J\r\n"); iin.inJmode = true } iin.Redraw(); loadDefaultDic(cmdch); iin.Redraw(); } func (iin*IInput)Replace(cmdch int){ iin.LastCmd = fmt.Sprintf("\\%v",string(cmdch)) iin.Redraw(); loadDefaultDic(cmdch); dst := convs(iin.line+iin.right); iin.line = dst iin.right = "" if( cmdch == 'I' ){ fprintf(stderr,"I\r\n"); iin.inJmode = true } iin.Redraw(); } // aa 12 a1a1 func isAlpha(ch rune)(bool){ if 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' { return true } return false } func isAlnum(ch rune)(bool){ if 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' { return true } if '0' <= ch && ch <= '9' { return true } return false } // 0.2.8 2020-0901 created // DecodeRuneInString func (iin*IInput)GotoTOPW(){ str := iin.line i := len(str) if i <= 0 { return } //i0 := i i -= 1 lastSize := 0 var lastRune rune var found = -1 for 0 < i { // skip preamble spaces lastRune,lastSize = utf8.DecodeRuneInString(str[i:]) if !isAlnum(lastRune) { // character, type, or string to be searched i -= lastSize continue } break } for 0 < i { lastRune,lastSize = utf8.DecodeRuneInString(str[i:]) if lastSize <= 0 { continue } // not the character top if !isAlnum(lastRune) { // character, type, or string to be searched found = i break } i -= lastSize } if found < 0 && i == 0 { found = 0 } if 0 <= found { if isAlnum(lastRune) { // or non-kana character }else{ // when positioning to the top o the word i += lastSize } iin.right = str[i:] + iin.right if 0 < i { iin.line = str[0:i] }else{ iin.line = "" } } //fmt.Printf("\n(%d,%d,%d)[%s][%s]\n",i0,i,found,iin.line,iin.right) //fmt.Printf("") // set debug messae at the end of line } // 0.2.8 2020-0901 created func (iin*IInput)GotoENDW(){ str := iin.right if len(str) <= 0 { return } lastSize := 0 var lastRune rune var lastW = 0 i := 0 inWord := false lastRune,lastSize = utf8.DecodeRuneInString(str[0:]) if isAlnum(lastRune) { r,z := utf8.DecodeRuneInString(str[lastSize:]) if 0 < z && isAlnum(r) { inWord = true } } for i < len(str) { lastRune,lastSize = utf8.DecodeRuneInString(str[i:]) if lastSize <= 0 { break } // broken data? if !isAlnum(lastRune) { // character, type, or string to be searched break } lastW = i // the last alnum if in alnum word i += lastSize } if inWord { goto DISP } for i < len(str) { lastRune,lastSize = utf8.DecodeRuneInString(str[i:]) if lastSize <= 0 { break } // broken data? if isAlnum(lastRune) { // character, type, or string to be searched break } i += lastSize } for i < len(str) { lastRune,lastSize = utf8.DecodeRuneInString(str[i:]) if lastSize <= 0 { break } // broken data? if !isAlnum(lastRune) { // character, type, or string to be searched break } lastW = i i += lastSize } DISP: if 0 < lastW { iin.line = iin.line + str[0:lastW] iin.right = str[lastW:] } //fmt.Printf("\n(%d)[%s][%s]\n",i,iin.line,iin.right) //fmt.Printf("") // set debug messae at the end of line } // 0.2.8 2020-0901 created func (iin*IInput)GotoNEXTW(){ str := iin.right if len(str) <= 0 { return } lastSize := 0 var lastRune rune var found = -1 i := 1 for i < len(str) { lastRune,lastSize = utf8.DecodeRuneInString(str[i:]) if lastSize <= 0 { break } // broken data? if !isAlnum(lastRune) { // character, type, or string to be searched found = i break } i += lastSize } if 0 < found { if isAlnum(lastRune) { // or non-kana character }else{ // when positioning to the top o the word found += lastSize } iin.line = iin.line + str[0:found] if 0 < found { iin.right = str[found:] }else{ iin.right = "" } } //fmt.Printf("\n(%d)[%s][%s]\n",i,iin.line,iin.right) //fmt.Printf("") // set debug messae at the end of line } // 0.2.8 2020-0902 created func (iin*IInput)GotoPAIRCH(){ str := iin.right if len(str) <= 0 { return } lastRune,lastSize := utf8.DecodeRuneInString(str[0:]) if lastSize <= 0 { return } forw := false back := false pair := "" switch string(lastRune){ case "{": pair = "}"; forw = true case "}": pair = "{"; back = true case "(": pair = ")"; forw = true case ")": pair = "("; back = true case "[": pair = "]"; forw = true case "]": pair = "["; back = true case "<": pair = ">"; forw = true case ">": pair = "<"; back = true case "\"": pair = "\""; // context depednet, can be f" or back-double quote case "'": pair = "'"; // context depednet, can be f' or back-quote // case Japanese Kakkos } if forw { iin.SearchForward(pair) } if back { iin.SearchBackward(pair) } } // 0.2.8 2020-0902 created func (iin*IInput)SearchForward(pat string)(bool){ right := iin.right found := -1 i := 0 if strBegins(right,pat) { _,z := utf8.DecodeRuneInString(right[i:]) if 0 < z { i += z } } for i < len(right) { if strBegins(right[i:],pat) { found = i break } _,z := utf8.DecodeRuneInString(right[i:]) if z <= 0 { break } i += z } if 0 <= found { iin.line = iin.line + right[0:found] iin.right = iin.right[found:] return true }else{ return false } } // 0.2.8 2020-0902 created func (iin*IInput)SearchBackward(pat string)(bool){ line := iin.line found := -1 i := len(line)-1 for i = i; 0 <= i; i-- { _,z := utf8.DecodeRuneInString(line[i:]) if z <= 0 { continue } //fprintf(stderr,"-- %v %v\n",pat,line[i:]) if strBegins(line[i:],pat) { found = i break } } //fprintf(stderr,"--%d\n",found) if 0 <= found { iin.right = line[found:] + iin.right iin.line = line[0:found] return true }else{ return false } } // 0.2.8 2020-0902 created // search from top, end, or current position func (gsh*GshContext)SearchHistory(pat string, forw bool)(bool,string){ if forw { for _,v := range gsh.CommandHistory { if 0 <= strings.Index(v.CmdLine,pat) { //fprintf(stderr,"\n--De-- found !%v [%v]%v\n",i,pat,v.CmdLine) return true,v.CmdLine } } }else{ hlen := len(gsh.CommandHistory) for i := hlen-1; 0 < i ; i-- { v := gsh.CommandHistory[i] if 0 <= strings.Index(v.CmdLine,pat) { //fprintf(stderr,"\n--De-- found !%v [%v]%v\n",i,pat,v.CmdLine) return true,v.CmdLine } } } //fprintf(stderr,"\n--De-- not-found(%v)\n",pat) return false,"(Not Found in History)" } // 0.2.8 2020-0902 created func (iin*IInput)GotoFORWSTR(pat string,gsh*GshContext){ found := false if 0 < len(iin.right) { found = iin.SearchForward(pat) } if !found { found,line := gsh.SearchHistory(pat,true) if found { iin.line = line iin.right = "" } } } func (iin*IInput)GotoBACKSTR(pat string, gsh*GshContext){ found := false if 0 < len(iin.line) { found = iin.SearchBackward(pat) } if !found { found,line := gsh.SearchHistory(pat,false) if found { iin.line = line iin.right = "" } } } func (iin*IInput)getstring1(prompt string)(string){ // should be editable iin.clearline(); fprintf(stderr,"\r%v",prompt) str := "" for { ch := iin.Getc(10*1000*1000) if ch == '\n' || ch == '\r' { break } sch := string(ch) str += sch fprintf(stderr,"%s",sch) } return str } // search pattern must be an array and selectable with ^N/^P var SearchPat = "" var SearchForw = true func (iin*IInput)xgetline1(prevline string, gsh*GshContext)(string){ var ch int; MODE_ShowMode = false MODE_VicMode = false iin.Redraw(); first := true for cix := 0; ; cix++ { iin.pinJmode = iin.inJmode iin.inJmode = false ch = iin.Getc(1000*1000) if ch != EV_TIMEOUT && first { first = false mode := 0 if romkanmode { mode = 1 } now := time.Now() Events = append(Events,Event{now,EV_MODE,int64(mode),CmdIndex}) } if ch == 033 { MODE_ShowMode = true MODE_VicMode = !MODE_VicMode iin.Redraw(); continue } if MODE_VicMode { switch ch { case '0': ch = GO_TOPL case '$': ch = GO_ENDL case 'b': ch = GO_TOPW case 'e': ch = GO_ENDW case 'w': ch = GO_NEXTW case '%': ch = GO_PAIRCH case 'j': ch = GO_DOWN case 'k': ch = GO_UP case 'h': ch = GO_LEFT case 'l': ch = GO_RIGHT case 'x': ch = DEL_RIGHT case 'a': MODE_VicMode = !MODE_VicMode ch = GO_RIGHT case 'i': MODE_VicMode = !MODE_VicMode iin.Redraw(); continue case '~': right,head := delHeadChar(iin.right) if len([]byte(head)) == 1 { ch = int(head[0]) if( 'a' <= ch && ch <= 'z' ){ ch = ch + 'A'-'a' }else if( 'A' <= ch && ch <= 'Z' ){ ch = ch + 'a'-'A' } iin.right = string(ch) + right } iin.Redraw(); continue case 'f': // GO_FORWCH iin.Redraw(); ch = iin.Getc(3*1000*1000) if ch == EV_TIMEOUT { iin.Redraw(); continue } SearchPat = string(ch) SearchForw = true iin.GotoFORWSTR(SearchPat,gsh) iin.Redraw(); continue case '/': SearchPat = iin.getstring1("/") // should be editable SearchForw = true iin.GotoFORWSTR(SearchPat,gsh) iin.Redraw(); continue case '?': SearchPat = iin.getstring1("?") // should be editable SearchForw = false iin.GotoBACKSTR(SearchPat,gsh) iin.Redraw(); continue case 'n': if SearchForw { iin.GotoFORWSTR(SearchPat,gsh) }else{ iin.GotoBACKSTR(SearchPat,gsh) } iin.Redraw(); continue case 'N': if !SearchForw { iin.GotoFORWSTR(SearchPat,gsh) }else{ iin.GotoBACKSTR(SearchPat,gsh) } iin.Redraw(); continue } } switch ch { case GO_TOPW: iin.GotoTOPW() iin.Redraw(); continue case GO_ENDW: iin.GotoENDW() iin.Redraw(); continue case GO_NEXTW: // to next space then iin.GotoNEXTW() iin.Redraw(); continue case GO_PAIRCH: iin.GotoPAIRCH() iin.Redraw(); continue } //fprintf(stderr,"A[%02X]\n",ch); if( ch == '\\' || ch == 033 ){ MODE_ShowMode = true metach := ch iin.waitingMeta = string(ch) iin.Redraw(); // set cursor //fprintf(stderr,"???\b\b\b") ch = fgetcTimeout(stdin,2000*1000) // reset cursor iin.waitingMeta = "" cmdch := ch if( ch == EV_TIMEOUT ){ if metach == 033 { continue } ch = metach }else /* if( ch == 'm' || ch == 'M' ){ mch := fgetcTimeout(stdin,1000*1000) if mch == 'r' { romkanmode = true }else{ romkanmode = false } continue }else */ if( ch == 'k' || ch == 'K' ){ MODE_Recursive = !MODE_Recursive iin.Translate(cmdch); continue }else if( ch == 'j' || ch == 'J' ){ iin.Translate(cmdch); continue }else if( ch == 'i' || ch == 'I' ){ iin.Replace(cmdch); continue }else if( ch == 'l' || ch == 'L' ){ MODE_LowerLock = !MODE_LowerLock MODE_CapsLock = false if MODE_ViTrace { fprintf(stderr,"%v\r\n",string(cmdch)); } iin.Redraw(); continue }else if( ch == 'u' || ch == 'U' ){ MODE_CapsLock = !MODE_CapsLock MODE_LowerLock = false if MODE_ViTrace { fprintf(stderr,"%v\r\n",string(cmdch)); } iin.Redraw(); continue }else if( ch == 'v' || ch == 'V' ){ MODE_ViTrace = !MODE_ViTrace if MODE_ViTrace { fprintf(stderr,"%v\r\n",string(cmdch)); } iin.Redraw(); continue }else if( ch == 'c' || ch == 'C' ){ if 0 < len(iin.line) { xline,tail := delTailChar(iin.line) if len([]byte(tail)) == 1 { ch = int(tail[0]) if( 'a' <= ch && ch <= 'z' ){ ch = ch + 'A'-'a' }else if( 'A' <= ch && ch <= 'Z' ){ ch = ch + 'a'-'A' } iin.line = xline + string(ch) } } if MODE_ViTrace { fprintf(stderr,"%v\r\n",string(cmdch)); } iin.Redraw(); continue }else{ iin.pch = append(iin.pch,ch) // push ch = '\\' } } switch( ch ){ case 'P'-0x40: ch = GO_UP case 'N'-0x40: ch = GO_DOWN case 'B'-0x40: ch = GO_LEFT case 'F'-0x40: ch = GO_RIGHT } //fprintf(stderr,"B[%02X]\n",ch); switch( ch ){ case 0: continue; case '\t': iin.Replace('j'); continue case 'X'-0x40: iin.Replace('j'); continue case EV_TIMEOUT: iin.Redraw(); if iin.pinJmode { fprintf(stderr,"\\J\r\n") iin.inJmode = true } continue case GO_UP: if iin.lno == 1 { continue } cmd,ok := gsh.cmdStringInHistory(iin.lno-1) if ok { iin.line = cmd iin.right = "" iin.lno = iin.lno - 1 } iin.Redraw(); continue case GO_DOWN: cmd,ok := gsh.cmdStringInHistory(iin.lno+1) if ok { iin.line = cmd iin.right = "" iin.lno = iin.lno + 1 }else{ iin.line = "" iin.right = "" if iin.lno == iin.lastlno-1 { iin.lno = iin.lno + 1 } } iin.Redraw(); continue case GO_LEFT: if 0 < len(iin.line) { xline,tail := delTailChar(iin.line) iin.line = xline iin.right = tail + iin.right } iin.Redraw(); continue; case GO_RIGHT: if( 0 < len(iin.right) && iin.right[0] != 0 ){ xright,head := delHeadChar(iin.right) iin.right = xright iin.line += head } iin.Redraw(); continue; case EOF: goto EXIT; case 'R'-0x40: // replace dst := convs(iin.line+iin.right); iin.line = dst iin.right = "" iin.Redraw(); continue; case 'T'-0x40: // just show the result readDic(); romkanmode = !romkanmode; iin.Redraw(); continue; case 'L'-0x40: iin.Redraw(); continue case 'K'-0x40: iin.right = "" iin.Redraw(); continue case 'E'-0x40: iin.line += iin.right iin.right = "" iin.Redraw(); continue case 'A'-0x40: iin.right = iin.line + iin.right iin.line = "" iin.Redraw(); continue case 'U'-0x40: iin.line = "" iin.right = "" iin.clearline(); iin.Redraw(); continue; case DEL_RIGHT: if( 0 < len(iin.right) ){ iin.right,_ = delHeadChar(iin.right) iin.Redraw(); } continue; case 0x7F: // BS? not DEL if( 0 < len(iin.line) ){ iin.line,_ = delTailChar(iin.line) iin.Redraw(); } /* else if( 0 < len(iin.right) ){ iin.right,_ = delHeadChar(iin.right) iin.Redraw(); } */ continue; case 'H'-0x40: if( 0 < len(iin.line) ){ iin.line,_ = delTailChar(iin.line) iin.Redraw(); } continue; } if( OnWindows && ch == '\n' ){ continue; } if( ch == '\n' || ch == '\r' ){ iin.line += iin.right; iin.right = "" iin.Redraw(); //fputc(ch,stderr); fprintf(stderr,"\r\n"); // NL on Unix, CR on Windows AtConsoleLineTop = true break; } if MODE_CapsLock { if 'a' <= ch && ch <= 'z' { ch = ch+'A'-'a' } } if MODE_LowerLock { if 'A' <= ch && ch <= 'Z' { ch = ch+'a'-'A' } } iin.line += string(ch); iin.Redraw(); } EXIT: return iin.line + iin.right; } func getline_main(){ line := xgetline(0,"",nil) fprintf(stderr,"%s\n",line); /* dp = strpbrk(line,"\r\n"); if( dp != NULL ){ *dp = 0; } if( 0 ){ fprintf(stderr,"\n(%d)\n",int(strlen(line))); } if( lseek(3,0,0) == 0 ){ if( romkanmode ){ var buf [8*1024]byte; convs(line,buff); strcpy(line,buff); } write(3,line,strlen(line)); ftruncate(3,lseek(3,0,SEEK_CUR)); //fprintf(stderr,"outsize=%d\n",(int)lseek(3,0,SEEK_END)); lseek(3,0,SEEK_SET); close(3); }else{ fprintf(stderr,"\r\ngotline: "); trans(line); //printf("%s\n",line); printf("\n"); } */ } //== end ========================================================= getline // // $USERHOME/.gsh/ // gsh-rc.txt, or gsh-configure.txt // gsh-history.txt // gsh-aliases.txt // should be conditional? // func (gshCtx *GshContext)gshSetupHomedir()(bool) { homedir,found := userHomeDir() if !found { fmt.Printf("--E-- You have no UserHomeDir\n") return true } gshhome := homedir + "/" + GSH_HOME _, err2 := os.Stat(gshhome) if err2 != nil { err3 := os.Mkdir(gshhome,0700) if err3 != nil { fmt.Printf("--E-- Could not Create %s (%s)\n", gshhome,err3) return true } fmt.Printf("--I-- Created %s\n",gshhome) } gshCtx.GshHomeDir = gshhome return false } func setupGshContext()(GshContext,bool){ //gshPA := syscall.ProcAttr { gshPA := os.ProcAttr { "", // the staring directory os.Environ(), // environ[] //[]uintptr{os.Stdin.Fd(),os.Stdout.Fd(),os.Stderr.Fd()}, []*os.File{os.Stdin,os.Stdout,os.Stderr}, nil, // OS specific } cwd, _ := os.Getwd() gshCtx := GshContext { cwd, // StartDir "", // GetLine []GChdirHistory { {cwd,time.Now(),0} }, // ChdirHistory gshPA, []GCommandHistory{}, //something for invokation? GCommandHistory{}, // CmdCurrent false, []os.ProcessState{}, //[]int{}, aRusage{}, "", // GshHomeDir Ttyid(), false, false, []PluginInfo{}, []string{}, " ", "v", ValueStack{}, GServer{"",""}, // LastServer "", // RSERV cwd, // RWD CheckSum{}, } err := gshCtx.gshSetupHomedir() return gshCtx, err } func (gsh*GshContext)gshelllh(gline string)(bool){ ghist := gsh.CmdCurrent ghist.WorkDir,_ = os.Getwd() ghist.WorkDirX = len(gsh.ChdirHistory)-1 //fmt.Printf("--D--ChdirHistory(@%d)\n",len(gsh.ChdirHistory)) ghist.StartAt = time.Now() rusagev1 := Getrusagev() gsh.CmdCurrent.FoundFile = []string{} fin := gsh.tgshelll(gline) rusagev2 := Getrusagev() ghist.Rusagev = RusageSubv(rusagev2,rusagev1) ghist.EndAt = time.Now() ghist.CmdLine = gline ghist.FoundFile = gsh.CmdCurrent.FoundFile /* record it but not show in list by default if len(gline) == 0 { continue } if gline == "hi" || gline == "history" { // don't record it continue } */ gsh.CommandHistory = append(gsh.CommandHistory, ghist) return fin } // Main loop func script(gshCtxGiven *GshContext) (_ GshContext) { gshCtxBuf,err0 := setupGshContext() if err0 { return gshCtxBuf; } gshCtx := &gshCtxBuf //fmt.Printf("--I-- GSH_HOME=%s\n",gshCtx.GshHomeDir) //resmap() /* if false { gsh_getlinev, with_exgetline := which("PATH",[]string{"which","gsh-getline","-s"}) if with_exgetline { gsh_getlinev[0] = toFullpath(gsh_getlinev[0]) gshCtx.GetLine = toFullpath(gsh_getlinev[0]) }else{ fmt.Printf("--W-- No gsh-getline found. Using internal getline.\n"); } } */ ghist0 := gshCtx.CmdCurrent // something special, or gshrc script, or permanent history gshCtx.CommandHistory = append(gshCtx.CommandHistory,ghist0) prevline := "" skipping := false for hix := len(gshCtx.CommandHistory); ; { gline := gshCtx.getline(hix,skipping,prevline) if skipping { if strings.Index(gline,"fi") == 0 { fmt.Printf("fi\n"); skipping = false; }else{ //fmt.Printf("%s\n",gline); } continue } if strings.Index(gline,"if") == 0 { //fmt.Printf("--D-- if start: %s\n",gline); skipping = true; continue } if false { os.Stdout.Write([]byte("gotline:")) os.Stdout.Write([]byte(gline)) os.Stdout.Write([]byte("\n")) } gline = strsubst(gshCtx,gline,true) if false { fmt.Printf("fmt.Printf %%v - %v\n",gline) fmt.Printf("fmt.Printf %%s - %s\n",gline) fmt.Printf("fmt.Printf %%x - %s\n",gline) fmt.Printf("fmt.Printf %%U - %s\n",gline) fmt.Printf("Stouut.Write -") os.Stdout.Write([]byte(gline)) fmt.Printf("\n") } /* // should be cared in substitution ? if 0 < len(gline) && gline[0] == '!' { xgline, set, err := searchHistory(gshCtx,gline) if err { continue } if set { // set the line in command line editor } gline = xgline } */ fin := gshCtx.gshelllh(gline) if fin { break; } prevline = gline; hix++; } return *gshCtx } func ftest(where, path string){ //fi,err := os.Stat(path); //fmt.Printf("-- %v os.Stat(%v)=(%v)%v\n",where,path,err,fi); } func main() { initGshEnv(); ftest("gsh-main","."); ftest("gsh-main","gsh.go"); ftest("gsh-main","gsh.exe"); ftest("gsh-main","gsh"); gshCtxBuf := GshContext{} gsh := &gshCtxBuf argv := os.Args if( isin("wss",argv) ){ gj_server(argv[1:]); return; } if( isin("wsc",argv) ){ gj_client(argv[1:]); return; } if 1 < len(argv) { if isin("version",argv){ gsh.showVersion(argv) return } if argv[1] == "gj" { if argv[2] == "listen" { go gj_server(argv[2:]); } if argv[2] == "server" { go gj_server(argv[2:]); } if argv[2] == "serve" { go gj_server(argv[2:]); } if argv[2] == "client" { go gj_client(argv[2:]); } if argv[2] == "join" { go gj_client(argv[2:]); } } comx := isinX("-c",argv) if 0 < comx { gshCtxBuf,err := setupGshContext() gsh := &gshCtxBuf if !err { gsh.gshellv(argv[comx+1:]) } return } } if 1 < len(argv) && isin("-s",argv) { }else{ gsh.showVersion(append(argv,[]string{"-l","-a"}...)) } script(nil) //gshCtx := script(nil) //gshelll(gshCtx,"time") } //
//
Considerations
// - inter gsh communication, possibly running in remote hosts -- to be remote shell // - merged histories of multiple parallel gsh sessions // - alias as a function or macro // - instant alias end environ export to the permanent > ~/.gsh/gsh-alias and gsh-environ // - retrieval PATH of files by its type // - gsh as an IME with completion using history and file names as dictionaies // - gsh a scheduler in precise time of within a millisecond // - all commands have its subucomand after "---" symbol // - filename expansion by "-find" command // - history of ext code and output of each commoand // - "script" output for each command by pty-tee or telnet-tee // - $BUILTIN command in PATH to show the priority // - "?" symbol in the command (not as in arguments) shows help request // - searching command with wild card like: which ssh-* // - longformat prompt after long idle time (should dismiss by BS) // - customizing by building plugin and dynamically linking it // - generating syntactic element like "if" by macro expansion (like CPP) >> alias // - "!" symbol should be used for negation, don't wast it just for job control // - don't put too long output to tty, record it into GSH_HOME/session-id/comand-id.log // - making canonical form of command at the start adding quatation or white spaces // - name(a,b,c) ... use "(" and ")" to show both delimiter and realm // - name? or name! might be useful // - htar format - packing directory contents into a single html file using data scheme // - filepath substitution shold be done by each command, expecially in case of builtins // - @N substition for the history of working directory, and @spec for more generic ones // - @dir prefix to do the command at there, that means like (chdir @dir; command) // - GSH_PATH for plugins // - standard command output: list of data with name, size, resouce usage, modified time // - generic sort key option -nm name, -sz size, -ru rusage, -ts start-time, -tm mod-time // -wc word-count, grep match line count, ... // - standard command execution result: a list of string, -tm, -ts, -ru, -sz, ... // - -tailf-filename like tail -f filename, repeat close and open before read // - max. size and max. duration and timeout of (generated) data transfer // - auto. numbering, aliasing, IME completion of file name (especially rm of quieer name) // - IME "?" at the top of the command line means searching history // - IME %d/0x10000/ %x/ffff/ // - IME ESC to go the edit mode like in vi, and use :command as :s/x/y/g to edit history // - gsh in WebAssembly // - gsh as a HTTP server of online-manual //---END--- (^-^)//ITS more
// var WorldDic = // "data:text/dic;base64,"+ "Ly8gTXlJTUUvMC4wLjEg6L6e5pu4ICgyMDIwLTA4MTlhKQpzZWthaSDkuJbnlYwKa28g44GT"+ "Cm5uIOOCkwpuaSDjgasKY2hpIOOBoQp0aSDjgaEKaGEg44GvCnNlIOOBmwprYSDjgYsKaSDj"+ "gYQK"; // var WnnDic = // "data:text/dic;base64,"+ "PG1ldGEgY2hhcnNldD0iVVRGLTgiPgo8dGV4dGFyZWEgY29scz04MCByb3dzPTQwPgovL2Rp"+ "Y3ZlcglHU2hlbGxcc0lNRVxzZGljdGlvbmFyeVxzZm9yXHNXbm5ccy8vXHMyMDIwLTA4MzAK"+ "R1NoZWxsCUdTaGVsbArjgo/jgZ/jgZcJ56eBCndhdGFzaGkJ56eBCndhdGFzaQnnp4EK44Gq"+ "44G+44GICeWQjeWJjQpuYW1hZQnlkI3liY0K44Gq44GL44GuCeS4remHjgpuYWthbm8J5Lit"+ "6YeOCndhCeOCjwp0YQnjgZ8Kc2kJ44GXCnNoaQnjgZcKbm8J44GuCm5hCeOBqgptYQnjgb4K"+ "ZQnjgYgKaGEJ44GvCm5hCeOBqgprYQnjgYsKbm8J44GuCmRlCeOBpwpzdQnjgZkKZVxzCWVj"+ "aG8KZGljCWRpYwplY2hvCWVjaG8KcmVwbGF5CXJlcGxheQpyZXBlYXQJcmVwZWF0CmR0CWRh"+ "dGVccysnJVklbSVkLSVIOiVNOiVTJwp0aW9uCXRpb24KJXQJJXQJLy8gdG8gYmUgYW4gYWN0"+ "aW9uCjwvdGV4dGFyZWE+Cg==" // var SumomoDic = // "data:text/dic;base64,"+ "PG1ldGEgY2hhcnNldD0iVVRGLTgiPgo8dGV4dGFyZWEgY29scz04MCByb3dzPTQwPgovL3Zl"+ "cglHU2hlbGxcc0lNRVxzZGljdGlvbmFyeVxzZm9yXHNTdW1vbW9ccy8vXHMyMDIwLTA4MzAK"+ "c3UJ44GZCm1vCeOCggpubwnjga4KdQnjgYYKY2hpCeOBoQp0aQnjgaEKdWNoaQnlhoUKdXRp"+ "CeWGhQpzdW1vbW8J44GZ44KC44KCCnN1bW9tb21vCeOBmeOCguOCguOCggptb21vCeahgwpt"+ "b21vbW8J5qGD44KCCiwsCeOAgQouLgnjgIIKPC90ZXh0YXJlYT4K" // var SijimiDic = // "data:text/dic;base64,"+ "PG1ldGEgY2hhcnNldD0iVVRGLTgiPgo8dGV4dGFyZWEgY29scz04MCByb3dzPTQwPgovL3Zl"+ "cglHU2hlbGxcc0lNRVxzZGljdGlvbmFyeVxzZm9yXHNTaGlqaW1pXHMvL1xzMjAyMC0wODMw"+ "CnNpCeOBlwpzaGkJ44GXCmppCeOBmAptaQnjgb8KbmEJ44GqCmp1CeOBmOOChQp4eXUJ44KF"+ "CnUJ44GGCm5pCeOBqwprbwnjgZMKYnUJ44G2Cm5uCeOCkwpubwnjga4KY2hpCeOBoQp0aQnj"+ "gaEKa2EJ44GLCnJhCeOCiQosLAnjgIEKLi4J44CCCnhuYW5hCeS4gwp4anV1CeWNgQp4bmkJ"+ "5LqMCmtveAnlgIsKa29xCeWAiwprb3gJ5YCLCm5hbmFqdXVuaXgJNzIKbmFuYWp1dW5peHgJ"+ "77yX77ySCm5hbmFqdXVuaVgJ77yX77ySCuS4g+WNgeS6jHgJNzIKa29idW5uCeWAi+WIhgp0"+ "aWthcmFxCeOBoeOBi+OCiQp0aWthcmEJ5YqbCmNoaWthcmEJ5YqbCjwvdGV4dGFyZWE+Cg=" // var JA_JKLDic = // "data:text/dic;base64,"+ "Ly92ZXJsCU15SU1FamRpY2ptb3JzZWpKQWpKS0woMjAyMGowODE5KSheLV4pL1NhdG94SVRT"+ "CmtqamprbGtqa2tsa2psIOS4lueVjApqamtqamwJ44GCCmtqbAnjgYQKa2tqbAnjgYYKamtq"+ "amwJ44GICmtqa2trbAnjgYoKa2pra2wJ44GLCmpramtrbAnjgY0Ka2tramwJ44GPCmpramps"+ "CeOBkQpqampqbAnjgZMKamtqa2psCeOBlQpqamtqa2wJ44GXCmpqamtqbAnjgZkKa2pqamts"+ "CeOBmwpqamprbAnjgZ0KamtsCeOBnwpra2prbAnjgaEKa2pqa2wJ44GkCmtqa2pqbAnjgaYK"+ "a2tqa2tsCeOBqApramtsCeOBqgpqa2prbAnjgasKa2tra2wJ44GsCmpqa2psCeOBrQpra2pq"+ "bAnjga4Kamtra2wJ44GvCmpqa2tqbAnjgbIKampra2wJ44G1CmtsCeOBuApqa2tsCeOBuwpq"+ "a2tqbAnjgb4Ka2tqa2psCeOBvwpqbAnjgoAKamtra2psCeOCgQpqa2tqa2wJ44KCCmtqamwJ"+ "44KECmpra2pqbAnjgoYKampsCeOCiApra2tsCeOCiQpqamtsCeOCigpqa2pqa2wJ44KLCmpq"+ "amwJ44KMCmtqa2psCeOCjQpqa2psCeOCjwpramtramwJ44KQCmtqamtrbAnjgpEKa2pqamwJ"+ "44KSCmtqa2prbAnjgpMKa2pqa2psCeODvApra2wJ44KbCmtramprbAnjgpwKa2pramtqbAnj"+ "gIEK"; // // /*
References
*/ /*
Raw Source
Whole file
CSS part
JavaScript part
Builtin data part
*/ /*
(^_^)//{Hit j k l h}
CLOSE
*/ // // /*
PackmonGo

PackmonGo

*/ //
// // // /*
SightGlass

SightGlass

meter
(0000, 0000)(0000 x 0000)
(0000, 0000)
(0000, 0000)(0000 x 0000)
(0000, 0000)(0000 x 0000)
Wheel
*/ //
// /*
GJ Console

*/ /*
Form Auto. Filling
Location: Username: Password: SessionId:
*/ /*
BlinderText // https://w3c.github.io/uievents/#event-type-keydown // // 2020-09-21 class BlinderText - textarea element not to be readable // // BlinderText attributes // bl_plainText - null // bl_hideChecksum - [false] // bl_showLength - [false] // bl_visible - [false] // data-bl_config - [] // - min. length // - max. length // - acceptable charset in generete text // function BlinderChecksum(text){ plain = text.bl_plainText; return strCRC32(plain,plain.length).toFixed(0); } function BlinderKeydown(ev){ pass = ev.target if( ev.code == 'Enter' ){ ev.preventDefault(); } ev.stopPropagation() } function BlinderKeyup1(ev){ blind = ev.target if( ev.code == 'Backspace'){ blind.bl_plainText = blind.bl_plainText.slice(0,blind.bl_plainText.length-1) }else if( and(ev.code == 'KeyV', ev.ctrlKey) ){ blind.bl_visible = !blind.bl_visible; }else if( and(ev.code == 'KeyL', ev.ctrlKey) ){ blind.bl_showLength = !blind.bl_showLength; }else if( and(ev.code == 'KeyU', ev.ctrlKey) ){ blind.bl_plainText = ""; }else if( and(ev.code == 'KeyR', ev.ctrlKey) ){ checksum = BlinderChecksum(blind); blind.bl_plainText = checksum; //.toString(32); }else if( ev.code == 'Enter' ){ ev.stopPropagation(); ev.preventDefault(); return; }else if( ev.key.length != 1 ){ console.log('KeyUp: '+ev.code+'/'+ev.key); return; }else{ blind.bl_plainText += ev.key; } leng = blind.bl_plainText.length; //console.log('KeyUp: '+ev.code+'/'+blind.bl_plainText); checksum = BlinderChecksum(blind) % 10; // show last one digit only visual = ''; if( !blind.bl_hideCheckSum || blind.bl_showLength ){ visual += '['; } if( !blind.bl_hideCheckSum ){ visual += '#'+checksum.toString(10); } if( blind.bl_showLength ){ visual += '/' + leng; } if( !blind.bl_hideCheckSum || blind.bl_showLength ){ visual += '] '; } if( blind.bl_visible ){ visual += blind.bl_plainText; }else{ visual += '*'.repeat(leng); } blind.value = visual; } function BlinderKeyup(ev){ BlinderKeyup1(ev); ev.stopPropagation(); } // https://w3c.github.io/uievents/#keyboardevent // https://w3c.github.io/uievents/#uievent // https://dom.spec.whatwg.org/#event function BlinderTextEvent(){ ev = event; blind = ev.target; console.log('Event '+ev.type+'@'+blind.nodeName+'#'+blind.id) if( ev.type == 'keyup' ){ BlinderKeyup(ev); }else if( ev.type == 'keydown' ){ BlinderKeydown(ev); }else{ console.log('thru-event '+ev.type+'@'+blind.nodeName+'#'+blind.id) } } //< textarea hidden id="BlinderTextClassDef" class="textField"" // onkeydown="BlinderTextEvent()" onkeyup="BlinderTextEvent()" // spellcheck="false">< /textarea> //< textarea hidden id="gj_pass1" // class="textField BlinderText" // placeholder="PassWord1" // onkeydown="BlinderTextEvent()" // onkeyup="BlinderTextEvent()" // spellcheck="false"< /textarea> function SetupBlinderText(parent,txa,phold){ if( txa == null ){ txa = document.createElement('textarea'); //txa.id = id; } txa.setAttribute('class','textField BlinderText'); txa.setAttribute('placeholder',phold); txa.setAttribute('onkeydown','BlinderTextEvent()'); txa.setAttribute('onkeyup','BlinderTextEvent()'); txa.setAttribute('spellcheck','false'); //txa.setAttribute('bl_plainText','false'); txa.bl_plainText = ''; //parent.appendChild(txa); } function DestroyBlinderText(txa){ txa.removeAttribute('class'); txa.removeAttribute('placeholder'); txa.removeAttribute('onkeydown'); txa.removeAttribute('onkeyup'); txa.removeAttribute('spellcheck'); txa.bl_plainText = ''; } // // visible textarea like Username // function VisibleTextEvent(){ if( event.code == 'Enter' ){ if( event.target.NoEnter ){ event.preventDefault(); } } event.stopPropagation(); } function SetupVisibleText(parent,txa,phold){ if( false ){ txa.setAttribute('class','textField VisibleText'); }else{ newclass = txa.getAttribute('class'); if( and(newclass != null, newclass != '') ){ newclass += ' '; } newclass += 'VisibleText'; txa.setAttribute('class',newclass); } //console.log('SetupVisibleText class='+txa.class); txa.setAttribute('placeholder',phold); txa.setAttribute('onkeydown','VisibleTextEvent()'); txa.setAttribute('onkeyup', 'VisibleTextEvent()'); txa.setAttribute('spellcheck','false'); cols = txa.getAttribute('cols'); if( cols != null ){ txa.style.width = '580px'; //console.log('VisualText#'+txa.id+' cols='+cols) }else{ //console.log('VisualText#'+txa.id+' NO cols') } rows = txa.getAttribute('rows'); if( rows != null ){ txa.style.height = '30px'; txa.style.resize = 'both'; txa.NoEnter = false; }else{ txa.NoEnter = true; } } function DestroyVisibleText(txa){ txa.removeAttribute('class'); txa.removeAttribute('placeholder'); txa.removeAttribute('onkeydown'); txa.removeAttribute('onkeyup'); txa.removeAttribute('spellcheck'); cols = txa.removeAttribute('cols'); }
*/ /* */ // //
Golang / JavaScript Link // // 2020-0920 created // WS // WS // INSTALL: go get golang.org/x/net/websocket // INSTALL: sudo {apt,yum} install git (if git is not instlled yet) // import "golang.org/x/net/websocket" const gshws_origin = "http://locahost:9999" const gshws_server = "localhost:9999" const gshws_port = 9999 const gshws_path = "gjlink1" const gshws_url = "ws://"+gshws_server+"/"+gshws_path const GSHWS_MSGSIZE = (8*1024) func fmtstring(fmts string, params ...interface{})(string){ return fmt.Sprintf(fmts,params...) } func GSHWS_MARK(what string)(string){ now := time.Now() us := fmtstring("%06d",now.Nanosecond() / 1000) mark := "" if( !AtConsoleLineTop ){ mark += "\n" AtConsoleLineTop = true } mark += "["+now.Format(time.Stamp)+"."+us+"] -GJ-" + what + ": " return mark } func gchk(what string,err error){ if( err != nil ){ panic(GSHWS_MARK(what)+err.Error()) } } func glog(what string, fmts string, params ...interface{}){ fmt.Print(GSHWS_MARK(what)) fmt.Printf(fmts+"\n",params...) } var WSV = []*websocket.Conn{} func jsend(argv []string){ if len(argv) <= 1 { fmt.Printf("--Ij %v [-m] command arguments\n",argv[0]) return } argv = argv[1:] if( len(WSV) == 0 ){ fmt.Printf("--Ej-- No link now\n") return } if( 1 < len(WSV) ){ fmt.Printf("--Ij-- multiple links (%v)\n",len(WSV)) } multicast := false // should be filtered with regexp if( 0 < len(argv) && argv[0] == "-m" ){ multicast = true argv = argv[1:] } args := strings.Join(argv," ") now := time.Now() msec := now.UnixNano() / 1000000; tstamp := fmtstring("%.3f",float64(msec)/1000.0) msg := fmtstring("%v SEND gshell|* %v",tstamp,args) if( multicast ){ for i,ws := range WSV { wn,werr := ws.Write([]byte(msg)) if( werr != nil ){ fmt.Printf("[%v] wn=%v, werr=%v\n",i,wn,werr) } glog("SQ",fmtstring("(%v) %v",wn,msg)) } }else{ i := 0 ws := WSV[i] wn,werr := ws.Write([]byte(msg)) if( werr != nil ){ fmt.Printf("[%v] wn=%v, werr=%v\n",i,wn,werr) } glog("SQ",fmtstring("(%v) %v",wn,msg)) } } func ws_broadcast(msg string)(wn int,werr error){ for i,ws := range WSV { wn,werr = ws.Write([]byte(msg)) if( werr != nil ){ fmt.Printf("[%v] wn=%v, werr=%v\n",i,wn,werr) } glog("SQ",fmtstring("(%v) %v",wn,msg)) } return wn,werr; } func serv1(ws *websocket.Conn) { WSV = append(WSV,ws) //fmt.Print("\n") glog("CO","accepted connections[%v]",len(WSV)) //remoteAddr := ws.RemoteAddr //fmt.Printf("-- accepted %v\n",remoteAddr) //fmt.Printf("-- accepted %v\n",ws.Config()) //fmt.Printf("-- accepted %v\n",ws.Config().Header) //fmt.Printf("-- accepted %v // %v\n",ws,serv1) var reqb = make([]byte,GSHWS_MSGSIZE) for { rn, rerr := ws.Read(reqb) if( rerr != nil || rn < 0 ){ glog("SQ",fmtstring("(%v,%v)",rn,rerr)) break } req := string(reqb[0:rn]) glog("SQ",fmtstring("(%v) %v",rn,req)) margv := strings.Split(req," "); margv = margv[1:] if( 0 < len(margv) ){ if( margv[0] == "RESP" ){ // should forward to the destination continue; } } now := time.Now() msec := now.UnixNano() / 1000000; tstamp := fmtstring("%.3f",float64(msec)/1000.0) res := fmtstring("%v "+"CAST"+" %v",tstamp,req) wn := 0; werr := error(nil); if( 0 < len(margv) && margv[0] == "BCAST" ){ wn, werr = ws_broadcast(res); }else{ wn, werr = ws.Write([]byte(res)) } gchk("SE",werr) glog("SR",fmtstring("(%v) %v",wn,string(res))) } glog("SF","WS response finish") wsv := []*websocket.Conn{} wsx := 0 for i,v := range WSV { if( v != ws ){ wsx = i wsv = append(wsv,v) } } WSV = wsv //glog("CO","closed %v",ws) glog("CO","closed connection [%v/%v]",wsx+1,len(WSV)+1) ws.Close() } // url ::= [scheme://]host[:port][/path] func decomp_URL(url string){ } func full_wsURL(){ } func gj_server(argv []string) { gjserv := gshws_url gjport := gshws_server gjpath := gshws_path gjscheme := "ws" //cmd := argv[0] argv = argv[1:] if( 1 <= len(argv) ){ serv := argv[0] if( 0 < strings.Index(serv,"://") ){ schemev := strings.Split(serv,"://") gjscheme = schemev[0] serv = schemev[1] } if( 0 < strings.Index(serv,"/") ){ pathv := strings.Split(serv,"/") serv = pathv[0] gjpath = pathv[1] } servv := strings.Split(serv,":") host := "localhost" port := 9999 if( servv[0] != "" ){ host = servv[0] } if( len(servv) == 2 ){ fmt.Sscanf(servv[1],"%d",&port) } //glog("LC","hostport=%v (%v : %v)",servv,host,port) gjport = fmt.Sprintf("%v:%v",host,port) gjserv = gjscheme + "://" + gjport + "/" + gjpath } glog("LS",fmtstring("listening at %v",gjserv)) http.Handle("/"+gjpath,websocket.Handler(serv1)) err := error(nil) if( gjscheme == "wss" ){ // https://golang.org/pkg/net/http/#ListenAndServeTLS //err = http.ListenAndServeTLS(gjport,nil) }else{ err = http.ListenAndServe(gjport,nil) } gchk("LE",err) } func gj_client(argv []string) { glog("CS",fmtstring("connecting to %v",gshws_url)) ws, err := websocket.Dial(gshws_url,"",gshws_origin) gchk("C",err) var resb = make([]byte, GSHWS_MSGSIZE) for qi := 0; qi < 3; qi++ { req := fmtstring("Hello, GShell! (%v)",qi) wn, werr := ws.Write([]byte(req)) glog("QM",fmtstring("(%v) %v",wn,req)) gchk("QE",werr) rn, rerr := ws.Read(resb) gchk("RE",rerr) glog("RM",fmtstring("(%v) %v",rn,string(resb))) } glog("CF","WS request finish") } // //
/* */ /* */ /*
Live HTML Snapshot
Edit Save Load Vers
*/ /*
Event sharing

Inter-window communicaiton

frame0 >>> frame1 and frame2
frame1 >>> frame0 and frame2
frame2 >>> frame0 and frame1




*/ /* // /*
Wirtual Desktop

CosmoScreen 0.0.8

ScopeControl command keys
g ... grid on/off
i ... zoom in
o ... zoom out
s ... save current scope
r ... restore saved scope

Monitor < x wall-paper: WD-WallPaler03.png

Desktop < x

Display < x < <

Content X Y shift+wheel for horizontal scroll

Scopexx < | >

Scopeyy < | >

Scopezz < | >

Display

Overflo "scroll" imprisons windows inside the display

CosmoScreen 0.0.8
00:00

WirtualSpace 1.

Reload
Reload
Reload
*/ //
// // /*
SBSidebar

SBSidebar

// 2020-1006 its-more.jp-blog-60000-style.css
*/ //
// // // /*
Affiliate

Supportive Affiliate

*/ //
// // // /*
TextCanvas
Font Selection

Font Selection

Drawing Text on Canvas

Bold Italic
Pixels

PNG JPEG DataURL

(inline image)

(background image)
(Data URL)
*/ //
// // // /*
Shading Canvas

Shading Canvas

Commands
Placement Mode
a ... apply (into absolute position)
j ... bring down (ArrowDown)
k ... bring up (ArrowUp)
h ... bring left (ArrowLeft)
l ... bring right (ArrowRight)
0 ... z-index = 0
+ ... z-index += 1
- ... z-index -= 1
r ... return to here (relative position)
c ... clear the log text
Note: the HTML text must be contenteditable to catch Key Event.
My Canvas.
*/ //
// // // /*
Character Map Mandala

Unicode Character Map

code 0x0000 - 0xFFFF / 16px / 3200 x 3200 px / zoom:0.25

*/ //
// // // /*
Collaborated Pointillism

Collaborated Pointillism

Share
XY1 XY1-remote XY2 XY2-remote

*/ //
// // // /*
StatCounter

StatCounter

hit counter (counter as image tag)
(counter by inline script)
*/ //
// // // /*
CascadedCanvasBook

Cascaded Canvas Book


Undo / Redo / Replay

From To

Color Picker

Select value by Mouse Motion Auto. add to history Mouse Wheel Auto. add to history
R G B A RGBA Sample
R G B A RGBA Sample
R G B A RGBA Sample
R G B A RGBA Sample
Sample

/ Max.

Colors

#CO-0 BW FC xxx xxx BC
#CO-1 BW FC xxx xxx BC
#CO-2 BW FC xxx xxx BC
#CO-3 BW FC xxx xxx BC
#CO-4 BW FC xxx xxx BC
#CO-5 BW FC xxx xxx BC

Parts

Resize% Zoom% Redraw Immediate
#CL-0 F X Y Z W H RO #CO-
#RE-0 F X Y Z W H RO #CO-
#CI-0 F X Y Z R S E #CO-
#PA-0 F X Y Z R S E #CO-
#EL-0 F X Y Z W H RO S E #CO-
#BA-0 F X Y Z W H RO S E #CO-
#BA-0 F X Y Z W H RO S E #CO-
#XA-0 F X Y Z W H RO S E #CO-
#XA-1 F X Y Z W H RO S E #CO-

Animation

#ANIMA-0 TYPE ITVLms DURAs

Canvas

the above part #0 W H Zoom%

CanvasBook


*/ //
// // //SVG/SVG /*
SVG Getting Started

Getting Started SVG

*/ //
// // //X3DROFL/X3DROFL /*
X3DROFL Getting Started

X3D-ROFL

x3dom-1.8.2-dev

Viewpoint position= orientation=
Box position= rotation=
*/ //
// // // /*
Work Template

Template of Work

*/ //
// // // /*
Original Source

Original Source of GShell

*/ //
// //
//
//2050173303 430272 gsh (2020/10/12 07:09:29)

3D : Canvas vs SVG

社長:さて、当社製ROFLですが。

基盤:3Dで定義するということは決定事項ですね。

開発:それで、canvas でやるか SVG かという選択なんですが。前提として、ブラウザ依存でないということが必須です。で、canvasでやる場合、どうやらキモは WebGL のようです。

社長:GPUを使い倒そうって感じですかね。

開発:たとえば立方体の面に絵を貼り付けて回転させる

基盤:どのブラウザでもOKですね。

社長:Chromiumは痛々しすぎるのでやめましょう。

基盤:だいたい、CPUのほうは10%、GPUのほうは7%というところですね。

開発:一方 SVG における、X3DOMですが。プラグイン不要と主張しているところは同じですね。

開発:WebGL 2.0 もサポートしているとあります。

開発:ただし、X3DOMの人々が強調している点は「declarative」です。

社長:その点は、SVGなら 2Dでも3Dでも共通というか連続的ということですね。

開発:まあ、宣言的に書けるならそれに越したことは無いと思うわけです。そもそもJavaScriptが嫌いだったのは、HTMLやCSSで宣言的に書けるべきところをいちいち手続きで書くというのが気持ち悪かったからです。それはもうHTML5ではほぼ解決済みと思われます。

社長:SVGは個々のパーツがいちいちDOMの要素であってCSSで制御できてイベントハンドリングできるのは魅力ですね。

開発:まあ宣言的というのは、WebGLも似たようなところはあるんですけど。

社長:うちはちょっとだけ3Dしてみたいだけなので、やっぱSVGですかね。

開発:実装上はかなりかぶってると思うんですよね。

開発:ほとんどの場合は2Dですし、WirtualBrowserの記述にもSVGが適当だろうと思われます。

社長:じゃま、しばらくX3DOMでやってみましょう。

-- 2020-1110 SatoxITS

3次元ROFL

開発:さて、今日の取り組みは。

社長:球体にROFLを描いて踊らせてみたいかな。

基盤:投げたり転がしたら面白そうですね。ビリヤードみたいにするとか。

開発:その前に顔が問題ですが。

社長:笑いっぷりという意味では、Skypeかコジマ坊やですかね。

開発:30年以上前になりますが、最初これを見た時は衝撃を受けましたね。

社長;当時はYの字型の架台みたいのに載って県道の脇に立ってたりしました。

基盤:おっと、macOSのアップデート来てます。片方ずつアップデートします。

開発:爆笑という意味では、目は⌒o⌒とかより>o<的な感じかなと思います。

社長:球面にAAで書くってのもありかもですね。

基盤:おーっと、日経平均+2%超えました。

開発:大統領選の結果を受けてでしょうかね。

社長:それはそうと、我社の日々の行為は仕事でも作業でも無い、「取り組み」と表現するのが良いように思います。

Work // 取り組み、作品

基盤:業とか仕とかいやなイメージですよね。

開発:取り組むっていうと、タックルする感じですね。

社長:円筒にHTMLのページを巻きつけて、回転させてブラウズするのも面白そうです。

基盤:SVGって3D出来るんでしょうか?

開発:検索… できるみたいですね。X3DOMというので。たとえば、こんな感じ↓ドラッグで回転、ホイールで拡大・縮小になってます。

<html> 
   <head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/> 
     <title>My first X3DOM page</title> 
     <script type='text/javascript' src='https://www.x3dom.org/download/x3dom.js'> </script> 
     <link rel='stylesheet' type='text/css' href='https://www.x3dom.org/download/x3dom.css'></link> 
   </head> 
   <body> 
     <h3>Hello, X3DOM!</h3> 
     <p> 
       This is my first html page with some 3d objects. 
     </p> 
	 <x3d width='500px' height='200px'> 
	   <scene> 
		<shape> 
		   <appearance> 
			 <material diffuseColor='1 0 0'></material> 
		   </appearance> 
		   <box></box> 
		</shape> 
		<transform translation='-3 0 0'> 
		  <shape> 
			 <appearance> 
			   <material diffuseColor='0 1 0'></material> 
			 </appearance> 
			 <cone></cone> 
		  </shape> 
		</transform> 
		<transform translation='3 0 0'> 
		  <shape> 
			 <appearance> 
			   <material diffuseColor='0 0 1'></material> 
			 </appearance> 
			 <sphere></sphere> 
		  </shape> 
		</transform> 
	   </scene> 
	</x3d> 
   </body> 
</html>
My first X3DOM page

Hello, X3DOM!

This is my first html page with some 3d objects.

社長、基盤:おおーっ。

開発:こちらは、X3DOMのフロントページに載ってる、もっとシンプルな版。

<x3d width='500px' height='400px'>
    <scene>
        <shape>
            <appearance>
                <material diffuseColor='1 0 0'></material>
            </appearance>
            <box></box>
        </shape>
    </scene>
</x3d>

基盤:どのブラウザでもイケますね。

開発:まあ、JavaScriptですしね。

社長:これって、回したり拡大した時にブラウザの枠の外まで飛び出してくると面白いでしょうね。

基盤:なお、Chromiumでも動きますが、異常に重い模様。

開発:これは…

基盤:7月に自前でビルドしたやつなんですが…

社長:ChromiumとFirefoxはJavaScriptの実行が重いように見えます。

開発:ベンチマーク用には良いかも知れませんね。

-- 2020-1009 SatoxITS

GShell 0.8.0 − SVG事始め

社長:いやー、1週間ぽっかり休みましたね。

基盤:大統領選がきっかけですっかりSNSにはまりました。

社長:でも、おかげでいろいろヒントを得ました。

開発:脳味噌がすっからかーんな感じになりましたwある種の爽快感。

社長:SVGをやりかけて止まりましたので、あれから再開しましょうか。

* * *

開発:それではまずスマイルマークより。だけでお試し。タグの書き方が2とおりあるようなので、両方で。

基盤:おおっと、viewBoxのほうは顔がでかい!

開発:周囲との比率指定みたいですね。とりあえず CSSで枠を決めます。

社長:どっちの書き方にすべきなんでしょうね?

開発:どのブラウザもどっちでも良いみたいですが。MDNに書いてある方法のほうがきっと将来的には良いのかな… 後で調べましょう。

基盤:まずは口ですね。口を半円か円弧にしましょうよ。

開発:そうですね。えーと円弧の角度…

* * *

開発:どうもよくわからないですが、canvas の arc に相当するものはないですね。path で書くみたいなんですが、始点と終点、あと曲率?を指定するみたいな。

社長:もろに昔のプロッターのコマンドみたいな指示方法ですね。

社長、基盤:おおーっ。

社長:canvas で描いたスマイルマークと同じものを書くとどうなるでしょう?

開発:まず、輪郭だけにするとこうなりますね。

開発:で、顔のパーツの位置を適当に調整するとこういう感じ。

社長、基盤:おおーっ、スマイルマークだ。

開発:ちなみにMDNのオリジナルのスマイルマークがこれ。

開発:まあ、計算して出せば、ピクセル単位でまったく同じものができると思いますが。

社長:SVG に canvas 互換関数で書くというような標準関数はないんでしょうかね?

開発:標準では無いように見えますが・・・

基盤:回転してみたいですね。

開発:これは普通に、CSSで回せば良いのかなと思います。

Canvas ver. SVG ver.

開発:うーん、Safari / iMac で 30ms 間隔でですが、回転させるのにまずタイマーイベント処理に2%、 canvas だとCPUを 2%、SVG だと 7% くらい食ってるように見えますね。

基盤:タイマーイベント処理の部分はCSSアニメ機能を使えば軽くなりそうです。

社長:SVGが重いのは、構成してるエレメントの位置とかを再計算してるからでしょうかね。

開発:SVGだと拡大縮小しても最適な表示が描画されるわけですけど、それは canvasだってリサイズに応じてパラメータを変えて再描画すればいいわけで、それ自体はSVGのメリットとは言い難い感じはします。

社長:やはり、パーツ単位で移動できたり、クリックしてイベントを発生させたりするようなアプリに向いているということでしょうね。

Canvas ver. SVG ver.

基盤:これ、実行するとなぜかFirefoxだけ重いように見えます。

社長:色々個性があって面白いですね。

-- 2020-1108 SatoxITS

/* GShell-0.8.0 by SatoxITS
GShell version 0.8.0 // 2020-11-08 // SatoxITS
*/ // // /*
Topbar

Topbar

*/ //
// // // /*
Indexer

Indexer

*/ //
// /*

GShell // a General purpose Shell built on the top of Golang

It is a shell for myself, by myself, of myself. --SatoxITS(^-^) prev.

Edit Save Load Vers 0 Fork Stop Unfold Digest Source
*/ /*
Statement

Fun to create a shell

For a programmer, it must be far easy and fun to create his own simple shell rightly fitting to his favor and necessities, than learning existing shells with complex full features that he never use. I, as one of programmers, am writing this tiny shell for my own real needs, totally from scratch, with fun.

For a programmer, it is fun to learn new computer languages. For long years before writing this software, I had been specialized to C and early HTML2 :-). Now writing this software, I'm learning Go language, HTML5, JavaScript and CSS on demand as a novice of these, with fun.

This single file "gsh.go", that is executable by Go, contains all of the code written in Go. Also it can be displayed as "gsh.go.html" by browsers. It is a standalone HTML file that works as the viewer of the code of itself, and as the "home page" of this software.

Because this HTML file is a Go program, you may run it as a real shell program on your computer. But you must be aware that this program is written under situation like above. Needless to say, there is no warranty for this program in any means.

Aug 2020, SatoxITS (sato@its-more.jp)
*/ /*
Features

Cross-browser communication

... to be written ...

Vi compatible command line editor

The command line of GShell can be edited with commands compatible with vi. As in vi, you can enter command mode by ESC key, then move around in the history by j k / ? n N, or within the current line by l h f w b 0 $ % or so.

*/ /*
Index
Documents Command summary Go lang part Package structures import struct Main functions str-expansion // macro processor finder // builtin find + du grep // builtin grep + wc + cksum + ... plugin // plugin commands system // external commands builtin // builtin commands network // socket handler remote-sh // remote shell redirect // StdIn/Out redireciton history // command history rusage // resouce usage encode // encode / decode IME // command line IME getline // line editor scanf // string decomposer interpreter // command interpreter main JavaScript part Source Builtin data CSS part Source References Internal External Whole parts Source Download Dump
*/ //
//Go Source
// gsh - Go lang based Shell // (c) 2020 ITS more Co., Ltd. // 2020-0807 created by SatoxITS (sato@its-more.jp) package main // gsh main // Imported packages // Packages import ( "fmt" // fmt "errors" "strings" // strings "strconv" // strconv "sort" // sort "time" // time "bufio" // bufio "io/ioutil" // ioutil "os" // os "syscall" // syscall "plugin" // plugin "net" // net "net/http" // http //"html" // html "path/filepath" // filepath "go/types" // types "go/token" // token "encoding/base64" // base64 "unicode/utf8" // utf8 //"gshdata" // gshell's logo and source code "hash/crc32" // crc32 "golang.org/x/net/websocket" "runtime" ) /* #include // to be closed as HTML tag :-p #ifdef _WIN32 #include // // 2020-1022 added -- terminal mode on Windows // https://docs.microsoft.com/en-us/windows/console/setconsolemode // https://docs.microsoft.com/en-us/windows/win32/inputdev/using-keyboard-input int setTermRaw(){ HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); DWORD tmode = 0; if( GetConsoleMode(hStdin,&tmode) ){ DWORD xmode = tmode; xmode &= ~ENABLE_ECHO_INPUT; xmode &= ~ENABLE_LINE_INPUT; xmode |= ENABLE_PROCESSED_INPUT; // Control+C for SIGINT if( SetConsoleMode(hStdin,xmode) ){ return tmode; } } return 0; } int setTermMode(int tmode){ HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); SetConsoleMode(hStdin,tmode); return 0; } #else int setTermRaw(){ return -1; } int setTermMode(int tmode){ return 0; } #endif */ import "C" /* // // 2020-0906 added, // // CGo // #include "poll.h" // // to be closed as HTML tag :-p // typedef struct { struct pollfd fdv[8]; } pollFdv; // int pollx(pollFdv *fdv, int nfds, int timeout){ // return poll(fdv->fdv,nfds,timeout); // } import "C" // 2020-1021 replaced poll() with channel/select // // 2020-0906 added, func CFpollIn1(fp*os.File, timeoutUs int)(ready uintptr){ var fdv = C.pollFdv{} var nfds = 1 var timeout = timeoutUs/1000 fdv.fdv[0].fd = C.int(fp.Fd()) fdv.fdv[0].events = C.POLLIN if( 0 < EventRecvFd ){ fdv.fdv[1].fd = C.int(EventRecvFd) fdv.fdv[1].events = C.POLLIN nfds += 1 } r := C.pollx(&fdv,C.int(nfds),C.int(timeout)) if( r <= 0 ){ return 0 } if (int(fdv.fdv[1].revents) & int(C.POLLIN)) != 0 { //fprintf(stderr,"--De-- got Event\n"); return uintptr(EventFdOffset + fdv.fdv[1].fd) } if (int(fdv.fdv[0].revents) & int(C.POLLIN)) != 0 { return uintptr(NormalFdOffset + fdv.fdv[0].fd) } return 0 } */ const ( NAME = "gsh" VERSION = "0.8.0" DATE = "2020-11-08" AUTHOR = "SatoxITS(^-^)//" ) var ( GSH_HOME = ".gsh" // under home directory GSH_PORT = 9999 MaxStreamSize = int64(128*1024*1024*1024) // 128GiB is too large? PROMPT = "> " LINESIZE = (8*1024) PATHSEP = ":" // should be ";" in Windows DIRSEP = "/" // canbe \ in Windows OnWindows = false; ) func initGshEnv(){ if( runtime.GOOS == "windows" ){ PATHSEP = ";"; DIRSEP = "\\"; OnWindows = true; }else{ } } // -xX logging control // --A-- all // --I-- info. // --D-- debug // --T-- time and resource usage // --W-- warning // --E-- error // --F-- fatal error // --Xn- network // Structures // 2020-1022 Unix/Windows // ---------------------- //type aStat_t syscall.Stat_t; //type aStat_t struct { syscall.Stat_t } type aStat_t struct { Size int64 Mode os.FileMode Rdev int64 Blocks int64 Nlink int64 } func aLstat(path string, astat *aStat_t)(error){ /* sstat := syscall.Stat_t{}; err := syscall.Lstat(path,&sstat); *astat = aStat_t(sstat); */ fi,err := os.Stat(path); if( err == nil ){ astat.Mode = fi.Mode(); astat.Size = fi.Size(); } return err; } func aFstat(fd int, astat *aStat_t)(error){ /* sstat := syscall.Stat_t{}; err := syscall.Fstat(fd,&sstat); *astat = aStat_t(sstat); */ err := errors.New("NotImplemented-Fstat"); //fmt.Printf("---E-- fstat(%v)(%v)\n",fd,err); return err; } func aAccess(path string, mode uint32)(error){ //err := syscall.Access(path,mode); //err := errors.New("NotImplemented-Access"); fi,err := os.Stat(path) //fmt.Printf("-- Access(%v,%v)\n(%v)\n",path,mode,err); if( err == nil ){ fmode := fi.Mode(); if( fmode.IsRegular() ){ perm := fmode.Perm(); if( (uint32(perm) & mode) != 0 ){ return nil; } return errors.New("NotAccessible"); } return errors.New("NotRegularFile"); } return err; } // 2020-1022 Unix/Windows // ---------------------- type aRusage struct { syscall.Rusage Utime time.Duration Stime time.Duration //Sys interface{} } /* const aRUSAGE_SELF = syscall.RUSAGE_SELF const aRUSAGE_CHILDREN = syscall.RUSAGE_CHILDREN */ const aRUSAGE_SELF = 0 const aRUSAGE_CHILDREN = 1 func aGetrusage(sel int, ru *aRusage){ /* sysru := syscall.Rusage{}; syscall.Getrusage(sel,&sysru); ru.Utime = time.Duration(int64(sysru.Utime.Sec)*1000000000+int64(sysru.Utime.Usec)*1000); ru.Stime = time.Duration(int64(sysru.Stime.Sec)*1000000000+int64(sysru.Stime.Usec)*1000); */ } func aSetrusage(ru *aRusage, ps *os.ProcessState){ ru.Utime = ps.UserTime(); ru.Stime = ps.SystemTime(); } func showRusage(what string,argv []string, ru *aRusage){ fmt.Printf("%s: ",what); //fmt.Printf("Usr=%d.%06ds",ru.Utime.Sec,ru.Utime.Usec) //fmt.Printf(" Sys=%d.%06ds",ru.Stime.Sec,ru.Stime.Usec) fmt.Printf("Usr=%d.%06ds ",ru.Utime/1000000000,(ru.Utime/1000)%1000000); fmt.Printf(" Sys=%d.%06ds ",ru.Stime/1000000000,(ru.Stime/1000)%1000000); /* fmt.Printf(" Rss=%vB",ru.Maxrss) if isin("-l",argv) { fmt.Printf(" MinFlt=%v",ru.Minflt) fmt.Printf(" MajFlt=%v",ru.Majflt) fmt.Printf(" IxRSS=%vB",ru.Ixrss) fmt.Printf(" IdRSS=%vB",ru.Idrss) fmt.Printf(" Nswap=%vB",ru.Nswap) fmt.Printf(" Read=%v",ru.Inblock) fmt.Printf(" Write=%v",ru.Oublock) } fmt.Printf(" Snd=%v",ru.Msgsnd) fmt.Printf(" Rcv=%v",ru.Msgrcv) //if isin("-l",argv) { fmt.Printf(" Sig=%v",ru.Nsignals) //} */ fmt.Printf("\n"); } type GCommandHistory struct { StartAt time.Time // command line execution started at EndAt time.Time // command line execution ended at ResCode int // exit code of (external command) CmdError error // error string OutData *os.File // output of the command FoundFile []string // output - result of ufind Rusagev [2]aRusage // Resource consumption, CPU time or so CmdId int // maybe with identified with arguments or impact // redireciton commands should not be the CmdId WorkDir string // working directory at start WorkDirX int // index in ChdirHistory CmdLine string // command line } type GChdirHistory struct { Dir string MovedAt time.Time CmdIndex int } type CmdMode struct { BackGround bool } type Event struct { when time.Time event int evarg int64 CmdIndex int } var CmdIndex int var Events []Event type PluginInfo struct { Spec *plugin.Plugin Addr plugin.Symbol Name string // maybe relative Path string // this is in Plugin but hidden } type GServer struct { host string port string } // Digest const ( // SumType SUM_ITEMS = 0x000001 // items count SUM_SIZE = 0x000002 // data length (simplly added) SUM_SIZEHASH = 0x000004 // data length (hashed sequence) SUM_DATEHASH = 0x000008 // date of data (hashed sequence) // also envelope attributes like time stamp can be a part of digest // hashed value of sizes or mod-date of files will be useful to detect changes SUM_WORDS = 0x000010 // word count is a kind of digest SUM_LINES = 0x000020 // line count is a kind of digest SUM_SUM64 = 0x000040 // simple add of bytes, useful for human too SUM_SUM32_BITS = 0x000100 // the number of true bits SUM_SUM32_2BYTE = 0x000200 // 16bits words SUM_SUM32_4BYTE = 0x000400 // 32bits words SUM_SUM32_8BYTE = 0x000800 // 64bits words SUM_SUM16_BSD = 0x001000 // UNIXsum -sum -bsd SUM_SUM16_SYSV = 0x002000 // UNIXsum -sum -sysv SUM_UNIXFILE = 0x004000 SUM_CRCIEEE = 0x008000 ) type CheckSum struct { Files int64 // the number of files (or data) Size int64 // content size Words int64 // word count Lines int64 // line count SumType int Sum64 uint64 Crc32Table crc32.Table Crc32Val uint32 Sum16 int Ctime time.Time Atime time.Time Mtime time.Time Start time.Time Done time.Time RusgAtStart [2]aRusage RusgAtEnd [2]aRusage } type ValueStack [][]string type GshContext struct { StartDir string // the current directory at the start GetLine string // gsh-getline command as a input line editor ChdirHistory []GChdirHistory // the 1st entry is wd at the start //gshPA syscall.ProcAttr gshPA os.ProcAttr CommandHistory []GCommandHistory CmdCurrent GCommandHistory BackGround bool BackGroundJobs []os.ProcessState; //[]int LastRusage aRusage GshHomeDir string TerminalId int CmdTrace bool // should be [map] CmdTime bool // should be [map] PluginFuncs []PluginInfo iValues []string iDelimiter string // field sepearater of print out iFormat string // default print format (of integer) iValStack ValueStack LastServer GServer RSERV string // [gsh://]host[:port] RWD string // remote (target, there) working directory lastCheckSum CheckSum } func nsleep(ns time.Duration){ time.Sleep(ns) } func usleep(ns time.Duration){ nsleep(ns*1000) } func msleep(ns time.Duration){ nsleep(ns*1000000) } func sleep(ns time.Duration){ nsleep(ns*1000000000) } func strBegins(str, pat string)(bool){ if len(pat) <= len(str){ yes := str[0:len(pat)] == pat //fmt.Printf("--D-- strBegins(%v,%v)=%v\n",str,pat,yes) return yes } //fmt.Printf("--D-- strBegins(%v,%v)=%v\n",str,pat,false) return false } func isin(what string, list []string) bool { for _, v := range list { if v == what { return true } } return false } func isinX(what string,list[]string)(int){ for i,v := range list { if v == what { return i } } return -1 } func env(opts []string) { env := os.Environ() if isin("-s", opts){ sort.Slice(env, func(i,j int) bool { return env[i] < env[j] }) } for _, v := range env { fmt.Printf("%v\n",v) } } // - rewriting should be context dependent // - should postpone until the real point of evaluation // - should rewrite only known notation of symobl func scanInt(str string)(val int,leng int){ leng = -1 for i,ch := range str { if '0' <= ch && ch <= '9' { leng = i+1 }else{ break } } if 0 < leng { ival,_ := strconv.Atoi(str[0:leng]) return ival,leng }else{ return 0,0 } } func substHistory(gshCtx *GshContext,str string,i int,rstr string)(leng int,rst string){ if len(str[i+1:]) == 0 { return 0,rstr } hi := 0 histlen := len(gshCtx.CommandHistory) if str[i+1] == '!' { hi = histlen - 1 leng = 1 }else{ hi,leng = scanInt(str[i+1:]) if leng == 0 { return 0,rstr } if hi < 0 { hi = histlen + hi } } if 0 <= hi && hi < histlen { var ext byte if 1 < len(str[i+leng:]) { ext = str[i+leng:][1] } //fmt.Printf("--D-- %v(%c)\n",str[i+leng:],str[i+leng]) if ext == 'f' { leng += 1 xlist := []string{} list := gshCtx.CommandHistory[hi].FoundFile for _,v := range list { //list[i] = escapeWhiteSP(v) xlist = append(xlist,escapeWhiteSP(v)) } //rstr += strings.Join(list," ") rstr += strings.Join(xlist," ") }else if ext == '@' || ext == 'd' { // !N@ .. workdir at the start of the command leng += 1 rstr += gshCtx.CommandHistory[hi].WorkDir }else{ rstr += gshCtx.CommandHistory[hi].CmdLine } }else{ leng = 0 } return leng,rstr } func escapeWhiteSP(str string)(string){ if len(str) == 0 { return "\\z" // empty, to be ignored } rstr := "" for _,ch := range str { switch ch { case '\\': rstr += "\\\\" case ' ': rstr += "\\s" case '\t': rstr += "\\t" case '\r': rstr += "\\r" case '\n': rstr += "\\n" default: rstr += string(ch) } } return rstr } func unescapeWhiteSP(str string)(string){ // strip original escapes rstr := "" for i := 0; i < len(str); i++ { ch := str[i] if ch == '\\' { if i+1 < len(str) { switch str[i+1] { case 'z': continue; } } } rstr += string(ch) } return rstr } func unescapeWhiteSPV(strv []string)([]string){ // strip original escapes ustrv := []string{} for _,v := range strv { ustrv = append(ustrv,unescapeWhiteSP(v)) } return ustrv } // str-expansion // - this should be a macro processor func strsubst(gshCtx *GshContext,str string,histonly bool) string { rbuff := []byte{} if false { //@@U Unicode should be cared as a character return str } //rstr := "" inEsc := 0 // escape characer mode for i := 0; i < len(str); i++ { //fmt.Printf("--D--Subst %v:%v\n",i,str[i:]) ch := str[i] if inEsc == 0 { if ch == '!' { //leng,xrstr := substHistory(gshCtx,str,i,rstr) leng,rs := substHistory(gshCtx,str,i,"") if 0 < leng { //_,rs := substHistory(gshCtx,str,i,"") rbuff = append(rbuff,[]byte(rs)...) i += leng //rstr = xrstr continue } } switch ch { case '\\': inEsc = '\\'; continue //case '%': inEsc = '%'; continue case '$': } } switch inEsc { case '\\': switch ch { case '\\': ch = '\\' case 's': ch = ' ' case 't': ch = '\t' case 'r': ch = '\r' case 'n': ch = '\n' case 'z': inEsc = 0; continue // empty, to be ignored } inEsc = 0 case '%': switch { case ch == '%': ch = '%' case ch == 'T': //rstr = rstr + time.Now().Format(time.Stamp) rs := time.Now().Format(time.Stamp) rbuff = append(rbuff,[]byte(rs)...) inEsc = 0 continue; default: // postpone the interpretation //rstr = rstr + "%" + string(ch) rbuff = append(rbuff,ch) inEsc = 0 continue; } inEsc = 0 } //rstr = rstr + string(ch) rbuff = append(rbuff,ch) } //fmt.Printf("--D--subst(%s)(%s)\n",str,string(rbuff)) return string(rbuff) //return rstr } func showFileInfo(path string, opts []string) { if isin("-l",opts) || isin("-ls",opts) { fi, err := os.Stat(path) if err != nil { fmt.Printf("---------- ((%v))",err) }else{ mod := fi.ModTime() date := mod.Format(time.Stamp) fmt.Printf("%v %8v %s ",fi.Mode(),fi.Size(),date) } } fmt.Printf("%s",path) if isin("-sp",opts) { fmt.Printf(" ") }else if ! isin("-n",opts) { fmt.Printf("\n") } } func userHomeDir()(string,bool){ /* homedir,_ = os.UserHomeDir() // not implemented in older Golang */ homedir,found := os.LookupEnv("HOME") //fmt.Printf("--I-- HOME=%v(%v)\n",homedir,found) if !found { return "/tmp",found } return homedir,found } func toFullpath(path string) (fullpath string) { if path[0] == '/' { return path } pathv := strings.Split(path,DIRSEP) switch { case pathv[0] == ".": pathv[0], _ = os.Getwd() case pathv[0] == "..": // all ones should be interpreted cwd, _ := os.Getwd() ppathv := strings.Split(cwd,DIRSEP) pathv[0] = strings.Join(ppathv,DIRSEP) case pathv[0] == "~": pathv[0],_ = userHomeDir() default: cwd, _ := os.Getwd() pathv[0] = cwd + DIRSEP + pathv[0] } return strings.Join(pathv,DIRSEP) } func IsRegFile(path string)(bool){ fi, err := os.Stat(path) if err == nil { fm := fi.Mode() return fm.IsRegular(); } return false } // Encode / Decode // Encoder func (gshCtx *GshContext)Enc(argv[]string){ file := os.Stdin buff := make([]byte,LINESIZE) li := 0 encoder := base64.NewEncoder(base64.StdEncoding,os.Stdout) for li = 0; ; li++ { count, err := file.Read(buff) if count <= 0 { break } if err != nil { break } encoder.Write(buff[0:count]) } encoder.Close() } func (gshCtx *GshContext)Dec(argv[]string){ decoder := base64.NewDecoder(base64.StdEncoding,os.Stdin) li := 0 buff := make([]byte,LINESIZE) for li = 0; ; li++ { count, err := decoder.Read(buff) if count <= 0 { break } if err != nil { break } os.Stdout.Write(buff[0:count]) } } // lnsp [N] [-crlf][-C \\] func (gshCtx *GshContext)SplitLine(argv[]string){ strRep := isin("-str",argv) // "..."+ reader := bufio.NewReaderSize(os.Stdin,64*1024) ni := 0 toi := 0 for ni = 0; ; ni++ { line, err := reader.ReadString('\n') if len(line) <= 0 { if err != nil { fmt.Fprintf(os.Stderr,"--I-- lnsp %d to %d (%v)\n",ni,toi,err) break } } off := 0 ilen := len(line) remlen := len(line) if strRep { os.Stdout.Write([]byte("\"")) } for oi := 0; 0 < remlen; oi++ { olen := remlen addnl := false if 72 < olen { olen = 72 addnl = true } fmt.Fprintf(os.Stderr,"--D-- write %d [%d.%d] %d %d/%d/%d\n", toi,ni,oi,off,olen,remlen,ilen) toi += 1 os.Stdout.Write([]byte(line[0:olen])) if addnl { if strRep { os.Stdout.Write([]byte("\"+\n\"")) }else{ //os.Stdout.Write([]byte("\r\n")) os.Stdout.Write([]byte("\\")) os.Stdout.Write([]byte("\n")) } } line = line[olen:] off += olen remlen -= olen } if strRep { os.Stdout.Write([]byte("\"\n")) } } fmt.Fprintf(os.Stderr,"--I-- lnsp %d to %d\n",ni,toi) } // CRC32 crc32 // 1 0000 0100 1100 0001 0001 1101 1011 0111 var CRC32UNIX uint32 = uint32(0x04C11DB7) // Unix cksum var CRC32IEEE uint32 = uint32(0xEDB88320) func byteCRC32add(crc uint32,str[]byte,len uint64)(uint32){ var oi uint64 for oi = 0; oi < len; oi++ { var oct = str[oi] for bi := 0; bi < 8; bi++ { //fprintf(stderr,"--CRC32 %d %X (%d.%d)\n",crc,oct,oi,bi) ovf1 := (crc & 0x80000000) != 0 ovf2 := (oct & 0x80) != 0 ovf := (ovf1 && !ovf2) || (!ovf1 && ovf2) oct <<= 1 crc <<= 1 if ovf { crc ^= CRC32UNIX } } } //fprintf(stderr,"--CRC32 return %d %d\n",crc,len) return crc; } func byteCRC32end(crc uint32, len uint64)(uint32){ var slen = make([]byte,4) var li = 0 for li = 0; li < 4; { slen[li] = byte(len) li += 1 len >>= 8 if( len == 0 ){ break } } crc = byteCRC32add(crc,slen,uint64(li)) crc ^= 0xFFFFFFFF return crc } func strCRC32(str string,len uint64)(crc uint32){ crc = byteCRC32add(0,[]byte(str),len) crc = byteCRC32end(crc,len) //fprintf(stderr,"--CRC32 %d %d\n",crc,len) return crc } func CRC32Finish(crc uint32, table *crc32.Table, len uint64)(uint32){ var slen = make([]byte,4) var li = 0 for li = 0; li < 4; { slen[li] = byte(len & 0xFF) li += 1 len >>= 8 if( len == 0 ){ break } } crc = crc32.Update(crc,table,slen) crc ^= 0xFFFFFFFF return crc } func (gsh*GshContext)xCksum(path string,argv[]string, sum*CheckSum)(int64){ if isin("-type/f",argv) && !IsRegFile(path){ return 0 } if isin("-type/d",argv) && IsRegFile(path){ return 0 } file, err := os.OpenFile(path,os.O_RDONLY,0) if err != nil { fmt.Printf("--E-- cksum %v (%v)\n",path,err) return -1 } defer file.Close() if gsh.CmdTrace { fmt.Printf("--I-- cksum %v %v\n",path,argv) } bi := 0 var buff = make([]byte,32*1024) var total int64 = 0 var initTime = time.Time{} if sum.Start == initTime { sum.Start = time.Now() } for bi = 0; ; bi++ { count,err := file.Read(buff) if count <= 0 || err != nil { break } if (sum.SumType & SUM_SUM64) != 0 { s := sum.Sum64 for _,c := range buff[0:count] { s += uint64(c) } sum.Sum64 = s } if (sum.SumType & SUM_UNIXFILE) != 0 { sum.Crc32Val = byteCRC32add(sum.Crc32Val,buff,uint64(count)) } if (sum.SumType & SUM_CRCIEEE) != 0 { sum.Crc32Val = crc32.Update(sum.Crc32Val,&sum.Crc32Table,buff[0:count]) } // BSD checksum if (sum.SumType & SUM_SUM16_BSD) != 0 { s := sum.Sum16 for _,c := range buff[0:count] { s = (s >> 1) + ((s & 1) << 15) s += int(c) s &= 0xFFFF //fmt.Printf("BSDsum: %d[%d] %d\n",sum.Size+int64(i),i,s) } sum.Sum16 = s } if (sum.SumType & SUM_SUM16_SYSV) != 0 { for bj := 0; bj < count; bj++ { sum.Sum16 += int(buff[bj]) } } total += int64(count) } sum.Done = time.Now() sum.Files += 1 sum.Size += total if !isin("-s",argv) { fmt.Printf("%v ",total) } return 0 } // grep // "lines", "lin" or "lnp" for "(text) line processor" or "scanner" // a*,!ab,c, ... sequentioal combination of patterns // what "LINE" is should be definable // generic line-by-line processing // grep [-v] // cat -n -v // uniq [-c] // tail -f // sed s/x/y/ or awk // grep with line count like wc // rewrite contents if specified func (gsh*GshContext)xGrep(path string,rexpv[]string)(int){ file, err := os.OpenFile(path,os.O_RDONLY,0) if err != nil { fmt.Printf("--E-- grep %v (%v)\n",path,err) return -1 } defer file.Close() if gsh.CmdTrace { fmt.Printf("--I-- grep %v %v\n",path,rexpv) } //reader := bufio.NewReaderSize(file,LINESIZE) reader := bufio.NewReaderSize(file,80) li := 0 found := 0 for li = 0; ; li++ { line, err := reader.ReadString('\n') if len(line) <= 0 { break } if 150 < len(line) { // maybe binary break; } if err != nil { break } if 0 <= strings.Index(string(line),rexpv[0]) { found += 1 fmt.Printf("%s:%d: %s",path,li,line) } } //fmt.Printf("total %d lines %s\n",li,path) //if( 0 < found ){ fmt.Printf("((found %d lines %s))\n",found,path); } return found } // Finder // finding files with it name and contents // file names are ORed // show the content with %x fmt list // ls -R // tar command by adding output type fileSum struct { Err int64 // access error or so Size int64 // content size DupSize int64 // content size from hard links Blocks int64 // number of blocks (of 512 bytes) DupBlocks int64 // Blocks pointed from hard links HLinks int64 // hard links Words int64 Lines int64 Files int64 Dirs int64 // the num. of directories SymLink int64 Flats int64 // the num. of flat files MaxDepth int64 MaxNamlen int64 // max. name length nextRepo time.Time } func showFusage(dir string,fusage *fileSum){ bsume := float64(((fusage.Blocks-fusage.DupBlocks)/2)*1024)/1000000.0 //bsumdup := float64((fusage.Blocks/2)*1024)/1000000.0 fmt.Printf("%v: %v files (%vd %vs %vh) %.6f MB (%.2f MBK)\n", dir, fusage.Files, fusage.Dirs, fusage.SymLink, fusage.HLinks, float64(fusage.Size)/1000000.0,bsume); } const ( S_IFMT = 0170000 S_IFCHR = 0020000 S_IFDIR = 0040000 S_IFREG = 0100000 S_IFLNK = 0120000 S_IFSOCK = 0140000 ) func cumFinfo(fsum *fileSum, path string, staterr error, fstat aStat_t, argv[]string,verb bool)(*fileSum){ now := time.Now() if time.Second <= now.Sub(fsum.nextRepo) { if !fsum.nextRepo.IsZero(){ tstmp := now.Format(time.Stamp) showFusage(tstmp,fsum) } fsum.nextRepo = now.Add(time.Second) } if staterr != nil { fsum.Err += 1 return fsum } fsum.Files += 1 if 1 < fstat.Nlink { // must count only once... // at least ignore ones in the same directory //if finfo.Mode().IsRegular() { if (fstat.Mode & S_IFMT) == S_IFREG { fsum.HLinks += 1 fsum.DupBlocks += int64(fstat.Blocks) //fmt.Printf("---Dup HardLink %v %s\n",fstat.Nlink,path) } } //fsum.Size += finfo.Size() fsum.Size += fstat.Size fsum.Blocks += int64(fstat.Blocks) //if verb { fmt.Printf("(%8dBlk) %s",fstat.Blocks/2,path) } if isin("-ls",argv){ //if verb { fmt.Printf("%4d %8d ",fstat.Blksize,fstat.Blocks) } // fmt.Printf("%d\t",fstat.Blocks/2) } //if finfo.IsDir() if (fstat.Mode & S_IFMT) == S_IFDIR { fsum.Dirs += 1 } //if (finfo.Mode() & os.ModeSymlink) != 0 if (fstat.Mode & S_IFMT) == S_IFLNK { //if verb { fmt.Printf("symlink(%v,%s)\n",fstat.Mode,finfo.Name()) } //{ fmt.Printf("symlink(%o,%s)\n",fstat.Mode,finfo.Name()) } fsum.SymLink += 1 } return fsum } func (gsh*GshContext)xxFindEntv(depth int,total *fileSum,dir string, dstat aStat_t, ei int, entv []string,npatv[]string,argv[]string)(*fileSum){ nols := isin("-grep",argv) // sort entv /* if isin("-t",argv){ sort.Slice(filev, func(i,j int) bool { return 0 < filev[i].ModTime().Sub(filev[j].ModTime()) }) } */ /* if isin("-u",argv){ sort.Slice(filev, func(i,j int) bool { return 0 < filev[i].AccTime().Sub(filev[j].AccTime()) }) } if isin("-U",argv){ sort.Slice(filev, func(i,j int) bool { return 0 < filev[i].CreatTime().Sub(filev[j].CreatTime()) }) } */ /* if isin("-S",argv){ sort.Slice(filev, func(i,j int) bool { return filev[j].Size() < filev[i].Size() }) } */ for _,filename := range entv { for _,npat := range npatv { match := true if npat == "*" { match = true }else{ match, _ = filepath.Match(npat,filename) } path := dir + DIRSEP + filename if !match { continue } var fstat aStat_t staterr := aLstat(path,&fstat) if staterr != nil { if !isin("-w",argv){fmt.Printf("ufind: %v\n",staterr) } continue; } if isin("-du",argv) && (fstat.Mode & S_IFMT) == S_IFDIR { // should not show size of directory in "-du" mode ... }else if !nols && !isin("-s",argv) && (!isin("-du",argv) || isin("-a",argv)) { if isin("-du",argv) { fmt.Printf("%d\t",fstat.Blocks/2) } showFileInfo(path,argv) } if true { // && isin("-du",argv) total = cumFinfo(total,path,staterr,fstat,argv,false) } /* if isin("-wc",argv) { } */ if gsh.lastCheckSum.SumType != 0 { gsh.xCksum(path,argv,&gsh.lastCheckSum); } x := isinX("-grep",argv); // -grep will be convenient like -ls if 0 <= x && x+1 <= len(argv) { // -grep will be convenient like -ls if IsRegFile(path){ found := gsh.xGrep(path,argv[x+1:]) if 0 < found { foundv := gsh.CmdCurrent.FoundFile if len(foundv) < 10 { gsh.CmdCurrent.FoundFile = append(gsh.CmdCurrent.FoundFile,path) } } } } if !isin("-r0",argv) { // -d 0 in du, -depth n in find //total.Depth += 1 if (fstat.Mode & S_IFMT) == S_IFLNK { continue } if dstat.Rdev != fstat.Rdev { fmt.Printf("--I-- don't follow differnet device %v(%v) %v(%v)\n", dir,dstat.Rdev,path,fstat.Rdev) } if (fstat.Mode & S_IFMT) == S_IFDIR { total = gsh.xxFind(depth+1,total,path,npatv,argv) } } } } return total } func (gsh*GshContext)xxFind(depth int,total *fileSum,dir string,npatv[]string,argv[]string)(*fileSum){ nols := isin("-grep",argv) dirfile,oerr := os.OpenFile(dir,os.O_RDONLY,0) if oerr == nil { //fmt.Printf("--I-- %v(%v)[%d]\n",dir,dirfile,dirfile.Fd()) defer dirfile.Close() }else{ } prev := *total var dstat aStat_t staterr := aLstat(dir,&dstat) // should be flstat if staterr != nil { if !isin("-w",argv){ fmt.Printf("ufind: %v\n",staterr) } return total } //filev,err := ioutil.ReadDir(dir) //_,err := ioutil.ReadDir(dir) // ReadDir() heavy and bad for huge directory /* if err != nil { if !isin("-w",argv){ fmt.Printf("ufind: %v\n",err) } return total } */ if depth == 0 { total = cumFinfo(total,dir,staterr,dstat,argv,true) if !nols && !isin("-s",argv) && (!isin("-du",argv) || isin("-a",argv)) { showFileInfo(dir,argv) } } // it it is not a directory, just scan it and finish for ei := 0; ; ei++ { entv,rderr := dirfile.Readdirnames(8*1024) if len(entv) == 0 || rderr != nil { //if rderr != nil { fmt.Printf("[%d] len=%d (%v)\n",ei,len(entv),rderr) } break } if 0 < ei { fmt.Printf("--I-- xxFind[%d] %d large-dir: %s\n",ei,len(entv),dir) } total = gsh.xxFindEntv(depth,total,dir,dstat,ei,entv,npatv,argv) } if isin("-du",argv) { // if in "du" mode fmt.Printf("%d\t%s\n",(total.Blocks-prev.Blocks)/2,dir) } return total } // {ufind|fu|ls} [Files] [// Names] [-- Expressions] // Files is "." by default // Names is "*" by default // Expressions is "-print" by default for "ufind", or -du for "fu" command func (gsh*GshContext)xFind(argv[]string){ if 0 < len(argv) && strBegins(argv[0],"?"){ showFound(gsh,argv) return } if isin("-cksum",argv) || isin("-sum",argv) { gsh.lastCheckSum = CheckSum{} if isin("-sum",argv) && isin("-add",argv) { gsh.lastCheckSum.SumType |= SUM_SUM64 }else if isin("-sum",argv) && isin("-size",argv) { gsh.lastCheckSum.SumType |= SUM_SIZE }else if isin("-sum",argv) && isin("-bsd",argv) { gsh.lastCheckSum.SumType |= SUM_SUM16_BSD }else if isin("-sum",argv) && isin("-sysv",argv) { gsh.lastCheckSum.SumType |= SUM_SUM16_SYSV }else if isin("-sum",argv) { gsh.lastCheckSum.SumType |= SUM_SUM64 } if isin("-unix",argv) { gsh.lastCheckSum.SumType |= SUM_UNIXFILE gsh.lastCheckSum.Crc32Table = *crc32.MakeTable(CRC32UNIX) } if isin("-ieee",argv){ gsh.lastCheckSum.SumType |= SUM_CRCIEEE gsh.lastCheckSum.Crc32Table = *crc32.MakeTable(CRC32IEEE) } gsh.lastCheckSum.RusgAtStart = Getrusagev() } var total = fileSum{} npats := []string{} for _,v := range argv { if 0 < len(v) && v[0] != '-' { npats = append(npats,v) } if v == "//" { break } if v == "--" { break } if v == "-grep" { break } if v == "-ls" { break } } if len(npats) == 0 { npats = []string{"*"} } cwd := "." // if to be fullpath ::: cwd, _ := os.Getwd() if len(npats) == 0 { npats = []string{"*"} } fusage := gsh.xxFind(0,&total,cwd,npats,argv) if gsh.lastCheckSum.SumType != 0 { var sumi uint64 = 0 sum := &gsh.lastCheckSum if (sum.SumType & SUM_SIZE) != 0 { sumi = uint64(sum.Size) } if (sum.SumType & SUM_SUM64) != 0 { sumi = sum.Sum64 } if (sum.SumType & SUM_SUM16_SYSV) != 0 { s := uint32(sum.Sum16) r := (s & 0xFFFF) + ((s & 0xFFFFFFFF) >> 16) s = (r & 0xFFFF) + (r >> 16) sum.Crc32Val = uint32(s) sumi = uint64(s) } if (sum.SumType & SUM_SUM16_BSD) != 0 { sum.Crc32Val = uint32(sum.Sum16) sumi = uint64(sum.Sum16) } if (sum.SumType & SUM_UNIXFILE) != 0 { sum.Crc32Val = byteCRC32end(sum.Crc32Val,uint64(sum.Size)) sumi = uint64(byteCRC32end(sum.Crc32Val,uint64(sum.Size))) } if 1 < sum.Files { fmt.Printf("%v %v // %v / %v files, %v/file\r\n", sumi,sum.Size, abssize(sum.Size),sum.Files, abssize(sum.Size/sum.Files)) }else{ fmt.Printf("%v %v %v\n", sumi,sum.Size,npats[0]) } } if !isin("-grep",argv) { showFusage("total",fusage) } if !isin("-s",argv){ hits := len(gsh.CmdCurrent.FoundFile) if 0 < hits { fmt.Printf("--I-- %d files hits // can be refered with !%df\n", hits,len(gsh.CommandHistory)) } } if gsh.lastCheckSum.SumType != 0 { if isin("-ru",argv) { sum := &gsh.lastCheckSum sum.Done = time.Now() gsh.lastCheckSum.RusgAtEnd = Getrusagev() elps := sum.Done.Sub(sum.Start) fmt.Printf("--cksum-size: %v (%v) / %v files, %v/file\r\n", sum.Size,abssize(sum.Size),sum.Files,abssize(sum.Size/sum.Files)) nanos := int64(elps) dnanos := time.Duration(nanos); fmt.Printf("--cksum-time: %v/total, %v/file, %.1f files/s, %v\r\n", abbtime(dnanos), abbtime(time.Duration(nanos/sum.Files)), (float64(sum.Files)*1000000000.0)/float64(nanos), abbspeed(sum.Size,nanos)) diff := RusageSubv(sum.RusgAtEnd,sum.RusgAtStart) fmt.Printf("--cksum-rusg: %v\n",sRusagef("",argv,diff)) } } return } func showFiles(files[]string){ sp := "" for i,file := range files { if 0 < i { sp = " " } else { sp = "" } fmt.Printf(sp+"%s",escapeWhiteSP(file)) } } func showFound(gshCtx *GshContext, argv[]string){ for i,v := range gshCtx.CommandHistory { if 0 < len(v.FoundFile) { fmt.Printf("!%d (%d) ",i,len(v.FoundFile)) if isin("-ls",argv){ fmt.Printf("\n") for _,file := range v.FoundFile { fmt.Printf("") //sub number? showFileInfo(file,argv) } }else{ showFiles(v.FoundFile) fmt.Printf("\n") } } } } func showMatchFile(filev []os.FileInfo, npat,dir string, argv[]string)(string,bool){ fname := "" found := false for _,v := range filev { match, _ := filepath.Match(npat,(v.Name())) if match { fname = v.Name() found = true //fmt.Printf("[%d] %s\n",i,v.Name()) showIfExecutable(fname,dir,argv) } } return fname,found } func showIfExecutable(name,dir string,argv[]string)(ffullpath string,ffound bool){ var fullpath string if strBegins(name,DIRSEP){ fullpath = name }else if( len(dir) == 0 ){ fullpath = name; }else{ fullpath = dir + DIRSEP + name } fi, err := os.Stat(fullpath) //fmt.Printf("--Dp-- \"%v\"\n-- %v\n",fullpath,err); if err != nil { fullpath += ".exe"; fi, err = os.Stat(fullpath) } if err != nil { fullpath = dir + DIRSEP + name + ".go" fi, err = os.Stat(fullpath) } if err == nil { fm := fi.Mode() if fm.IsRegular() { // R_OK=4, W_OK=2, X_OK=1, F_OK=0 if aAccess(fullpath,5) == nil { ffullpath = fullpath ffound = true if ! isin("-s", argv) { showFileInfo(fullpath,argv) } } } } return ffullpath, ffound } func which(list string, argv []string) (fullpathv []string, itis bool){ if len(argv) <= 1 { fmt.Printf("Usage: which comand [-s] [-a] [-ls]\n") return []string{""}, false } path := argv[1] if strBegins(path,"/") { // should check if excecutable? _,exOK := showIfExecutable(path,"/",argv) fmt.Printf("--D-- %v exOK=%v\n",path,exOK) return []string{path},exOK } pathenv, efound := os.LookupEnv(list) if ! efound { fmt.Printf("--E-- which: no \"%s\" environment\n",list) return []string{""}, false } //fmt.Printf("PATH=%v\n",pathenv); showall := isin("-a",argv) || 0 <= strings.Index(path,"*") dirv := strings.Split(pathenv,PATHSEP) ffound := false ffullpath := path for _, dir := range dirv { if 0 <= strings.Index(path,"*") { // by wild-card list,_ := ioutil.ReadDir(dir) ffullpath, ffound = showMatchFile(list,path,dir,argv) }else{ ffullpath, ffound = showIfExecutable(path,dir,argv) } //if ffound && !isin("-a", argv) { if ffound && !showall { break; } } return []string{ffullpath}, ffound } func stripLeadingWSParg(argv[]string)([]string){ for ; 0 < len(argv); { if len(argv[0]) == 0 { argv = argv[1:] }else{ break } } return argv } func xEval(argv []string, nlend bool){ argv = stripLeadingWSParg(argv) if len(argv) == 0 { fmt.Printf("eval [%%format] [Go-expression]\n") return } pfmt := "%v" if argv[0][0] == '%' { pfmt = argv[0] argv = argv[1:] } if len(argv) == 0 { return } gocode := strings.Join(argv," "); //fmt.Printf("eval [%v] [%v]\n",pfmt,gocode) fset := token.NewFileSet() rval, _ := types.Eval(fset,nil,token.NoPos,gocode) fmt.Printf(pfmt,rval.Value) if nlend { fmt.Printf("\n") } } func getval(name string) (found bool, val int) { /* should expand the name here */ if name == "gsh.pid" { return true, os.Getpid() }else if name == "gsh.ppid" { return true, os.Getppid() } return false, 0 } func echo(argv []string, nlend bool){ for ai := 1; ai < len(argv); ai++ { if 1 < ai { fmt.Printf(" "); } arg := argv[ai] found, val := getval(arg) if found { fmt.Printf("%d",val) }else{ fmt.Printf("%s",arg) } } if nlend { fmt.Printf("\n"); } } func resfile() string { return "gsh.tmp" } //var resF *File func resmap() { //_ , err := os.OpenFile(resfile(), os.O_RDWR|os.O_CREATE, os.ModeAppend) // https://developpaper.com/solution-to-golang-bad-file-descriptor-problem/ _ , err := os.OpenFile(resfile(), os.O_RDWR|os.O_CREATE, 0600) if err != nil { fmt.Printf("refF could not open: %s\n",err) }else{ fmt.Printf("refF opened\n") } } // @@2020-0821 func gshScanArg(str string,strip int)(argv []string){ var si = 0 var sb = 0 var inBracket = 0 var arg1 = make([]byte,LINESIZE) var ax = 0 debug := false for ; si < len(str); si++ { if str[si] != ' ' { break } } sb = si for ; si < len(str); si++ { if sb <= si { if debug { fmt.Printf("--Da- +%d %2d-%2d %s ... %s\n", inBracket,sb,si,arg1[0:ax],str[si:]) } } ch := str[si] if ch == '{' { inBracket += 1 if 0 < strip && inBracket <= strip { //fmt.Printf("stripLEV %d <= %d?\n",inBracket,strip) continue } } if 0 < inBracket { if ch == '}' { inBracket -= 1 if 0 < strip && inBracket < strip { //fmt.Printf("stripLEV %d < %d?\n",inBracket,strip) continue } } arg1[ax] = ch ax += 1 continue } if str[si] == ' ' { argv = append(argv,string(arg1[0:ax])) if debug { fmt.Printf("--Da- [%v][%v-%v] %s ... %s\n", -1+len(argv),sb,si,str[sb:si],string(str[si:])) } sb = si+1 ax = 0 continue } arg1[ax] = ch ax += 1 } if sb < si { argv = append(argv,string(arg1[0:ax])) if debug { fmt.Printf("--Da- [%v][%v-%v] %s ... %s\n", -1+len(argv),sb,si,string(arg1[0:ax]),string(str[si:])) } } if debug { fmt.Printf("--Da- %d [%s] => [%d]%v\n",strip,str,len(argv),argv) } return argv } // should get stderr (into tmpfile ?) and return func (gsh*GshContext)Popen(name,mode string)(pin*os.File,pout*os.File,err bool){ //var pv = []int{-1,-1} //syscall.Pipe(pv) xarg := gshScanArg(name,1) name = strings.Join(xarg," ") //pin = os.NewFile(uintptr(pv[0]),"StdoutOf-{"+name+"}") //pout = os.NewFile(uintptr(pv[1]),"StdinOf-{"+name+"}") pin,pout,_ = os.Pipe(); fdix := 0 dir := "?" if mode == "r" { dir = "<" fdix = 1 // read from the stdout of the process }else{ dir = ">" fdix = 0 // write to the stdin of the process } gshPA := gsh.gshPA savfd := gshPA.Files[fdix] var fd uintptr = 0 if mode == "r" { //fd = pout.Fd() //gshPA.Files[fdix] = pout.Fd() gshPA.Files[fdix] = pout; }else{ //fd = pin.Fd() //gshPA.Files[fdix] = pin.Fd() gshPA.Files[fdix] = pin; } // should do this by Goroutine? if false { fmt.Printf("--Ip- Opened fd[%v] %s %v\n",fd,dir,name) fmt.Printf("--RED1 [%d,%d,%d]->[%d,%d,%d]\n", os.Stdin.Fd(),os.Stdout.Fd(),os.Stderr.Fd(), pin.Fd(),pout.Fd(),pout.Fd()) } savi := os.Stdin savo := os.Stdout save := os.Stderr os.Stdin = pin os.Stdout = pout os.Stderr = pout gsh.BackGround = true gsh.gshelllh(name) gsh.BackGround = false os.Stdin = savi os.Stdout = savo os.Stderr = save gshPA.Files[fdix] = savfd return pin,pout,false } // External commands func (gsh*GshContext)excommand(exec bool, argv []string) (notf bool,exit bool) { if gsh.CmdTrace { fmt.Printf("--I-- excommand[%v](%v)\n",exec,argv) } gshPA := gsh.gshPA fullpathv, itis := which("PATH",[]string{"which",argv[0],"-s"}) if itis == false { return true,false } fullpath := fullpathv[0] argv = unescapeWhiteSPV(argv) if 0 < strings.Index(fullpath,".go") { nargv := argv // []string{} gofullpathv, itis := which("PATH",[]string{"which","go","-s"}) if itis == false { fmt.Printf("--F-- Go not found\n") return false,true } gofullpath := gofullpathv[0] nargv = []string{ gofullpath, "run", fullpath } fmt.Printf("--I-- %s {%s %s %s}\n",gofullpath, nargv[0],nargv[1],nargv[2]) if exec { syscall.Exec(gofullpath,nargv,os.Environ()) }else{ //pid, _ := syscall.ForkExec(gofullpath,nargv,&gshPA) proc,_ := os.StartProcess(gofullpath,nargv,&gshPA); pstat,_ := proc.Wait(); pid := pstat.Pid(); if gsh.BackGround { fmt.Fprintf(stderr,"--Ip- in Background pid[%d]%d(%v)\n",pid,len(argv),nargv) //gsh.BackGroundJobs = append(gsh.BackGroundJobs,pid) gsh.BackGroundJobs = append(gsh.BackGroundJobs,*pstat) }else{ /* rusage := aRusage {} // syscall.Wait4(pid,nil,0,&rusage) gsh.LastRusage = rusage gsh.CmdCurrent.Rusagev[1] = rusage */ /* gsh.LastRusage = *pstat.SysUsage().(*aRusage); gsh.CmdCurrent.Rusagev[1] = *pstat.SysUsage().(*aRusage); */ aSetrusage(&gsh.LastRusage,pstat); gsh.CmdCurrent.Rusagev[1] = gsh.LastRusage; } } }else{ if exec { syscall.Exec(fullpath,argv,os.Environ()) }else{ //pid, _ := syscall.ForkExec(fullpath,argv,&gshPA) proc,_ := os.StartProcess(fullpath,argv,&gshPA); pstat,_ := proc.Wait(); pid := pstat.Pid(); //fmt.Printf("[%d]\n",pid); // '&' to be background if( false ){ fmt.Printf("Sys=%v\n",gshPA.Sys); if( gshPA.Sys != nil ){ //fmt.Printf("inFG=%v\n",gshPA.Sys.Foreground); } } if gsh.BackGround { fmt.Fprintf(stderr,"--Ip- in Background pid[%d]%d(%v)\n",pid,len(argv),argv) //gsh.BackGroundJobs = append(gsh.BackGroundJobs,pid) gsh.BackGroundJobs = append(gsh.BackGroundJobs,*pstat) }else{ /* rusage := aRusage {} // syscall.Wait4(pid,nil,0,&rusage); gsh.LastRusage = rusage gsh.CmdCurrent.Rusagev[1] = rusage */ /* gsh.LastRusage = *pstat.SysUsage().(*aRusage); gsh.CmdCurrent.Rusagev[1] = *pstat.SysUsage().(*aRusage); */ aSetrusage(&gsh.LastRusage,pstat); gsh.CmdCurrent.Rusagev[1] = gsh.LastRusage; } } } return false,false } // Builtin Commands func (gshCtx *GshContext) sleep(argv []string) { if len(argv) < 2 { fmt.Printf("Sleep 100ms, 100us, 100ns, ...\n") return } duration := argv[1]; d, err := time.ParseDuration(duration) if err != nil { d, err = time.ParseDuration(duration+"s") if err != nil { fmt.Printf("duration ? %s (%s)\n",duration,err) return } } //fmt.Printf("Sleep %v\n",duration) time.Sleep(d) if 0 < len(argv[2:]) { gshCtx.gshellv(argv[2:]) } } func (gshCtx *GshContext)repeat(argv []string) { if len(argv) < 2 { return } start0 := time.Now() for ri,_ := strconv.Atoi(argv[1]); 0 < ri; ri-- { if 0 < len(argv[2:]) { //start := time.Now() gshCtx.gshellv(argv[2:]) end := time.Now() elps := end.Sub(start0); if( 1000000000 < elps ){ fmt.Printf("(repeat#%d %v)\n",ri,elps); } } } } func (gshCtx *GshContext)gen(argv []string) { gshPA := gshCtx.gshPA if len(argv) < 2 { fmt.Printf("Usage: %s N\n",argv[0]) return } // should br repeated by "repeat" command count, _ := strconv.Atoi(argv[1]) //fd := gshPA.Files[1] // Stdout //file := os.NewFile(fd,"internalStdOut") file := gshPA.Files[1]; // Stdout fmt.Printf("--I-- Gen. Count=%d to [%d]\n",count,file.Fd()) //buf := []byte{} outdata := "0123 5678 0123 5678 0123 5678 0123 5678\r" for gi := 0; gi < count; gi++ { file.WriteString(outdata) } //file.WriteString("\n") fmt.Printf("\n(%d B)\n",count*len(outdata)); //file.Close() } // Remote Execution // 2020-0820 func Elapsed(from time.Time)(string){ elps := time.Now().Sub(from) if 1000000000 < elps { return fmt.Sprintf("[%5d.%02ds]",elps/1000000000,(elps%1000000000)/10000000) }else if 1000000 < elps { return fmt.Sprintf("[%3d.%03dms]",elps/1000000,(elps%1000000)/1000) }else{ return fmt.Sprintf("[%3d.%03dus]",elps/1000,(elps%1000)) } } //func abbtime(nanos int64)(string){ func abbtime(nanos time.Duration)(string){ if 1000000000 < nanos { return fmt.Sprintf("%d.%02ds",nanos/1000000000,(nanos%1000000000)/10000000) }else if 1000000 < nanos { return fmt.Sprintf("%d.%03dms",nanos/1000000,(nanos%1000000)/1000) }else{ return fmt.Sprintf("%d.%03dus",nanos/1000,(nanos%1000)) } } func abssize(size int64)(string){ fsize := float64(size) if 1024*1024*1024 < size { return fmt.Sprintf("%.2fGiB",fsize/(1024*1024*1024)) }else if 1024*1024 < size { return fmt.Sprintf("%.3fMiB",fsize/(1024*1024)) }else{ return fmt.Sprintf("%.3fKiB",fsize/1024) } } func absize(size int64)(string){ fsize := float64(size) if 1024*1024*1024 < size { return fmt.Sprintf("%8.2fGiB",fsize/(1024*1024*1024)) }else if 1024*1024 < size { return fmt.Sprintf("%8.3fMiB",fsize/(1024*1024)) }else{ return fmt.Sprintf("%8.3fKiB",fsize/1024) } } func abbspeed(totalB int64,ns int64)(string){ MBs := (float64(totalB)/1000000) / (float64(ns)/1000000000) if 1000 <= MBs { return fmt.Sprintf("%6.3fGB/s",MBs/1000) } if 1 <= MBs { return fmt.Sprintf("%6.3fMB/s",MBs) }else{ return fmt.Sprintf("%6.3fKB/s",MBs*1000) } } func abspeed(totalB int64,ns time.Duration)(string){ MBs := (float64(totalB)/1000000) / (float64(ns)/1000000000) if 1000 <= MBs { return fmt.Sprintf("%6.3fGBps",MBs/1000) } if 1 <= MBs { return fmt.Sprintf("%6.3fMBps",MBs) }else{ return fmt.Sprintf("%6.3fKBps",MBs*1000) } } func fileRelay(what string,in*os.File,out*os.File,size int64,bsiz int)(wcount int64){ Start := time.Now() buff := make([]byte,bsiz) var total int64 = 0 var rem int64 = size nio := 0 Prev := time.Now() var PrevSize int64 = 0 fmt.Printf(Elapsed(Start)+"--In- X: %s (%v/%v/%v) START\n", what,absize(total),size,nio) for i:= 0; ; i++ { var len = bsiz if int(rem) < len { len = int(rem) } Now := time.Now() Elps := Now.Sub(Prev); if 1000000000 < Now.Sub(Prev) { fmt.Printf(Elapsed(Start)+"--In- X: %s (%v/%v/%v) %s\n", what,absize(total),size,nio, abspeed((total-PrevSize),Elps)) Prev = Now; PrevSize = total } rlen := len if in != nil { // should watch the disconnection of out rcc,err := in.Read(buff[0:rlen]) if err != nil { fmt.Printf(Elapsed(Start)+"--En- X: %s read(%v,%v)<%v\n", what,rcc,err,in.Name()) break } rlen = rcc if string(buff[0:10]) == "((SoftEOF " { var ecc int64 = 0 fmt.Sscanf(string(buff),"((SoftEOF %v",&ecc) fmt.Printf(Elapsed(Start)+"--En- X: %s Recv ((SoftEOF %v))/%v\n", what,ecc,total) if ecc == total { break } } } wlen := rlen if out != nil { wcc,err := out.Write(buff[0:rlen]) if err != nil { fmt.Printf(Elapsed(Start)+"-En-- X: %s write(%v,%v)>%v\n", what,wcc,err,out.Name()) break } wlen = wcc } if wlen < rlen { fmt.Printf(Elapsed(Start)+"--En- X: %s incomplete write (%v/%v)\n", what,wlen,rlen) break; } nio += 1 total += int64(rlen) rem -= int64(rlen) if rem <= 0 { break } } Done := time.Now() Elps := float64(Done.Sub(Start))/1000000000 //Seconds TotalMB := float64(total)/1000000 //MB MBps := TotalMB / Elps fmt.Printf(Elapsed(Start)+"--In- X: %s (%v/%v/%v) %v %.3fMB/s\n", what,total,size,nio,absize(total),MBps) return total } func tcpPush(clnt *os.File){ // shrink socket buffer and recover usleep(100); } func (gsh*GshContext)RexecServer(argv[]string){ debug := true Start0 := time.Now() Start := Start0 // if local == ":" { local = "0.0.0.0:9999" } local := "0.0.0.0:9999" if 0 < len(argv) { if argv[0] == "-s" { debug = false argv = argv[1:] } } if 0 < len(argv) { argv = argv[1:] } port, err := net.ResolveTCPAddr("tcp",local); if err != nil { fmt.Printf("--En- S: Address error: %s (%s)\n",local,err) return } fmt.Printf(Elapsed(Start)+"--In- S: Listening at %s...\n",local); sconn, err := net.ListenTCP("tcp", port) if err != nil { fmt.Printf(Elapsed(Start)+"--En- S: Listen error: %s (%s)\n",local,err) return } reqbuf := make([]byte,LINESIZE) res := "" for { fmt.Printf(Elapsed(Start0)+"--In- S: Listening at %s...\n",local); aconn, err := sconn.AcceptTCP() Start = time.Now() if err != nil { fmt.Printf(Elapsed(Start)+"--En- S: Accept error: %s (%s)\n",local,err) return } clnt, _ := aconn.File() fd := clnt.Fd() ar := aconn.RemoteAddr() if debug { fmt.Printf(Elapsed(Start0)+"--In- S: Accepted TCP at %s [%d] <- %v\n", local,fd,ar) } res = fmt.Sprintf("220 GShell/%s Server\r\n",VERSION) fmt.Fprintf(clnt,"%s",res) if debug { fmt.Printf(Elapsed(Start)+"--In- S: %s",res) } count, err := clnt.Read(reqbuf) if err != nil { fmt.Printf(Elapsed(Start)+"--En- C: (%v %v) %v", count,err,string(reqbuf)) } req := string(reqbuf[:count]) if debug { fmt.Printf(Elapsed(Start)+"--In- C: %v",string(req)) } reqv := strings.Split(string(req),"\r") cmdv := gshScanArg(reqv[0],0) //cmdv := strings.Split(reqv[0]," ") switch cmdv[0] { case "HELO": res = fmt.Sprintf("250 %v",req) case "GET": // download {remotefile|-zN} [localfile] var dsize int64 = 32*1024*1024 var bsize int = 64*1024 var fname string = "" var in *os.File = nil var pseudoEOF = false if 1 < len(cmdv) { fname = cmdv[1] if strBegins(fname,"-z") { fmt.Sscanf(fname[2:],"%d",&dsize) }else if strBegins(fname,"{") { xin,xout,err := gsh.Popen(fname,"r") if err { }else{ xout.Close() defer xin.Close() in = xin dsize = MaxStreamSize pseudoEOF = true } }else{ xin,err := os.Open(fname) if err != nil { fmt.Printf("--En- GET (%v)\n",err) }else{ defer xin.Close() in = xin fi,_ := xin.Stat() dsize = fi.Size() } } } //fmt.Printf(Elapsed(Start)+"--In- GET %v:%v\n",dsize,bsize) res = fmt.Sprintf("200 %v\r\n",dsize) fmt.Fprintf(clnt,"%v",res) tcpPush(clnt); // should be separated as line in receiver fmt.Printf(Elapsed(Start)+"--In- S: %v",res) wcount := fileRelay("SendGET",in,clnt,dsize,bsize) if pseudoEOF { in.Close() // pipe from the command // show end of stream data (its size) by OOB? SoftEOF := fmt.Sprintf("((SoftEOF %v))",wcount) fmt.Printf(Elapsed(Start)+"--In- S: Send %v\n",SoftEOF) tcpPush(clnt); // to let SoftEOF data apper at the top of recevied data fmt.Fprintf(clnt,"%v\r\n",SoftEOF) tcpPush(clnt); // to let SoftEOF alone in a packet (separate with 200 OK) // with client generated random? //fmt.Printf("--In- L: close %v (%v)\n",in.Fd(),in.Name()) } res = fmt.Sprintf("200 GET done\r\n") case "PUT": // upload {srcfile|-zN} [dstfile] var dsize int64 = 32*1024*1024 var bsize int = 64*1024 var fname string = "" var out *os.File = nil if 1 < len(cmdv) { // localfile fmt.Sscanf(cmdv[1],"%d",&dsize) } if 2 < len(cmdv) { fname = cmdv[2] if fname == "-" { // nul dev }else if strBegins(fname,"{") { xin,xout,err := gsh.Popen(fname,"w") if err { }else{ xin.Close() defer xout.Close() out = xout } }else{ // should write to temporary file // should suppress ^C on tty xout,err := os.OpenFile(fname,os.O_CREATE|os.O_RDWR|os.O_TRUNC,0600) //fmt.Printf("--In- S: open(%v) out(%v) err(%v)\n",fname,xout,err) if err != nil { fmt.Printf("--En- PUT (%v)\n",err) }else{ out = xout } } fmt.Printf(Elapsed(Start)+"--In- L: open(%v,w) %v (%v)\n", fname,local,err) } fmt.Printf(Elapsed(Start)+"--In- PUT %v (/%v)\n",dsize,bsize) fmt.Printf(Elapsed(Start)+"--In- S: 200 %v OK\r\n",dsize) fmt.Fprintf(clnt,"200 %v OK\r\n",dsize) fileRelay("RecvPUT",clnt,out,dsize,bsize) res = fmt.Sprintf("200 PUT done\r\n") default: res = fmt.Sprintf("400 What? %v",req) } swcc,serr := clnt.Write([]byte(res)) if serr != nil { fmt.Printf(Elapsed(Start)+"--In- S: (wc=%v er=%v) %v",swcc,serr,res) }else{ fmt.Printf(Elapsed(Start)+"--In- S: %v",res) } aconn.Close(); clnt.Close(); } sconn.Close(); } func (gsh*GshContext)RexecClient(argv[]string)(int,string){ debug := true Start := time.Now() if len(argv) == 1 { return -1,"EmptyARG" } argv = argv[1:] if argv[0] == "-serv" { gsh.RexecServer(argv[1:]) return 0,"Server" } remote := "0.0.0.0:9999" if argv[0][0] == '@' { remote = argv[0][1:] argv = argv[1:] } if argv[0] == "-s" { debug = false argv = argv[1:] } dport, err := net.ResolveTCPAddr("tcp",remote); if err != nil { fmt.Printf(Elapsed(Start)+"Address error: %s (%s)\n",remote,err) return -1,"AddressError" } fmt.Printf(Elapsed(Start)+"--In- C: Connecting to %s\n",remote) serv, err := net.DialTCP("tcp",nil,dport) if err != nil { fmt.Printf(Elapsed(Start)+"Connection error: %s (%s)\n",remote,err) return -1,"CannotConnect" } if debug { al := serv.LocalAddr() fmt.Printf(Elapsed(Start)+"--In- C: Connected to %v <- %v\n",remote,al) } req := "" res := make([]byte,LINESIZE) count,err := serv.Read(res) if err != nil { fmt.Printf("--En- S: (%3d,%v) %v",count,err,string(res)) } if debug { fmt.Printf(Elapsed(Start)+"--In- S: %v",string(res)) } if argv[0] == "GET" { savPA := gsh.gshPA var bsize int = 64*1024 req = fmt.Sprintf("%v\r\n",strings.Join(argv," ")) fmt.Printf(Elapsed(Start)+"--In- C: %v",req) fmt.Fprintf(serv,req) count,err = serv.Read(res) if err != nil { }else{ var dsize int64 = 0 var out *os.File = nil var out_tobeclosed *os.File = nil var fname string = "" var rcode int = 0 var pid int = -1 fmt.Sscanf(string(res),"%d %d",&rcode,&dsize) fmt.Printf(Elapsed(Start)+"--In- S: %v",string(res[0:count])) if 3 <= len(argv) { fname = argv[2] if strBegins(fname,"{") { xin,xout,err := gsh.Popen(fname,"w") if err { }else{ xin.Close() defer xout.Close() out = xout out_tobeclosed = xout pid = 0 // should be its pid } }else{ // should write to temporary file // should suppress ^C on tty xout,err := os.OpenFile(fname,os.O_CREATE|os.O_RDWR|os.O_TRUNC,0600) if err != nil { fmt.Print("--En- %v\n",err) } out = xout //fmt.Printf("--In-- %d > %s\n",out.Fd(),fname) } } in,_ := serv.File() fileRelay("RecvGET",in,out,dsize,bsize) if 0 <= pid { gsh.gshPA = savPA // recovery of Fd(), and more? fmt.Printf(Elapsed(Start)+"--In- L: close Pipe > %v\n",fname) out_tobeclosed.Close() //syscall.Wait4(pid,nil,0,nil) //@@ } } }else if argv[0] == "PUT" { remote, _ := serv.File() var local *os.File = nil var dsize int64 = 32*1024*1024 var bsize int = 64*1024 var ofile string = "-" //fmt.Printf("--I-- Rex %v\n",argv) if 1 < len(argv) { fname := argv[1] if strBegins(fname,"-z") { fmt.Sscanf(fname[2:],"%d",&dsize) }else if strBegins(fname,"{") { xin,xout,err := gsh.Popen(fname,"r") if err { }else{ xout.Close() defer xin.Close() //in = xin local = xin fmt.Printf("--In- [%d] < Upload output of %v\n", local.Fd(),fname) ofile = "-from."+fname dsize = MaxStreamSize } }else{ xlocal,err := os.Open(fname) if err != nil { fmt.Printf("--En- (%s)\n",err) local = nil }else{ local = xlocal fi,_ := local.Stat() dsize = fi.Size() defer local.Close() //fmt.Printf("--I-- Rex in(%v / %v)\n",ofile,dsize) } ofile = fname fmt.Printf(Elapsed(Start)+"--In- L: open(%v,r)=%v %v (%v)\n", fname,dsize,local,err) } } if 2 < len(argv) && argv[2] != "" { ofile = argv[2] //fmt.Printf("(%d)%v B.ofile=%v\n",len(argv),argv,ofile) } //fmt.Printf(Elapsed(Start)+"--I-- Rex out(%v)\n",ofile) fmt.Printf(Elapsed(Start)+"--In- PUT %v (/%v)\n",dsize,bsize) req = fmt.Sprintf("PUT %v %v \r\n",dsize,ofile) if debug { fmt.Printf(Elapsed(Start)+"--In- C: %v",req) } fmt.Fprintf(serv,"%v",req) count,err = serv.Read(res) if debug { fmt.Printf(Elapsed(Start)+"--In- S: %v",string(res[0:count])) } fileRelay("SendPUT",local,remote,dsize,bsize) }else{ req = fmt.Sprintf("%v\r\n",strings.Join(argv," ")) if debug { fmt.Printf(Elapsed(Start)+"--In- C: %v",req) } fmt.Fprintf(serv,"%v",req) //fmt.Printf("--In- sending RexRequest(%v)\n",len(req)) } //fmt.Printf(Elapsed(Start)+"--In- waiting RexResponse...\n") count,err = serv.Read(res) ress := "" if count == 0 { ress = "(nil)\r\n" }else{ ress = string(res[:count]) } if err != nil { fmt.Printf(Elapsed(Start)+"--En- S: (%d,%v) %v",count,err,ress) }else{ fmt.Printf(Elapsed(Start)+"--In- S: %v",ress) } serv.Close() //conn.Close() var stat string var rcode int fmt.Sscanf(ress,"%d %s",&rcode,&stat) //fmt.Printf("--D-- Client: %v (%v)",rcode,stat) return rcode,ress } // Remote Shell // gcp file [...] { [host]:[port:][dir] | dir } // -p | -no-p func (gsh*GshContext)FileCopy(argv[]string){ var host = "" var port = "" var upload = false var download = false var xargv = []string{"rex-gcp"} var srcv = []string{} var dstv = []string{} argv = argv[1:] for _,v := range argv { /* if v[0] == '-' { // might be a pseudo file (generated date) continue } */ obj := strings.Split(v,":") //fmt.Printf("%d %v %v\n",len(obj),v,obj) if 1 < len(obj) { host = obj[0] file := "" if 0 < len(host) { gsh.LastServer.host = host }else{ host = gsh.LastServer.host port = gsh.LastServer.port } if 2 < len(obj) { port = obj[1] if 0 < len(port) { gsh.LastServer.port = port }else{ port = gsh.LastServer.port } file = obj[2] }else{ file = obj[1] } if len(srcv) == 0 { download = true srcv = append(srcv,file) continue } upload = true dstv = append(dstv,file) continue } /* idx := strings.Index(v,":") if 0 <= idx { remote = v[0:idx] if len(srcv) == 0 { download = true srcv = append(srcv,v[idx+1:]) continue } upload = true dstv = append(dstv,v[idx+1:]) continue } */ if download { dstv = append(dstv,v) }else{ srcv = append(srcv,v) } } hostport := "@" + host + ":" + port if upload { if host != "" { xargv = append(xargv,hostport) } xargv = append(xargv,"PUT") xargv = append(xargv,srcv[0:]...) xargv = append(xargv,dstv[0:]...) //fmt.Printf("--I-- FileCopy PUT gsh://%s/%v < %v // %v\n",hostport,dstv,srcv,xargv) fmt.Printf("--I-- FileCopy PUT gsh://%s/%v < %v\n",hostport,dstv,srcv) gsh.RexecClient(xargv) }else if download { if host != "" { xargv = append(xargv,hostport) } xargv = append(xargv,"GET") xargv = append(xargv,srcv[0:]...) xargv = append(xargv,dstv[0:]...) //fmt.Printf("--I-- FileCopy GET gsh://%v/%v > %v // %v\n",hostport,srcv,dstv,xargv) fmt.Printf("--I-- FileCopy GET gsh://%v/%v > %v\n",hostport,srcv,dstv) gsh.RexecClient(xargv) }else{ } } // target func (gsh*GshContext)Trelpath(rloc string)(string){ cwd, _ := os.Getwd() os.Chdir(gsh.RWD) os.Chdir(rloc) twd, _ := os.Getwd() os.Chdir(cwd) tpath := twd + "/" + rloc return tpath } // join to rmote GShell - [user@]host[:port] or cd host:[port]:path func (gsh*GshContext)Rjoin(argv[]string){ if len(argv) <= 1 { fmt.Printf("--I-- current server = %v\n",gsh.RSERV) return } serv := argv[1] servv := strings.Split(serv,":") if 1 <= len(servv) { if servv[0] == "lo" { servv[0] = "localhost" } } switch len(servv) { case 1: //if strings.Index(serv,":") < 0 { serv = servv[0] + ":" + fmt.Sprintf("%d",GSH_PORT) //} case 2: // host:port serv = strings.Join(servv,":") } xargv := []string{"rex-join","@"+serv,"HELO"} rcode,stat := gsh.RexecClient(xargv) if (rcode / 100) == 2 { fmt.Printf("--I-- OK Joined (%v) %v\n",rcode,stat) gsh.RSERV = serv }else{ fmt.Printf("--I-- NG, could not joined (%v) %v\n",rcode,stat) } } func (gsh*GshContext)Rexec(argv[]string){ if len(argv) <= 1 { fmt.Printf("--I-- rexec command [ | {file || {command} ]\n",gsh.RSERV) return } /* nargv := gshScanArg(strings.Join(argv," "),0) fmt.Printf("--D-- nargc=%d [%v]\n",len(nargv),nargv) if nargv[1][0] != '{' { nargv[1] = "{" + nargv[1] + "}" fmt.Printf("--D-- nargc=%d [%v]\n",len(nargv),nargv) } argv = nargv */ nargv := []string{} nargv = append(nargv,"{"+strings.Join(argv[1:]," ")+"}") fmt.Printf("--D-- nargc=%d %v\n",len(nargv),nargv) argv = nargv xargv := []string{"rex-exec","@"+gsh.RSERV,"GET"} xargv = append(xargv,argv...) xargv = append(xargv,"/dev/tty") rcode,stat := gsh.RexecClient(xargv) if (rcode / 100) == 2 { fmt.Printf("--I-- OK Rexec (%v) %v\n",rcode,stat) }else{ fmt.Printf("--I-- NG Rexec (%v) %v\n",rcode,stat) } } func (gsh*GshContext)Rchdir(argv[]string){ if len(argv) <= 1 { return } cwd, _ := os.Getwd() os.Chdir(gsh.RWD) os.Chdir(argv[1]) twd, _ := os.Getwd() gsh.RWD = twd fmt.Printf("--I-- JWD=%v\n",twd) os.Chdir(cwd) } func (gsh*GshContext)Rpwd(argv[]string){ fmt.Printf("%v\n",gsh.RWD) } func (gsh*GshContext)Rls(argv[]string){ cwd, _ := os.Getwd() os.Chdir(gsh.RWD) argv[0] = "-ls" gsh.xFind(argv) os.Chdir(cwd) } func (gsh*GshContext)Rput(argv[]string){ var local string = "" var remote string = "" if 1 < len(argv) { local = argv[1] remote = local // base name } if 2 < len(argv) { remote = argv[2] } fmt.Printf("--I-- jput from=%v to=%v\n",local,gsh.Trelpath(remote)) } func (gsh*GshContext)Rget(argv[]string){ var remote string = "" var local string = "" if 1 < len(argv) { remote = argv[1] local = remote // base name } if 2 < len(argv) { local = argv[2] } fmt.Printf("--I-- jget from=%v to=%v\n",gsh.Trelpath(remote),local) } // network // -s, -si, -so // bi-directional, source, sync (maybe socket) func (gshCtx*GshContext)sconnect(inTCP bool, argv []string) { gshPA := gshCtx.gshPA if len(argv) < 2 { fmt.Printf("Usage: -s [host]:[port[.udp]]\n") return } remote := argv[1] if remote == ":" { remote = "0.0.0.0:9999" } if inTCP { // TCP dport, err := net.ResolveTCPAddr("tcp",remote); if err != nil { fmt.Printf("Address error: %s (%s)\n",remote,err) return } conn, err := net.DialTCP("tcp",nil,dport) if err != nil { fmt.Printf("Connection error: %s (%s)\n",remote,err) return } file, _ := conn.File(); //fd := file.Fd() //fmt.Printf("Socket: connected to %s, socket[%d]\n",remote,fd) fmt.Printf("Socket: connected to %s, socket[%d]\n",remote,file.Fd()) savfd := gshPA.Files[1] //gshPA.Files[1] = fd; gshPA.Files[1] = file; gshCtx.gshellv(argv[2:]) gshPA.Files[1] = savfd file.Close() conn.Close() }else{ //dport, err := net.ResolveUDPAddr("udp4",remote); dport, err := net.ResolveUDPAddr("udp",remote); if err != nil { fmt.Printf("Address error: %s (%s)\n",remote,err) return } //conn, err := net.DialUDP("udp4",nil,dport) conn, err := net.DialUDP("udp",nil,dport) if err != nil { fmt.Printf("Connection error: %s (%s)\n",remote,err) return } file, _ := conn.File(); //fd := file.Fd() ar := conn.RemoteAddr() //al := conn.LocalAddr() fmt.Printf("Socket: connected to %s [%s], socket[%d]\n", //remote,ar.String(),fd) remote,ar.String(),file.Fd()) savfd := gshPA.Files[1] //gshPA.Files[1] = fd; gshPA.Files[1] = file; gshCtx.gshellv(argv[2:]) gshPA.Files[1] = savfd file.Close() conn.Close() } } func (gshCtx*GshContext)saccept(inTCP bool, argv []string) { gshPA := gshCtx.gshPA if len(argv) < 2 { fmt.Printf("Usage: -ac [host]:[port[.udp]]\n") return } local := argv[1] if local == ":" { local = "0.0.0.0:9999" } if inTCP { // TCP port, err := net.ResolveTCPAddr("tcp",local); if err != nil { fmt.Printf("Address error: %s (%s)\n",local,err) return } //fmt.Printf("Listen at %s...\n",local); sconn, err := net.ListenTCP("tcp", port) if err != nil { fmt.Printf("Listen error: %s (%s)\n",local,err) return } //fmt.Printf("Accepting at %s...\n",local); aconn, err := sconn.AcceptTCP() if err != nil { fmt.Printf("Accept error: %s (%s)\n",local,err) return } file, _ := aconn.File() //fd := file.Fd() //fmt.Printf("Accepted TCP at %s [%d]\n",local,fd) fmt.Printf("Accepted TCP at %s [%d]\n",local,file.Fd()) savfd := gshPA.Files[0] //gshPA.Files[0] = fd; gshPA.Files[0] = file; gshCtx.gshellv(argv[2:]) gshPA.Files[0] = savfd sconn.Close(); aconn.Close(); file.Close(); }else{ //port, err := net.ResolveUDPAddr("udp4",local); port, err := net.ResolveUDPAddr("udp",local); if err != nil { fmt.Printf("Address error: %s (%s)\n",local,err) return } fmt.Printf("Listen UDP at %s...\n",local); //uconn, err := net.ListenUDP("udp4", port) uconn, err := net.ListenUDP("udp", port) if err != nil { fmt.Printf("Listen error: %s (%s)\n",local,err) return } file, _ := uconn.File() //fd := file.Fd() ar := uconn.RemoteAddr() remote := "" if ar != nil { remote = ar.String() } if remote == "" { remote = "?" } // not yet received //fmt.Printf("Accepted at %s [%d] <- %s\n",local,fd,"") savfd := gshPA.Files[0] //gshPA.Files[0] = fd; gshPA.Files[0] = file; savenv := gshPA.Env gshPA.Env = append(savenv, "REMOTE_HOST="+remote) gshCtx.gshellv(argv[2:]) gshPA.Env = savenv gshPA.Files[0] = savfd uconn.Close(); file.Close(); } } // empty line command func (gshCtx*GshContext)xPwd(argv[]string){ // execute context command, pwd + date // context notation, representation scheme, to be resumed at re-login cwd, _ := os.Getwd() switch { case isin("-a",argv): gshCtx.ShowChdirHistory(argv) case isin("-ls",argv): showFileInfo(cwd,argv) default: fmt.Printf("%s\n",cwd) case isin("-v",argv): // obsolete emtpy command t := time.Now() date := t.Format(time.UnixDate) exe, _ := os.Executable() host, _ := os.Hostname() fmt.Printf("{PWD=\"%s\"",cwd) fmt.Printf(" HOST=\"%s\"",host) fmt.Printf(" DATE=\"%s\"",date) fmt.Printf(" TIME=\"%s\"",t.String()) fmt.Printf(" PID=\"%d\"",os.Getpid()) fmt.Printf(" EXE=\"%s\"",exe) fmt.Printf("}\n") } } // History // these should be browsed and edited by HTTP browser // show the time of command with -t and direcotry with -ls // openfile-history, sort by -a -m -c // sort by elapsed time by -t -s // search by "more" like interface // edit history // sort history, and wc or uniq // CPU and other resource consumptions // limit showing range (by time or so) // export / import history func (gshCtx *GshContext)xHistory(argv []string){ atWorkDirX := -1 if 1 < len(argv) && strBegins(argv[1],"@") { atWorkDirX,_ = strconv.Atoi(argv[1][1:]) } //fmt.Printf("--D-- showHistory(%v)\n",argv) for i, v := range gshCtx.CommandHistory { // exclude commands not to be listed by default // internal commands may be suppressed by default if v.CmdLine == "" && !isin("-a",argv) { continue; } if 0 <= atWorkDirX { if v.WorkDirX != atWorkDirX { continue } } if !isin("-n",argv){ // like "fc" fmt.Printf("!%-2d ",i) } if isin("-v",argv){ fmt.Println(v) // should be with it date }else{ if isin("-l",argv) || isin("-l0",argv) { elps := v.EndAt.Sub(v.StartAt); start := v.StartAt.Format(time.Stamp) fmt.Printf("@%d ",v.WorkDirX) fmt.Printf("[%v] %11v/t ",start,elps) } if isin("-l",argv) && !isin("-l0",argv){ fmt.Printf("%v",Rusagef("%t %u\t// %s",argv,v.Rusagev)) } if isin("-at",argv) { // isin("-ls",argv){ dhi := v.WorkDirX // workdir history index fmt.Printf("@%d %s\t",dhi,v.WorkDir) // show the FileInfo of the output command?? } fmt.Printf("%s",v.CmdLine) fmt.Printf("\n") } } } // !n - history index func searchHistory(gshCtx GshContext, gline string) (string, bool, bool){ if gline[0] == '!' { hix, err := strconv.Atoi(gline[1:]) if err != nil { fmt.Printf("--E-- (%s : range)\n",hix) return "", false, true } if hix < 0 || len(gshCtx.CommandHistory) <= hix { fmt.Printf("--E-- (%d : out of range)\n",hix) return "", false, true } return gshCtx.CommandHistory[hix].CmdLine, false, false } // search //for i, v := range gshCtx.CommandHistory { //} return gline, false, false } func (gsh*GshContext)cmdStringInHistory(hix int)(cmd string, ok bool){ if 0 <= hix && hix < len(gsh.CommandHistory) { return gsh.CommandHistory[hix].CmdLine,true } return "",false } // temporary adding to PATH environment // cd name -lib for LD_LIBRARY_PATH // chdir with directory history (date + full-path) // -s for sort option (by visit date or so) func (gsh*GshContext)ShowChdirHistory1(i int,v GChdirHistory, argv []string){ fmt.Printf("!%-2d ",v.CmdIndex) // the first command at this WorkDir fmt.Printf("@%d ",i) fmt.Printf("[%v] ",v.MovedAt.Format(time.Stamp)) showFileInfo(v.Dir,argv) } func (gsh*GshContext)ShowChdirHistory(argv []string){ for i, v := range gsh.ChdirHistory { gsh.ShowChdirHistory1(i,v,argv) } } func skipOpts(argv[]string)(int){ for i,v := range argv { if strBegins(v,"-") { }else{ return i } } return -1 } func (gshCtx*GshContext)xChdir(argv []string){ cdhist := gshCtx.ChdirHistory if isin("?",argv ) || isin("-t",argv) || isin("-a",argv) { gshCtx.ShowChdirHistory(argv) return } pwd, _ := os.Getwd() dir := "" if len(argv) <= 1 { dir = toFullpath("~") }else{ i := skipOpts(argv[1:]) if i < 0 { dir = toFullpath("~") }else{ dir = argv[1+i] } } if strBegins(dir,"@") { if dir == "@0" { // obsolete dir = gshCtx.StartDir }else if dir == "@!" { index := len(cdhist) - 1 if 0 < index { index -= 1 } dir = cdhist[index].Dir }else{ index, err := strconv.Atoi(dir[1:]) if err != nil { fmt.Printf("--E-- xChdir(%v)\n",err) dir = "?" }else if len(gshCtx.ChdirHistory) <= index { fmt.Printf("--E-- xChdir(history range error)\n") dir = "?" }else{ dir = cdhist[index].Dir } } } if dir != "?" { err := os.Chdir(dir) if err != nil { fmt.Printf("--E-- xChdir(%s)(%v)\n",argv[1],err) }else{ cwd, _ := os.Getwd() if cwd != pwd { hist1 := GChdirHistory { } hist1.Dir = cwd hist1.MovedAt = time.Now() hist1.CmdIndex = len(gshCtx.CommandHistory)+1 gshCtx.ChdirHistory = append(cdhist,hist1) if !isin("-s",argv){ //cwd, _ := os.Getwd() //fmt.Printf("%s\n",cwd) ix := len(gshCtx.ChdirHistory)-1 gshCtx.ShowChdirHistory1(ix,hist1,argv) } } } } if isin("-ls",argv){ cwd, _ := os.Getwd() showFileInfo(cwd,argv); } } func TimeValSub(tv1 *time.Duration, tv2 *time.Duration){ //*tv1 = syscall.NsecToTimeval(tv1.Nano() - tv2.Nano()) *tv1 -= *tv2; } func RusageSubv(ru1, ru2 [2]aRusage)([2]aRusage){ TimeValSub(&ru1[0].Utime,&ru2[0].Utime) TimeValSub(&ru1[0].Stime,&ru2[0].Stime) TimeValSub(&ru1[1].Utime,&ru2[1].Utime) TimeValSub(&ru1[1].Stime,&ru2[1].Stime) return ru1 } func TimeValAdd(tv1 time.Duration, tv2 time.Duration)(time.Duration){ //tvs := syscall.NsecToTimeval(tv1.Nano() + tv2.Nano()) tvs := tv1 + tv2; return tvs; } /* func RusageAddv(ru1, ru2 [2]aRusage)([2]aRusage){ TimeValAdd(ru1[0].Utime,ru2[0].Utime) TimeValAdd(ru1[0].Stime,ru2[0].Stime) TimeValAdd(ru1[1].Utime,ru2[1].Utime) TimeValAdd(ru1[1].Stime,ru2[1].Stime) return ru1 } */ // Resource Usage func sRusagef(fmtspec string, argv []string, ru [2]aRusage)(string){ // ru[0] self , ru[1] children ut := TimeValAdd(ru[0].Utime,ru[1].Utime) st := TimeValAdd(ru[0].Stime,ru[1].Stime) //uu := (int64(ut.Sec)*1000000 + int64(ut.Usec)) * 1000 //su := (int64(st.Sec)*1000000 + int64(st.Usec)) * 1000 uu := ut; // in nano sec su := st; // in nano sec tu := uu + su ret := fmt.Sprintf("%v/sum",abbtime(tu)) ret += fmt.Sprintf(", %v/usr",abbtime(uu)) ret += fmt.Sprintf(", %v/sys",abbtime(su)) return ret } func Rusagef(fmtspec string, argv []string, ru [2]aRusage)(string){ ut := TimeValAdd(ru[0].Utime,ru[1].Utime) st := TimeValAdd(ru[0].Stime,ru[1].Stime) //fmt.Printf("%d.%06ds/u ",ut.Sec,ut.Usec) //ru[1].Utime.Sec,ru[1].Utime.Usec) //fmt.Printf("%d.%06ds/s ",st.Sec,st.Usec) //ru[1].Stime.Sec,ru[1].Stime.Usec) fmt.Printf("%d.%06ds/u ",ut/1000000000,(ut/1000)%1000000); fmt.Printf("%d.%06ds/s ",st/1000000000,(st/1000)%1000000); return "" } func Getrusagev()([2]aRusage){ var ruv = [2]aRusage{} aGetrusage(aRUSAGE_SELF,&ruv[0]) aGetrusage(aRUSAGE_CHILDREN,&ruv[1]) return ruv } func (gshCtx *GshContext)xTime(argv[]string)(bool){ if 2 <= len(argv){ gshCtx.LastRusage = aRusage{} rusagev1 := Getrusagev() fin := gshCtx.gshellv(argv[1:]) rusagev2 := Getrusagev() showRusage(argv[1],argv,&gshCtx.LastRusage) rusagev := RusageSubv(rusagev2,rusagev1) showRusage("self",argv,&rusagev[0]) showRusage("chld",argv,&rusagev[1]) return fin }else{ rusage:= aRusage {} aGetrusage(aRUSAGE_SELF,&rusage) showRusage("self",argv, &rusage) aGetrusage(aRUSAGE_CHILDREN,&rusage) showRusage("chld",argv, &rusage) return false } } func (gshCtx *GshContext)xJobs(argv[]string){ fmt.Printf("%d Jobs\n",len(gshCtx.BackGroundJobs)) for ji, pid := range gshCtx.BackGroundJobs { //wstat := syscall.WaitStatus {0} rusage := aRusage {} //wpid, err := syscall.Wait4(pid,&wstat,syscall.WNOHANG,&rusage); //wpid, err := syscall.Wait4(pid,nil,syscall.WNOHANG,&rusage); wpid := pid.Pid(); err := errors.New("stab_NoError"); if err != nil { fmt.Printf("--E-- %%%d [%d] (%v)\n",ji,wpid,err) }else{ fmt.Printf("%%%d[%d]\n",ji,wpid) showRusage("chld",argv,&rusage) } } } func (gsh*GshContext)inBackground(argv[]string)(bool){ if gsh.CmdTrace { fmt.Printf("--I-- inBackground(%v)\n",argv) } gsh.BackGround = true // set background option xfin := false xfin = gsh.gshellv(argv) gsh.BackGround = false return xfin } // -o file without command means just opening it and refer by #N // should be listed by "files" comnmand func (gshCtx*GshContext)xOpen(argv[]string){ //var pv = []int{-1,-1} //err := syscall.Pipe(pv) //fmt.Printf("--I-- pipe()=[#%d,#%d](%v)\n",pv[0],pv[1],err) pin,pout,err := os.Pipe(); fmt.Printf("--I-- pipe()=[#%d,#%d](%v)\n",pin.Fd(),pout.Fd(),err) } func (gshCtx*GshContext)fromPipe(argv[]string){ } func (gshCtx*GshContext)xClose(argv[]string){ } // redirect func (gshCtx*GshContext)redirect(argv[]string)(bool){ if len(argv) < 2 { return false } cmd := argv[0] fname := argv[1] var file *os.File = nil fdix := 0 mode := os.O_RDONLY switch { case cmd == "-i" || cmd == "<": fdix = 0 mode = os.O_RDONLY case cmd == "-o" || cmd == ">": fdix = 1 mode = os.O_RDWR | os.O_CREATE case cmd == "-a" || cmd == ">>": fdix = 1 mode = os.O_RDWR | os.O_CREATE | os.O_APPEND } if fname[0] == '#' { fd, err := strconv.Atoi(fname[1:]) if err != nil { fmt.Printf("--E-- (%v)\n",err) return false } file = os.NewFile(uintptr(fd),"MaybePipe") }else{ xfile, err := os.OpenFile(argv[1], mode, 0600) if err != nil { fmt.Printf("--E-- (%s)\n",err) return false } file = xfile } gshPA := gshCtx.gshPA savfd := gshPA.Files[fdix] //gshPA.Files[fdix] = file.Fd() gshPA.Files[fdix] = file; fmt.Printf("--I-- Opened [%d] %s\n",file.Fd(),argv[1]) gshCtx.gshellv(argv[2:]) gshPA.Files[fdix] = savfd return false } //fmt.Fprintf(res, "GShell Status: %q", html.EscapeString(req.URL.Path)) func httpHandler(res http.ResponseWriter, req *http.Request){ path := req.URL.Path fmt.Printf("--I-- Got HTTP Request(%s)\n",path) { gshCtxBuf, _ := setupGshContext() gshCtx := &gshCtxBuf fmt.Printf("--I-- %s\n",path[1:]) gshCtx.tgshelll(path[1:]) } fmt.Fprintf(res, "Hello(^-^)//\n%s\n",path) } func (gshCtx *GshContext) httpServer(argv []string){ http.HandleFunc("/", httpHandler) accport := "localhost:9999" fmt.Printf("--I-- HTTP Server Start at [%s]\n",accport) http.ListenAndServe(accport,nil) } func (gshCtx *GshContext)xGo(argv[]string){ go gshCtx.gshellv(argv[1:]); } func (gshCtx *GshContext) xPs(argv[]string)(){ } // Plugin // plugin [-ls [names]] to list plugins // Reference: plugin source code func (gshCtx *GshContext) whichPlugin(name string,argv[]string)(pi *PluginInfo){ pi = nil for _,p := range gshCtx.PluginFuncs { if p.Name == name && pi == nil { pi = &p } if !isin("-s",argv){ //fmt.Printf("%v %v ",i,p) if isin("-ls",argv){ showFileInfo(p.Path,argv) }else{ fmt.Printf("%s\n",p.Name) } } } return pi } func (gshCtx *GshContext) xPlugin(argv[]string) (error) { if len(argv) == 0 || argv[0] == "-ls" { gshCtx.whichPlugin("",argv) return nil } name := argv[0] Pin := gshCtx.whichPlugin(name,[]string{"-s"}) if Pin != nil { os.Args = argv // should be recovered? Pin.Addr.(func())() return nil } sofile := toFullpath(argv[0] + ".so") // or find it by which($PATH) p, err := plugin.Open(sofile) if err != nil { fmt.Printf("--E-- plugin.Open(%s)(%v)\n",sofile,err) return err } fname := "Main" f, err := p.Lookup(fname) if( err != nil ){ fmt.Printf("--E-- plugin.Lookup(%s)(%v)\n",fname,err) return err } pin := PluginInfo {p,f,name,sofile} gshCtx.PluginFuncs = append(gshCtx.PluginFuncs,pin) fmt.Printf("--I-- added (%d)\n",len(gshCtx.PluginFuncs)) //fmt.Printf("--I-- first call(%s:%s)%v\n",sofile,fname,argv) os.Args = argv f.(func())() return err } func (gshCtx*GshContext)Args(argv[]string){ for i,v := range os.Args { fmt.Printf("[%v] %v\n",i,v) } } func (gshCtx *GshContext) showVersion(argv[]string){ if isin("-l",argv) { fmt.Printf("%v/%v (%v)",NAME,VERSION,DATE); }else{ fmt.Printf("%v",VERSION); } if isin("-a",argv) { fmt.Printf(" %s",AUTHOR) } if !isin("-n",argv) { fmt.Printf("\n") } } // Scanf // string decomposer // scanf [format] [input] func scanv(sstr string)(strv[]string){ strv = strings.Split(sstr," ") return strv } func scanUntil(src,end string)(rstr string,leng int){ idx := strings.Index(src,end) if 0 <= idx { rstr = src[0:idx] return rstr,idx+len(end) } return src,0 } // -bn -- display base-name part only // can be in some %fmt, for sed rewriting func (gsh*GshContext)printVal(fmts string, vstr string, optv[]string){ //vint,err := strconv.Atoi(vstr) var ival int64 = 0 n := 0 err := error(nil) if strBegins(vstr,"_") { vx,_ := strconv.Atoi(vstr[1:]) if vx < len(gsh.iValues) { vstr = gsh.iValues[vx] }else{ } } // should use Eval() if strBegins(vstr,"0x") { n,err = fmt.Sscanf(vstr[2:],"%x",&ival) }else{ n,err = fmt.Sscanf(vstr,"%d",&ival) //fmt.Printf("--D-- n=%d err=(%v) {%s}=%v\n",n,err,vstr, ival) } if n == 1 && err == nil { //fmt.Printf("--D-- formatn(%v) ival(%v)\n",fmts,ival) fmt.Printf("%"+fmts,ival) }else{ if isin("-bn",optv){ fmt.Printf("%"+fmts,filepath.Base(vstr)) }else{ fmt.Printf("%"+fmts,vstr) } } } func (gsh*GshContext)printfv(fmts,div string,argv[]string,optv[]string,list[]string){ //fmt.Printf("{%d}",len(list)) //curfmt := "v" outlen := 0 curfmt := gsh.iFormat if 0 < len(fmts) { for xi := 0; xi < len(fmts); xi++ { fch := fmts[xi] if fch == '%' { if xi+1 < len(fmts) { curfmt = string(fmts[xi+1]) gsh.iFormat = curfmt xi += 1 if xi+1 < len(fmts) && fmts[xi+1] == '(' { vals,leng := scanUntil(fmts[xi+2:],")") //fmt.Printf("--D-- show fmt(%v) val(%v) next(%v)\n",curfmt,vals,leng) gsh.printVal(curfmt,vals,optv) xi += 2+leng-1 outlen += 1 } continue } } if fch == '_' { hi,leng := scanInt(fmts[xi+1:]) if 0 < leng { if hi < len(gsh.iValues) { gsh.printVal(curfmt,gsh.iValues[hi],optv) outlen += 1 // should be the real length }else{ fmt.Printf("((out-range))") } xi += leng continue; } } fmt.Printf("%c",fch) outlen += 1 } }else{ //fmt.Printf("--D-- print {%s}\n") for i,v := range list { if 0 < i { fmt.Printf(div) } gsh.printVal(curfmt,v,optv) outlen += 1 } } if 0 < outlen { fmt.Printf("\n") } } func (gsh*GshContext)Scanv(argv[]string){ //fmt.Printf("--D-- Scanv(%v)\n",argv) if len(argv) == 1 { return } argv = argv[1:] fmts := "" if strBegins(argv[0],"-F") { fmts = argv[0] gsh.iDelimiter = fmts argv = argv[1:] } input := strings.Join(argv," ") if fmts == "" { // simple decomposition v := scanv(input) gsh.iValues = v //fmt.Printf("%v\n",strings.Join(v,",")) }else{ v := make([]string,8) n,err := fmt.Sscanf(input,fmts,&v[0],&v[1],&v[2],&v[3]) fmt.Printf("--D-- Scanf ->(%v) n=%d err=(%v)\n",v,n,err) gsh.iValues = v } } func (gsh*GshContext)Printv(argv[]string){ if false { //@@U fmt.Printf("%v\n",strings.Join(argv[1:]," ")) return } //fmt.Printf("--D-- Printv(%v)\n",argv) //fmt.Printf("%v\n",strings.Join(gsh.iValues,",")) div := gsh.iDelimiter fmts := "" argv = argv[1:] if 0 < len(argv) { if strBegins(argv[0],"-F") { div = argv[0][2:] argv = argv[1:] } } optv := []string{} for _,v := range argv { if strBegins(v,"-"){ optv = append(optv,v) argv = argv[1:] }else{ break; } } if 0 < len(argv) { fmts = strings.Join(argv," ") } gsh.printfv(fmts,div,argv,optv,gsh.iValues) } func (gsh*GshContext)Basename(argv[]string){ for i,v := range gsh.iValues { gsh.iValues[i] = filepath.Base(v) } } func (gsh*GshContext)Sortv(argv[]string){ sv := gsh.iValues sort.Slice(sv , func(i,j int) bool { return sv[i] < sv[j] }) } func (gsh*GshContext)Shiftv(argv[]string){ vi := len(gsh.iValues) if 0 < vi { if isin("-r",argv) { top := gsh.iValues[0] gsh.iValues = append(gsh.iValues[1:],top) }else{ gsh.iValues = gsh.iValues[1:] } } } func (gsh*GshContext)Enq(argv[]string){ } func (gsh*GshContext)Deq(argv[]string){ } func (gsh*GshContext)Push(argv[]string){ gsh.iValStack = append(gsh.iValStack,argv[1:]) fmt.Printf("depth=%d\n",len(gsh.iValStack)) } func (gsh*GshContext)Dump(argv[]string){ for i,v := range gsh.iValStack { fmt.Printf("%d %v\n",i,v) } } func (gsh*GshContext)Pop(argv[]string){ depth := len(gsh.iValStack) if 0 < depth { v := gsh.iValStack[depth-1] if isin("-cat",argv){ gsh.iValues = append(gsh.iValues,v...) }else{ gsh.iValues = v } gsh.iValStack = gsh.iValStack[0:depth-1] fmt.Printf("depth=%d %s\n",len(gsh.iValStack),gsh.iValues) }else{ fmt.Printf("depth=%d\n",depth) } } // Command Interpreter func (gshCtx*GshContext)gshellv(argv []string) (fin bool) { fin = false if gshCtx.CmdTrace { fmt.Fprintf(os.Stderr,"--I-- gshellv((%d))\n",len(argv)) } if len(argv) <= 0 { return false } xargv := []string{} for ai := 0; ai < len(argv); ai++ { xargv = append(xargv,strsubst(gshCtx,argv[ai],false)) } argv = xargv if false { for ai := 0; ai < len(argv); ai++ { fmt.Printf("[%d] %s [%d]%T\n", ai,argv[ai],len(argv[ai]),argv[ai]) } } cmd := argv[0] if gshCtx.CmdTrace { fmt.Fprintf(os.Stderr,"--I-- gshellv(%d)%v\n",len(argv),argv) } switch { // https://tour.golang.org/flowcontrol/11 case cmd == "": gshCtx.xPwd([]string{}); // emtpy command case cmd == "-x": gshCtx.CmdTrace = ! gshCtx.CmdTrace case cmd == "-xt": gshCtx.CmdTime = ! gshCtx.CmdTime case cmd == "-ot": gshCtx.sconnect(true, argv) case cmd == "-ou": gshCtx.sconnect(false, argv) case cmd == "-it": gshCtx.saccept(true , argv) case cmd == "-iu": gshCtx.saccept(false, argv) case cmd == "-i" || cmd == "<" || cmd == "-o" || cmd == ">" || cmd == "-a" || cmd == ">>" || cmd == "-s" || cmd == "><": gshCtx.redirect(argv) case cmd == "|": gshCtx.fromPipe(argv) case cmd == "args": gshCtx.Args(argv) case cmd == "bg" || cmd == "-bg": rfin := gshCtx.inBackground(argv[1:]) return rfin case cmd == "-bn": gshCtx.Basename(argv) case cmd == "call": _,_ = gshCtx.excommand(false,argv[1:]) case cmd == "cd" || cmd == "chdir": gshCtx.xChdir(argv); case cmd == "-cksum": gshCtx.xFind(argv) case cmd == "-sum": gshCtx.xFind(argv) case cmd == "-sumtest": str := "" if 1 < len(argv) { str = argv[1] } crc := strCRC32(str,uint64(len(str))) fprintf(stderr,"%v %v\n",crc,len(str)) case cmd == "close": gshCtx.xClose(argv) case cmd == "gcp": gshCtx.FileCopy(argv) case cmd == "dec" || cmd == "decode": gshCtx.Dec(argv) case cmd == "#define": case cmd == "dic" || cmd == "d": xDic(argv) case cmd == "dump": gshCtx.Dump(argv) case cmd == "echo" || cmd == "e": echo(argv,true) case cmd == "enc" || cmd == "encode": gshCtx.Enc(argv) case cmd == "env": env(argv) case cmd == "eval": xEval(argv[1:],true) case cmd == "ev" || cmd == "events": dumpEvents(argv) case cmd == "exec": _,_ = gshCtx.excommand(true,argv[1:]) // should not return here case cmd == "exit" || cmd == "quit": // write Result code EXIT to 3> return true case cmd == "fdls": // dump the attributes of fds (of other process) case cmd == "-find" || cmd == "fin" || cmd == "ufind" || cmd == "uf": gshCtx.xFind(argv[1:]) case cmd == "fu": gshCtx.xFind(argv[1:]) case cmd == "fork": // mainly for a server case cmd == "-gen": gshCtx.gen(argv) case cmd == "-go": gshCtx.xGo(argv) case cmd == "-grep": gshCtx.xFind(argv) case cmd == "gdeq": gshCtx.Deq(argv) case cmd == "genq": gshCtx.Enq(argv) case cmd == "gpop": gshCtx.Pop(argv) case cmd == "gpush": gshCtx.Push(argv) case cmd == "history" || cmd == "hi": // hi should be alias gshCtx.xHistory(argv) case cmd == "jobs": gshCtx.xJobs(argv) case cmd == "lnsp" || cmd == "nlsp": gshCtx.SplitLine(argv) case cmd == "-ls": gshCtx.xFind(argv) case cmd == "nop": // do nothing case cmd == "pipe": gshCtx.xOpen(argv) case cmd == "plug" || cmd == "plugin" || cmd == "pin": gshCtx.xPlugin(argv[1:]) case cmd == "print" || cmd == "-pr": // output internal slice // also sprintf should be gshCtx.Printv(argv) case cmd == "ps": gshCtx.xPs(argv) case cmd == "pstitle": // to be gsh.title case cmd == "rexecd" || cmd == "rexd": gshCtx.RexecServer(argv) case cmd == "rexec" || cmd == "rex": gshCtx.RexecClient(argv) case cmd == "repeat" || cmd == "rep": // repeat cond command gshCtx.repeat(argv) case cmd == "replay": gshCtx.xReplay(argv) case cmd == "scan": // scan input (or so in fscanf) to internal slice (like Files or map) gshCtx.Scanv(argv) case cmd == "set": // set name ... case cmd == "serv": gshCtx.httpServer(argv) case cmd == "shift": gshCtx.Shiftv(argv) case cmd == "sleep": gshCtx.sleep(argv) case cmd == "-sort": gshCtx.Sortv(argv) case cmd == "j" || cmd == "join": gshCtx.Rjoin(argv) case cmd == "a" || cmd == "alpa": gshCtx.Rexec(argv) case cmd == "jcd" || cmd == "jchdir": gshCtx.Rchdir(argv) case cmd == "jget": gshCtx.Rget(argv) case cmd == "jls": gshCtx.Rls(argv) case cmd == "jput": gshCtx.Rput(argv) case cmd == "jpwd": gshCtx.Rpwd(argv) case cmd == "time": fin = gshCtx.xTime(argv) case cmd == "ungets": if 1 < len(argv) { ungets(argv[1]+"\n") }else{ } case cmd == "pwd": gshCtx.xPwd(argv); case cmd == "ver" || cmd == "-ver" || cmd == "version": gshCtx.showVersion(argv) case cmd == "where": // data file or so? case cmd == "which": which("PATH",argv); case cmd == "gj" && 1 < len(argv) && argv[1] == "listen": go gj_server(argv[1:]); case cmd == "gj" && 1 < len(argv) && argv[1] == "serve": go gj_server(argv[1:]); case cmd == "gj" && 1 < len(argv) && argv[1] == "join": go gj_client(argv[1:]); case cmd == "gj": jsend(argv); case cmd == "jsend": jsend(argv); default: if gshCtx.whichPlugin(cmd,[]string{"-s"}) != nil { gshCtx.xPlugin(argv) }else{ notfound,_ := gshCtx.excommand(false,argv) if notfound { fmt.Printf("--E-- command not found (%v)\n",cmd) } } } return fin } func (gsh*GshContext)gshelll(gline string) (rfin bool) { argv := strings.Split(string(gline)," ") fin := gsh.gshellv(argv) return fin } func (gsh*GshContext)tgshelll(gline string)(xfin bool){ start := time.Now() fin := gsh.gshelll(gline) end := time.Now() elps := end.Sub(start); if gsh.CmdTime { fmt.Printf("--T-- " + time.Now().Format(time.Stamp) + "(%d.%09ds)\n", elps/1000000000,elps%1000000000) } return fin } func Ttyid() (int) { fi, err := os.Stdin.Stat() if err != nil { return 0; } //fmt.Printf("Stdin: %v Dev=%d\n", // fi.Mode(),fi.Mode()&os.ModeDevice) if (fi.Mode() & os.ModeDevice) != 0 { stat := aStat_t{}; err := aFstat(0,&stat) if err != nil { //fmt.Printf("--I-- Stdin: (%v)\n",err) }else{ //fmt.Printf("--I-- Stdin: rdev=%d %d\n", // stat.Rdev&0xFF,stat.Rdev); //fmt.Printf("--I-- Stdin: tty%d\n",stat.Rdev&0xFF); return int(stat.Rdev & 0xFF) } } return 0 } func (gshCtx *GshContext) ttyfile() string { //fmt.Printf("--I-- GSH_HOME=%s\n",gshCtx.GshHomeDir) ttyfile := gshCtx.GshHomeDir + "/" + "gsh-tty" + fmt.Sprintf("%02d",gshCtx.TerminalId) //strconv.Itoa(gshCtx.TerminalId) //fmt.Printf("--I-- ttyfile=%s\n",ttyfile) return ttyfile } func (gshCtx *GshContext) ttyline()(*os.File){ file, err := os.OpenFile(gshCtx.ttyfile(),os.O_RDWR|os.O_CREATE|os.O_TRUNC,0600) if err != nil { fmt.Printf("--F-- cannot open %s (%s)\n",gshCtx.ttyfile(),err) return file; } return file } func (gshCtx *GshContext)getline(hix int, skipping bool, prevline string) (string) { if( skipping ){ reader := bufio.NewReaderSize(os.Stdin,LINESIZE) line, _, _ := reader.ReadLine() return string(line) }else if true { return xgetline(hix,prevline,gshCtx) } /* else if( with_exgetline && gshCtx.GetLine != "" ){ //var xhix int64 = int64(hix); // cast newenv := os.Environ() newenv = append(newenv, "GSH_LINENO="+strconv.FormatInt(int64(hix),10) ) tty := gshCtx.ttyline() tty.WriteString(prevline) Pa := os.ProcAttr { "", // start dir newenv, //os.Environ(), []*os.File{os.Stdin,os.Stdout,os.Stderr,tty}, nil, } //fmt.Printf("--I-- getline=%s // %s\n",gsh_getlinev[0],gshCtx.GetLine) proc, err := os.StartProcess(gsh_getlinev[0],[]string{"getline","getline"},&Pa) if err != nil { fmt.Printf("--F-- getline process error (%v)\n",err) // for ; ; { } return "exit (getline program failed)" } //stat, err := proc.Wait() proc.Wait() buff := make([]byte,LINESIZE) count, err := tty.Read(buff) //_, err = tty.Read(buff) //fmt.Printf("--D-- getline (%d)\n",count) if err != nil { if ! (count == 0) { // && err.String() == "EOF" ) { fmt.Printf("--E-- getline error (%s)\n",err) } }else{ //fmt.Printf("--I-- getline OK \"%s\"\n",buff) } tty.Close() gline := string(buff[0:count]) return gline }else */ { // if isatty { fmt.Printf("!%d",hix) fmt.Print(PROMPT) // } reader := bufio.NewReaderSize(os.Stdin,LINESIZE) line, _, _ := reader.ReadLine() return string(line) } } //== begin ======================================================= getline /* * getline.c * 2020-0819 extracted from dog.c * getline.go * 2020-0822 ported to Go */ /* package main // getline main import ( "fmt" // fmt "strings" // strings "os" // os "syscall" // syscall //"bytes" // os //"os/exec" // os ) */ // C language compatibility functions var errno = 0 var stdin *os.File = os.Stdin var stdout *os.File = os.Stdout var stderr *os.File = os.Stderr var EOF = -1 var NULL = 0 type FILE os.File type StrBuff []byte var NULL_FP *os.File = nil var NULLSP = 0 //var LINESIZE = 1024 func system(cmdstr string)(int){ //PA := syscall.ProcAttr { PA := os.ProcAttr { "", // the starting directory os.Environ(), //[]uintptr{os.Stdin.Fd(),os.Stdout.Fd(),os.Stderr.Fd()}, []*os.File{os.Stdin,os.Stdout,os.Stderr}, nil, } argv := strings.Split(cmdstr," ") //pid,err := syscall.ForkExec(argv[0],argv,&PA) proc,err := os.StartProcess(argv[0],argv,&PA); if( err != nil ){ //fmt.Printf("--Es-- system(%v)\n(%v)\n",cmdstr,err); return -1; } pstat,_ := proc.Wait(); pid := pstat.Pid(); if( err != nil ){ fmt.Printf("--E-- pid=%v syscall(%v) err(%v)\n",pid,cmdstr,err) } //syscall.Wait4(pid,nil,0,nil) //fmt.Printf("====E== pid=%d exit=%v stat=%v\n",pid,pstat.Exited(),pstat.ExitCode()); /* argv := strings.Split(cmdstr," ") fmt.Fprintf(os.Stderr,"--I-- system(%v)\n",argv) //cmd := exec.Command(argv[0:]...) cmd := exec.Command(argv[0],argv[1],argv[2]) cmd.Stdin = strings.NewReader("output of system") var out bytes.Buffer cmd.Stdout = &out var serr bytes.Buffer cmd.Stderr = &serr err := cmd.Run() if err != nil { fmt.Fprintf(os.Stderr,"--E-- system(%v)err(%v)\n",argv,err) fmt.Printf("ERR:%s\n",serr.String()) }else{ fmt.Printf("%s",out.String()) } */ return 0 } func atoi(str string)(ret int){ ret,err := fmt.Sscanf(str,"%d",ret) if err == nil { return ret }else{ // should set errno return 0 } } func getenv(name string)(string){ val,got := os.LookupEnv(name) if got { return val }else{ return "?" } } func strcpy(dst StrBuff, src string){ var i int srcb := []byte(src) for i = 0; i < len(src) && srcb[i] != 0; i++ { dst[i] = srcb[i] } dst[i] = 0 } func xstrcpy(dst StrBuff, src StrBuff){ dst = src } func strcat(dst StrBuff, src StrBuff){ dst = append(dst,src...) } func strdup(str StrBuff)(string){ return string(str[0:strlen(str)]) } func sstrlen(str string)(int){ return len(str) } func strlen(str StrBuff)(int){ var i int for i = 0; i < len(str) && str[i] != 0; i++ { } return i } func sizeof(data StrBuff)(int){ return len(data) } func isatty(fd int)(ret int){ return 1 } func fopen(file string,mode string)(fp*os.File){ if mode == "r" { fp,err := os.Open(file) if( err != nil ){ fmt.Printf("--E-- fopen(%s,%s)=(%v)\n",file,mode,err) return NULL_FP; } return fp; }else{ fp,err := os.OpenFile(file,os.O_RDWR|os.O_CREATE|os.O_TRUNC,0600) if( err != nil ){ return NULL_FP; } return fp; } } func fclose(fp*os.File){ fp.Close() } func fflush(fp *os.File)(int){ return 0 } func fgetc(fp*os.File)(int){ var buf [1]byte _,err := fp.Read(buf[0:1]) if( err != nil ){ return EOF; }else{ return int(buf[0]) } } func sfgets(str*string, size int, fp*os.File)(int){ buf := make(StrBuff,size) var ch int var i int for i = 0; i < len(buf)-1; i++ { ch = fgetc(fp) //fprintf(stderr,"--fgets %d/%d %X\n",i,len(buf),ch) if( ch == EOF ){ break; } buf[i] = byte(ch); if( ch == '\n' ){ break; } } buf[i] = 0 //fprintf(stderr,"--fgets %d/%d (%s)\n",i,len(buf),buf[0:i]) return i } func fgets(buf StrBuff, size int, fp*os.File)(int){ var ch int var i int for i = 0; i < len(buf)-1; i++ { ch = fgetc(fp) //fprintf(stderr,"--fgets %d/%d %X\n",i,len(buf),ch) if( ch == EOF ){ break; } buf[i] = byte(ch); if( ch == '\n' ){ break; } } buf[i] = 0 //fprintf(stderr,"--fgets %d/%d (%s)\n",i,len(buf),buf[0:i]) return i } func fputc(ch int , fp*os.File)(int){ var buf [1]byte buf[0] = byte(ch) fp.Write(buf[0:1]) return 0 } func fputs(buf StrBuff, fp*os.File)(int){ fp.Write(buf) return 0 } func xfputss(str string, fp*os.File)(int){ return fputs([]byte(str),fp) } func sscanf(str StrBuff,fmts string, params ...interface{})(int){ fmt.Sscanf(string(str[0:strlen(str)]),fmts,params...) return 0 } func fprintf(fp*os.File,fmts string, params ...interface{})(int){ fmt.Fprintf(fp,fmts,params...) return 0 } // Command Line IME //----------------------------------------------------------------------- MyIME var MyIMEVER = "MyIME/0.0.2"; type RomKana struct { dic string // dictionaly ID pat string // input pattern out string // output pattern hit int64 // count of hit and used } var dicents = 0 var romkana [1024]RomKana var Romkan []RomKana func isinDic(str string)(int){ for i,v := range Romkan { if v.pat == str { return i } } return -1 } const ( DIC_COM_LOAD = "im" DIC_COM_DUMP = "s" DIC_COM_LIST = "ls" DIC_COM_ENA = "en" DIC_COM_DIS = "di" ) func helpDic(argv []string){ out := stderr cmd := "" if 0 < len(argv) { cmd = argv[0] } fprintf(out,"--- %v Usage\n",cmd) fprintf(out,"... Commands\n") fprintf(out,"... %v %-3v [dicName] [dicURL ] -- Import dictionary\n",cmd,DIC_COM_LOAD) fprintf(out,"... %v %-3v [pattern] -- Search in dictionary\n",cmd,DIC_COM_DUMP) fprintf(out,"... %v %-3v [dicName] -- List dictionaries\n",cmd,DIC_COM_LIST) fprintf(out,"... %v %-3v [dicName] -- Disable dictionaries\n",cmd,DIC_COM_DIS) fprintf(out,"... %v %-3v [dicName] -- Enable dictionaries\n",cmd,DIC_COM_ENA) fprintf(out,"... Keys ... %v\n","ESC can be used for '\\'") fprintf(out,"... \\c -- Reverse the case of the last character\n",) fprintf(out,"... \\i -- Replace input with translated text\n",) fprintf(out,"... \\j -- On/Off translation mode\n",) fprintf(out,"... \\l -- Force Lower Case\n",) fprintf(out,"... \\u -- Force Upper Case (software CapsLock)\n",) fprintf(out,"... \\v -- Show translation actions\n",) fprintf(out,"... \\x -- Replace the last input character with it Hexa-Decimal\n",) } func xDic(argv[]string){ if len(argv) <= 1 { helpDic(argv) return } argv = argv[1:] var debug = false var info = false var silent = false var dump = false var builtin = false cmd := argv[0] argv = argv[1:] opt := "" arg := "" if 0 < len(argv) { arg1 := argv[0] if arg1[0] == '-' { switch arg1 { default: fmt.Printf("--Ed-- Unknown option(%v)\n",arg1) return case "-b": builtin = true case "-d": debug = true case "-s": silent = true case "-v": info = true } opt = arg1 argv = argv[1:] } } dicName := "" dicURL := "" if 0 < len(argv) { arg = argv[0] dicName = arg argv = argv[1:] } if 0 < len(argv) { dicURL = argv[0] argv = argv[1:] } if false { fprintf(stderr,"--Dd-- com(%v) opt(%v) arg(%v)\n",cmd,opt,arg) } if cmd == DIC_COM_LOAD { //dicType := "" dicBody := "" if !builtin && dicName != "" && dicURL == "" { f,err := os.Open(dicName) if err == nil { dicURL = dicName }else{ f,err = os.Open(dicName+".html") if err == nil { dicURL = dicName+".html" }else{ f,err = os.Open("gshdic-"+dicName+".html") if err == nil { dicURL = "gshdic-"+dicName+".html" } } } if err == nil { var buf = make([]byte,128*1024) count,err := f.Read(buf) f.Close() if info { fprintf(stderr,"--Id-- ReadDic(%v,%v)\n",count,err) } dicBody = string(buf[0:count]) } } if dicBody == "" { switch arg { default: dicName = "WorldDic" dicURL = WorldDic if info { fprintf(stderr,"--Id-- default dictionary \"%v\"\n", dicName); } case "wnn": dicName = "WnnDic" dicURL = WnnDic case "sumomo": dicName = "SumomoDic" dicURL = SumomoDic case "sijimi": dicName = "SijimiDic" dicURL = SijimiDic case "jkl": dicName = "JKLJaDic" dicURL = JA_JKLDic } if debug { fprintf(stderr,"--Id-- %v URL=%v\n\n",dicName,dicURL); } dicv := strings.Split(dicURL,",") if debug { fprintf(stderr,"--Id-- %v encoded data...\n",dicName) fprintf(stderr,"Type: %v\n",dicv[0]) fprintf(stderr,"Body: %v\n",dicv[1]) fprintf(stderr,"\n") } body,_ := base64.StdEncoding.DecodeString(dicv[1]) dicBody = string(body) } if info { fmt.Printf("--Id-- %v %v\n",dicName,dicURL) fmt.Printf("%s\n",dicBody) } if debug { fprintf(stderr,"--Id-- dicName %v text...\n",dicName) fprintf(stderr,"%v\n",string(dicBody)) } entv := strings.Split(dicBody,"\n"); if info { fprintf(stderr,"--Id-- %v scan...\n",dicName); } var added int = 0 var dup int = 0 for i,v := range entv { var pat string var out string fmt.Sscanf(v,"%s %s",&pat,&out) if len(pat) <= 0 { }else{ if 0 <= isinDic(pat) { dup += 1 continue } romkana[dicents] = RomKana{dicName,pat,out,0} dicents += 1 added += 1 Romkan = append(Romkan,RomKana{dicName,pat,out,0}) if debug { fmt.Printf("[%3v]:[%2v]%-8v [%2v]%v\n", i,len(pat),pat,len(out),out) } } } if !silent { url := dicURL if strBegins(url,"data:") { url = "builtin" } fprintf(stderr,"--Id-- %v scan... %v added, %v dup. / %v total (%v)\n", dicName,added,dup,len(Romkan),url); } // should sort by pattern length for conclete match, for performance if debug { arg = "" // search pattern dump = true } } if cmd == DIC_COM_DUMP || dump { fprintf(stderr,"--Id-- %v dump... %v entries:\n",dicName,len(Romkan)); var match = 0 for i := 0; i < len(Romkan); i++ { dic := Romkan[i].dic pat := Romkan[i].pat out := Romkan[i].out if arg == "" || 0 <= strings.Index(pat,arg)||0 <= strings.Index(out,arg) { fmt.Printf("\\\\%v\t%v [%2v]%-8v [%2v]%v\n", i,dic,len(pat),pat,len(out),out) match += 1 } } fprintf(stderr,"--Id-- %v matched %v / %v entries:\n",arg,match,len(Romkan)); } } func loadDefaultDic(dic int){ if( 0 < len(Romkan) ){ return } //fprintf(stderr,"\r\n") xDic([]string{"dic",DIC_COM_LOAD}); var info = false if info { fprintf(stderr,"--Id-- Conguraturations!! WorldDic is now activated.\r\n") fprintf(stderr,"--Id-- enter \"dic\" command for help.\r\n") } } func readDic()(int){ /* var rk *os.File; var dic = "MyIME-dic.txt"; //rk = fopen("romkana.txt","r"); //rk = fopen("JK-JA-morse-dic.txt","r"); rk = fopen(dic,"r"); if( rk == NULL_FP ){ if( true ){ fprintf(stderr,"--%s-- Could not load %s\n",MyIMEVER,dic); } return -1; } if( true ){ var di int; var line = make(StrBuff,1024); var pat string var out string for di = 0; di < 1024; di++ { if( fgets(line,sizeof(line),rk) == NULLSP ){ break; } fmt.Sscanf(string(line[0:strlen(line)]),"%s %s",&pat,&out); //sscanf(line,"%s %[^\r\n]",&pat,&out); romkana[di].pat = pat; romkana[di].out = out; //fprintf(stderr,"--Dd- %-10s %s\n",pat,out) } dicents += di if( false ){ fprintf(stderr,"--%s-- loaded romkana.txt [%d]\n",MyIMEVER,di); for di = 0; di < dicents; di++ { fprintf(stderr, "%s %s\n",romkana[di].pat,romkana[di].out); } } } fclose(rk); //romkana[dicents].pat = "//ddump" //romkana[dicents].pat = "//ddump" // dump the dic. and clean the command input */ return 0; } func matchlen(stri string, pati string)(int){ if strBegins(stri,pati) { return len(pati) }else{ return 0 } } func convs(src string)(string){ var si int; var sx = len(src); var di int; var mi int; var dstb []byte for si = 0; si < sx; { // search max. match from the position if strBegins(src[si:],"%x/") { // %x/integer/ // s/a/b/ ix := strings.Index(src[si+3:],"/") if 0 < ix { var iv int = 0 //fmt.Sscanf(src[si+3:si+3+ix],"%d",&iv) fmt.Sscanf(src[si+3:si+3+ix],"%v",&iv) sval := fmt.Sprintf("%x",iv) bval := []byte(sval) dstb = append(dstb,bval...) si = si+3+ix+1 continue } } if strBegins(src[si:],"%d/") { // %d/integer/ // s/a/b/ ix := strings.Index(src[si+3:],"/") if 0 < ix { var iv int = 0 fmt.Sscanf(src[si+3:si+3+ix],"%v",&iv) sval := fmt.Sprintf("%d",iv) bval := []byte(sval) dstb = append(dstb,bval...) si = si+3+ix+1 continue } } if strBegins(src[si:],"%t") { now := time.Now() if true { date := now.Format(time.Stamp) dstb = append(dstb,[]byte(date)...) si = si+3 } continue } var maxlen int = 0; var len int; mi = -1; for di = 0; di < dicents; di++ { len = matchlen(src[si:],romkana[di].pat); if( maxlen < len ){ maxlen = len; mi = di; } } if( 0 < maxlen ){ out := romkana[mi].out; dstb = append(dstb,[]byte(out)...); si += maxlen; }else{ dstb = append(dstb,src[si]) si += 1; } } return string(dstb) } func trans(src string)(int){ dst := convs(src); xfputss(dst,stderr); return 0; } //------------------------------------------------------------- LINEEDIT // "?" at the top of the line means searching history // should be compatilbe with Telnet const ( EV_MODE = 255 EV_IDLE = 254 EV_TIMEOUT = 253 GO_UP = 252 // k GO_DOWN = 251 // j GO_RIGHT = 250 // l GO_LEFT = 249 // h DEL_RIGHT = 248 // x GO_TOPL = 'A'-0x40 // 0 GO_ENDL = 'E'-0x40 // $ GO_TOPW = 239 // b GO_ENDW = 238 // e GO_NEXTW = 237 // w GO_FORWCH = 229 // f GO_PAIRCH = 228 // % GO_DEL = 219 // d HI_SRCH_FW = 209 // / HI_SRCH_BK = 208 // ? HI_SRCH_RFW = 207 // n HI_SRCH_RBK = 206 // N ) // should return number of octets ready to be read immediately //fprintf(stderr,"\n--Select(%v %v)\n",err,r.Bits[0]) var EventRecvFd = -1 // file descriptor var EventSendFd = -1 const EventFdOffset = 1000000 const NormalFdOffset = 100 /* 2020-1021 replaced poll() with channel/select func putKeyinEvent(event int, evarg int){ if true { if EventRecvFd < 0 { var pv = []int{-1,-1} // syscall.Pipe(pv) EventRecvFd = pv[0] EventSendFd = pv[1] //fmt.Printf("--De-- EventPipe created[%v,%v]\n",EventRecvFd,EventSendFd) } }else{ if EventRecvFd < 0 { // the document differs from this spec // https://golang.org/src/syscall/syscall_unix.go?s=8096:8158#L340 sv,err := syscall.Socketpair(syscall.AF_UNIX,syscall.SOCK_STREAM,0) EventRecvFd = sv[0] EventSendFd = sv[1] if err != nil { fmt.Printf("--De-- EventSock created[%v,%v](%v)\n", EventRecvFd,EventSendFd,err) } } } var buf = []byte{ byte(event)} n,err := syscall.Write(EventSendFd,buf) if err != nil { fmt.Printf("--De-- putEvent[%v](%3v)(%v %v)\n",EventSendFd,event,n,err) } } */ func ungets(str string){ for _,ch := range str { putKeyinEvent(int(ch),0) } } func (gsh*GshContext)xReplay(argv[]string){ hix := 0 tempo := 1.0 xtempo := 1.0 repeat := 1 for _,a := range argv { // tempo if strBegins(a,"x") { fmt.Sscanf(a[1:],"%f",&xtempo) tempo = 1 / xtempo //fprintf(stderr,"--Dr-- tempo=[%v]%v\n",a[2:],tempo); }else if strBegins(a,"r") { // repeat fmt.Sscanf(a[1:],"%v",&repeat) }else if strBegins(a,"!") { fmt.Sscanf(a[1:],"%d",&hix) }else{ fmt.Sscanf(a,"%d",&hix) } } if hix == 0 || len(argv) <= 1 { hix = len(gsh.CommandHistory)-1 } fmt.Printf("--Ir-- Replay(!%v x%v r%v)\n",hix,xtempo,repeat) //dumpEvents(hix) //gsh.xScanReplay(hix,false,repeat,tempo,argv) go gsh.xScanReplay(hix,true,repeat,tempo,argv) runtime.Gosched(); // wait xScanReplay is launched //fmt.Printf("--Ir-- Replay set\n"); } // syscall.Select // 2020-0827 GShell-0.2.3 /* func FpollIn1(fp *os.File,usec int)(uintptr){ nfd := 1 rdv := syscall.FdSet {} fd1 := fp.Fd() bank1 := fd1/32 mask1 := int32(1 << fd1) rdv.Bits[bank1] = mask1 fd2 := -1 bank2 := -1 var mask2 int32 = 0 if 0 <= EventRecvFd { fd2 = EventRecvFd nfd = fd2 + 1 bank2 = fd2/32 mask2 = int32(1 << fd2) rdv.Bits[bank2] |= mask2 //fmt.Printf("--De-- EventPoll mask added [%d][%v][%v]\n",fd2,bank2,mask2) } tout := syscall.NsecToTimeval(int64(usec*1000)) //n,err := syscall.Select(nfd,&rdv,nil,nil,&tout) // spec. mismatch err := syscall.Select(nfd,&rdv,nil,nil,&tout) if err != nil { //fmt.Printf("--De-- select() err(%v)\n",err) } if err == nil { if 0 <= fd2 && (rdv.Bits[bank2] & mask2) != 0 { if false { fmt.Printf("--De-- got Event\n") } return uintptr(EventFdOffset + fd2) }else if (rdv.Bits[bank1] & mask1) != 0 { return uintptr(NormalFdOffset + fd1) }else{ return 1 } }else{ return 0 } } */ /* func fgetcTimeout1(fp *os.File,usec int)(int){ READ1: //readyFd := FpollIn1(fp,usec) readyFd := CFpollIn1(fp,usec) if readyFd < 100 { return EV_TIMEOUT } var buf [1]byte if EventFdOffset <= readyFd { fd := int(readyFd-EventFdOffset) _,err := syscall.Read(fd,buf[0:1]) if( err != nil ){ return EOF; }else{ if buf[0] == EV_MODE { recvKeyEvent(fd) goto READ1 } return int(buf[0]) } } _,err := fp.Read(buf[0:1]) if( err != nil ){ return EOF; }else{ return int(buf[0]) } } */ func visibleChar(ch int)(string){ switch { case '!' <= ch && ch <= '~': return string(ch) } switch ch { case ' ': return "\\s" case '\n': return "\\n" case '\r': return "\\r" case '\t': return "\\t" } switch ch { case 0x00: return "NUL" case 0x07: return "BEL" case 0x08: return "BS" case 0x0E: return "SO" case 0x0F: return "SI" case 0x1B: return "ESC" case 0x7F: return "DEL" } switch ch { case EV_IDLE: return fmt.Sprintf("IDLE") case EV_MODE: return fmt.Sprintf("MODE") } return fmt.Sprintf("%X",ch) } /* func recvKeyEvent(fd int){ var buf = make([]byte,1) _,_ = syscall.Read(fd,buf[0:1]) if( buf[0] != 0 ){ romkanmode = true }else{ romkanmode = false } } */ func (gsh*GshContext)xScanReplay(hix int,replay bool,repeat int,tempo float64,argv[]string){ var Start time.Time var events = []Event{} for _,e := range Events { if hix == 0 || e.CmdIndex == hix { events = append(events,e) } } elen := len(events) if 0 < elen { if events[elen-1].event == EV_IDLE { events = events[0:elen-1] } } for r := 0; r < repeat; r++ { for i,e := range events { nano := e.when.Nanosecond() micro := nano / 1000 if Start.Second() == 0 { Start = time.Now() } diff := time.Now().Sub(Start) if replay { if e.event != EV_IDLE { //fmt.Printf("--replay %v / %v event=%X\n",i,len(events),e.event); putKeyinEvent(e.event,0) if e.event == EV_MODE { // event with arg putKeyinEvent(int(e.evarg),0) } }else{ //fmt.Printf("--replay %v / %v idle=%X\n",i,len(events),e.event); } }else{ fmt.Printf("%7.3fms #%-3v !%-3v [%v.%06d] %3v %02X %-4v %10.3fms\n", float64(diff)/1000000.0, i, e.CmdIndex, e.when.Format(time.Stamp),micro, e.event,e.event,visibleChar(e.event), float64(e.evarg)/1000000.0) } if e.event == EV_IDLE { //fmt.Printf("--replay %v / %v delay\n",i,len(events)); d := time.Duration(float64(time.Duration(e.evarg)) * tempo) //nsleep(time.Duration(e.evarg)) nsleep(d) } } } } func dumpEvents(arg[]string){ hix := 0 if 1 < len(arg) { fmt.Sscanf(arg[1],"%d",&hix) } for i,e := range Events { nano := e.when.Nanosecond() micro := nano / 1000 //if e.event != EV_TIMEOUT { if hix == 0 || e.CmdIndex == hix { fmt.Printf("#%-3v !%-3v [%v.%06d] %3v %02X %-4v %10.3fms\n",i, e.CmdIndex, e.when.Format(time.Stamp),micro, e.event,e.event,visibleChar(e.event),float64(e.evarg)/1000000.0) } //} } } /* func fgetcTimeout(fp *os.File,usec int)(int){ ch := fgetcTimeout1(fp,usec) if ch != EV_TIMEOUT { now := time.Now() if 0 < len(Events) { last := Events[len(Events)-1] dura := int64(now.Sub(last.when)) Events = append(Events,Event{last.when,EV_IDLE,dura,last.CmdIndex}) } Events = append(Events,Event{time.Now(),ch,0,CmdIndex}) } return ch } */ // 2020-1021 replaced poll() with channel/select var Kbd = make(chan int); var Kbinit = false; var evQ = make(chan int); /* func keyInput(kbd chan int, fp *os.File){ for { ch := C.getc(C.stdin); if( ch == C.EOF ){ break; } kbd <- int(ch); } } */ // https://godoc.org/golang.org/x/crypto/ssh/terminal // https://stackoverflow.com/questions/14094190/function-similar-to-getchar func keyInput(kbd chan int, tty *os.File){ tmode := C.setTermRaw(); defer func(){ C.setTermMode(tmode); }(); if( !OnWindows ){ system("/bin/stty -echo -icanon"); defer func(){ system("/bin/stty echo sane"); }(); } for { var rbuf []byte = make([]byte,1); if( OnWindows ){ C.setTermRaw(); } _,rerr := tty.Read(rbuf); if( rerr != nil ){ break; } //fmt.Printf("++KBD[%X]\n",rbuf[0]); kbd <- int(rbuf[0]); } if( !OnWindows ){ system("/bin/stty echo sane"); } } func fgetcTimeout(fp *os.File,usec int)(int){ if( !Kbinit ){ Kbinit = true; go keyInput(Kbd,fp); } for { select { case <- time.After(time.Duration(usec*1000)): //fmt.Printf("--Timeout %v us\n",usec); return EV_TIMEOUT; case ch := <- Kbd: //fmt.Printf("--KBD[%X]\n",ch); // record a Keyin(ch) Event { now := time.Now() if 0 < len(Events) { last := Events[len(Events)-1] dura := int64(now.Sub(last.when)) Events = append(Events,Event{last.when,EV_IDLE,dura,last.CmdIndex}) } Events = append(Events,Event{time.Now(),ch,0,CmdIndex}) } return ch; case ch := <- evQ: if( ch == EV_MODE ){ recvKeyEvent() }else{ return ch; } } } } func putKeyinEvent(event int, evarg int){ evQ <- event; } func recvKeyEvent(){ ch := <- evQ; if( ch != 0 ){ romkanmode = true }else{ romkanmode = false } } var AtConsoleLineTop = true var TtyMaxCol = 72 // to be obtained by ioctl? var EscTimeout = (100*1000) var ( MODE_VicMode bool // vi compatible command mode MODE_ShowMode bool romkanmode bool // shown translation mode, the mode to be retained MODE_Recursive bool // recursive translation MODE_CapsLock bool // software CapsLock MODE_LowerLock bool // force lower-case character lock MODE_ViInsert int // visible insert mode, should be like "I" icon in X Window MODE_ViTrace bool // output newline before translation ) type IInput struct { lno int lastlno int pch []int // input queue prompt string line string right string inJmode bool pinJmode bool waitingMeta string // waiting meta character LastCmd string } func (iin*IInput)Getc(timeoutUs int)(int){ ch1 := EOF ch2 := EOF ch3 := EOF if( 0 < len(iin.pch) ){ // deQ ch1 = iin.pch[0] iin.pch = iin.pch[1:] }else{ ch1 = fgetcTimeout(stdin,timeoutUs); } if( ch1 == 033 ){ /// escape sequence ch2 = fgetcTimeout(stdin,EscTimeout); if( ch2 == EV_TIMEOUT ){ }else{ ch3 = fgetcTimeout(stdin,EscTimeout); if( ch3 == EV_TIMEOUT ){ iin.pch = append(iin.pch,ch2) // enQ }else{ switch( ch2 ){ default: iin.pch = append(iin.pch,ch2) // enQ iin.pch = append(iin.pch,ch3) // enQ case '[': switch( ch3 ){ case 'A': ch1 = GO_UP; // ^ case 'B': ch1 = GO_DOWN; // v case 'C': ch1 = GO_RIGHT; // > case 'D': ch1 = GO_LEFT; // < case '3': ch4 := fgetcTimeout(stdin,EscTimeout); if( ch4 == '~' ){ //fprintf(stderr,"x[%02X %02X %02X %02X]\n",ch1,ch2,ch3,ch4); ch1 = DEL_RIGHT } } case '\\': //ch4 := fgetcTimeout(stdin,EscTimeout); //fprintf(stderr,"y[%02X %02X %02X %02X]\n",ch1,ch2,ch3,ch4); switch( ch3 ){ case '~': ch1 = DEL_RIGHT } } } } } return ch1 } func (inn*IInput)clearline(){ var i int fprintf(stderr,"\r"); // should be ANSI ESC sequence for i = 0; i < TtyMaxCol; i++ { // to the max. position in this input action fputc(' ',os.Stderr); } fprintf(stderr,"\r"); } func (iin*IInput)Redraw(){ redraw(iin,iin.lno,iin.line,iin.right) } func redraw(iin *IInput,lno int,line string,right string){ inMeta := false showMode := "" showMeta := "" // visible Meta mode on the cursor position showLino := fmt.Sprintf("!%d! ",lno) InsertMark := "" // in visible insert mode if MODE_VicMode { }else if 0 < len(iin.right) { InsertMark = " " } if( 0 < len(iin.waitingMeta) ){ inMeta = true if iin.waitingMeta[0] != 033 { showMeta = iin.waitingMeta } } if( romkanmode ){ //romkanmark = " *"; }else{ //romkanmark = ""; } if MODE_ShowMode { romkan := "--" inmeta := "-" inveri := "" if MODE_CapsLock { inmeta = "A" } if MODE_LowerLock { inmeta = "a" } if MODE_ViTrace { inveri = "v" } if MODE_VicMode { inveri = ":" } if romkanmode { romkan = "\343\201\202" if MODE_CapsLock { inmeta = "R" }else{ inmeta = "r" } } if inMeta { inmeta = "\\" } showMode = "["+romkan+inmeta+inveri+"]"; } Pre := "\r" + showMode + showLino Output := "" Left := "" Right := "" if romkanmode { Left = convs(line) Right = InsertMark+convs(right) }else{ Left = line Right = InsertMark+right } Output = Pre+Left if MODE_ViTrace { Output += iin.LastCmd } Output += showMeta+Right for len(Output) < TtyMaxCol { // to the max. position that may be dirty Output += " " // should be ANSI ESC sequence // not necessary just after newline } Output += Pre+Left+showMeta // to set the cursor to the current input position fprintf(stderr,"%s",Output) if MODE_ViTrace { if 0 < len(iin.LastCmd) { iin.LastCmd = "" fprintf(stderr,"\r\n") } } AtConsoleLineTop = false //fmt.Printf("(Redraw(%v)(%v))\n",len(line),len(right)); } // utf8 func delHeadChar(str string)(rline string,head string){ _,clen := utf8.DecodeRune([]byte(str)) head = string(str[0:clen]) return str[clen:],head } func delTailChar(str string)(rline string, last string){ var i = 0 var clen = 0 for { _,siz := utf8.DecodeRune([]byte(str)[i:]) if siz <= 0 { break } clen = siz i += siz } last = str[len(str)-clen:] return str[0:len(str)-clen],last } // 3> for output and history // 4> for keylog? // Command Line Editor func xgetline(lno int, prevline string, gsh*GshContext)(string){ var iin IInput iin.lastlno = lno iin.lno = lno CmdIndex = len(gsh.CommandHistory) if( isatty(0) == 0 ){ if( sfgets(&iin.line,LINESIZE,stdin) == NULL ){ iin.line = "exit\n"; }else{ } return iin.line } if( true ){ //var pts string; //pts = ptsname(0); //pts = ttyname(0); //fprintf(stderr,"--pts[0] = %s\n",pts?pts:"?"); } if( false ){ fprintf(stderr,"! "); fflush(stderr); sfgets(&iin.line,LINESIZE,stdin); return iin.line } if( !OnWindows ){ system("/bin/stty -echo -icanon"); } xline := iin.xgetline1(prevline,gsh) if( !OnWindows ){system("/bin/stty echo sane"); } return xline } func (iin*IInput)Translate(cmdch int){ romkanmode = !romkanmode; if MODE_ViTrace { fprintf(stderr,"%v\r\n",string(cmdch)); }else if( cmdch == 'J' ){ fprintf(stderr,"J\r\n"); iin.inJmode = true } iin.Redraw(); loadDefaultDic(cmdch); iin.Redraw(); } func (iin*IInput)Replace(cmdch int){ iin.LastCmd = fmt.Sprintf("\\%v",string(cmdch)) iin.Redraw(); loadDefaultDic(cmdch); dst := convs(iin.line+iin.right); iin.line = dst iin.right = "" if( cmdch == 'I' ){ fprintf(stderr,"I\r\n"); iin.inJmode = true } iin.Redraw(); } // aa 12 a1a1 func isAlpha(ch rune)(bool){ if 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' { return true } return false } func isAlnum(ch rune)(bool){ if 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' { return true } if '0' <= ch && ch <= '9' { return true } return false } // 0.2.8 2020-0901 created // DecodeRuneInString func (iin*IInput)GotoTOPW(){ str := iin.line i := len(str) if i <= 0 { return } //i0 := i i -= 1 lastSize := 0 var lastRune rune var found = -1 for 0 < i { // skip preamble spaces lastRune,lastSize = utf8.DecodeRuneInString(str[i:]) if !isAlnum(lastRune) { // character, type, or string to be searched i -= lastSize continue } break } for 0 < i { lastRune,lastSize = utf8.DecodeRuneInString(str[i:]) if lastSize <= 0 { continue } // not the character top if !isAlnum(lastRune) { // character, type, or string to be searched found = i break } i -= lastSize } if found < 0 && i == 0 { found = 0 } if 0 <= found { if isAlnum(lastRune) { // or non-kana character }else{ // when positioning to the top o the word i += lastSize } iin.right = str[i:] + iin.right if 0 < i { iin.line = str[0:i] }else{ iin.line = "" } } //fmt.Printf("\n(%d,%d,%d)[%s][%s]\n",i0,i,found,iin.line,iin.right) //fmt.Printf("") // set debug messae at the end of line } // 0.2.8 2020-0901 created func (iin*IInput)GotoENDW(){ str := iin.right if len(str) <= 0 { return } lastSize := 0 var lastRune rune var lastW = 0 i := 0 inWord := false lastRune,lastSize = utf8.DecodeRuneInString(str[0:]) if isAlnum(lastRune) { r,z := utf8.DecodeRuneInString(str[lastSize:]) if 0 < z && isAlnum(r) { inWord = true } } for i < len(str) { lastRune,lastSize = utf8.DecodeRuneInString(str[i:]) if lastSize <= 0 { break } // broken data? if !isAlnum(lastRune) { // character, type, or string to be searched break } lastW = i // the last alnum if in alnum word i += lastSize } if inWord { goto DISP } for i < len(str) { lastRune,lastSize = utf8.DecodeRuneInString(str[i:]) if lastSize <= 0 { break } // broken data? if isAlnum(lastRune) { // character, type, or string to be searched break } i += lastSize } for i < len(str) { lastRune,lastSize = utf8.DecodeRuneInString(str[i:]) if lastSize <= 0 { break } // broken data? if !isAlnum(lastRune) { // character, type, or string to be searched break } lastW = i i += lastSize } DISP: if 0 < lastW { iin.line = iin.line + str[0:lastW] iin.right = str[lastW:] } //fmt.Printf("\n(%d)[%s][%s]\n",i,iin.line,iin.right) //fmt.Printf("") // set debug messae at the end of line } // 0.2.8 2020-0901 created func (iin*IInput)GotoNEXTW(){ str := iin.right if len(str) <= 0 { return } lastSize := 0 var lastRune rune var found = -1 i := 1 for i < len(str) { lastRune,lastSize = utf8.DecodeRuneInString(str[i:]) if lastSize <= 0 { break } // broken data? if !isAlnum(lastRune) { // character, type, or string to be searched found = i break } i += lastSize } if 0 < found { if isAlnum(lastRune) { // or non-kana character }else{ // when positioning to the top o the word found += lastSize } iin.line = iin.line + str[0:found] if 0 < found { iin.right = str[found:] }else{ iin.right = "" } } //fmt.Printf("\n(%d)[%s][%s]\n",i,iin.line,iin.right) //fmt.Printf("") // set debug messae at the end of line } // 0.2.8 2020-0902 created func (iin*IInput)GotoPAIRCH(){ str := iin.right if len(str) <= 0 { return } lastRune,lastSize := utf8.DecodeRuneInString(str[0:]) if lastSize <= 0 { return } forw := false back := false pair := "" switch string(lastRune){ case "{": pair = "}"; forw = true case "}": pair = "{"; back = true case "(": pair = ")"; forw = true case ")": pair = "("; back = true case "[": pair = "]"; forw = true case "]": pair = "["; back = true case "<": pair = ">"; forw = true case ">": pair = "<"; back = true case "\"": pair = "\""; // context depednet, can be f" or back-double quote case "'": pair = "'"; // context depednet, can be f' or back-quote // case Japanese Kakkos } if forw { iin.SearchForward(pair) } if back { iin.SearchBackward(pair) } } // 0.2.8 2020-0902 created func (iin*IInput)SearchForward(pat string)(bool){ right := iin.right found := -1 i := 0 if strBegins(right,pat) { _,z := utf8.DecodeRuneInString(right[i:]) if 0 < z { i += z } } for i < len(right) { if strBegins(right[i:],pat) { found = i break } _,z := utf8.DecodeRuneInString(right[i:]) if z <= 0 { break } i += z } if 0 <= found { iin.line = iin.line + right[0:found] iin.right = iin.right[found:] return true }else{ return false } } // 0.2.8 2020-0902 created func (iin*IInput)SearchBackward(pat string)(bool){ line := iin.line found := -1 i := len(line)-1 for i = i; 0 <= i; i-- { _,z := utf8.DecodeRuneInString(line[i:]) if z <= 0 { continue } //fprintf(stderr,"-- %v %v\n",pat,line[i:]) if strBegins(line[i:],pat) { found = i break } } //fprintf(stderr,"--%d\n",found) if 0 <= found { iin.right = line[found:] + iin.right iin.line = line[0:found] return true }else{ return false } } // 0.2.8 2020-0902 created // search from top, end, or current position func (gsh*GshContext)SearchHistory(pat string, forw bool)(bool,string){ if forw { for _,v := range gsh.CommandHistory { if 0 <= strings.Index(v.CmdLine,pat) { //fprintf(stderr,"\n--De-- found !%v [%v]%v\n",i,pat,v.CmdLine) return true,v.CmdLine } } }else{ hlen := len(gsh.CommandHistory) for i := hlen-1; 0 < i ; i-- { v := gsh.CommandHistory[i] if 0 <= strings.Index(v.CmdLine,pat) { //fprintf(stderr,"\n--De-- found !%v [%v]%v\n",i,pat,v.CmdLine) return true,v.CmdLine } } } //fprintf(stderr,"\n--De-- not-found(%v)\n",pat) return false,"(Not Found in History)" } // 0.2.8 2020-0902 created func (iin*IInput)GotoFORWSTR(pat string,gsh*GshContext){ found := false if 0 < len(iin.right) { found = iin.SearchForward(pat) } if !found { found,line := gsh.SearchHistory(pat,true) if found { iin.line = line iin.right = "" } } } func (iin*IInput)GotoBACKSTR(pat string, gsh*GshContext){ found := false if 0 < len(iin.line) { found = iin.SearchBackward(pat) } if !found { found,line := gsh.SearchHistory(pat,false) if found { iin.line = line iin.right = "" } } } func (iin*IInput)getstring1(prompt string)(string){ // should be editable iin.clearline(); fprintf(stderr,"\r%v",prompt) str := "" for { ch := iin.Getc(10*1000*1000) if ch == '\n' || ch == '\r' { break } sch := string(ch) str += sch fprintf(stderr,"%s",sch) } return str } // search pattern must be an array and selectable with ^N/^P var SearchPat = "" var SearchForw = true func (iin*IInput)xgetline1(prevline string, gsh*GshContext)(string){ var ch int; MODE_ShowMode = false MODE_VicMode = false iin.Redraw(); first := true for cix := 0; ; cix++ { iin.pinJmode = iin.inJmode iin.inJmode = false ch = iin.Getc(1000*1000) if ch != EV_TIMEOUT && first { first = false mode := 0 if romkanmode { mode = 1 } now := time.Now() Events = append(Events,Event{now,EV_MODE,int64(mode),CmdIndex}) } if ch == 033 { MODE_ShowMode = true MODE_VicMode = !MODE_VicMode iin.Redraw(); continue } if MODE_VicMode { switch ch { case '0': ch = GO_TOPL case '$': ch = GO_ENDL case 'b': ch = GO_TOPW case 'e': ch = GO_ENDW case 'w': ch = GO_NEXTW case '%': ch = GO_PAIRCH case 'j': ch = GO_DOWN case 'k': ch = GO_UP case 'h': ch = GO_LEFT case 'l': ch = GO_RIGHT case 'x': ch = DEL_RIGHT case 'a': MODE_VicMode = !MODE_VicMode ch = GO_RIGHT case 'i': MODE_VicMode = !MODE_VicMode iin.Redraw(); continue case '~': right,head := delHeadChar(iin.right) if len([]byte(head)) == 1 { ch = int(head[0]) if( 'a' <= ch && ch <= 'z' ){ ch = ch + 'A'-'a' }else if( 'A' <= ch && ch <= 'Z' ){ ch = ch + 'a'-'A' } iin.right = string(ch) + right } iin.Redraw(); continue case 'f': // GO_FORWCH iin.Redraw(); ch = iin.Getc(3*1000*1000) if ch == EV_TIMEOUT { iin.Redraw(); continue } SearchPat = string(ch) SearchForw = true iin.GotoFORWSTR(SearchPat,gsh) iin.Redraw(); continue case '/': SearchPat = iin.getstring1("/") // should be editable SearchForw = true iin.GotoFORWSTR(SearchPat,gsh) iin.Redraw(); continue case '?': SearchPat = iin.getstring1("?") // should be editable SearchForw = false iin.GotoBACKSTR(SearchPat,gsh) iin.Redraw(); continue case 'n': if SearchForw { iin.GotoFORWSTR(SearchPat,gsh) }else{ iin.GotoBACKSTR(SearchPat,gsh) } iin.Redraw(); continue case 'N': if !SearchForw { iin.GotoFORWSTR(SearchPat,gsh) }else{ iin.GotoBACKSTR(SearchPat,gsh) } iin.Redraw(); continue } } switch ch { case GO_TOPW: iin.GotoTOPW() iin.Redraw(); continue case GO_ENDW: iin.GotoENDW() iin.Redraw(); continue case GO_NEXTW: // to next space then iin.GotoNEXTW() iin.Redraw(); continue case GO_PAIRCH: iin.GotoPAIRCH() iin.Redraw(); continue } //fprintf(stderr,"A[%02X]\n",ch); if( ch == '\\' || ch == 033 ){ MODE_ShowMode = true metach := ch iin.waitingMeta = string(ch) iin.Redraw(); // set cursor //fprintf(stderr,"???\b\b\b") ch = fgetcTimeout(stdin,2000*1000) // reset cursor iin.waitingMeta = "" cmdch := ch if( ch == EV_TIMEOUT ){ if metach == 033 { continue } ch = metach }else /* if( ch == 'm' || ch == 'M' ){ mch := fgetcTimeout(stdin,1000*1000) if mch == 'r' { romkanmode = true }else{ romkanmode = false } continue }else */ if( ch == 'k' || ch == 'K' ){ MODE_Recursive = !MODE_Recursive iin.Translate(cmdch); continue }else if( ch == 'j' || ch == 'J' ){ iin.Translate(cmdch); continue }else if( ch == 'i' || ch == 'I' ){ iin.Replace(cmdch); continue }else if( ch == 'l' || ch == 'L' ){ MODE_LowerLock = !MODE_LowerLock MODE_CapsLock = false if MODE_ViTrace { fprintf(stderr,"%v\r\n",string(cmdch)); } iin.Redraw(); continue }else if( ch == 'u' || ch == 'U' ){ MODE_CapsLock = !MODE_CapsLock MODE_LowerLock = false if MODE_ViTrace { fprintf(stderr,"%v\r\n",string(cmdch)); } iin.Redraw(); continue }else if( ch == 'v' || ch == 'V' ){ MODE_ViTrace = !MODE_ViTrace if MODE_ViTrace { fprintf(stderr,"%v\r\n",string(cmdch)); } iin.Redraw(); continue }else if( ch == 'c' || ch == 'C' ){ if 0 < len(iin.line) { xline,tail := delTailChar(iin.line) if len([]byte(tail)) == 1 { ch = int(tail[0]) if( 'a' <= ch && ch <= 'z' ){ ch = ch + 'A'-'a' }else if( 'A' <= ch && ch <= 'Z' ){ ch = ch + 'a'-'A' } iin.line = xline + string(ch) } } if MODE_ViTrace { fprintf(stderr,"%v\r\n",string(cmdch)); } iin.Redraw(); continue }else{ iin.pch = append(iin.pch,ch) // push ch = '\\' } } switch( ch ){ case 'P'-0x40: ch = GO_UP case 'N'-0x40: ch = GO_DOWN case 'B'-0x40: ch = GO_LEFT case 'F'-0x40: ch = GO_RIGHT } //fprintf(stderr,"B[%02X]\n",ch); switch( ch ){ case 0: continue; case '\t': iin.Replace('j'); continue case 'X'-0x40: iin.Replace('j'); continue case EV_TIMEOUT: iin.Redraw(); if iin.pinJmode { fprintf(stderr,"\\J\r\n") iin.inJmode = true } continue case GO_UP: if iin.lno == 1 { continue } cmd,ok := gsh.cmdStringInHistory(iin.lno-1) if ok { iin.line = cmd iin.right = "" iin.lno = iin.lno - 1 } iin.Redraw(); continue case GO_DOWN: cmd,ok := gsh.cmdStringInHistory(iin.lno+1) if ok { iin.line = cmd iin.right = "" iin.lno = iin.lno + 1 }else{ iin.line = "" iin.right = "" if iin.lno == iin.lastlno-1 { iin.lno = iin.lno + 1 } } iin.Redraw(); continue case GO_LEFT: if 0 < len(iin.line) { xline,tail := delTailChar(iin.line) iin.line = xline iin.right = tail + iin.right } iin.Redraw(); continue; case GO_RIGHT: if( 0 < len(iin.right) && iin.right[0] != 0 ){ xright,head := delHeadChar(iin.right) iin.right = xright iin.line += head } iin.Redraw(); continue; case EOF: goto EXIT; case 'R'-0x40: // replace dst := convs(iin.line+iin.right); iin.line = dst iin.right = "" iin.Redraw(); continue; case 'T'-0x40: // just show the result readDic(); romkanmode = !romkanmode; iin.Redraw(); continue; case 'L'-0x40: iin.Redraw(); continue case 'K'-0x40: iin.right = "" iin.Redraw(); continue case 'E'-0x40: iin.line += iin.right iin.right = "" iin.Redraw(); continue case 'A'-0x40: iin.right = iin.line + iin.right iin.line = "" iin.Redraw(); continue case 'U'-0x40: iin.line = "" iin.right = "" iin.clearline(); iin.Redraw(); continue; case DEL_RIGHT: if( 0 < len(iin.right) ){ iin.right,_ = delHeadChar(iin.right) iin.Redraw(); } continue; case 0x7F: // BS? not DEL if( 0 < len(iin.line) ){ iin.line,_ = delTailChar(iin.line) iin.Redraw(); } /* else if( 0 < len(iin.right) ){ iin.right,_ = delHeadChar(iin.right) iin.Redraw(); } */ continue; case 'H'-0x40: if( 0 < len(iin.line) ){ iin.line,_ = delTailChar(iin.line) iin.Redraw(); } continue; } if( OnWindows && ch == '\n' ){ continue; } if( ch == '\n' || ch == '\r' ){ iin.line += iin.right; iin.right = "" iin.Redraw(); //fputc(ch,stderr); fprintf(stderr,"\r\n"); // NL on Unix, CR on Windows AtConsoleLineTop = true break; } if MODE_CapsLock { if 'a' <= ch && ch <= 'z' { ch = ch+'A'-'a' } } if MODE_LowerLock { if 'A' <= ch && ch <= 'Z' { ch = ch+'a'-'A' } } iin.line += string(ch); iin.Redraw(); } EXIT: return iin.line + iin.right; } func getline_main(){ line := xgetline(0,"",nil) fprintf(stderr,"%s\n",line); /* dp = strpbrk(line,"\r\n"); if( dp != NULL ){ *dp = 0; } if( 0 ){ fprintf(stderr,"\n(%d)\n",int(strlen(line))); } if( lseek(3,0,0) == 0 ){ if( romkanmode ){ var buf [8*1024]byte; convs(line,buff); strcpy(line,buff); } write(3,line,strlen(line)); ftruncate(3,lseek(3,0,SEEK_CUR)); //fprintf(stderr,"outsize=%d\n",(int)lseek(3,0,SEEK_END)); lseek(3,0,SEEK_SET); close(3); }else{ fprintf(stderr,"\r\ngotline: "); trans(line); //printf("%s\n",line); printf("\n"); } */ } //== end ========================================================= getline // // $USERHOME/.gsh/ // gsh-rc.txt, or gsh-configure.txt // gsh-history.txt // gsh-aliases.txt // should be conditional? // func (gshCtx *GshContext)gshSetupHomedir()(bool) { homedir,found := userHomeDir() if !found { fmt.Printf("--E-- You have no UserHomeDir\n") return true } gshhome := homedir + "/" + GSH_HOME _, err2 := os.Stat(gshhome) if err2 != nil { err3 := os.Mkdir(gshhome,0700) if err3 != nil { fmt.Printf("--E-- Could not Create %s (%s)\n", gshhome,err3) return true } fmt.Printf("--I-- Created %s\n",gshhome) } gshCtx.GshHomeDir = gshhome return false } func setupGshContext()(GshContext,bool){ //gshPA := syscall.ProcAttr { gshPA := os.ProcAttr { "", // the staring directory os.Environ(), // environ[] //[]uintptr{os.Stdin.Fd(),os.Stdout.Fd(),os.Stderr.Fd()}, []*os.File{os.Stdin,os.Stdout,os.Stderr}, nil, // OS specific } cwd, _ := os.Getwd() gshCtx := GshContext { cwd, // StartDir "", // GetLine []GChdirHistory { {cwd,time.Now(),0} }, // ChdirHistory gshPA, []GCommandHistory{}, //something for invokation? GCommandHistory{}, // CmdCurrent false, []os.ProcessState{}, //[]int{}, aRusage{}, "", // GshHomeDir Ttyid(), false, false, []PluginInfo{}, []string{}, " ", "v", ValueStack{}, GServer{"",""}, // LastServer "", // RSERV cwd, // RWD CheckSum{}, } err := gshCtx.gshSetupHomedir() return gshCtx, err } func (gsh*GshContext)gshelllh(gline string)(bool){ ghist := gsh.CmdCurrent ghist.WorkDir,_ = os.Getwd() ghist.WorkDirX = len(gsh.ChdirHistory)-1 //fmt.Printf("--D--ChdirHistory(@%d)\n",len(gsh.ChdirHistory)) ghist.StartAt = time.Now() rusagev1 := Getrusagev() gsh.CmdCurrent.FoundFile = []string{} fin := gsh.tgshelll(gline) rusagev2 := Getrusagev() ghist.Rusagev = RusageSubv(rusagev2,rusagev1) ghist.EndAt = time.Now() ghist.CmdLine = gline ghist.FoundFile = gsh.CmdCurrent.FoundFile /* record it but not show in list by default if len(gline) == 0 { continue } if gline == "hi" || gline == "history" { // don't record it continue } */ gsh.CommandHistory = append(gsh.CommandHistory, ghist) return fin } // Main loop func script(gshCtxGiven *GshContext) (_ GshContext) { gshCtxBuf,err0 := setupGshContext() if err0 { return gshCtxBuf; } gshCtx := &gshCtxBuf //fmt.Printf("--I-- GSH_HOME=%s\n",gshCtx.GshHomeDir) //resmap() /* if false { gsh_getlinev, with_exgetline := which("PATH",[]string{"which","gsh-getline","-s"}) if with_exgetline { gsh_getlinev[0] = toFullpath(gsh_getlinev[0]) gshCtx.GetLine = toFullpath(gsh_getlinev[0]) }else{ fmt.Printf("--W-- No gsh-getline found. Using internal getline.\n"); } } */ ghist0 := gshCtx.CmdCurrent // something special, or gshrc script, or permanent history gshCtx.CommandHistory = append(gshCtx.CommandHistory,ghist0) prevline := "" skipping := false for hix := len(gshCtx.CommandHistory); ; { gline := gshCtx.getline(hix,skipping,prevline) if skipping { if strings.Index(gline,"fi") == 0 { fmt.Printf("fi\n"); skipping = false; }else{ //fmt.Printf("%s\n",gline); } continue } if strings.Index(gline,"if") == 0 { //fmt.Printf("--D-- if start: %s\n",gline); skipping = true; continue } if false { os.Stdout.Write([]byte("gotline:")) os.Stdout.Write([]byte(gline)) os.Stdout.Write([]byte("\n")) } gline = strsubst(gshCtx,gline,true) if false { fmt.Printf("fmt.Printf %%v - %v\n",gline) fmt.Printf("fmt.Printf %%s - %s\n",gline) fmt.Printf("fmt.Printf %%x - %s\n",gline) fmt.Printf("fmt.Printf %%U - %s\n",gline) fmt.Printf("Stouut.Write -") os.Stdout.Write([]byte(gline)) fmt.Printf("\n") } /* // should be cared in substitution ? if 0 < len(gline) && gline[0] == '!' { xgline, set, err := searchHistory(gshCtx,gline) if err { continue } if set { // set the line in command line editor } gline = xgline } */ fin := gshCtx.gshelllh(gline) if fin { break; } prevline = gline; hix++; } return *gshCtx } func ftest(where, path string){ //fi,err := os.Stat(path); //fmt.Printf("-- %v os.Stat(%v)=(%v)%v\n",where,path,err,fi); } func main() { initGshEnv(); ftest("gsh-main","."); ftest("gsh-main","gsh.go"); ftest("gsh-main","gsh.exe"); ftest("gsh-main","gsh"); gshCtxBuf := GshContext{} gsh := &gshCtxBuf argv := os.Args if( isin("wss",argv) ){ gj_server(argv[1:]); return; } if( isin("wsc",argv) ){ gj_client(argv[1:]); return; } if 1 < len(argv) { if isin("version",argv){ gsh.showVersion(argv) return } if argv[1] == "gj" { if argv[2] == "listen" { go gj_server(argv[2:]); } if argv[2] == "server" { go gj_server(argv[2:]); } if argv[2] == "serve" { go gj_server(argv[2:]); } if argv[2] == "client" { go gj_client(argv[2:]); } if argv[2] == "join" { go gj_client(argv[2:]); } } comx := isinX("-c",argv) if 0 < comx { gshCtxBuf,err := setupGshContext() gsh := &gshCtxBuf if !err { gsh.gshellv(argv[comx+1:]) } return } } if 1 < len(argv) && isin("-s",argv) { }else{ gsh.showVersion(append(argv,[]string{"-l","-a"}...)) } script(nil) //gshCtx := script(nil) //gshelll(gshCtx,"time") } //
//
Considerations
// - inter gsh communication, possibly running in remote hosts -- to be remote shell // - merged histories of multiple parallel gsh sessions // - alias as a function or macro // - instant alias end environ export to the permanent > ~/.gsh/gsh-alias and gsh-environ // - retrieval PATH of files by its type // - gsh as an IME with completion using history and file names as dictionaies // - gsh a scheduler in precise time of within a millisecond // - all commands have its subucomand after "---" symbol // - filename expansion by "-find" command // - history of ext code and output of each commoand // - "script" output for each command by pty-tee or telnet-tee // - $BUILTIN command in PATH to show the priority // - "?" symbol in the command (not as in arguments) shows help request // - searching command with wild card like: which ssh-* // - longformat prompt after long idle time (should dismiss by BS) // - customizing by building plugin and dynamically linking it // - generating syntactic element like "if" by macro expansion (like CPP) >> alias // - "!" symbol should be used for negation, don't wast it just for job control // - don't put too long output to tty, record it into GSH_HOME/session-id/comand-id.log // - making canonical form of command at the start adding quatation or white spaces // - name(a,b,c) ... use "(" and ")" to show both delimiter and realm // - name? or name! might be useful // - htar format - packing directory contents into a single html file using data scheme // - filepath substitution shold be done by each command, expecially in case of builtins // - @N substition for the history of working directory, and @spec for more generic ones // - @dir prefix to do the command at there, that means like (chdir @dir; command) // - GSH_PATH for plugins // - standard command output: list of data with name, size, resouce usage, modified time // - generic sort key option -nm name, -sz size, -ru rusage, -ts start-time, -tm mod-time // -wc word-count, grep match line count, ... // - standard command execution result: a list of string, -tm, -ts, -ru, -sz, ... // - -tailf-filename like tail -f filename, repeat close and open before read // - max. size and max. duration and timeout of (generated) data transfer // - auto. numbering, aliasing, IME completion of file name (especially rm of quieer name) // - IME "?" at the top of the command line means searching history // - IME %d/0x10000/ %x/ffff/ // - IME ESC to go the edit mode like in vi, and use :command as :s/x/y/g to edit history // - gsh in WebAssembly // - gsh as a HTTP server of online-manual //---END--- (^-^)//ITS more
// var WorldDic = // "data:text/dic;base64,"+ "Ly8gTXlJTUUvMC4wLjEg6L6e5pu4ICgyMDIwLTA4MTlhKQpzZWthaSDkuJbnlYwKa28g44GT"+ "Cm5uIOOCkwpuaSDjgasKY2hpIOOBoQp0aSDjgaEKaGEg44GvCnNlIOOBmwprYSDjgYsKaSDj"+ "gYQK"; // var WnnDic = // "data:text/dic;base64,"+ "PG1ldGEgY2hhcnNldD0iVVRGLTgiPgo8dGV4dGFyZWEgY29scz04MCByb3dzPTQwPgovL2Rp"+ "Y3ZlcglHU2hlbGxcc0lNRVxzZGljdGlvbmFyeVxzZm9yXHNXbm5ccy8vXHMyMDIwLTA4MzAK"+ "R1NoZWxsCUdTaGVsbArjgo/jgZ/jgZcJ56eBCndhdGFzaGkJ56eBCndhdGFzaQnnp4EK44Gq"+ "44G+44GICeWQjeWJjQpuYW1hZQnlkI3liY0K44Gq44GL44GuCeS4remHjgpuYWthbm8J5Lit"+ "6YeOCndhCeOCjwp0YQnjgZ8Kc2kJ44GXCnNoaQnjgZcKbm8J44GuCm5hCeOBqgptYQnjgb4K"+ "ZQnjgYgKaGEJ44GvCm5hCeOBqgprYQnjgYsKbm8J44GuCmRlCeOBpwpzdQnjgZkKZVxzCWVj"+ "aG8KZGljCWRpYwplY2hvCWVjaG8KcmVwbGF5CXJlcGxheQpyZXBlYXQJcmVwZWF0CmR0CWRh"+ "dGVccysnJVklbSVkLSVIOiVNOiVTJwp0aW9uCXRpb24KJXQJJXQJLy8gdG8gYmUgYW4gYWN0"+ "aW9uCjwvdGV4dGFyZWE+Cg==" // var SumomoDic = // "data:text/dic;base64,"+ "PG1ldGEgY2hhcnNldD0iVVRGLTgiPgo8dGV4dGFyZWEgY29scz04MCByb3dzPTQwPgovL3Zl"+ "cglHU2hlbGxcc0lNRVxzZGljdGlvbmFyeVxzZm9yXHNTdW1vbW9ccy8vXHMyMDIwLTA4MzAK"+ "c3UJ44GZCm1vCeOCggpubwnjga4KdQnjgYYKY2hpCeOBoQp0aQnjgaEKdWNoaQnlhoUKdXRp"+ "CeWGhQpzdW1vbW8J44GZ44KC44KCCnN1bW9tb21vCeOBmeOCguOCguOCggptb21vCeahgwpt"+ "b21vbW8J5qGD44KCCiwsCeOAgQouLgnjgIIKPC90ZXh0YXJlYT4K" // var SijimiDic = // "data:text/dic;base64,"+ "PG1ldGEgY2hhcnNldD0iVVRGLTgiPgo8dGV4dGFyZWEgY29scz04MCByb3dzPTQwPgovL3Zl"+ "cglHU2hlbGxcc0lNRVxzZGljdGlvbmFyeVxzZm9yXHNTaGlqaW1pXHMvL1xzMjAyMC0wODMw"+ "CnNpCeOBlwpzaGkJ44GXCmppCeOBmAptaQnjgb8KbmEJ44GqCmp1CeOBmOOChQp4eXUJ44KF"+ "CnUJ44GGCm5pCeOBqwprbwnjgZMKYnUJ44G2Cm5uCeOCkwpubwnjga4KY2hpCeOBoQp0aQnj"+ "gaEKa2EJ44GLCnJhCeOCiQosLAnjgIEKLi4J44CCCnhuYW5hCeS4gwp4anV1CeWNgQp4bmkJ"+ "5LqMCmtveAnlgIsKa29xCeWAiwprb3gJ5YCLCm5hbmFqdXVuaXgJNzIKbmFuYWp1dW5peHgJ"+ "77yX77ySCm5hbmFqdXVuaVgJ77yX77ySCuS4g+WNgeS6jHgJNzIKa29idW5uCeWAi+WIhgp0"+ "aWthcmFxCeOBoeOBi+OCiQp0aWthcmEJ5YqbCmNoaWthcmEJ5YqbCjwvdGV4dGFyZWE+Cg=" // var JA_JKLDic = // "data:text/dic;base64,"+ "Ly92ZXJsCU15SU1FamRpY2ptb3JzZWpKQWpKS0woMjAyMGowODE5KSheLV4pL1NhdG94SVRT"+ "CmtqamprbGtqa2tsa2psIOS4lueVjApqamtqamwJ44GCCmtqbAnjgYQKa2tqbAnjgYYKamtq"+ "amwJ44GICmtqa2trbAnjgYoKa2pra2wJ44GLCmpramtrbAnjgY0Ka2tramwJ44GPCmpramps"+ "CeOBkQpqampqbAnjgZMKamtqa2psCeOBlQpqamtqa2wJ44GXCmpqamtqbAnjgZkKa2pqamts"+ "CeOBmwpqamprbAnjgZ0KamtsCeOBnwpra2prbAnjgaEKa2pqa2wJ44GkCmtqa2pqbAnjgaYK"+ "a2tqa2tsCeOBqApramtsCeOBqgpqa2prbAnjgasKa2tra2wJ44GsCmpqa2psCeOBrQpra2pq"+ "bAnjga4Kamtra2wJ44GvCmpqa2tqbAnjgbIKampra2wJ44G1CmtsCeOBuApqa2tsCeOBuwpq"+ "a2tqbAnjgb4Ka2tqa2psCeOBvwpqbAnjgoAKamtra2psCeOCgQpqa2tqa2wJ44KCCmtqamwJ"+ "44KECmpra2pqbAnjgoYKampsCeOCiApra2tsCeOCiQpqamtsCeOCigpqa2pqa2wJ44KLCmpq"+ "amwJ44KMCmtqa2psCeOCjQpqa2psCeOCjwpramtramwJ44KQCmtqamtrbAnjgpEKa2pqamwJ"+ "44KSCmtqa2prbAnjgpMKa2pqa2psCeODvApra2wJ44KbCmtramprbAnjgpwKa2pramtqbAnj"+ "gIEK"; // // /*
References
*/ /*
Raw Source
Whole file
CSS part
JavaScript part
Builtin data part
*/ /*
(^_^)//{Hit j k l h}
CLOSE
*/ // // /*
PackmonGo

PackmonGo

*/ //
// // // /*
SightGlass

SightGlass

meter
(0000, 0000)(0000 x 0000)
(0000, 0000)
(0000, 0000)(0000 x 0000)
(0000, 0000)(0000 x 0000)
Wheel
*/ //
// /*
GJ Console

*/ /*
Form Auto. Filling
Location: Username: Password: SessionId:
*/ /*
BlinderText // https://w3c.github.io/uievents/#event-type-keydown // // 2020-09-21 class BlinderText - textarea element not to be readable // // BlinderText attributes // bl_plainText - null // bl_hideChecksum - [false] // bl_showLength - [false] // bl_visible - [false] // data-bl_config - [] // - min. length // - max. length // - acceptable charset in generete text // function BlinderChecksum(text){ plain = text.bl_plainText; return strCRC32(plain,plain.length).toFixed(0); } function BlinderKeydown(ev){ pass = ev.target if( ev.code == 'Enter' ){ ev.preventDefault(); } ev.stopPropagation() } function BlinderKeyup1(ev){ blind = ev.target if( ev.code == 'Backspace'){ blind.bl_plainText = blind.bl_plainText.slice(0,blind.bl_plainText.length-1) }else if( and(ev.code == 'KeyV', ev.ctrlKey) ){ blind.bl_visible = !blind.bl_visible; }else if( and(ev.code == 'KeyL', ev.ctrlKey) ){ blind.bl_showLength = !blind.bl_showLength; }else if( and(ev.code == 'KeyU', ev.ctrlKey) ){ blind.bl_plainText = ""; }else if( and(ev.code == 'KeyR', ev.ctrlKey) ){ checksum = BlinderChecksum(blind); blind.bl_plainText = checksum; //.toString(32); }else if( ev.code == 'Enter' ){ ev.stopPropagation(); ev.preventDefault(); return; }else if( ev.key.length != 1 ){ console.log('KeyUp: '+ev.code+'/'+ev.key); return; }else{ blind.bl_plainText += ev.key; } leng = blind.bl_plainText.length; //console.log('KeyUp: '+ev.code+'/'+blind.bl_plainText); checksum = BlinderChecksum(blind) % 10; // show last one digit only visual = ''; if( !blind.bl_hideCheckSum || blind.bl_showLength ){ visual += '['; } if( !blind.bl_hideCheckSum ){ visual += '#'+checksum.toString(10); } if( blind.bl_showLength ){ visual += '/' + leng; } if( !blind.bl_hideCheckSum || blind.bl_showLength ){ visual += '] '; } if( blind.bl_visible ){ visual += blind.bl_plainText; }else{ visual += '*'.repeat(leng); } blind.value = visual; } function BlinderKeyup(ev){ BlinderKeyup1(ev); ev.stopPropagation(); } // https://w3c.github.io/uievents/#keyboardevent // https://w3c.github.io/uievents/#uievent // https://dom.spec.whatwg.org/#event function BlinderTextEvent(){ ev = event; blind = ev.target; console.log('Event '+ev.type+'@'+blind.nodeName+'#'+blind.id) if( ev.type == 'keyup' ){ BlinderKeyup(ev); }else if( ev.type == 'keydown' ){ BlinderKeydown(ev); }else{ console.log('thru-event '+ev.type+'@'+blind.nodeName+'#'+blind.id) } } //< textarea hidden id="BlinderTextClassDef" class="textField"" // onkeydown="BlinderTextEvent()" onkeyup="BlinderTextEvent()" // spellcheck="false">< /textarea> //< textarea hidden id="gj_pass1" // class="textField BlinderText" // placeholder="PassWord1" // onkeydown="BlinderTextEvent()" // onkeyup="BlinderTextEvent()" // spellcheck="false"< /textarea> function SetupBlinderText(parent,txa,phold){ if( txa == null ){ txa = document.createElement('textarea'); //txa.id = id; } txa.setAttribute('class','textField BlinderText'); txa.setAttribute('placeholder',phold); txa.setAttribute('onkeydown','BlinderTextEvent()'); txa.setAttribute('onkeyup','BlinderTextEvent()'); txa.setAttribute('spellcheck','false'); //txa.setAttribute('bl_plainText','false'); txa.bl_plainText = ''; //parent.appendChild(txa); } function DestroyBlinderText(txa){ txa.removeAttribute('class'); txa.removeAttribute('placeholder'); txa.removeAttribute('onkeydown'); txa.removeAttribute('onkeyup'); txa.removeAttribute('spellcheck'); txa.bl_plainText = ''; } // // visible textarea like Username // function VisibleTextEvent(){ if( event.code == 'Enter' ){ if( event.target.NoEnter ){ event.preventDefault(); } } event.stopPropagation(); } function SetupVisibleText(parent,txa,phold){ if( false ){ txa.setAttribute('class','textField VisibleText'); }else{ newclass = txa.getAttribute('class'); if( and(newclass != null, newclass != '') ){ newclass += ' '; } newclass += 'VisibleText'; txa.setAttribute('class',newclass); } //console.log('SetupVisibleText class='+txa.class); txa.setAttribute('placeholder',phold); txa.setAttribute('onkeydown','VisibleTextEvent()'); txa.setAttribute('onkeyup', 'VisibleTextEvent()'); txa.setAttribute('spellcheck','false'); cols = txa.getAttribute('cols'); if( cols != null ){ txa.style.width = '580px'; //console.log('VisualText#'+txa.id+' cols='+cols) }else{ //console.log('VisualText#'+txa.id+' NO cols') } rows = txa.getAttribute('rows'); if( rows != null ){ txa.style.height = '30px'; txa.style.resize = 'both'; txa.NoEnter = false; }else{ txa.NoEnter = true; } } function DestroyVisibleText(txa){ txa.removeAttribute('class'); txa.removeAttribute('placeholder'); txa.removeAttribute('onkeydown'); txa.removeAttribute('onkeyup'); txa.removeAttribute('spellcheck'); cols = txa.removeAttribute('cols'); }
*/ /* */ // //
Golang / JavaScript Link // // 2020-0920 created // WS // WS // INSTALL: go get golang.org/x/net/websocket // INSTALL: sudo {apt,yum} install git (if git is not instlled yet) // import "golang.org/x/net/websocket" const gshws_origin = "http://locahost:9999" const gshws_server = "localhost:9999" const gshws_port = 9999 const gshws_path = "gjlink1" const gshws_url = "ws://"+gshws_server+"/"+gshws_path const GSHWS_MSGSIZE = (8*1024) func fmtstring(fmts string, params ...interface{})(string){ return fmt.Sprintf(fmts,params...) } func GSHWS_MARK(what string)(string){ now := time.Now() us := fmtstring("%06d",now.Nanosecond() / 1000) mark := "" if( !AtConsoleLineTop ){ mark += "\n" AtConsoleLineTop = true } mark += "["+now.Format(time.Stamp)+"."+us+"] -GJ-" + what + ": " return mark } func gchk(what string,err error){ if( err != nil ){ panic(GSHWS_MARK(what)+err.Error()) } } func glog(what string, fmts string, params ...interface{}){ fmt.Print(GSHWS_MARK(what)) fmt.Printf(fmts+"\n",params...) } var WSV = []*websocket.Conn{} func jsend(argv []string){ if len(argv) <= 1 { fmt.Printf("--Ij %v [-m] command arguments\n",argv[0]) return } argv = argv[1:] if( len(WSV) == 0 ){ fmt.Printf("--Ej-- No link now\n") return } if( 1 < len(WSV) ){ fmt.Printf("--Ij-- multiple links (%v)\n",len(WSV)) } multicast := false // should be filtered with regexp if( 0 < len(argv) && argv[0] == "-m" ){ multicast = true argv = argv[1:] } args := strings.Join(argv," ") now := time.Now() msec := now.UnixNano() / 1000000; tstamp := fmtstring("%.3f",float64(msec)/1000.0) msg := fmtstring("%v SEND gshell|* %v",tstamp,args) if( multicast ){ for i,ws := range WSV { wn,werr := ws.Write([]byte(msg)) if( werr != nil ){ fmt.Printf("[%v] wn=%v, werr=%v\n",i,wn,werr) } glog("SQ",fmtstring("(%v) %v",wn,msg)) } }else{ i := 0 ws := WSV[i] wn,werr := ws.Write([]byte(msg)) if( werr != nil ){ fmt.Printf("[%v] wn=%v, werr=%v\n",i,wn,werr) } glog("SQ",fmtstring("(%v) %v",wn,msg)) } } func ws_broadcast(msg string)(wn int,werr error){ for i,ws := range WSV { wn,werr = ws.Write([]byte(msg)) if( werr != nil ){ fmt.Printf("[%v] wn=%v, werr=%v\n",i,wn,werr) } glog("SQ",fmtstring("(%v) %v",wn,msg)) } return wn,werr; } func serv1(ws *websocket.Conn) { WSV = append(WSV,ws) //fmt.Print("\n") glog("CO","accepted connections[%v]",len(WSV)) //remoteAddr := ws.RemoteAddr //fmt.Printf("-- accepted %v\n",remoteAddr) //fmt.Printf("-- accepted %v\n",ws.Config()) //fmt.Printf("-- accepted %v\n",ws.Config().Header) //fmt.Printf("-- accepted %v // %v\n",ws,serv1) var reqb = make([]byte,GSHWS_MSGSIZE) for { rn, rerr := ws.Read(reqb) if( rerr != nil || rn < 0 ){ glog("SQ",fmtstring("(%v,%v)",rn,rerr)) break } req := string(reqb[0:rn]) glog("SQ",fmtstring("(%v) %v",rn,req)) margv := strings.Split(req," "); margv = margv[1:] if( 0 < len(margv) ){ if( margv[0] == "RESP" ){ // should forward to the destination continue; } } now := time.Now() msec := now.UnixNano() / 1000000; tstamp := fmtstring("%.3f",float64(msec)/1000.0) res := fmtstring("%v "+"CAST"+" %v",tstamp,req) wn := 0; werr := error(nil); if( 0 < len(margv) && margv[0] == "BCAST" ){ wn, werr = ws_broadcast(res); }else{ wn, werr = ws.Write([]byte(res)) } gchk("SE",werr) glog("SR",fmtstring("(%v) %v",wn,string(res))) } glog("SF","WS response finish") wsv := []*websocket.Conn{} wsx := 0 for i,v := range WSV { if( v != ws ){ wsx = i wsv = append(wsv,v) } } WSV = wsv //glog("CO","closed %v",ws) glog("CO","closed connection [%v/%v]",wsx+1,len(WSV)+1) ws.Close() } // url ::= [scheme://]host[:port][/path] func decomp_URL(url string){ } func full_wsURL(){ } func gj_server(argv []string) { gjserv := gshws_url gjport := gshws_server gjpath := gshws_path gjscheme := "ws" //cmd := argv[0] argv = argv[1:] if( 1 <= len(argv) ){ serv := argv[0] if( 0 < strings.Index(serv,"://") ){ schemev := strings.Split(serv,"://") gjscheme = schemev[0] serv = schemev[1] } if( 0 < strings.Index(serv,"/") ){ pathv := strings.Split(serv,"/") serv = pathv[0] gjpath = pathv[1] } servv := strings.Split(serv,":") host := "localhost" port := 9999 if( servv[0] != "" ){ host = servv[0] } if( len(servv) == 2 ){ fmt.Sscanf(servv[1],"%d",&port) } //glog("LC","hostport=%v (%v : %v)",servv,host,port) gjport = fmt.Sprintf("%v:%v",host,port) gjserv = gjscheme + "://" + gjport + "/" + gjpath } glog("LS",fmtstring("listening at %v",gjserv)) http.Handle("/"+gjpath,websocket.Handler(serv1)) err := error(nil) if( gjscheme == "wss" ){ // https://golang.org/pkg/net/http/#ListenAndServeTLS //err = http.ListenAndServeTLS(gjport,nil) }else{ err = http.ListenAndServe(gjport,nil) } gchk("LE",err) } func gj_client(argv []string) { glog("CS",fmtstring("connecting to %v",gshws_url)) ws, err := websocket.Dial(gshws_url,"",gshws_origin) gchk("C",err) var resb = make([]byte, GSHWS_MSGSIZE) for qi := 0; qi < 3; qi++ { req := fmtstring("Hello, GShell! (%v)",qi) wn, werr := ws.Write([]byte(req)) glog("QM",fmtstring("(%v) %v",wn,req)) gchk("QE",werr) rn, rerr := ws.Read(resb) gchk("RE",rerr) glog("RM",fmtstring("(%v) %v",rn,string(resb))) } glog("CF","WS request finish") } // //
/* */ /* */ /*
Live HTML Snapshot
Edit Save Load Vers
*/ /*
Event sharing

Inter-window communicaiton

frame0 >>> frame1 and frame2
frame1 >>> frame0 and frame2
frame2 >>> frame0 and frame1




*/ /* // /*
Wirtual Desktop

CosmoScreen 0.0.8

ScopeControl command keys
g ... grid on/off
i ... zoom in
o ... zoom out
s ... save current scope
r ... restore saved scope

Monitor < x wall-paper: WD-WallPaler03.png

Desktop < x

Display < x < <

Content X Y shift+wheel for horizontal scroll

Scopexx < | >

Scopeyy < | >

Scopezz < | >

Display

Overflo "scroll" imprisons windows inside the display

CosmoScreen 0.0.8
00:00

WirtualSpace 1.

Reload
Reload
Reload
*/ //
// // /*
SBSidebar

SBSidebar

// 2020-1006 its-more.jp-blog-60000-style.css
*/ //
// // // /*
Affiliate

Supportive Affiliate

*/ //
// // // /*
TextCanvas
Font Selection

Font Selection

Drawing Text on Canvas

Bold Italic
Pixels

PNG JPEG DataURL

(inline image)

(background image)
(Data URL)
*/ //
// // // /*
Shading Canvas

Shading Canvas

Commands
Placement Mode
a ... apply (into absolute position)
j ... bring down (ArrowDown)
k ... bring up (ArrowUp)
h ... bring left (ArrowLeft)
l ... bring right (ArrowRight)
0 ... z-index = 0
+ ... z-index += 1
- ... z-index -= 1
r ... return to here (relative position)
c ... clear the log text
Note: the HTML text must be contenteditable to catch Key Event.
My Canvas.
*/ //
// // // /*
Character Map Mandala

Unicode Character Map

code 0x0000 - 0xFFFF / 16px / 3200 x 3200 px / zoom:0.25

*/ //
// // // /*
Collaborated Pointillism

Collaborated Pointillism

Share
XY1 XY1-remote XY2 XY2-remote

*/ //
// // // /*
StatCounter

StatCounter

hit counter (counter as image tag)
(counter by inline script)
*/ //
// // // /*
CascadedCanvasBook

Cascaded Canvas Book


Undo / Redo / Replay

From To

Color Picker

Select value by Mouse Motion Auto. add to history Mouse Wheel Auto. add to history
R G B A RGBA Sample
R G B A RGBA Sample
R G B A RGBA Sample
R G B A RGBA Sample
Sample

/ Max.

Colors

#CO-0 BW FC xxx xxx BC
#CO-1 BW FC xxx xxx BC
#CO-2 BW FC xxx xxx BC
#CO-3 BW FC xxx xxx BC
#CO-4 BW FC xxx xxx BC
#CO-5 BW FC xxx xxx BC

Parts

Resize% Zoom% Redraw Immediate
#CL-0 F X Y Z W H RO #CO-
#RE-0 F X Y Z W H RO #CO-
#CI-0 F X Y Z R S E #CO-
#PA-0 F X Y Z R S E #CO-
#EL-0 F X Y Z W H RO S E #CO-
#BA-0 F X Y Z W H RO S E #CO-
#BA-0 F X Y Z W H RO S E #CO-
#XA-0 F X Y Z W H RO S E #CO-
#XA-1 F X Y Z W H RO S E #CO-

Animation

#ANIMA-0 TYPE ITVLms DURAs

Canvas

the above part #0 W H Zoom%

CanvasBook


*/ //
// // //SVG/SVG /*
SVG Getting Started

Getting Started SVG

*/ //
// // // /*
Work Template

Template of Work

*/ //
// // // /*
Original Source

Original Source of GShell

*/ //
// //
//
//1258738470 423413 gsh (2020/10/09 02:58:28)

秋の空

社長:今日はちょっと遅めの昼食を食べに出たのですが、天久保公園の脇を走行中、クルマの中から空を見上げて大きな衝撃を受けました。

基盤:ロケットマンが飛んでいたとか。

社長:残念ながらiPhone SEのカメラではその壮絶な空模様がうまく撮れなかったのですが、こんな感じで…

開発:うろこ雲というやつでしょうか。

社長:なにかすごく実体的で、ある種気味が悪いというか、距離感がわからないというか。裂け目の迫力とか。鳥肌が立つというか。天地がひっくり返ったような圧迫感というか。

基盤:低層に漂ってる雲もポイントですね。

社長:それで、お腹もすいてるけど、これはちゃんと写真に残しておこうということで、どこかひらけた所… 東大通りを北上して、筑波大の一ノ矢フィールドに向かいました。

社長:フィールドに着いて、撮り始めたのですが、なんというかああいう広いところで見上げると、街の風景の上に覆いかぶさってる時のような迫力は感じないんですよね。すこし拍子抜け的な。

基盤:でもこの半年くらい見て来た中で、一番印象的な空ではあります。

社長:ともかく夏のもくもくとは全く違う、いろんな形の雲が、全天どこを見回しても、出てるわけです。消化しきれない感じでした。

開発:ビデオで一周してパノラマを撮ればよかったかもですね。

社長:次からそうしましょう。できれば iPhone12でw。で、帰りがけなのでうなぎ屋で食事することにしたのですが、その駐車場から見上げた空がまたなかなか面白くて。

社長:食事を終えて帰り着いて見上げたら、秋は夕暮れという空に。

開発:地表の影絵に趣がないのが残念w

社長:それで、私はもう空の美しさに感動して、呆然と見上げてる状態だったんですが、周囲を通る人たちはあの素晴らしい空を一瞥もしないんでよ。歩きながらスマホなんかに見入っちゃってたりしてw。

開発:まあ、私達も4月より前はそうでしたけどね。

基盤:オーロラというのも一度見てみたいですけど、たぶんこういう鮮明なインパクトじゃないんでしょうね。

社長:花火も綺麗ですが、せいぜい直径1km。それに比べると空の見通しは、天気が良ければ100kmはあるのかなと。当たり前ですが、ひろがりのスケール感が凄いです。

開発:楽しまないともったいないですよね。

2020-1107 SatoxITS

大統領選休暇

基盤:大統領選、もつれてますねぇ。

開発:面白すぎて疲れたというか、さすがに飽きましたけど。

社長:それはそうと、仕事脳を全く働かせない日を作るのは有益ですね。とてもリフレッシュした気分です。

基盤:当社にも休日という概念を導入してはどうでしょうか?

開発:5日働いて2日休むのが世間の常識ですしね。

社長:開票開始直後はバイデンで決まりかと思ったら途中であートランプなのかと思ったのに今はどうやらバイデンなのかなーみたいな。

開発:アメリカの選挙システムの設計の面白さと、実装方式の過渡期を生で目撃できた感じです。

社長:日付の書いてない文書って本当に嫌いです。

開発:修正条項のほうには成立年が書いてありますけどね… Wikiを見ます…「この憲法は、1787年9月17日に作成され、1788年に発効し、現在も機能している世界最古の成文憲法」。

基盤:200年以上も骨組みが変わってないシステムって凄いですね。

社長:現在では非合理になってる所がエンタメ要素になってる感じがしますね。

開発:この件で見かけた面白いGIFアニメがこれです。

開発:時系列的には右から左じゃないかとは思いますけどね。

基盤:GIF版でも4.4MBあるんですね。

社長:これ、生データからJavaScriptでこのスピードでcanvasに書けますかね。右から左とかも好きに選べるような。

開発:大した計算量でも無いようにも思えますし、凄く重いかもしれないし、わかんないですね。

SVG vs Canvas

社長:始動の準備体操にXeyesでも作ってみてはどうかと思うんですが。

開発:それなんですが、その前に、これまで canvas は使って見たので、SVGも試してみたいなと。

社長:どういう違いなんでしょう。

開発:w3schoolにはこうあります。

社長:おー、書いたものにイベントハンドラーを付けられるのですか。すごく魅力的ですね。

開発:比較表にするとこういうことです。

社長:画像ならcanvas、図形ならSVGって感じですかね。書いておしまいならcanvas、ユーザのアクションをフィードバックしたいならSVGというか。

開発:いちいちオブジェクトなのでDOMが重いとありますが、どのみちcanvasに書いて元データを自前でDOMに管理するなら、それより軽いのではないかと思われます。それ以上に、canvas ではテキストがHTMLで書けないのがやだなと思っていたのですが、SVGでは普通にHTML+CSSで書けるみたいなんです。

基盤:これは利用しない手はないですね。

社長:では、今日の作業は SVG でスマイルマークの作成。時間があったら Xeyes ということにしましょう。

* * *

携帯:ぶぶー…

基盤:おっと、iPhone12 にしませんかSMSが来ました。

開発:11月13日発売!って、auはまだ発売してなかったんですね。

基盤:へー、このURLのパスは送り先個体毎メッセージ毎に生成してる一意IDなんでしょうかね。デスクトップからアクセスするとNot Foundになります。にしては桁数が少ないように思いますが。

社長:光学2倍に興味ありです。ぜひ検討しましょう。

基盤:片手親指で画面全域に触れるっていう要件からは、5.4インチ一択でしょうね。

社長:今の第一世代SEは4インチですが、すでに限界です。

基盤:外形的には、SEが 58.6 x 123.8 x 7.6mm / 113g、iPhone12miniが64.2 x 131.5 x 7.4mm / 133gですね。わずかに大きくなりますが、わずかに薄くなります。解像度は 1136 x 640(326ppi) から 2340 x 1080(476ppi)へ。

社長:大きさ的には少し余裕があるのでOKですが、20g重くなるのはどうなのかなー…

経理:1円硬貨で20円、500円なら3枚ぶんですね。

基盤:500円玉って意外と軽いんですね。

開発:解像度とズームがうまくマッチするかも気になります。

基盤:おーっと、ただいまGeorgiaが凄いことに…

社長:残り1パーセントでパーミルの争いですね。

基盤:ネバダも16%残ってて、1%差っていう。

開発:政権って、勝ったら総取りじゃなくて、得票率に応じた権力というか、予算決定権を獲得できるようにしたらどうなんでしょうね?

社長:税金を集めるほうの責任も同等に負わせないと、ばらまき合戦で終わってしまいそうです。

開発:他のものと違って住む所は簡単にとっかえられない、住むところの役所は選べない、というのが難しい所ですね。

* * *

基盤:この間、トランプ+102、バイデン+704。現在の差が665。

開発:次の発表ではひっくり返りそうですね。

基盤:膠着して動かないですねw

開発:選挙人を20人持ってるペンシルベニアが残り5万として、トランプがそのうちの4万を取らないと3万の差はひっくり返せないということですが、流れからしてあり得ないと。

基盤:お前は既に死んでいる。

開発:テープカット待ち状態みたいな。

社長:まあ、大統領選がくれた休日だったということでしょうか。

-- 2020-1107 SatoxITS

自動ブラウザ切り替え器

開発:大統領選の開票が始まりましたね。

基盤:今回もすごい接戦みたいです。

社長:これ、速報を眺めながらお昼食べたいと思います。

* * *

社長:ただいま。

基盤:だいぶ時間がかかりましたね。

社長:天気が良かったので松見公園の展望台に登ってきました。半年ぶりです。

開発:栓抜きから光線が出ているようです。

社長:転落防止の柵がちょっと邪魔で。檻の中から見てるみたいな。中央にある少し高い台の所からせいいっぱい上に手を伸ばして撮れたのがこれです。

開発:立ち入りできない外周を作ってそこに柵を立てるべきですよね。設計ミスでは。

経理:建設費の問題だったのかも。

社長:昔は目障りだった記憶が無いのですが、あれは昔からああだったんでしたっけ?

基盤:あざやかで綺麗ですけど、うちから見た景色のほうが趣がありますね。採石場も目障りだし。

社長:360度パノラマはやっぱり絶景というか壮観ですけどね。高所の恐怖感もあり風も結構強くて。うちの玄関を出た時はこんなふうでした。

開発:給水タンクに景観条例を適用すべきですよね。おまえはバクテリオファージかっつうの。

社長:土浦の高架道沿いにあるマリンブルーの丸い給水タンクは秀逸ですよね。ネーム入りだし看板代わりなんだと思いますが。

* * *

開発:それで昨日の「ブラウザ切り替え器」ですが、やはりプロキシ切り替え器であるPACからやってみるべきかなと。

社長:PACってJavaScriptですよね。WebSocketが使えたりしないですかね。

基盤:何でも書けたら、PACでブラウザができちゃいますねw

開発:でちょっと検索すると、どうやら非常にやれることが限定されているようです。sandboxで動くとありますから、関数的と言うか、引数をもらってマッチングして戻り値を返すだけかなと。

社長:やってみましょう。

* * *

基盤:大統領選、案の定、凄いことになってますね。

開発:各社の集計、このあたりで止まっているようです。

社長:まあ、あっちは夜明け前ですしね。

基盤:見やすさ的にはyahoo!の勝利ですかね。

社長:Bingはマップと横に並べてるところが特徴的ですね。画面を幅広に使うってのは Azureでも見られますが、MS のUIコンセプトなのかも知れません。

開発:ロイターのページはもちょっとCSSでpaddingを入れたほうが良いように思います。

開発:NHKのはスッキリしてて良いと思いますが、数字のフォントの選択がなんだかなのが残念。

基盤:この傾いてる黄色線が面白いです。

社長:何がまず見たいかってこの数字なわけですから、この数字をもう200%くらい拡大したって良いですよね。閲覧者がデザインを選べるとか。

開発:ウェブデザイナーの花火競技大会みたいですね。

社長:現時点ではバイデンのほうが多いですが、残ってる州はほとんどトランプ優位になってるから、あれが総取りの州ならもうトランプで決まりってことでしょうか。

社長:お昼食べてる時点ではバイデン200対トランプ100くらいでしたからね。なんともドラマチックなシステムです。

開発:今回はコロナの影響で「郵便投票」が多かったみたいですが、これって電子投票化できないもんでしょうかね?

基盤:ICTは完全には信用されてないってことなんですかね。この電子商取引の時代に。

社長:選挙では匿名性を確保しながら確実性を実現しないといけないという特殊性がありますよね。まあ逆探知の容易さというかコストの問題なんだろうと思いますが。

* * *

開発:さてそれでPACなんですが、macOSでは file URL で指定しても見てくれないように見えます。かといってエラーを表示するでもないので、なんだかなーなのですが。

基盤:さっきからやたらMacMiniが重いのですが、アクティビティモニタを見ると、ロイター速報のSafariがこんなことに。

開発:PortsってTCPコネクション数?

基盤:一旦ロイター版を閉じて、もいっかい開きます。速報5サイト同時リロード…

* * *

基盤:ロイター版のPorts大量生成は再現しないですね。

開発:どれかの iframe のせいだったかも。

社長:店子の素行のせいでも問題視されるのは大家のURLですよね。

開発:iframeのコンテンツは治外法権な感じもしますが、リソース使用量は大家が制約できるべきだと思います。リソースを食い尽くすJavaScriptは簡単に書けてしまうわけですから。

開発:ざっと検索したところでは、同じ問題意識のって5年前のこれですかね。解答としては Google Caja というのがあるって事みたいなんですが。

基盤:おっとー、またもやMacMiniがずっしりと重くなりました。

基盤:やたらabout:blankってのが出てますけど、ウィンドウを開いてるってことでしょうかね?… 使い物にならないので一旦閉じます。

* * *

基盤:なんか、接戦州で郵便投票の開票が始まったらバイデン票が急激に伸びたとかで騒ぎになってますね。

社長:思うに、有権者数の分のユニークな鍵を生成してUSBメモリとかバーコードとかにして、物理的にシャッフルしてから有権者に郵送する。投票所に行くにしても電子投票するにしても、その鍵を使って投票する。で、良いような気がします。

開発:郵便投票の場合には暗号化してプリントするようなアプリを配布して使ってもらうんでしょうね。

基盤:コンビニか郵便局で投票でも良いように思います。

* * *

開発:あー、でも検索すると電子投票システムってとっくの昔から普及している国もあるみたいですね。

社長:こないだ市長選がありましたが、封も切らずでした。実はあの中には電子投票用の何かが入っていたのかも・・・

-- 2020-1105 SatoxITS

趣味系サイト再構成

社長:わたしの個人的趣味系のサイトを再構成したいと思います。

開発:ここは趣味系ではないんでしょうか?

社長:趣味の意味が違いますし、一応会社のサイトですから。

基盤:再構成と言っても、いままでサーバを立ててはお試しページを作っただけで放置状態かと。

社長:お試しで作ったところに入れてみたTwentySeventeenが気に入って会社にも導入しました。気になっているのは、趣味系サーバのレンタル費用です。何時でも切れるように月払いにしていたのですが、すでに7ヶ月たってしまいました。

経理:今、すぐに切りましょう。

基盤:趣味でネットする時のブラウザも分けたほうが良いかもしれませんね。

開発:ネットも分けると良いかもですね。大家さんのネットを使う。

社長:ところが困ったことに、Safariは複数のプロファイルを作れないらしいんです。Safariのプライベートブラウジングというのは履歴を残してくれないだけのもので、逆に通常のユーザの情報は全部見えちゃう。欲しいのは、通常のユーザの情報が全く見えず、履歴は残してくれる事なんですが。

基盤:検索してみると、そういう議論があります。実際、無いようですね。その代わり、Fast User Switching を使えばっていう話です。

開発:さくっと切り替えられるなら、完全に別ユーザに切り替えるのも悪くないですね。

社長:やってみましょう。

* * *

社長:この遅さはいったい・・・

基盤:MacMiniでメモリが無いせいかもしれませんね。

開発:専用のVMを作るのが良いのでは。

社長:そうしましょう。

基盤:おっと、ParallelsにUbuntu20.04がありますね。

社長:ではそれで。

開発:このインストールの簡単さと速さ。

社長:でもUbuntuの壁紙は18.04のがよかったですね。

基盤:じゃ差し替えましょう。ちょっと当社用のロゴでも上書きして。。。

基盤:げ、apt で Go をインストールしたら go1.13.8 ですって。

開発:昨日作ったライトセールのWordPress機はDebianみたいですが、apt で go1.11.6 が入りました。

基盤:その前に作ったAzureのUbuntu18.04機ではgo1.10.4て出てますね (^-^;

社長:Google自体が産直で配布するのを好んでる感はあります。まあ、ちょっとしたインストールの手間の違いですからね。

* * *

社長:うーん、やっぱりブラウザの使い分けは面倒ですね。つい忘れちゃうし。なんか自動化できないですかね。

基盤:アクセス先によって自動的にブラウザかプロファイルが切り替わると良いですね。

開発:まあプロキシですかね。アクセス先とUser-Agentを見て、WebSurkitで適切なブラウザをアクティブにする。

社長:プロキシのパスワードとか、昔なつかしのPACを使うでも良いかも知れません。検討しましょう。

-- 2020-1103 SatoxITS