github.com/sentienttechnologies/studio-go-runner@v0.0.0-20201118202441-6d21f2ced8ee/internal/runner/io.go (about) 1 // Copyright 2018-2020 (c) Cognizant Digital Business, Evolutionary AI. All rights reserved. Issued under the Apache 2.0 License. 2 3 package runner 4 5 // This file contains routines for performing file io 6 7 import ( 8 "bufio" 9 "bytes" 10 "io" 11 "net/http" 12 "os" 13 "path/filepath" 14 15 "github.com/karlmutch/circbuf" 16 "github.com/karlmutch/vtclean" 17 18 "github.com/go-stack/stack" 19 "github.com/jjeffery/kv" // MIT License 20 ) 21 22 // ReadLast will extract the last portion of data from a file up to a maximum specified by 23 // the caller. 24 // 25 func ReadLast(fn string, max uint32) (data string, err kv.Error) { 26 file, errOs := os.Open(filepath.Clean(fn)) 27 if errOs != nil { 28 return "", kv.Wrap(errOs, fn).With("stack", stack.Trace().TrimRuntime()) 29 } 30 defer file.Close() 31 32 fi, errOs := file.Stat() 33 if errOs != nil { 34 return "", kv.Wrap(errOs, fn).With("stack", stack.Trace().TrimRuntime()) 35 } 36 37 // Suck up a lot of data to allow us to process lines with backspaces etc and still be left with 38 // something useful 39 // 40 buf := make([]byte, 1024*1024) 41 readStart := fi.Size() - int64(len(buf)) 42 43 if readStart <= 0 { 44 readStart = 0 45 } 46 47 n, errOs := file.ReadAt(buf, readStart) 48 if errOs != nil && errOs != io.EOF { 49 return "", kv.Wrap(errOs, fn).With("stack", stack.Trace().TrimRuntime()) 50 } 51 52 ring, _ := circbuf.NewBuffer(int64(max)) 53 s := bufio.NewScanner(bytes.NewReader(buf[:n])) 54 for s.Scan() { 55 ring.Write([]byte(vtclean.Clean(s.Text(), true))) 56 ring.Write([]byte{'\n'}) 57 } 58 return string(ring.Bytes()), nil 59 } 60 61 // DetectFileType can be used to examine the contexts of a file and return 62 // the most likely match for its contents as a mime type. 63 // 64 func DetectFileType(fn string) (typ string, err kv.Error) { 65 file, errOs := os.Open(filepath.Clean(fn)) 66 if errOs != nil { 67 return "", kv.Wrap(errOs).With("filename", fn).With("stack", stack.Trace().TrimRuntime()) 68 } 69 defer file.Close() 70 71 // Only the first 512 bytes are used to sniff the content type. 72 buffer := make([]byte, 512) 73 if _, errOs = file.Read(buffer); errOs != nil && errOs != io.EOF { 74 return "", kv.Wrap(errOs).With("filename", fn).With("stack", stack.Trace().TrimRuntime()) 75 } 76 77 // Always returns a valid content-type and "application/octet-stream" if no others seemed to match. 78 return http.DetectContentType(buffer), nil 79 } 80 81 // CopyFile is a simple file copy that will overwrite any destination 82 // 83 func CopyFile(srcFN string, dstFN string) (n int64, err kv.Error) { 84 stat, errGo := os.Stat(srcFN) 85 if errGo != nil { 86 return 0, kv.Wrap(errGo).With("source", src).With("stack", stack.Trace().TrimRuntime()) 87 } 88 89 if !stat.Mode().IsRegular() { 90 return 0, kv.NewError("not a regular file").With("source", src).With("stack", stack.Trace().TrimRuntime()) 91 } 92 93 src, errGo := os.Open(srcFN) 94 if errGo != nil { 95 return 0, kv.Wrap(errGo).With("source", srcFN).With("stack", stack.Trace().TrimRuntime()) 96 } 97 defer src.Close() 98 99 dst, errGo := os.Create(dstFN) 100 if err != nil { 101 return 0, kv.Wrap(errGo).With("dst", dstFN).With("stack", stack.Trace().TrimRuntime()) 102 } 103 defer dst.Close() 104 105 if n, errGo = io.Copy(dst, src); errGo != nil { 106 return 0, kv.Wrap(errGo).With("source", src, "dst", dstFN).With("stack", stack.Trace().TrimRuntime()) 107 } 108 return n, nil 109 }