podman
293 строки · 9.2 Кб
1// Copyright 2013-2022 Frank Schroeder. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package properties
6
7import (
8"fmt"
9"io/ioutil"
10"net/http"
11"os"
12"strings"
13)
14
15// Encoding specifies encoding of the input data.
16type Encoding uint
17
18const (
19// utf8Default is a private placeholder for the zero value of Encoding to
20// ensure that it has the correct meaning. UTF8 is the default encoding but
21// was assigned a non-zero value which cannot be changed without breaking
22// existing code. Clients should continue to use the public constants.
23utf8Default Encoding = iota
24
25// UTF8 interprets the input data as UTF-8.
26UTF8
27
28// ISO_8859_1 interprets the input data as ISO-8859-1.
29ISO_8859_1
30)
31
32type Loader struct {
33// Encoding determines how the data from files and byte buffers
34// is interpreted. For URLs the Content-Type header is used
35// to determine the encoding of the data.
36Encoding Encoding
37
38// DisableExpansion configures the property expansion of the
39// returned property object. When set to true, the property values
40// will not be expanded and the Property object will not be checked
41// for invalid expansion expressions.
42DisableExpansion bool
43
44// IgnoreMissing configures whether missing files or URLs which return
45// 404 are reported as errors. When set to true, missing files and 404
46// status codes are not reported as errors.
47IgnoreMissing bool
48}
49
50// Load reads a buffer into a Properties struct.
51func (l *Loader) LoadBytes(buf []byte) (*Properties, error) {
52return l.loadBytes(buf, l.Encoding)
53}
54
55// LoadAll reads the content of multiple URLs or files in the given order into
56// a Properties struct. If IgnoreMissing is true then a 404 status code or
57// missing file will not be reported as error. Encoding sets the encoding for
58// files. For the URLs see LoadURL for the Content-Type header and the
59// encoding.
60func (l *Loader) LoadAll(names []string) (*Properties, error) {
61all := NewProperties()
62for _, name := range names {
63n, err := expandName(name)
64if err != nil {
65return nil, err
66}
67
68var p *Properties
69switch {
70case strings.HasPrefix(n, "http://"):
71p, err = l.LoadURL(n)
72case strings.HasPrefix(n, "https://"):
73p, err = l.LoadURL(n)
74default:
75p, err = l.LoadFile(n)
76}
77if err != nil {
78return nil, err
79}
80all.Merge(p)
81}
82
83all.DisableExpansion = l.DisableExpansion
84if all.DisableExpansion {
85return all, nil
86}
87return all, all.check()
88}
89
90// LoadFile reads a file into a Properties struct.
91// If IgnoreMissing is true then a missing file will not be
92// reported as error.
93func (l *Loader) LoadFile(filename string) (*Properties, error) {
94data, err := ioutil.ReadFile(filename)
95if err != nil {
96if l.IgnoreMissing && os.IsNotExist(err) {
97LogPrintf("properties: %s not found. skipping", filename)
98return NewProperties(), nil
99}
100return nil, err
101}
102return l.loadBytes(data, l.Encoding)
103}
104
105// LoadURL reads the content of the URL into a Properties struct.
106//
107// The encoding is determined via the Content-Type header which
108// should be set to 'text/plain'. If the 'charset' parameter is
109// missing, 'iso-8859-1' or 'latin1' the encoding is set to
110// ISO-8859-1. If the 'charset' parameter is set to 'utf-8' the
111// encoding is set to UTF-8. A missing content type header is
112// interpreted as 'text/plain; charset=utf-8'.
113func (l *Loader) LoadURL(url string) (*Properties, error) {
114resp, err := http.Get(url)
115if err != nil {
116return nil, fmt.Errorf("properties: error fetching %q. %s", url, err)
117}
118defer resp.Body.Close()
119
120if resp.StatusCode == 404 && l.IgnoreMissing {
121LogPrintf("properties: %s returned %d. skipping", url, resp.StatusCode)
122return NewProperties(), nil
123}
124
125if resp.StatusCode != 200 {
126return nil, fmt.Errorf("properties: %s returned %d", url, resp.StatusCode)
127}
128
129body, err := ioutil.ReadAll(resp.Body)
130if err != nil {
131return nil, fmt.Errorf("properties: %s error reading response. %s", url, err)
132}
133
134ct := resp.Header.Get("Content-Type")
135ct = strings.Join(strings.Fields(ct), "")
136var enc Encoding
137switch strings.ToLower(ct) {
138case "text/plain", "text/plain;charset=iso-8859-1", "text/plain;charset=latin1":
139enc = ISO_8859_1
140case "", "text/plain;charset=utf-8":
141enc = UTF8
142default:
143return nil, fmt.Errorf("properties: invalid content type %s", ct)
144}
145
146return l.loadBytes(body, enc)
147}
148
149func (l *Loader) loadBytes(buf []byte, enc Encoding) (*Properties, error) {
150p, err := parse(convert(buf, enc))
151if err != nil {
152return nil, err
153}
154p.DisableExpansion = l.DisableExpansion
155if p.DisableExpansion {
156return p, nil
157}
158return p, p.check()
159}
160
161// Load reads a buffer into a Properties struct.
162func Load(buf []byte, enc Encoding) (*Properties, error) {
163l := &Loader{Encoding: enc}
164return l.LoadBytes(buf)
165}
166
167// LoadString reads an UTF8 string into a properties struct.
168func LoadString(s string) (*Properties, error) {
169l := &Loader{Encoding: UTF8}
170return l.LoadBytes([]byte(s))
171}
172
173// LoadMap creates a new Properties struct from a string map.
174func LoadMap(m map[string]string) *Properties {
175p := NewProperties()
176for k, v := range m {
177p.Set(k, v)
178}
179return p
180}
181
182// LoadFile reads a file into a Properties struct.
183func LoadFile(filename string, enc Encoding) (*Properties, error) {
184l := &Loader{Encoding: enc}
185return l.LoadAll([]string{filename})
186}
187
188// LoadFiles reads multiple files in the given order into
189// a Properties struct. If 'ignoreMissing' is true then
190// non-existent files will not be reported as error.
191func LoadFiles(filenames []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
192l := &Loader{Encoding: enc, IgnoreMissing: ignoreMissing}
193return l.LoadAll(filenames)
194}
195
196// LoadURL reads the content of the URL into a Properties struct.
197// See Loader#LoadURL for details.
198func LoadURL(url string) (*Properties, error) {
199l := &Loader{Encoding: UTF8}
200return l.LoadAll([]string{url})
201}
202
203// LoadURLs reads the content of multiple URLs in the given order into a
204// Properties struct. If IgnoreMissing is true then a 404 status code will
205// not be reported as error. See Loader#LoadURL for the Content-Type header
206// and the encoding.
207func LoadURLs(urls []string, ignoreMissing bool) (*Properties, error) {
208l := &Loader{Encoding: UTF8, IgnoreMissing: ignoreMissing}
209return l.LoadAll(urls)
210}
211
212// LoadAll reads the content of multiple URLs or files in the given order into a
213// Properties struct. If 'ignoreMissing' is true then a 404 status code or missing file will
214// not be reported as error. Encoding sets the encoding for files. For the URLs please see
215// LoadURL for the Content-Type header and the encoding.
216func LoadAll(names []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
217l := &Loader{Encoding: enc, IgnoreMissing: ignoreMissing}
218return l.LoadAll(names)
219}
220
221// MustLoadString reads an UTF8 string into a Properties struct and
222// panics on error.
223func MustLoadString(s string) *Properties {
224return must(LoadString(s))
225}
226
227// MustLoadFile reads a file into a Properties struct and
228// panics on error.
229func MustLoadFile(filename string, enc Encoding) *Properties {
230return must(LoadFile(filename, enc))
231}
232
233// MustLoadFiles reads multiple files in the given order into
234// a Properties struct and panics on error. If 'ignoreMissing'
235// is true then non-existent files will not be reported as error.
236func MustLoadFiles(filenames []string, enc Encoding, ignoreMissing bool) *Properties {
237return must(LoadFiles(filenames, enc, ignoreMissing))
238}
239
240// MustLoadURL reads the content of a URL into a Properties struct and
241// panics on error.
242func MustLoadURL(url string) *Properties {
243return must(LoadURL(url))
244}
245
246// MustLoadURLs reads the content of multiple URLs in the given order into a
247// Properties struct and panics on error. If 'ignoreMissing' is true then a 404
248// status code will not be reported as error.
249func MustLoadURLs(urls []string, ignoreMissing bool) *Properties {
250return must(LoadURLs(urls, ignoreMissing))
251}
252
253// MustLoadAll reads the content of multiple URLs or files in the given order into a
254// Properties struct. If 'ignoreMissing' is true then a 404 status code or missing file will
255// not be reported as error. Encoding sets the encoding for files. For the URLs please see
256// LoadURL for the Content-Type header and the encoding. It panics on error.
257func MustLoadAll(names []string, enc Encoding, ignoreMissing bool) *Properties {
258return must(LoadAll(names, enc, ignoreMissing))
259}
260
261func must(p *Properties, err error) *Properties {
262if err != nil {
263ErrorHandler(err)
264}
265return p
266}
267
268// expandName expands ${ENV_VAR} expressions in a name.
269// If the environment variable does not exist then it will be replaced
270// with an empty string. Malformed expressions like "${ENV_VAR" will
271// be reported as error.
272func expandName(name string) (string, error) {
273return expand(name, []string{}, "${", "}", make(map[string]string))
274}
275
276// Interprets a byte buffer either as an ISO-8859-1 or UTF-8 encoded string.
277// For ISO-8859-1 we can convert each byte straight into a rune since the
278// first 256 unicode code points cover ISO-8859-1.
279func convert(buf []byte, enc Encoding) string {
280switch enc {
281case utf8Default, UTF8:
282return string(buf)
283case ISO_8859_1:
284runes := make([]rune, len(buf))
285for i, b := range buf {
286runes[i] = rune(b)
287}
288return string(runes)
289default:
290ErrorHandler(fmt.Errorf("unsupported encoding %v", enc))
291}
292panic("ErrorHandler should exit")
293}
294