package sysconf import ( "bytes" "errors" "fmt" "io/ioutil" "reflect" "strconv" "strings" "sync" "b612.me/staros" ) type SysConf struct { Data []*SysSegment segmap map[string]int64 segId int64 HaveSegMent bool //是否有节这个概念 SegStart string SegEnd string CommentFlag []string //评论标识符,如# EqualFlag string //赋值标识符,如= ValueFlag string //值标识符,如" EscapeFlag string //转义字符 CommentCR bool //评论是否能与value同一行,true不行,false可以 SpaceStr string //美化符号 lock sync.RWMutex } type SysSegment struct { Name string //nodeMap Comment string NodeData []*SysNode nodeId int64 nodeMap map[string]int64 lock sync.RWMutex } type SysNode struct { Key string Value []string Comment string NoValue bool lock sync.RWMutex } func NewSysConf(EqualFlag string) *SysConf { syscnf := new(SysConf) syscnf.EqualFlag = EqualFlag return syscnf } // NewLinuxConf sysctl.conf like file func NewLinuxConf(EqualFlag string) *SysConf { syscnf := new(SysConf) syscnf.EqualFlag = EqualFlag syscnf.HaveSegMent = false syscnf.CommentCR = true syscnf.CommentFlag = []string{"#"} return syscnf } func (syscfg *SysConf) ParseFromFile(filepath string) error { if !staros.Exists(filepath) { return errors.New(filepath + " 不存在") } data, err := ioutil.ReadFile(filepath) if err != nil { return err } syscfg.Parse(data) return nil } // Parse 生成INI文件结构 func (syscfg *SysConf) Parse(data []byte) error { syscfg.lock.Lock() defer syscfg.lock.Unlock() if syscfg.HaveSegMent && (syscfg.SegStart == "" || syscfg.SegEnd == "") { return errors.New("SegMent Start or End Flag Not Allowed!") } if !syscfg.CommentCR { return syscfg.parseNOCRComment(data) } return syscfg.parseCRComment(data) } func (syscfg *SysConf) parseNOCRComment(data []byte) error { //允许comment在同一行 data = bytes.TrimSpace(data) dataLists := bytes.Split(data, []byte("\n")) seg := new(SysSegment) seg.nodeMap = make(map[string]int64) syscfg.segmap = make(map[string]int64) if syscfg.HaveSegMent { seg.Name = "unnamed" } syscfg.segId = 0 var node *SysNode for _, v1 := range dataLists { var ( isSegStart bool = false isEscape bool = false isEqual bool = false isComment bool = false tsuMo string = "" ) cowStr := strings.TrimSpace(string(v1)) for i := 0; i < len(cowStr); i++ { runeStr := cowStr[i : i+1] //当前字符,rune扫描 if runeStr == syscfg.EscapeFlag && (!isEscape) { isEscape = true continue } if runeStr == syscfg.SegStart && (!isEscape) { isSegStart = true continue } if runeStr == syscfg.SegEnd && (!isEscape) { isSegStart = false //New segment start from here if seg.Name == "unnamed" && len(seg.NodeData) == 0 { seg.Name = tsuMo tsuMo = "" continue } syscfg.segmap[seg.Name] = syscfg.segId syscfg.segId++ syscfg.Data = append(syscfg.Data, seg) seg = new(SysSegment) seg.nodeMap = make(map[string]int64) seg.Name = tsuMo tsuMo = "" continue } if isSegStart { tsuMo += runeStr if isEscape { isEscape = false } continue } if syscfg.EqualFlag == runeStr && (!isEscape) && (!isEqual) { key := strings.TrimSpace(tsuMo) if val, ok := seg.nodeMap[key]; ok { node = seg.NodeData[val] } else { node = new(SysNode) node.Key = key seg.nodeMap[node.Key] = seg.nodeId seg.nodeId++ seg.NodeData = append(seg.NodeData, node) } tsuMo = "" isEqual = true if syscfg.ValueFlag != "" { nokoriStr := strings.TrimSpace(cowStr[i+1:]) isFound := false isValue := false for k4, v4 := range nokoriStr { if string([]rune{v4}) == syscfg.ValueFlag { isValue = !isValue } if SliceIn(syscfg.CommentFlag, string([]rune{v4})) && !isValue { val := nokoriStr[:k4] isFound = true startFinder := strings.Index(val, syscfg.ValueFlag) endFinder := strings.LastIndex(val, syscfg.ValueFlag) if !((startFinder == -1 || endFinder == -1) || (endFinder-startFinder <= 0)) { node.Value = append(node.Value, strings.TrimSpace(val[startFinder+1:endFinder])) } node.Comment = nokoriStr[k4+1:] + "\n" } } if !isFound { startFinder := strings.Index(nokoriStr, syscfg.ValueFlag) endFinder := strings.LastIndex(nokoriStr, syscfg.ValueFlag) if (startFinder == -1 || endFinder == -1) || (endFinder-startFinder <= 0) { break } node.Value = append(node.Value, strings.TrimSpace(nokoriStr[startFinder+1:endFinder])) } break } continue } if SliceIn(syscfg.CommentFlag, runeStr) && (!isEscape) { isComment = true if seg.nodeId == 0 { seg.Comment += strings.TrimSpace(cowStr[i+1:]) + "\n" break } if tsuMo != "" { node.Value = append(node.Value, strings.TrimSpace(tsuMo)) tsuMo = "" } node.Comment += strings.TrimSpace(cowStr[i+1:]) + "\n" break } isEscape = false tsuMo += runeStr } if isEqual && tsuMo != "" { node.Value = append(node.Value, strings.TrimSpace(tsuMo)) } if !isEqual && tsuMo != "" && !isComment { node = new(SysNode) node.Key = tsuMo seg.nodeMap[node.Key] = seg.nodeId seg.nodeId++ seg.NodeData = append(seg.NodeData, node) node.NoValue = true } } if seg != nil { syscfg.segmap[seg.Name] = syscfg.segId syscfg.segId++ syscfg.Data = append(syscfg.Data, seg) } return nil } func (syscfg *SysConf) parseCRComment(data []byte) error { //不允许comment在同一行 data = bytes.TrimSpace(data) dataLists := bytes.Split(data, []byte("\n")) seg := new(SysSegment) seg.nodeMap = make(map[string]int64) syscfg.segmap = make(map[string]int64) if syscfg.HaveSegMent { seg.Name = "unnamed" } syscfg.segId = 0 var node *SysNode for _, v1 := range dataLists { var ( isSegStart bool = false isEscape bool = false isEqual bool = false isComment bool = false tsuMo string = "" ) cowStr := strings.TrimSpace(string(v1)) for i := 0; i < len(cowStr); i++ { runeStr := cowStr[i : i+1] //当前字符,rune扫描 if runeStr == syscfg.EscapeFlag && (!isEscape) { isEscape = true continue } if runeStr == syscfg.SegStart && (!isEscape) { isSegStart = true continue } if runeStr == syscfg.SegEnd && (!isEscape) { isSegStart = false //New segment start from here if seg.Name == "unnamed" && len(seg.NodeData) == 0 { seg.Name = tsuMo tsuMo = "" break } syscfg.segmap[seg.Name] = syscfg.segId syscfg.segId++ syscfg.Data = append(syscfg.Data, seg) seg = new(SysSegment) seg.nodeMap = make(map[string]int64) seg.Name = tsuMo tsuMo = "" break } if isSegStart { tsuMo += runeStr if isEscape { isEscape = false } continue } if syscfg.EqualFlag == runeStr && (!isEscape) && (!isEqual) { key := strings.TrimSpace(tsuMo) if val, ok := seg.nodeMap[key]; ok { node = seg.NodeData[val] } else { node = new(SysNode) node.Key = key seg.nodeMap[node.Key] = seg.nodeId seg.nodeId++ seg.NodeData = append(seg.NodeData, node) } tsuMo = "" isEqual = true if syscfg.ValueFlag == "" { node.Value = append(node.Value, TrimEscape(strings.TrimSpace(cowStr[i+1:]), syscfg.EscapeFlag)) } else { nokoriStr := strings.TrimSpace(cowStr[i+1:]) startFinder := strings.Index(nokoriStr, syscfg.ValueFlag) endFinder := strings.LastIndex(nokoriStr, syscfg.ValueFlag) if (startFinder == -1 || endFinder == -1) || (endFinder-startFinder <= 0) { break } node.Value = append(node.Value, strings.TrimSpace(nokoriStr[startFinder+1:endFinder])) } break } if SliceIn(syscfg.CommentFlag, runeStr) && (!isEscape) { isComment = true tsuMo = "" if seg.nodeId == 0 { seg.Comment += strings.TrimSpace(cowStr[i+1:]) + "\n" break } node.Comment += strings.TrimSpace(cowStr[i+1:]) + "\n" break } isEscape = false tsuMo += runeStr } if !isEqual && tsuMo != "" && !isComment { node = new(SysNode) node.Key = tsuMo seg.nodeMap[node.Key] = seg.nodeId seg.nodeId++ seg.NodeData = append(seg.NodeData, node) node.NoValue = true } } if seg != nil { syscfg.segmap[seg.Name] = syscfg.segId syscfg.segId++ syscfg.Data = append(syscfg.Data, seg) } return nil } func (syscfg *SysConf) Build() []byte { syscfg.lock.Lock() defer syscfg.lock.Unlock() var outPut string for _, v := range syscfg.Data { if v == nil { continue } if syscfg.HaveSegMent { outPut += syscfg.SegStart + v.Name + syscfg.SegEnd + "\n" } if v.Comment != "" { v.Comment = v.Comment[:len(v.Comment)-1] comment := strings.Split(v.Comment, "\n") for _, vc := range comment { if vc != "" { outPut += syscfg.CommentFlag[0] + vc + "\n" } else { outPut += "\n" } } } for _, v2 := range v.NodeData { if v2 == nil { continue } if v2.NoValue { outPut += v2.Key + "\n" } else { for _, v3 := range v2.Value { if syscfg.ValueFlag != "" { outPut += v2.Key + syscfg.SpaceStr + syscfg.EqualFlag + syscfg.SpaceStr + syscfg.ValueFlag + v3 + syscfg.ValueFlag + "\n" } else { outPut += v2.Key + syscfg.SpaceStr + syscfg.EqualFlag + syscfg.SpaceStr + syscfg.addEscape(v3) + "\n" } } if len(v2.Value) == 0 { outPut += v2.Key + syscfg.SpaceStr + syscfg.EqualFlag + "\n" } if v2.Comment != "" { v2.Comment = v2.Comment[:len(v2.Comment)-1] comment := strings.Split(v2.Comment, "\n") for _, vc := range comment { if vc != "" { outPut += syscfg.CommentFlag[0] + vc + "\n" } else { outPut += "\n" } } } } } } return []byte(outPut) } func (syscfg *SysConf) addEscape(str string) string { str = strings.ReplaceAll(str, syscfg.EscapeFlag, syscfg.EscapeFlag+syscfg.EscapeFlag) str = strings.ReplaceAll(str, syscfg.EqualFlag, syscfg.EscapeFlag+syscfg.EqualFlag) str = strings.ReplaceAll(str, syscfg.SegStart, syscfg.EscapeFlag+syscfg.SegStart) str = strings.ReplaceAll(str, syscfg.SegEnd, syscfg.EscapeFlag+syscfg.SegEnd) for _, v := range syscfg.CommentFlag { str = strings.ReplaceAll(str, v, syscfg.EscapeFlag+v) } return str } func (syscfg *SysConf) Reverse() { syscfg.lock.Lock() defer syscfg.lock.Unlock() for _, v := range syscfg.Data { if v == nil { continue } var ( NodeData []*SysNode nodeId int64 nodeMap map[string]int64 ) nodeMap = make(map[string]int64) for _, v2 := range v.NodeData { if v2 == nil { continue } for _, v3 := range v2.Value { var node *SysNode if val, ok := nodeMap[v3]; ok { node = NodeData[val] } else { node = new(SysNode) node.Key = v3 NodeData = append(NodeData, node) nodeMap[v3] = nodeId nodeId++ } node.Value = append(node.Value, strings.TrimSpace(v2.Key)) node.Comment += v2.Comment } v.NodeData = NodeData v.nodeId = nodeId v.nodeMap = nodeMap } } } func TrimEscape(text, escape string) string { var isEscape bool = false var outPut []rune if escape == "" { return text } text = strings.TrimSpace(text) for _, v := range text { if v == []rune(escape)[0] && !isEscape { isEscape = true continue } outPut = append(outPut, v) } return string(outPut) } func SliceIn(slice interface{}, data interface{}) bool { typed := reflect.ValueOf(slice) if typed.Kind() == reflect.Slice || typed.Kind() == reflect.Array { for i := 0; i < typed.Len(); i++ { if typed.Index(i).Interface() == data { return true } } } return false } // Unmarshal 输出结果到结构体中 func (cfg *SysConf) Unmarshal(ins interface{}) error { var structSet func(t reflect.Type, v reflect.Value) error t := reflect.TypeOf(ins) v := reflect.ValueOf(ins).Elem() if v.Kind() != reflect.Struct { return errors.New("Not a Struct") } if t.Kind() != reflect.Ptr || !v.CanSet() { return errors.New("Cannot Write!") } t = t.Elem() structSet = func(t reflect.Type, v reflect.Value) error { for i := 0; i < t.NumField(); i++ { tp := t.Field(i) vl := v.Field(i) if !vl.CanSet() { continue } if vl.Type().Kind() == reflect.Struct { structSet(vl.Type(), vl) continue } seg := tp.Tag.Get("seg") key := tp.Tag.Get("key") if seg == "" || key == "" { continue } if _, ok := cfg.segmap[seg]; !ok { continue } segs := cfg.Data[cfg.segmap[seg]] if segs.Get(key) == "" { continue } switch vl.Kind() { case reflect.String: vl.SetString(segs.Get(key)) case reflect.Int, reflect.Int32, reflect.Int64: vl.SetInt(segs.Int64(key)) case reflect.Float32, reflect.Float64: vl.SetFloat(segs.Float64(key)) case reflect.Bool: vl.SetBool(segs.Bool(key)) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: vl.SetUint(uint64(segs.Int64(key))) default: continue } } return nil } return structSet(t, v) } // Marshal 输出结果到结构体中 func (cfg *SysConf) Marshal(ins interface{}) ([]byte, error) { var structSet func(t reflect.Type, v reflect.Value) t := reflect.TypeOf(ins) v := reflect.ValueOf(ins) if v.Kind() != reflect.Struct { return nil, errors.New("Not a Struct") } if t.Kind() == reflect.Ptr { t = t.Elem() v = v.Elem() } structSet = func(t reflect.Type, v reflect.Value) { for i := 0; i < t.NumField(); i++ { var seg, key, comment string = "", "", "" tp := t.Field(i) vl := v.Field(i) if vl.Type().Kind() == reflect.Struct { structSet(vl.Type(), vl) continue } seg = tp.Tag.Get("seg") key = tp.Tag.Get("key") comment = tp.Tag.Get("comment") if seg == "" || key == "" { continue } if _, ok := cfg.segmap[seg]; !ok { cfg.AddSeg(seg) } cfg.Seg(seg).Set(key, fmt.Sprint(vl), comment) } } structSet(t, v) return cfg.Build(), nil } func (syscfg *SysConf) Seg(name string) *SysSegment { if _, ok := syscfg.segmap[name]; !ok { return nil } seg := syscfg.Data[syscfg.segmap[name]] return seg } func (syscfg *SysConf) AddSeg(name string) *SysSegment { syscfg.lock.Lock() defer syscfg.lock.Unlock() if _, ok := syscfg.segmap[name]; !ok { newseg := new(SysSegment) newseg.Name = name newseg.nodeMap = make(map[string]int64) syscfg.Data = append(syscfg.Data, newseg) syscfg.segId++ if syscfg.segmap == nil { syscfg.segId = 0 syscfg.segmap = make(map[string]int64) } syscfg.segmap[newseg.Name] = syscfg.segId return newseg } seg := syscfg.Data[syscfg.segmap[name]] return seg } func (syscfg *SysConf) DeleteSeg(name string) error { syscfg.lock.Lock() defer syscfg.lock.Unlock() if _, ok := syscfg.segmap[name]; !ok { return errors.New("Seg Not Exists") } syscfg.Data[syscfg.segmap[name]] = nil delete(syscfg.segmap, name) return nil } func (syscfg *SysSegment) GetComment(key string) string { if v, ok := syscfg.nodeMap[key]; !ok { return "" } else { return syscfg.NodeData[v].Comment } } func (syscfg *SysSegment) SetComment(key, comment string) error { syscfg.lock.Lock() defer syscfg.lock.Unlock() if v, ok := syscfg.nodeMap[key]; !ok { return errors.New("Key Not Exists") } else { syscfg.NodeData[v].Comment = comment return nil } } func (syscfg *SysSegment) Exist(key string) bool { if _, ok := syscfg.nodeMap[key]; !ok { return false } else { return true } } func (syscfg *SysSegment) Get(key string) string { if v, ok := syscfg.nodeMap[key]; !ok { return "" } else { if len(syscfg.NodeData[v].Value) >= 1 { return syscfg.NodeData[v].Value[0] } } return "" } func (syscfg *SysSegment) GetAll(key string) []string { if v, ok := syscfg.nodeMap[key]; !ok { return []string{} } else { return syscfg.NodeData[v].Value } } func (syscfg *SysSegment) Int(key string) int { val := syscfg.Get(key) if val == "" { return 0 } res, _ := strconv.Atoi(val) return res } func (syscfg *SysSegment) Int64(key string) int64 { val := syscfg.Get(key) if val == "" { return 0 } res, _ := strconv.ParseInt(val, 10, 64) return res } func (syscfg *SysSegment) Int32(key string) int32 { val := syscfg.Get(key) if val == "" { return 0 } res, _ := strconv.ParseInt(val, 10, 32) return int32(res) } func (syscfg *SysSegment) Float64(key string) float64 { val := syscfg.Get(key) if val == "" { return 0 } res, _ := strconv.ParseFloat(val, 64) return res } func (syscfg *SysSegment) Float32(key string) float32 { val := syscfg.Get(key) if val == "" { return 0 } res, _ := strconv.ParseFloat(val, 32) return float32(res) } func (syscfg *SysSegment) Bool(key string) bool { val := syscfg.Get(key) if val == "" { return false } res, _ := strconv.ParseBool(val) return res } func (syscfg *SysSegment) SetBool(key string, value bool, comment string) error { res := strconv.FormatBool(value) return syscfg.Set(key, res, comment) } func (syscfg *SysSegment) SetFloat64(key string, prec int, value float64, comment string) error { res := strconv.FormatFloat(value, 'f', prec, 64) return syscfg.Set(key, res, comment) } func (syscfg *SysSegment) SetFloat32(key string, prec int, value float32, comment string) error { res := strconv.FormatFloat(float64(value), 'f', prec, 32) return syscfg.Set(key, res, comment) } func (syscfg *SysSegment) SetUint64(key string, value uint64, comment string) error { res := strconv.FormatUint(value, 10) return syscfg.Set(key, res, comment) } func (syscfg *SysSegment) SetInt64(key string, value int64, comment string) error { res := strconv.FormatInt(value, 10) return syscfg.Set(key, res, comment) } func (syscfg *SysSegment) SetInt32(key string, value int32, comment string) error { res := strconv.FormatInt(int64(value), 10) return syscfg.Set(key, res, comment) } func (syscfg *SysSegment) SetInt(key string, value int, comment string) error { res := strconv.Itoa(value) return syscfg.Set(key, res, comment) } func (syscfg *SysSegment) Set(key, value, comment string) error { syscfg.lock.Lock() defer syscfg.lock.Unlock() if v, ok := syscfg.nodeMap[key]; !ok { node := new(SysNode) node.Key = key node.Value = append(node.Value, value) node.Comment = comment syscfg.NodeData = append(syscfg.NodeData, node) syscfg.nodeMap[key] = syscfg.nodeId syscfg.nodeId++ return nil } else { syscfg.NodeData[v].Value = []string{value} if comment != "" { syscfg.NodeData[v].Comment = comment } } return nil } func (syscfg *SysSegment) SetAll(key string, value []string, comment string) error { syscfg.lock.Lock() defer syscfg.lock.Unlock() if v, ok := syscfg.nodeMap[key]; !ok { node := new(SysNode) node.Key = key node.Value = value node.Comment = comment syscfg.NodeData = append(syscfg.NodeData, node) syscfg.nodeMap[key] = syscfg.nodeId syscfg.nodeId++ return nil } else { syscfg.NodeData[v].Value = value if comment != "" { syscfg.NodeData[v].Comment = comment } } return nil } func (syscfg *SysSegment) AddValue(key, value, comment string) error { syscfg.lock.Lock() defer syscfg.lock.Unlock() if v, ok := syscfg.nodeMap[key]; !ok { node := new(SysNode) node.Key = key node.Value = append(node.Value, value) node.Comment = comment syscfg.NodeData = append(syscfg.NodeData, node) syscfg.nodeMap[key] = syscfg.nodeId syscfg.nodeId++ return nil } else { syscfg.NodeData[v].Value = append(syscfg.NodeData[v].Value, value) if comment != "" { syscfg.NodeData[v].Comment = comment } } return nil } func (syscfg *SysSegment) Delete(key string) error { syscfg.lock.Lock() defer syscfg.lock.Unlock() if v, ok := syscfg.nodeMap[key]; !ok { return errors.New("Key not exists!") } else { if syscfg.NodeData[v].Comment != "" { cmtSet := false for j := v - 1; j >= 0; j-- { if syscfg.NodeData[j] != nil { syscfg.NodeData[j].Comment += syscfg.NodeData[v].Comment cmtSet = true break } } if !cmtSet { syscfg.Comment += syscfg.NodeData[v].Comment } } syscfg.NodeData[v] = nil delete(syscfg.nodeMap, key) } return nil } func (syscfg *SysSegment) DeleteValue(key string, Value string) error { syscfg.lock.Lock() defer syscfg.lock.Unlock() if v, ok := syscfg.nodeMap[key]; !ok { return errors.New("Key not exists!") } else { data := syscfg.NodeData[v].Value var vals []string for _, v := range data { if v != Value { vals = append(vals, v) } } syscfg.NodeData[v].Value = vals } return nil }