github.com/dennwc/btrfs@v0.0.0-20221026161108-3097362dc072/internal/cmd/hgen.go (about) 1 package cmd 2 3 import ( 4 "bufio" 5 "bytes" 6 "flag" 7 "fmt" 8 "io" 9 "log" 10 "os" 11 "regexp" 12 "strings" 13 "unicode" 14 ) 15 16 var ( 17 f_pkg = flag.String("p", "main", "package name for generated file") 18 f_out = flag.String("o", "-", "output file") 19 f_unexport = flag.Bool("u", true, "make all definitions unexported") 20 f_goname = flag.Bool("g", true, "rename symbols to follow Go conventions") 21 f_trim = flag.String("t", "", "prefix to trim from names") 22 23 f_constSuf = flag.String("cs", "", "comma-separated list of constant suffixes to create typed constants") 24 f_constPref = flag.String("cp", "", "comma-separated list of constant prefixes to create typed constants") 25 ) 26 27 var ( 28 reDefineIntConst = regexp.MustCompile(`#define\s+([A-Za-z_][A-Za-z\d_]*)\s+(\(?-?\d+(?:U?LL)?(?:\s*<<\s*\d+)?\)?)`) 29 reNegULL = regexp.MustCompile(`-(\d+)ULL`) 30 ) 31 32 var ( 33 constTypes []constType 34 ) 35 36 type constType struct { 37 Name string 38 Type string 39 Suffix string 40 Prefix string 41 } 42 43 func constName(s string) string { 44 s = strings.TrimPrefix(s, *f_trim) 45 typ := "" 46 for _, t := range constTypes { 47 if t.Suffix != "" && strings.HasSuffix(s, t.Suffix) { 48 //s = strings.TrimSuffix(s, t.Suffix) 49 typ = t.Name 50 break 51 } else if t.Prefix != "" && strings.HasPrefix(s, t.Prefix) { 52 typ = t.Name 53 break 54 } 55 } 56 if *f_goname { 57 buf := bytes.NewBuffer(nil) 58 buf.Grow(len(s)) 59 up := !*f_unexport 60 for _, r := range s { 61 if r == '_' { 62 up = true 63 continue 64 } 65 if up { 66 up = false 67 r = unicode.ToUpper(r) 68 } else { 69 r = unicode.ToLower(r) 70 } 71 buf.WriteRune(r) 72 } 73 s = buf.String() 74 } else if *f_unexport { 75 s = "_" + s 76 } 77 if typ != "" { 78 s += " " + typ 79 } 80 return s 81 } 82 83 func process(w io.Writer, path string) error { 84 file, err := os.Open(path) 85 if err != nil { 86 return err 87 } 88 defer file.Close() 89 r := bufio.NewReader(file) 90 91 var ( 92 comment = false 93 firstComment = true 94 firstLineInComment = false 95 ) 96 97 nl := true 98 defer fmt.Fprintln(w, ")") 99 for { 100 line, err := r.ReadBytes('\n') 101 if err == io.EOF { 102 return nil 103 } else if err != nil { 104 return err 105 } 106 line = bytes.TrimSpace(line) 107 if len(line) == 0 { 108 if !nl { 109 nl = true 110 w.Write([]byte("\n")) 111 } 112 continue 113 } 114 nl = false 115 116 if bytes.HasPrefix(line, []byte("/*")) { 117 comment = true 118 firstLineInComment = true 119 line = bytes.TrimPrefix(line, []byte("/*")) 120 } 121 if comment { 122 ends := bytes.HasSuffix(line, []byte("*/")) 123 if ends { 124 comment = false 125 line = bytes.TrimSuffix(line, []byte("*/")) 126 } 127 line = bytes.TrimLeft(line, " \t*") 128 if len(line) > 0 { 129 if !firstComment { 130 w.Write([]byte("\t")) 131 } 132 w.Write([]byte("// ")) 133 if firstLineInComment { 134 line[0] = byte(unicode.ToUpper(rune(line[0]))) 135 } 136 line = bytes.Replace(line, []byte(" "), []byte(" "), -1) 137 w.Write(line) 138 w.Write([]byte("\n")) 139 firstLineInComment = false 140 } 141 if ends && firstComment { 142 firstComment = false 143 fmt.Fprint(w, "\nconst (\n") 144 nl = true 145 } 146 firstLineInComment = firstLineInComment && !ends 147 continue 148 } 149 if bytes.HasPrefix(line, []byte("#define")) { 150 sub := reDefineIntConst.FindStringSubmatch(string(line)) 151 if len(sub) > 0 { 152 name, val := sub[1], sub[2] 153 if sub := reNegULL.FindAllStringSubmatch(val, -1); len(sub) > 0 { 154 for _, s := range sub { 155 val = strings.Replace(val, s[0], fmt.Sprintf("(1<<64 - %s)", s[1]), -1) 156 } 157 } 158 val = strings.Replace(val, "ULL", "", -1) 159 fmt.Fprintf(w, "\t%s = %s\n", constName(name), val) 160 continue 161 } 162 } 163 } 164 } 165 166 func regConstTypes(str string, fnc func(*constType, string)) { 167 for _, s := range strings.Split(str, ",") { 168 kv := strings.Split(s, "=") 169 if len(kv) != 2 { 170 continue 171 } 172 st := strings.Split(kv[0], ":") 173 typ := "int" 174 if len(st) > 1 { 175 typ = st[1] 176 } 177 t := constType{Name: st[0], Type: typ} 178 fnc(&t, kv[1]) 179 constTypes = append(constTypes, t) 180 } 181 } 182 183 func main() { 184 flag.Parse() 185 if suf := *f_constSuf; suf != "" { 186 regConstTypes(suf, func(t *constType, v string) { t.Suffix = v }) 187 } 188 if pref := *f_constPref; pref != "" { 189 regConstTypes(pref, func(t *constType, v string) { t.Prefix = v }) 190 } 191 var w io.Writer = os.Stdout 192 if path := *f_out; path != "" && path != "-" { 193 file, err := os.Create(path) 194 if err != nil { 195 log.Fatal(err) 196 } 197 defer file.Close() 198 w = file 199 } 200 201 fmt.Fprintf(w, "package %s\n\n", *f_pkg) 202 fmt.Fprint(w, "// This code was auto-generated; DO NOT EDIT!\n\n") 203 for _, t := range constTypes { 204 fmt.Fprintf(w, "type %s %s\n\n", t.Name, t.Type) 205 } 206 for _, path := range flag.Args() { 207 if err := process(w, path); err != nil { 208 log.Fatal(err) 209 } 210 } 211 }