diff --git a/.github/workflows/go-build.yml b/.github/workflows/go-build.yml index c470c23..d5ae9b3 100644 --- a/.github/workflows/go-build.yml +++ b/.github/workflows/go-build.yml @@ -34,10 +34,26 @@ jobs: working-directory: ./backup4abap_go run: go build -o backup_mac64 ./main.go + - name: Build Scan Windows + env: + GOOS: windows + GOARCH: amd64 + working-directory: ./abapscan_go + run: go build -o scan_win64.exe ./main.go + + - name: Build Scan Mac + env: + GOOS: darwin + GOARCH: amd64 + working-directory: ./abapscan_go + run: go build -o scan_mac64 ./main.go + - name: Create Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') with: - body: "压缩代码下载器,用于从已配置 backup4abap_interface 的 ICF 服务通过 HTTP 请求下载代码包到本地,并自动解压。" - files: ./backup4abap_go/backup* + body: ${{ variables.RCOMMIT }} + files: | + ./backup4abap_go/backup* + ./abapscan_go/scan* token: ${{ secrets.RELEASE_TOKEN }} diff --git a/abapscan_go/go.mod b/abapscan_go/go.mod new file mode 100644 index 0000000..a35f121 --- /dev/null +++ b/abapscan_go/go.mod @@ -0,0 +1,3 @@ +module abapscan + +go 1.19 diff --git a/abapscan_go/main.go b/abapscan_go/main.go new file mode 100644 index 0000000..456f195 --- /dev/null +++ b/abapscan_go/main.go @@ -0,0 +1,45 @@ +package main + +import ( + "abapscan/util" + "fmt" + "os" +) + +func main() { + print("\tABAP Scan\n") + + // 暂不从外部读取 改写死 + // rl := util.ReadList{} + // err := rl.Load() + // if err != nil { + // fmt.Println(err) + // } + rl := util.ReadList{ + Pwd: []string{"SE38", "SE24", "SE37", "ench"}, + } + + abapTables := []util.KeyValue{} + + if len(rl.Pwd) > 0 { + pwd, _ := os.Getwd() + for pw := range rl.Pwd { + fmt.Printf("\n处理路径:") + fmt.Printf("\t%v\t%s\n", pw, rl.Pwd[pw]) + abapTables = append(abapTables, util.ReadFile(pwd+"/"+rl.Pwd[pw])...) + } + } else { + pwd, _ := os.Getwd() + fmt.Printf("处理路径: %s\n", pwd) + abapTables = util.ReadFile(pwd) + } + + // fmt.Println("\n匹配结果") + // fmt.Println(abapTables) + + fmt.Println("\n保存自建表") + err := util.Save(abapTables) + if err != nil { + fmt.Println(err) + } +} diff --git a/abapscan_go/util/processprint.go b/abapscan_go/util/processprint.go new file mode 100644 index 0000000..81986e0 --- /dev/null +++ b/abapscan_go/util/processprint.go @@ -0,0 +1,62 @@ +package util + +import ( + "errors" + "fmt" + "math" +) + +type PrintList struct { + count int + index int + print []string + value []int +} + +func (pl *PrintList) Init(pt []string) { + // if pl.count != 0 { + // return + // } + pl.count = len(pt) + pl.print = pt + + pl.index++ + + for i := 0; i < pl.count; i++ { + fmt.Printf("%-30s\t%-3v\n", pl.print[i], 0) + pl.index++ + pl.value = append(pl.value, 0) + } +} + +func (pl *PrintList) Print(idx int, vl int) error { + // \033[nA 上移动n行 B 下移 + if idx >= pl.count { + return errors.New("索引超长") + } + newIndex := pl.index - idx + + pl.value[idx-1] += vl + + if newIndex > 0 { + fmt.Printf("\r\033[%vA%-30s\t%-3v", math.Abs(float64(newIndex)), pl.print[idx-1], pl.value[idx-1]) + } else if newIndex < 0 { + fmt.Printf("\r\033[%vB%-30s\t%-3v", math.Abs(float64(newIndex)), pl.print[idx-1], pl.value[idx-1]) + } else { + fmt.Printf("\r%-30s\t%-3v", pl.print[idx-1], pl.value[idx-1]) + } + + pl.index = idx + + return nil +} + +func (pl *PrintList) End() { + if pl.index < pl.count { + fmt.Printf("\r\033[%vB", (pl.count - pl.index + 1)) + } + pl.index = 0 + pl.value = nil + pl.count = 0 + pl.print = nil +} diff --git a/abapscan_go/util/readcode.go b/abapscan_go/util/readcode.go new file mode 100644 index 0000000..8c51bd4 --- /dev/null +++ b/abapscan_go/util/readcode.go @@ -0,0 +1,250 @@ +package util + +import ( + "bufio" + "fmt" + "io" + "os" + "path/filepath" + "regexp" + "strings" +) + +type Relation struct { + Table []string `json:"table,omitempty"` + TableUsed []string `json:"tableUsed,omitempty"` + Cds []string `json:"cds,omitempty"` + ProxyClass []string `json:"proxyClas,omitempty"` + Function []string `json:"function,omitempty"` + Struct []string `json:"struct,omitempty"` + Lock []string `json:"lock,omitempty"` + AuthCheck []string `json:"authCheck,omitempty"` + Transaction []string `json:"transaction,omitempty"` +} + +type KeyValue struct { + path string + Main string + index int + Relations Relation `json:"relations,omitempty"` +} + +var Plt PrintList + +func ReadFile(pwd string) []KeyValue { + + abapTables := []KeyValue{} + + Plt.Init([]string{ + "table", + "tableUsed", + "cds", + "proxyClas", + "function", + "struct", + "lock", + "authCheck", + "transaction", + }) + + // fmt.Println("\n处理文件: ") + filepath.Walk(pwd, func(path string, info os.FileInfo, err error) error { + + if !info.IsDir() { + // fmt.Println(path) //打印path信息 + // fmt.Println(info.Name()) //打印文件 + + if info.Name() == ResultName { + return nil + } else { + // fmt.Printf("\n%s:\n", info.Name()) + } + + abapTable := KeyValue{Main: info.Name(), path: path} + + err := read(&abapTable) + if err != nil { + fmt.Println("Read:", err) + return nil + } + + if abapTable.index > 0 { + abapTables = append(abapTables, abapTable) + } + } + return nil + }) + + Plt.End() + + return abapTables +} + +func read(kv *KeyValue) error { + + file, err := os.Open(kv.path) + if err != nil { + return err + } + + defer file.Close() + + reader := bufio.NewReader(file) + + for { + line, _, err := reader.ReadLine() + if err == io.EOF { + break + } + + str := string(line) + + table := Relation{} + + regex := `(?i)` + + `(?: +(ZT(?:[PM]{2}|SD|FI|API|JOB)\d{3}\w*))|` + + // `(?: +(?:(?:MODIFY)|(?:INSERT)|(?:UPDATE))(?: +((?:ZT[PM]{2}|ZTSD|ZTFI)\d{3}\w*)))|` + + `(?: +(ZV.*?_?(?:(?:DDL)|(?:VIEW))))|` + + `(ZCO_SI_IF\d{3}\w+)|` + + `(?:CALL +FUNCTION +'(Z\w*)')|` + + `(?: +(Z(?:S|TT)(?:[PM]{2}|SD|FI|XX)\d{3}\w*))|` + + `(?: +AUTHORITY-CHECK +OBJECT +'(.*?)')|` + + `(?:CALL +TRANSACTION[ \n]+'(.*?)')` + + reg := regexp.MustCompile(regex) + + sub := reg.FindAllStringSubmatch(str, -1) + + if len(sub) > 0 { + for rp := range sub { + for s := range sub[rp] { + if s > 0 && len(sub[rp][s]) > 0 { + kv.index++ + + // 显示 + scanPrint(s, sub[rp][s]) + + value := strings.ToUpper(sub[rp][s]) + + switch s { + case 1: + table.Table = append(table.Table, value) + Plt.Print(1, 1) + case 2: + table.Cds = append(table.Cds, value) + Plt.Print(3, 1) + case 3: + table.ProxyClass = append(table.ProxyClass, value) + Plt.Print(4, 1) + case 4: + table.Function = append(table.Function, value) + Plt.Print(5, 1) + case 5: + table.Struct = append(table.Struct, value) + Plt.Print(6, 1) + case 6: + table.AuthCheck = append(table.AuthCheck, value) + Plt.Print(8, 1) + case 7: + table.Transaction = append(table.Transaction, value) + Plt.Print(9, 1) + } + + } + } + } + } + + // 使用的表 + if len(table.Table) != 0 { + regex = `(?i)` + + `(?: +(?:(?:MODIFY)|(?:INSERT)|(?:UPDATE))(?: +((?:ZT[PM]{2}|ZTSD|ZTFI)\d{3}\w*)))` + reg = regexp.MustCompile(regex) + sub = reg.FindAllStringSubmatch(str, -1) + if len(sub) > 0 { + for rp := range sub { + for s := range sub[rp] { + if len(sub[rp][s]) > 0 { + kv.index++ + + // 显示 + scanPrint(s, sub[rp][1]) + + Plt.Print(2, 1) + + table.TableUsed = append(table.TableUsed, strings.ToUpper(sub[rp][1])) + } + } + } + } + } + + // 锁情况 + regex = `(?:'(?:ENQUEUE_|DEQUEUE_)(\w+)')` // 不忽略大小写 + reg = regexp.MustCompile(regex) + sub = reg.FindAllStringSubmatch(str, -1) + if len(sub) > 0 { + for rp := range sub { + for s := range sub[rp] { + if s > 0 && len(sub[rp][s]) > 0 { + kv.index++ + + // 显示 + scanPrint(s, sub[rp][1]) + Plt.Print(7, 1) + + table.Lock = append(table.Lock, strings.ToUpper(sub[rp][1])) + } + } + } + } + + kv.Relations.Table = append(kv.Relations.Table, table.Table...) + kv.Relations.Cds = append(kv.Relations.Cds, table.Cds...) + kv.Relations.ProxyClass = append(kv.Relations.ProxyClass, table.ProxyClass...) + kv.Relations.Function = append(kv.Relations.Function, table.Function...) + kv.Relations.Struct = append(kv.Relations.Struct, table.Struct...) + kv.Relations.TableUsed = append(kv.Relations.TableUsed, table.TableUsed...) + kv.Relations.Lock = append(kv.Relations.Lock, table.Lock...) + kv.Relations.AuthCheck = append(kv.Relations.AuthCheck, table.AuthCheck...) + kv.Relations.Transaction = append(kv.Relations.Transaction, table.Transaction...) + } + + // fmt.Println(kv.index) + + if kv.index > 0 { + kv.Relations.Table = removeDuplication_map(kv.Relations.Table) + kv.Relations.Cds = removeDuplication_map(kv.Relations.Cds) + kv.Relations.ProxyClass = removeDuplication_map(kv.Relations.ProxyClass) + kv.Relations.Function = removeDuplication_map(kv.Relations.Function) + kv.Relations.Struct = removeDuplication_map(kv.Relations.Struct) + kv.Relations.TableUsed = removeDuplication_map(kv.Relations.TableUsed) + kv.Relations.Lock = removeDuplication_map(kv.Relations.Lock) + kv.Relations.AuthCheck = removeDuplication_map(kv.Relations.AuthCheck) + kv.Relations.Transaction = removeDuplication_map(kv.Relations.Transaction) + } + + return nil +} + +func removeDuplication_map(arr []string) []string { + set := make(map[string]struct{}, len(arr)) + j := 0 + for _, v := range arr { + _, ok := set[v] + if ok { + continue + } + set[v] = struct{}{} + arr[j] = v + j++ + } + + return arr[:j] +} + +func scanPrint(index int, str string) { + // fmt.Printf("%v\t", s) + // fmt.Println(sub[1]) + // fmt.Printf("\r%-10v%-30s", index, str) +} diff --git a/abapscan_go/util/readlist.go b/abapscan_go/util/readlist.go new file mode 100644 index 0000000..66b799b --- /dev/null +++ b/abapscan_go/util/readlist.go @@ -0,0 +1,25 @@ +package util + +import ( + "encoding/json" + "os" +) + +type ReadList struct { + Pwd []string `json:"pwd"` +} + +func (rl *ReadList) Load() error { + setFile, err := os.Open("readlist.json") + if err != nil { + return err + } + + defer setFile.Close() + + if err := json.NewDecoder(setFile).Decode(rl); err != nil { + return err + } + + return nil +} diff --git a/abapscan_go/util/saveresult.go b/abapscan_go/util/saveresult.go new file mode 100644 index 0000000..6dc5218 --- /dev/null +++ b/abapscan_go/util/saveresult.go @@ -0,0 +1,27 @@ +package util + +import ( + "encoding/json" + "os" +) + +var ResultName string = "relations.json" + +func Save(kv []KeyValue) error { + + setFile, err := os.Create(ResultName) + if err != nil { + return err + } + + defer setFile.Close() + + // 编码写入配置文件; + cfgEncoder := json.NewEncoder(setFile) + cfgEncoder.SetIndent("", " ") + if err := cfgEncoder.Encode(kv); err != nil { + return err + } + + return nil +}