github.com/goplus/llgo@v0.8.3/xtool/nm/query.go (about) 1 /* 2 * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package nm 18 19 import ( 20 "bufio" 21 "os" 22 "strings" 23 ) 24 25 // MatchedItem represents a matched item 26 type MatchedItem struct { 27 ObjFile string 28 Symbol string 29 Type SymbolType 30 } 31 32 // MatchedFile represents a matched file 33 type MatchedFile struct { 34 ArFile string 35 Items []*MatchedItem 36 } 37 38 // Query queries symbol in index files (allow wildcard). 39 func Query(dir string, query string) (files []*MatchedFile, err error) { 40 fis, err := os.ReadDir(dir) 41 if err != nil { 42 return 43 } 44 dir += "/" 45 for _, fi := range fis { 46 if fi.IsDir() { 47 continue 48 } 49 idxFile := fi.Name() 50 if !strings.HasSuffix(idxFile, ".pub") { 51 continue 52 } 53 files = queryIndex(files, dir+fi.Name(), query) 54 } 55 return 56 } 57 58 func queryIndex(files []*MatchedFile, idxFile, query string) []*MatchedFile { 59 f, err := os.Open(idxFile) 60 if err != nil { 61 return files 62 } 63 defer f.Close() 64 65 r := bufio.NewReader(f) 66 line, err := r.ReadString('\n') 67 if err != nil || !strings.HasPrefix(line, "nm ") { 68 return files 69 } 70 var items []*MatchedItem 71 arFile := line[3 : len(line)-1] 72 objFile := "" 73 query, flags := parseQuery(query) 74 for { 75 line, err = r.ReadString('\n') 76 if err != nil { 77 break 78 } 79 if strings.HasPrefix(line, "file ") { 80 objFile = line[5 : len(line)-1] 81 continue 82 } 83 typ := line[0] 84 sym := line[2 : len(line)-1] 85 if !match(sym, query, flags) { 86 continue 87 } 88 items = append(items, &MatchedItem{ 89 ObjFile: objFile, 90 Symbol: sym, 91 Type: SymbolType(typ), 92 }) 93 } 94 if len(items) > 0 { 95 files = append(files, &MatchedFile{ArFile: arFile, Items: items}) 96 } 97 return files 98 } 99 100 const ( 101 flagSuffix = 1 << iota 102 flagPrefix 103 ) 104 105 func parseQuery(query string) (text string, flags int) { 106 if strings.HasSuffix(query, "*") { 107 query = query[:len(query)-1] 108 flags = flagPrefix 109 } 110 if strings.HasPrefix(query, "*") { 111 query = query[1:] 112 flags |= flagSuffix 113 } 114 text = query 115 return 116 } 117 118 func match(s, query string, flags int) bool { 119 switch flags { 120 case 0: 121 return s == query 122 case flagPrefix: 123 return strings.HasPrefix(s, query) 124 case flagSuffix: 125 return strings.HasSuffix(s, query) 126 default: 127 return strings.Contains(s, query) 128 } 129 }