github.com/viant/toolbox@v0.34.5/storage/scp/fileinfo_parser.go (about) 1 package scp 2 3 import ( 4 "fmt" 5 "github.com/lunixbochs/vtclean" 6 "github.com/viant/toolbox" 7 "github.com/viant/toolbox/storage" 8 "net/url" 9 "strings" 10 "time" 11 "unicode" 12 ) 13 14 const ( 15 fileInfoPermission = iota 16 _ 17 fileInfoOwner 18 fileInfoGroup 19 fileInfoSize 20 fileInfoDateMonth 21 fileInfoDateDay 22 fileInfoDateHour 23 fileInfoDateYear 24 fileInfoName 25 ) 26 27 const ( 28 fileIsoInfoPermission = iota 29 _ 30 fileIsoInfoOwner 31 fileIsoInfoGroup 32 fileIsoInfoSize 33 fileIsoDate 34 fileIsoTime 35 fileIsoTimezone 36 fileIsoInfoName 37 ) 38 39 //Parser represents fileinfo parser from stdout 40 type Parser struct { 41 IsoTimeStyle bool 42 } 43 44 func (p *Parser) Parse(parsedURL *url.URL, stdout string, isURLDir bool) ([]storage.Object, error) { 45 var err error 46 var result = make([]storage.Object, 0) 47 if strings.Contains(stdout, "No such file or directory") { 48 return result, nil 49 } 50 for _, line := range strings.Split(stdout, "\n") { 51 if line == "" { 52 continue 53 } 54 var object storage.Object 55 if p.IsoTimeStyle { 56 if object, err = p.extractObjectFromIsoBasedTimeCommand(parsedURL, line, isURLDir); err != nil { 57 object, err = p.extractObjectFromNonIsoBaseTimeCommand(parsedURL, line, isURLDir) 58 } 59 } else { 60 if object, err = p.extractObjectFromNonIsoBaseTimeCommand(parsedURL, line, isURLDir); err != nil { 61 object, err = p.extractObjectFromIsoBasedTimeCommand(parsedURL, line, isURLDir) 62 } 63 } 64 if err != nil { 65 return nil, err 66 } 67 result = append(result, object) 68 } 69 return result, nil 70 } 71 72 func (p *Parser) HasNextTokenInout(nextTokenPosition int, line string) bool { 73 if nextTokenPosition >= len(line) { 74 return false 75 } 76 nextToken := []rune(string(line[nextTokenPosition:]))[0] 77 return !unicode.IsSpace(nextToken) 78 } 79 80 func (p *Parser) newObject(parsedURL *url.URL, name, permission, line, size string, modificationTime time.Time, isURLDirectory bool) (storage.Object, error) { 81 var URLPath = parsedURL.Path 82 var URL = parsedURL.String() 83 var pathPosition = strings.Index(URL, parsedURL.Host) + len(parsedURL.Host) 84 var URLPrefix = URL[:pathPosition] 85 86 fileMode, err := storage.NewFileMode(permission) 87 if err != nil { 88 return nil, fmt.Errorf("failed to parse line for lineinfo: %v, unable to file attributes: %v", line, err) 89 } 90 if isURLDirectory { 91 name = strings.Replace(name, URLPath, "", 1) 92 URLPath = toolbox.URLPathJoin(URLPath, name) 93 } else { 94 URLPath = name 95 } 96 97 var objectURL = URLPrefix + URLPath 98 fileInfo := storage.NewFileInfo(name, int64(toolbox.AsInt(size)), fileMode, modificationTime, fileMode.IsDir()) 99 object := newStorageObject(objectURL, fileInfo, fileInfo) 100 return object, nil 101 } 102 103 //extractObjectFromNonIsoBaseTimeCommand extract file storage object from line, 104 // it expects a file info without iso i.e -rw-r--r-- 1 awitas 1742120565 414 Jun 8 14:14:08 2017 id_rsa.pub 105 func (p *Parser) extractObjectFromNonIsoBaseTimeCommand(parsedURL *url.URL, line string, isURLDirectory bool) (storage.Object, error) { 106 tokenIndex := 0 107 if strings.TrimSpace(line) == "" { 108 return nil, nil 109 } 110 var owner, name, permission, group, size, year, month, day, hour string 111 for i, aRune := range line { 112 if unicode.IsSpace(aRune) { 113 if p.HasNextTokenInout(i+1, line) { 114 tokenIndex++ 115 } 116 continue 117 } 118 119 aChar := string(aRune) 120 switch tokenIndex { 121 case fileInfoPermission: 122 permission += aChar 123 case fileInfoOwner: 124 owner += aChar 125 case fileInfoGroup: 126 group += aChar 127 case fileInfoSize: 128 if size == "" && !unicode.IsNumber(aRune) { 129 tokenIndex-- 130 group += " " + aChar 131 continue 132 } 133 size += aChar 134 case fileInfoDateMonth: 135 month += aChar 136 case fileInfoDateDay: 137 day += aChar 138 case fileInfoDateHour: 139 hour += aChar 140 case fileInfoDateYear: 141 year += aChar 142 case fileInfoName: 143 name += aChar 144 } 145 } 146 147 if name == "" { 148 return nil, fmt.Errorf("failed to parse line for fileinfo: %v\n", line) 149 } 150 dateTime := year + " " + month + " " + day + " " + hour 151 layout := toolbox.DateFormatToLayout("yyyy MMM ddd HH:mm:s") 152 modificationTime, err := time.Parse(layout, dateTime) 153 if err != nil { 154 return nil, fmt.Errorf("failed to extract file info from stdout: %v, err: %v", line, err) 155 } 156 157 return p.newObject(parsedURL, name, permission, line, size, modificationTime, isURLDirectory) 158 } 159 160 //extractObjectFromNonIsoBaseTimeCommand extract file storage object from line, 161 // it expects a file info with iso i.e. -rw-r--r-- 1 awitas awitas 2002 2017-11-04 22:29:33.363458941 +0000 aerospikeciads_aerospike.conf 162 func (p *Parser) extractObjectFromIsoBasedTimeCommand(parsedURL *url.URL, line string, isURLDirectory bool) (storage.Object, error) { 163 tokenIndex := 0 164 if strings.TrimSpace(line) == "" { 165 return nil, nil 166 } 167 var owner, name, permission, group, timezone, date, modTime, size string 168 line = vtclean.Clean(line, false) 169 for i, aRune := range line { 170 171 if unicode.IsSpace(aRune) { 172 if p.HasNextTokenInout(i+1, line) { 173 tokenIndex++ 174 } 175 continue 176 } 177 178 aChar := string(aRune) 179 switch tokenIndex { 180 case fileIsoInfoPermission: 181 permission += aChar 182 case fileIsoInfoOwner: 183 owner += aChar 184 case fileIsoInfoGroup: 185 group += aChar 186 case fileIsoInfoSize: 187 if size == "" && !unicode.IsNumber(aRune) { 188 tokenIndex-- 189 group += " " + aChar 190 continue 191 } 192 size += aChar 193 case fileIsoDate: 194 date += aChar 195 case fileIsoTime: 196 modTime += aChar 197 case fileIsoTimezone: 198 timezone += aChar 199 case fileIsoInfoName: 200 name += aChar 201 } 202 continue 203 } 204 timeLen := len(modTime) 205 if timeLen > 12 { 206 modTime = string(modTime[:12]) 207 } 208 dateTime := date + " " + modTime + " " + timezone 209 layout := toolbox.DateFormatToLayout("yyyy-MM-dd HH:mm:ss.SSS ZZ") 210 if len(date+" "+modTime) <= len("yyyy-MM-dd HH:mm:ss") { 211 layout = toolbox.DateFormatToLayout("yyyy-MM-dd HH:mm:ss ZZ") 212 } 213 modificationTime, err := time.Parse(layout, dateTime) 214 if err != nil { 215 return nil, fmt.Errorf("failed to extract file info from stdout: %v, err: %v", line, err) 216 } 217 return p.newObject(parsedURL, name, permission, line, size, modificationTime, isURLDirectory) 218 }