-
Notifications
You must be signed in to change notification settings - Fork 0
/
util.go
161 lines (142 loc) · 3.43 KB
/
util.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// Copyright 2016 by Chris Palmer (https://noncombatant.org)
// SPDX-License-Identifier: GPL-3.0
// Assorted utility functions.
package main
import (
crand "crypto/rand"
"embed"
"io/fs"
"os"
"path/filepath"
"regexp"
"slices"
"strings"
"unicode"
"golang.org/x/text/runes"
"golang.org/x/text/transform"
"golang.org/x/text/unicode/norm"
)
var (
// NOTE: These must be kept in sync with the format extensions arrays in the
// JS code.
audioFormatExtensions = []string{
".flac",
".m4a",
".mid",
".midi",
".mp3",
".ogg",
".wav",
".wave",
}
videoFormatExtensions = []string{
".avi",
".m4v",
".mkv",
".mov",
".mp4",
".mpeg",
".mpg",
".ogv",
".webm",
}
documentFormatExtensions = []string{
".pdf",
".txt",
}
imageFormatExtensions = []string{
".gif",
".jpeg",
".jpg",
".png",
".webp",
}
digitsFinder = regexp.MustCompile(`(\d+)`)
)
// escapeDoubleQuotes returns a copy of `s`, with all double quotes escaped with
// a backslash.
func escapeDoubleQuotes(s string) string {
return strings.ReplaceAll(s, "\"", "\\\"")
}
// extractDigits returns the first substring of decimal digits in `s`, or an
// empty string if there is no such substring.
func extractDigits(s string) string {
results := digitsFinder.FindStringSubmatch(s)
if len(results) > 0 {
return results[0]
}
return ""
}
// getBasenameExtension returns the basename's extension, including the '.',
// normalized to lowercase. If the basename has no extension, returns an empty
// string.
func getBasenameExtension(pathname string) string {
return strings.ToLower(filepath.Ext(pathname))
}
func isAudioPathname(pathname string) bool {
return slices.Contains(audioFormatExtensions, getBasenameExtension(pathname))
}
func isDocumentPathname(pathname string) bool {
return slices.Contains(documentFormatExtensions, getBasenameExtension(pathname))
}
func isFileWorldReadable(info os.FileInfo) bool {
return info.Mode()&0004 == 04
}
func isImagePathname(pathname string) bool {
return slices.Contains(imageFormatExtensions, getBasenameExtension(pathname))
}
func isVideoPathname(pathname string) bool {
return slices.Contains(videoFormatExtensions, getBasenameExtension(pathname))
}
func getRandomBytes(count int) []byte {
bytes := make([]byte, count)
// crypto/Read calls io.ReadFull.
crand.Read(bytes)
return bytes
}
func removeBasenameExtension(pathname string) string {
dot := strings.LastIndex(pathname, ".")
if dot == -1 {
return pathname
}
slash := strings.LastIndex(pathname, string(os.PathSeparator))
if slash > dot {
// There may be a dot, but it's not in the basename. In that case, return
// the whole pathname.
return pathname
}
return pathname[:dot]
}
func openFileAndInfo(pathname string) (*os.File, os.FileInfo, error) {
file, e := os.Open(pathname)
if e != nil {
return nil, nil, e
}
info, e := file.Stat()
if e != nil {
_ = file.Close()
return nil, nil, e
}
return file, info, nil
}
func openFileAndInfoFS(pathname string, fs embed.FS) (fs.File, os.FileInfo, error) {
file, e := fs.Open(pathname)
if e != nil {
return nil, nil, e
}
info, e := file.Stat()
if e != nil {
_ = file.Close()
return nil, nil, e
}
return file, info, nil
}
// https://twinnation.org/articles/33/remove-accents-from-characters-in-go
func removeAccents(s string) string {
t := transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC)
output, _, e := transform.String(t, s)
if e != nil {
panic(e)
}
return output
}