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  }