github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/builtins/core/open/open.go (about) 1 package open 2 3 import ( 4 "compress/gzip" 5 "fmt" 6 "io" 7 "os" 8 "regexp" 9 "strings" 10 11 "github.com/lmorg/murex/lang" 12 "github.com/lmorg/murex/lang/stdio" 13 "github.com/lmorg/murex/lang/types" 14 "github.com/lmorg/murex/utils" 15 ) 16 17 var rxExt = regexp.MustCompile(`(?i)\.([a-z0-9]+)(\.gz)?$`) 18 19 func init() { 20 lang.DefineFunction("open", open, types.Any) 21 } 22 23 func open(p *lang.Process) (err error) { 24 var dataType string 25 26 if p.IsMethod { 27 return OpenPipe(p, p.Stdin) 28 } 29 30 path, err := p.Parameters.String(0) 31 if err != nil { 32 return err 33 } 34 35 closers, err := OpenFile(p, &path, &dataType) 36 if err != nil { 37 return err 38 } 39 40 err = preview(p, path, dataType) 41 if err != nil { 42 return err 43 } 44 45 return CloseFiles(closers) 46 } 47 48 func OpenPipe(p *lang.Process, pipe stdio.Io) error { 49 dataType := pipe.GetDataType() 50 p.Stdout.SetDataType(dataType) 51 52 ext := GetExt("", dataType) 53 tmp, err := utils.NewTempFile(p.Stdin, ext) 54 if err != nil { 55 return err 56 } 57 defer tmp.Close() 58 59 return preview(p, tmp.FileName, dataType) 60 } 61 62 func OpenFile(p *lang.Process, path *string, dataType *string) ([]io.Closer, error) { 63 var ( 64 closers []io.Closer 65 err error 66 ext string 67 ) 68 69 switch { 70 case utils.IsURL(*path): 71 var body io.ReadCloser 72 body, *dataType, err = http(p, *path) 73 if err != nil { 74 return closers, err 75 } 76 77 ext = GetExt("", *dataType) 78 tmp, err := utils.NewTempFile(body, ext) 79 if err != nil { 80 return closers, err 81 } 82 83 *path = tmp.FileName 84 85 default: 86 ext = GetExt(*path, "") 87 *dataType = lang.GetExtType(ext) 88 } 89 90 if *dataType == "gz" || (len(*path) > 3 && strings.ToLower((*path)[len(*path)-3:]) == ".gz") { 91 file, err := os.Open(*path) 92 if err != nil { 93 return closers, err 94 } 95 //defer file.Close() 96 closers = append(closers, file) 97 98 gz, err := gzip.NewReader(file) 99 if err != nil { 100 return closers, err 101 } 102 //defer gz.Close() 103 closers = append(closers, gz) 104 105 ext = GetExt(*path, "") 106 *dataType = lang.GetExtType(ext) 107 tmp, err := utils.NewTempFile(gz, ext) 108 //defer tmp.Close() 109 closers = append(closers, tmp) 110 111 if err != nil { 112 return closers, err 113 } 114 115 *path = tmp.FileName 116 } 117 118 return closers, err 119 } 120 121 func CloseFiles(closers []io.Closer) error { 122 var s string 123 124 for i := len(closers) - 1; i > -1; i-- { 125 err := closers[i].Close() 126 if err != nil { 127 s = fmt.Sprintf("%s: %s", err.Error(), s) 128 } 129 } 130 131 if len(s) > 0 { 132 return fmt.Errorf("unable to close files: %s", s[:len(s)-2]) 133 } 134 135 return nil 136 } 137 138 func GetExt(path, dataType string) string { 139 if path != "" { 140 match := rxExt.FindAllStringSubmatch(path, -1) 141 if len(match) > 0 && len(match[0]) > 1 { 142 return strings.ToLower(match[0][1]) 143 } 144 } 145 146 m := lang.GetFileExts() 147 for ext := range m { 148 if m[ext] == dataType { 149 return ext 150 } 151 } 152 153 return "" 154 } 155 156 func preview(p *lang.Process, path, dataType string) error { 157 if dataType == "" { 158 dataType = types.Generic 159 } 160 161 p.Stdout.SetDataType(dataType) 162 agent, err := OpenAgents.Get(dataType) 163 164 // we check if std(in|err) is a TTY because stdout might be a fake TTY in preview pane 165 if (p.Stdout.IsTTY() || (p.Stdin.IsTTY() && p.Stderr.IsTTY())) && dataType == types.Generic { 166 return openSystemCommand(p, path) 167 } 168 169 if !p.Stdout.IsTTY() || err != nil { 170 // Not a TTY or no open agent exists so fallback to passing []bytes along 171 file, err := os.Open(path) 172 if err != nil { 173 return err 174 } 175 176 defer file.Close() 177 178 _, err = io.Copy(p.Stdout, file) 179 return err 180 } 181 182 fork := p.Fork(lang.F_FUNCTION | lang.F_NEW_MODULE | lang.F_NO_STDIN) 183 fork.Name.Set("open") 184 fork.Parameters.DefineParsed([]string{path}) 185 fork.FileRef = agent.FileRef 186 _, err = fork.Execute(agent.Block) 187 188 if err != nil { 189 p.Stderr.Writeln([]byte("`open` code could not compile: " + err.Error())) 190 } 191 192 return err 193 }