/* SHS: Syntactically Homogeneous Shell * Copyright (C) 2019 Aidan Hahn * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package stdlib import ( "os" "io/ioutil" "gitlab.com/whom/shs/ast" "gitlab.com/whom/shs/log" ) /* Take a path, return the absolute path * does not verify that the absolute path is correct * currently only supports paths using forward slashes * * TODO: handle ~ */ func AbsPath(arg string) string { if arg[0] != '/' { dir, err := os.Getwd() if err != nil { log.Log(log.ERR, "Couldnt get working directory: " + err.Error(), "path") return arg } return dir + "/" + arg } return arg } /* Takes one arg, returns nil * changes directory to the path in the arg * fails if arg is not stringable * * Example: * (cd (concat HOME "/go")) */ func Cd(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { in = in.Eval(ft, vt, true) if in == nil { log.Log(log.ERR, "arguments to cd evaluated to nil!", "cd") return nil } if in.Tag == ast.LIST { log.Log(log.ERR, "Couldnt change dir to a list", "cd") return nil } err := os.Chdir(in.Value()) if err != nil { log.Log(log.ERR, err.Error(), "cd") } return nil } /* Takes one arg, returns a bool * Returns true if arg is a filepath that exists * returns nil if arg is not a string type * * Example: * (touch test) * (fexists test) */ func Fexists(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { in = in.Eval(ft, vt, false) if in == nil || in.Tag != ast.STRING { log.Log(log.ERR, "argument to fexists must be a string", "fexists") return nil } filename := in.Value() out := ast.TRUE if _, err := os.Stat(AbsPath(filename)); err != nil { log.Log(log.DEBUG, "couldnt stat file: " + err.Error(), "fexists") out = ast.FALSE } ret := &ast.Token{Tag: ast.BOOL} ret.Set(out) return ret } /* Takes one arg, returns a string * Returns contents of file in arg * returns nil if file doesnt exist * * Example: * (fread (concat HOME ".shsrc")) */ func Fread(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { in = in.Eval(ft, vt, false) exists := fexists(in, vt, ft) // some waste, extra use of Eval if exists == nil || exists.Tag != ast.BOOL || exists.Value() == ast.FALSE { log.Log(log.ERR, "error calling fexists or file doesnt exist", "fread") return nil } fname := in.Value() text, err := ioutil.ReadFile(fname) if err != nil { log.Log(log.ERR, "error reading file" + err.Error(), "fread") return nil } ret := &ast.Token{Tag: ast.STRING} ret.Set(string(text)) return ret } /* Takes two arguments a filepath and a string * CLOBBERS FILE CONTENTS * Returns nil * * Example: * (fwrite "test" "one two three") */ func Fwrite(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { in = in.Eval(ft, vt, false) if in == nil || in.Tag == ast.SYMBOL || in.Tag == ast.LIST { log.Log(log.ERR, "first argument must be a filename", "fwrite") return nil } text := in.Next if text == nil || text.Tag == ast.SYMBOL || text.Tag == ast.LIST { log.Log(log.ERR, "second argument must be stringable", "fwrite") return nil } err := ioutil.WriteFile( AbsPath(in.Value()), []byte(text.Value()), 0644) if err != nil { log.Log(log.ERR, "error writing file: " + err.Error(), "fwrite") } return nil } /* Takes two arguments a filepath and a string * DOES NOT CLOBBER FILE CONTENTS * Returns nil * * Example: * (fwrite "test" "one two three") */ func Fappend(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { in = in.Eval(ft, vt, false) if in == nil || in.Tag == ast.SYMBOL || in.Tag == ast.LIST { log.Log(log.ERR, "first argument must be a filename", "fappend") return nil } text := in.Next if text == nil || text.Tag == ast.SYMBOL || text.Tag == ast.LIST { log.Log(log.ERR, "second argument must be stringable", "fappend") return nil } exists := fexists(in, vt, ft) if exists.Value() == ast.FALSE { log.Log(log.ERR, "file "+in.Value()+" does not exist", "fappend") return nil } f, err := os.OpenFile( AbsPath(in.Value()), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { log.Log(log.ERR, "couldnt open file for append: " + err.Error(), "fappend") return nil } defer f.Close() if _, err := f.WriteString(text.Value()); err != nil { log.Log(log.ERR, "error appending to file: " + err.Error(), "fappend") } return nil }