|
@@ -229,17 +229,19 @@ func (t Result) ForEach(iterator func(key, value Result) bool) {
|
229
|
229
|
return
|
230
|
230
|
}
|
231
|
231
|
json := t.Raw
|
232
|
|
- var keys bool
|
|
232
|
+ var obj bool
|
233
|
233
|
var i int
|
234
|
234
|
var key, value Result
|
235
|
235
|
for ; i < len(json); i++ {
|
236
|
236
|
if json[i] == '{' {
|
237
|
237
|
i++
|
238
|
238
|
key.Type = String
|
239
|
|
- keys = true
|
|
239
|
+ obj = true
|
240
|
240
|
break
|
241
|
241
|
} else if json[i] == '[' {
|
242
|
242
|
i++
|
|
243
|
+ key.Type = Number
|
|
244
|
+ key.Num = -1
|
243
|
245
|
break
|
244
|
246
|
}
|
245
|
247
|
if json[i] > ' ' {
|
|
@@ -249,8 +251,9 @@ func (t Result) ForEach(iterator func(key, value Result) bool) {
|
249
|
251
|
var str string
|
250
|
252
|
var vesc bool
|
251
|
253
|
var ok bool
|
|
254
|
+ var idx int
|
252
|
255
|
for ; i < len(json); i++ {
|
253
|
|
- if keys {
|
|
256
|
+ if obj {
|
254
|
257
|
if json[i] != '"' {
|
255
|
258
|
continue
|
256
|
259
|
}
|
|
@@ -265,7 +268,9 @@ func (t Result) ForEach(iterator func(key, value Result) bool) {
|
265
|
268
|
key.Str = str[1 : len(str)-1]
|
266
|
269
|
}
|
267
|
270
|
key.Raw = str
|
268
|
|
- key.Index = s
|
|
271
|
+ key.Index = s + t.Index
|
|
272
|
+ } else {
|
|
273
|
+ key.Num += 1
|
269
|
274
|
}
|
270
|
275
|
for ; i < len(json); i++ {
|
271
|
276
|
if json[i] <= ' ' || json[i] == ',' || json[i] == ':' {
|
|
@@ -278,10 +283,17 @@ func (t Result) ForEach(iterator func(key, value Result) bool) {
|
278
|
283
|
if !ok {
|
279
|
284
|
return
|
280
|
285
|
}
|
281
|
|
- value.Index = s
|
|
286
|
+ if t.Indexes != nil {
|
|
287
|
+ if idx < len(t.Indexes) {
|
|
288
|
+ value.Index = t.Indexes[idx]
|
|
289
|
+ }
|
|
290
|
+ } else {
|
|
291
|
+ value.Index = s + t.Index
|
|
292
|
+ }
|
282
|
293
|
if !iterator(key, value) {
|
283
|
294
|
return
|
284
|
295
|
}
|
|
296
|
+ idx++
|
285
|
297
|
}
|
286
|
298
|
}
|
287
|
299
|
|
|
@@ -298,7 +310,15 @@ func (t Result) Map() map[string]Result {
|
298
|
310
|
// Get searches result for the specified path.
|
299
|
311
|
// The result should be a JSON array or object.
|
300
|
312
|
func (t Result) Get(path string) Result {
|
301
|
|
- return Get(t.Raw, path)
|
|
313
|
+ r := Get(t.Raw, path)
|
|
314
|
+ if r.Indexes != nil {
|
|
315
|
+ for i := 0; i < len(r.Indexes); i++ {
|
|
316
|
+ r.Indexes[i] += t.Index
|
|
317
|
+ }
|
|
318
|
+ } else {
|
|
319
|
+ r.Index += t.Index
|
|
320
|
+ }
|
|
321
|
+ return r
|
302
|
322
|
}
|
303
|
323
|
|
304
|
324
|
type arrayOrMapResult struct {
|
|
@@ -389,6 +409,8 @@ func (t Result) arrayOrMap(vc byte, valueize bool) (r arrayOrMapResult) {
|
389
|
409
|
value.Raw, value.Str = tostr(json[i:])
|
390
|
410
|
value.Num = 0
|
391
|
411
|
}
|
|
412
|
+ value.Index = i + t.Index
|
|
413
|
+
|
392
|
414
|
i += len(value.Raw) - 1
|
393
|
415
|
|
394
|
416
|
if r.vc == '{' {
|
|
@@ -415,6 +437,17 @@ func (t Result) arrayOrMap(vc byte, valueize bool) (r arrayOrMapResult) {
|
415
|
437
|
}
|
416
|
438
|
}
|
417
|
439
|
end:
|
|
440
|
+ if t.Indexes != nil {
|
|
441
|
+ if len(t.Indexes) != len(r.a) {
|
|
442
|
+ for i := 0; i < len(r.a); i++ {
|
|
443
|
+ r.a[i].Index = 0
|
|
444
|
+ }
|
|
445
|
+ } else {
|
|
446
|
+ for i := 0; i < len(r.a); i++ {
|
|
447
|
+ r.a[i].Index = t.Indexes[i]
|
|
448
|
+ }
|
|
449
|
+ }
|
|
450
|
+ }
|
418
|
451
|
return
|
419
|
452
|
}
|
420
|
453
|
|
|
@@ -426,7 +459,8 @@ end:
|
426
|
459
|
// use the Valid function first.
|
427
|
460
|
func Parse(json string) Result {
|
428
|
461
|
var value Result
|
429
|
|
- for i := 0; i < len(json); i++ {
|
|
462
|
+ i := 0
|
|
463
|
+ for ; i < len(json); i++ {
|
430
|
464
|
if json[i] == '{' || json[i] == '[' {
|
431
|
465
|
value.Type = JSON
|
432
|
466
|
value.Raw = json[i:] // just take the entire raw
|
|
@@ -436,16 +470,20 @@ func Parse(json string) Result {
|
436
|
470
|
continue
|
437
|
471
|
}
|
438
|
472
|
switch json[i] {
|
439
|
|
- default:
|
440
|
|
- if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' {
|
|
473
|
+ case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
|
474
|
+ 'i', 'I', 'N':
|
|
475
|
+ value.Type = Number
|
|
476
|
+ value.Raw, value.Num = tonum(json[i:])
|
|
477
|
+ case 'n':
|
|
478
|
+ if i+1 < len(json) && json[i+1] != 'u' {
|
|
479
|
+ // nan
|
441
|
480
|
value.Type = Number
|
442
|
481
|
value.Raw, value.Num = tonum(json[i:])
|
443
|
482
|
} else {
|
444
|
|
- return Result{}
|
|
483
|
+ // null
|
|
484
|
+ value.Type = Null
|
|
485
|
+ value.Raw = tolit(json[i:])
|
445
|
486
|
}
|
446
|
|
- case 'n':
|
447
|
|
- value.Type = Null
|
448
|
|
- value.Raw = tolit(json[i:])
|
449
|
487
|
case 't':
|
450
|
488
|
value.Type = True
|
451
|
489
|
value.Raw = tolit(json[i:])
|
|
@@ -455,9 +493,14 @@ func Parse(json string) Result {
|
455
|
493
|
case '"':
|
456
|
494
|
value.Type = String
|
457
|
495
|
value.Raw, value.Str = tostr(json[i:])
|
|
496
|
+ default:
|
|
497
|
+ return Result{}
|
458
|
498
|
}
|
459
|
499
|
break
|
460
|
500
|
}
|
|
501
|
+ if value.Exists() {
|
|
502
|
+ value.Index = i
|
|
503
|
+ }
|
461
|
504
|
return value
|
462
|
505
|
}
|
463
|
506
|
|
|
@@ -531,20 +574,12 @@ func tonum(json string) (raw string, num float64) {
|
531
|
574
|
return
|
532
|
575
|
}
|
533
|
576
|
// could be a '+' or '-'. let's assume so.
|
534
|
|
- continue
|
535
|
|
- }
|
536
|
|
- if json[i] < ']' {
|
537
|
|
- // probably a valid number
|
538
|
|
- continue
|
539
|
|
- }
|
540
|
|
- if json[i] == 'e' || json[i] == 'E' {
|
541
|
|
- // allow for exponential numbers
|
542
|
|
- continue
|
|
577
|
+ } else if json[i] == ']' || json[i] == '}' {
|
|
578
|
+ // break on ']' or '}'
|
|
579
|
+ raw = json[:i]
|
|
580
|
+ num, _ = strconv.ParseFloat(raw, 64)
|
|
581
|
+ return
|
543
|
582
|
}
|
544
|
|
- // likely a ']' or '}'
|
545
|
|
- raw = json[:i]
|
546
|
|
- num, _ = strconv.ParseFloat(raw, 64)
|
547
|
|
- return
|
548
|
583
|
}
|
549
|
584
|
raw = json
|
550
|
585
|
num, _ = strconv.ParseFloat(raw, 64)
|
|
@@ -1513,7 +1548,6 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
|
1513
|
1548
|
}
|
1514
|
1549
|
if idx < len(c.json) && c.json[idx] != ']' {
|
1515
|
1550
|
_, res, ok := parseAny(c.json, idx, true)
|
1516
|
|
- parentIndex := res.Index
|
1517
|
1551
|
if ok {
|
1518
|
1552
|
res := res.Get(rp.alogkey)
|
1519
|
1553
|
if res.Exists() {
|
|
@@ -1525,8 +1559,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
|
1525
|
1559
|
raw = res.String()
|
1526
|
1560
|
}
|
1527
|
1561
|
jsons = append(jsons, []byte(raw)...)
|
1528
|
|
- indexes = append(indexes,
|
1529
|
|
- res.Index+parentIndex)
|
|
1562
|
+ indexes = append(indexes, res.Index)
|
1530
|
1563
|
k++
|
1531
|
1564
|
}
|
1532
|
1565
|
}
|
|
@@ -1699,7 +1732,7 @@ type subSelector struct {
|
1699
|
1732
|
// first character in path is either '[' or '{', and has already been checked
|
1700
|
1733
|
// prior to calling this function.
|
1701
|
1734
|
func parseSubSelectors(path string) (sels []subSelector, out string, ok bool) {
|
1702
|
|
- modifer := 0
|
|
1735
|
+ modifier := 0
|
1703
|
1736
|
depth := 1
|
1704
|
1737
|
colon := 0
|
1705
|
1738
|
start := 1
|
|
@@ -1714,6 +1747,7 @@ func parseSubSelectors(path string) (sels []subSelector, out string, ok bool) {
|
1714
|
1747
|
}
|
1715
|
1748
|
sels = append(sels, sel)
|
1716
|
1749
|
colon = 0
|
|
1750
|
+ modifier = 0
|
1717
|
1751
|
start = i + 1
|
1718
|
1752
|
}
|
1719
|
1753
|
for ; i < len(path); i++ {
|
|
@@ -1721,11 +1755,11 @@ func parseSubSelectors(path string) (sels []subSelector, out string, ok bool) {
|
1721
|
1755
|
case '\\':
|
1722
|
1756
|
i++
|
1723
|
1757
|
case '@':
|
1724
|
|
- if modifer == 0 && i > 0 && (path[i-1] == '.' || path[i-1] == '|') {
|
1725
|
|
- modifer = i
|
|
1758
|
+ if modifier == 0 && i > 0 && (path[i-1] == '.' || path[i-1] == '|') {
|
|
1759
|
+ modifier = i
|
1726
|
1760
|
}
|
1727
|
1761
|
case ':':
|
1728
|
|
- if modifer == 0 && colon == 0 && depth == 1 {
|
|
1762
|
+ if modifier == 0 && colon == 0 && depth == 1 {
|
1729
|
1763
|
colon = i
|
1730
|
1764
|
}
|
1731
|
1765
|
case ',':
|
|
@@ -1778,7 +1812,7 @@ func isSimpleName(component string) bool {
|
1778
|
1812
|
return false
|
1779
|
1813
|
}
|
1780
|
1814
|
switch component[i] {
|
1781
|
|
- case '[', ']', '{', '}', '(', ')', '#', '|':
|
|
1815
|
+ case '[', ']', '{', '}', '(', ')', '#', '|', '!':
|
1782
|
1816
|
return false
|
1783
|
1817
|
}
|
1784
|
1818
|
}
|
|
@@ -1842,23 +1876,25 @@ type parseContext struct {
|
1842
|
1876
|
// use the Valid function first.
|
1843
|
1877
|
func Get(json, path string) Result {
|
1844
|
1878
|
if len(path) > 1 {
|
1845
|
|
- if !DisableModifiers {
|
1846
|
|
- if path[0] == '@' {
|
1847
|
|
- // possible modifier
|
1848
|
|
- var ok bool
|
1849
|
|
- var npath string
|
1850
|
|
- var rjson string
|
|
1879
|
+ if (path[0] == '@' && !DisableModifiers) || path[0] == '!' {
|
|
1880
|
+ // possible modifier
|
|
1881
|
+ var ok bool
|
|
1882
|
+ var npath string
|
|
1883
|
+ var rjson string
|
|
1884
|
+ if path[0] == '@' && !DisableModifiers {
|
1851
|
1885
|
npath, rjson, ok = execModifier(json, path)
|
1852
|
|
- if ok {
|
1853
|
|
- path = npath
|
1854
|
|
- if len(path) > 0 && (path[0] == '|' || path[0] == '.') {
|
1855
|
|
- res := Get(rjson, path[1:])
|
1856
|
|
- res.Index = 0
|
1857
|
|
- res.Indexes = nil
|
1858
|
|
- return res
|
1859
|
|
- }
|
1860
|
|
- return Parse(rjson)
|
|
1886
|
+ } else if path[0] == '!' {
|
|
1887
|
+ npath, rjson, ok = execStatic(json, path)
|
|
1888
|
+ }
|
|
1889
|
+ if ok {
|
|
1890
|
+ path = npath
|
|
1891
|
+ if len(path) > 0 && (path[0] == '|' || path[0] == '.') {
|
|
1892
|
+ res := Get(rjson, path[1:])
|
|
1893
|
+ res.Index = 0
|
|
1894
|
+ res.Indexes = nil
|
|
1895
|
+ return res
|
1861
|
1896
|
}
|
|
1897
|
+ return Parse(rjson)
|
1862
|
1898
|
}
|
1863
|
1899
|
}
|
1864
|
1900
|
if path[0] == '[' || path[0] == '{' {
|
|
@@ -2527,8 +2563,40 @@ func safeInt(f float64) (n int64, ok bool) {
|
2527
|
2563
|
return int64(f), true
|
2528
|
2564
|
}
|
2529
|
2565
|
|
|
2566
|
+// execStatic parses the path to find a static value.
|
|
2567
|
+// The input expects that the path already starts with a '!'
|
|
2568
|
+func execStatic(json, path string) (pathOut, res string, ok bool) {
|
|
2569
|
+ name := path[1:]
|
|
2570
|
+ if len(name) > 0 {
|
|
2571
|
+ switch name[0] {
|
|
2572
|
+ case '{', '[', '"', '+', '-', '0', '1', '2', '3', '4', '5', '6', '7',
|
|
2573
|
+ '8', '9':
|
|
2574
|
+ _, res = parseSquash(name, 0)
|
|
2575
|
+ pathOut = name[len(res):]
|
|
2576
|
+ return pathOut, res, true
|
|
2577
|
+ }
|
|
2578
|
+ }
|
|
2579
|
+ for i := 1; i < len(path); i++ {
|
|
2580
|
+ if path[i] == '|' {
|
|
2581
|
+ pathOut = path[i:]
|
|
2582
|
+ name = path[1:i]
|
|
2583
|
+ break
|
|
2584
|
+ }
|
|
2585
|
+ if path[i] == '.' {
|
|
2586
|
+ pathOut = path[i:]
|
|
2587
|
+ name = path[1:i]
|
|
2588
|
+ break
|
|
2589
|
+ }
|
|
2590
|
+ }
|
|
2591
|
+ switch strings.ToLower(name) {
|
|
2592
|
+ case "true", "false", "null", "nan", "inf":
|
|
2593
|
+ return pathOut, name, true
|
|
2594
|
+ }
|
|
2595
|
+ return pathOut, res, false
|
|
2596
|
+}
|
|
2597
|
+
|
2530
|
2598
|
// execModifier parses the path to find a matching modifier function.
|
2531
|
|
-// then input expects that the path already starts with a '@'
|
|
2599
|
+// The input expects that the path already starts with a '@'
|
2532
|
2600
|
func execModifier(json, path string) (pathOut, res string, ok bool) {
|
2533
|
2601
|
name := path[1:]
|
2534
|
2602
|
var hasArgs bool
|
|
@@ -2971,3 +3039,176 @@ func stringBytes(s string) []byte {
|
2971
|
3039
|
func bytesString(b []byte) string {
|
2972
|
3040
|
return *(*string)(unsafe.Pointer(&b))
|
2973
|
3041
|
}
|
|
3042
|
+
|
|
3043
|
+func revSquash(json string) string {
|
|
3044
|
+ // reverse squash
|
|
3045
|
+ // expects that the tail character is a ']' or '}' or ')' or '"'
|
|
3046
|
+ // squash the value, ignoring all nested arrays and objects.
|
|
3047
|
+ i := len(json) - 1
|
|
3048
|
+ var depth int
|
|
3049
|
+ if json[i] != '"' {
|
|
3050
|
+ depth++
|
|
3051
|
+ }
|
|
3052
|
+ if json[i] == '}' || json[i] == ']' || json[i] == ')' {
|
|
3053
|
+ i--
|
|
3054
|
+ }
|
|
3055
|
+ for ; i >= 0; i-- {
|
|
3056
|
+ switch json[i] {
|
|
3057
|
+ case '"':
|
|
3058
|
+ i--
|
|
3059
|
+ for ; i >= 0; i-- {
|
|
3060
|
+ if json[i] == '"' {
|
|
3061
|
+ esc := 0
|
|
3062
|
+ for i > 0 && json[i-1] == '\\' {
|
|
3063
|
+ i--
|
|
3064
|
+ esc++
|
|
3065
|
+ }
|
|
3066
|
+ if esc%2 == 1 {
|
|
3067
|
+ continue
|
|
3068
|
+ }
|
|
3069
|
+ i += esc
|
|
3070
|
+ break
|
|
3071
|
+ }
|
|
3072
|
+ }
|
|
3073
|
+ if depth == 0 {
|
|
3074
|
+ if i < 0 {
|
|
3075
|
+ i = 0
|
|
3076
|
+ }
|
|
3077
|
+ return json[i:]
|
|
3078
|
+ }
|
|
3079
|
+ case '}', ']', ')':
|
|
3080
|
+ depth++
|
|
3081
|
+ case '{', '[', '(':
|
|
3082
|
+ depth--
|
|
3083
|
+ if depth == 0 {
|
|
3084
|
+ return json[i:]
|
|
3085
|
+ }
|
|
3086
|
+ }
|
|
3087
|
+ }
|
|
3088
|
+ return json
|
|
3089
|
+}
|
|
3090
|
+
|
|
3091
|
+func (t Result) Paths(json string) []string {
|
|
3092
|
+ if t.Indexes == nil {
|
|
3093
|
+ return nil
|
|
3094
|
+ }
|
|
3095
|
+ paths := make([]string, 0, len(t.Indexes))
|
|
3096
|
+ t.ForEach(func(_, value Result) bool {
|
|
3097
|
+ paths = append(paths, value.Path(json))
|
|
3098
|
+ return true
|
|
3099
|
+ })
|
|
3100
|
+ if len(paths) != len(t.Indexes) {
|
|
3101
|
+ return nil
|
|
3102
|
+ }
|
|
3103
|
+ return paths
|
|
3104
|
+}
|
|
3105
|
+
|
|
3106
|
+// Path returns the original GJSON path for Result.
|
|
3107
|
+// The json param must be the original JSON used when calling Get.
|
|
3108
|
+func (t Result) Path(json string) string {
|
|
3109
|
+ var path []byte
|
|
3110
|
+ var comps []string // raw components
|
|
3111
|
+ i := t.Index - 1
|
|
3112
|
+ if t.Index+len(t.Raw) > len(json) {
|
|
3113
|
+ // JSON cannot safely contain Result.
|
|
3114
|
+ goto fail
|
|
3115
|
+ }
|
|
3116
|
+ if !strings.HasPrefix(json[t.Index:], t.Raw) {
|
|
3117
|
+ // Result is not at the JSON index as exepcted.
|
|
3118
|
+ goto fail
|
|
3119
|
+ }
|
|
3120
|
+ for ; i >= 0; i-- {
|
|
3121
|
+ if json[i] <= ' ' {
|
|
3122
|
+ continue
|
|
3123
|
+ }
|
|
3124
|
+ if json[i] == ':' {
|
|
3125
|
+ // inside of object, get the key
|
|
3126
|
+ for ; i >= 0; i-- {
|
|
3127
|
+ if json[i] != '"' {
|
|
3128
|
+ continue
|
|
3129
|
+ }
|
|
3130
|
+ break
|
|
3131
|
+ }
|
|
3132
|
+ raw := revSquash(json[:i+1])
|
|
3133
|
+ i = i - len(raw)
|
|
3134
|
+ comps = append(comps, raw)
|
|
3135
|
+ // key gotten, now squash the rest
|
|
3136
|
+ raw = revSquash(json[:i+1])
|
|
3137
|
+ i = i - len(raw)
|
|
3138
|
+ i++ // increment the index for next loop step
|
|
3139
|
+ } else if json[i] == '{' {
|
|
3140
|
+ // Encountered an open object. The original result was probably an
|
|
3141
|
+ // object key.
|
|
3142
|
+ goto fail
|
|
3143
|
+ } else if json[i] == ',' || json[i] == '[' {
|
|
3144
|
+ // inside of an array, count the position
|
|
3145
|
+ var arrIdx int
|
|
3146
|
+ if json[i] == ',' {
|
|
3147
|
+ arrIdx++
|
|
3148
|
+ i--
|
|
3149
|
+ }
|
|
3150
|
+ for ; i >= 0; i-- {
|
|
3151
|
+ if json[i] == ':' {
|
|
3152
|
+ // Encountered an unexpected colon. The original result was
|
|
3153
|
+ // probably an object key.
|
|
3154
|
+ goto fail
|
|
3155
|
+ } else if json[i] == ',' {
|
|
3156
|
+ arrIdx++
|
|
3157
|
+ } else if json[i] == '[' {
|
|
3158
|
+ comps = append(comps, strconv.Itoa(arrIdx))
|
|
3159
|
+ break
|
|
3160
|
+ } else if json[i] == ']' || json[i] == '}' || json[i] == '"' {
|
|
3161
|
+ raw := revSquash(json[:i+1])
|
|
3162
|
+ i = i - len(raw) + 1
|
|
3163
|
+ }
|
|
3164
|
+ }
|
|
3165
|
+ }
|
|
3166
|
+ }
|
|
3167
|
+ if len(comps) == 0 {
|
|
3168
|
+ if DisableModifiers {
|
|
3169
|
+ goto fail
|
|
3170
|
+ }
|
|
3171
|
+ return "@this"
|
|
3172
|
+ }
|
|
3173
|
+ for i := len(comps) - 1; i >= 0; i-- {
|
|
3174
|
+ rcomp := Parse(comps[i])
|
|
3175
|
+ if !rcomp.Exists() {
|
|
3176
|
+ goto fail
|
|
3177
|
+ }
|
|
3178
|
+ comp := escapeComp(rcomp.String())
|
|
3179
|
+ path = append(path, '.')
|
|
3180
|
+ path = append(path, comp...)
|
|
3181
|
+ }
|
|
3182
|
+ if len(path) > 0 {
|
|
3183
|
+ path = path[1:]
|
|
3184
|
+ }
|
|
3185
|
+ return string(path)
|
|
3186
|
+fail:
|
|
3187
|
+ return ""
|
|
3188
|
+}
|
|
3189
|
+
|
|
3190
|
+// isSafePathKeyChar returns true if the input character is safe for not
|
|
3191
|
+// needing escaping.
|
|
3192
|
+func isSafePathKeyChar(c byte) bool {
|
|
3193
|
+ return c <= ' ' || c > '~' || c == '_' || c == '-' || c == ':' ||
|
|
3194
|
+ (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
|
3195
|
+ (c >= '0' && c <= '9')
|
|
3196
|
+}
|
|
3197
|
+
|
|
3198
|
+// escapeComp escaped a path compontent, making it safe for generating a
|
|
3199
|
+// path for later use.
|
|
3200
|
+func escapeComp(comp string) string {
|
|
3201
|
+ for i := 0; i < len(comp); i++ {
|
|
3202
|
+ if !isSafePathKeyChar(comp[i]) {
|
|
3203
|
+ ncomp := []byte(comp[:i])
|
|
3204
|
+ for ; i < len(comp); i++ {
|
|
3205
|
+ if !isSafePathKeyChar(comp[i]) {
|
|
3206
|
+ ncomp = append(ncomp, '\\')
|
|
3207
|
+ }
|
|
3208
|
+ ncomp = append(ncomp, comp[i])
|
|
3209
|
+ }
|
|
3210
|
+ return string(ncomp)
|
|
3211
|
+ }
|
|
3212
|
+ }
|
|
3213
|
+ return comp
|
|
3214
|
+}
|