libgo: update to Go1.10beta1
[gcc.git] / libgo / go / mime / type.go
1 // Copyright 2010 The Go Authors. 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
5 // Package mime implements parts of the MIME spec.
6 package mime
7
8 import (
9 "fmt"
10 "strings"
11 "sync"
12 )
13
14 var (
15 mimeTypes sync.Map // map[string]string; ".Z" => "application/x-compress"
16 mimeTypesLower sync.Map // map[string]string; ".z" => "application/x-compress"
17
18 // extensions maps from MIME type to list of lowercase file
19 // extensions: "image/jpeg" => [".jpg", ".jpeg"]
20 extensionsMu sync.Mutex // Guards stores (but not loads) on extensions.
21 extensions sync.Map // map[string][]string; slice values are append-only.
22 )
23
24 func clearSyncMap(m *sync.Map) {
25 m.Range(func(k, _ interface{}) bool {
26 m.Delete(k)
27 return true
28 })
29 }
30
31 // setMimeTypes is used by initMime's non-test path, and by tests.
32 func setMimeTypes(lowerExt, mixExt map[string]string) {
33 clearSyncMap(&mimeTypes)
34 clearSyncMap(&mimeTypesLower)
35 clearSyncMap(&extensions)
36
37 for k, v := range lowerExt {
38 mimeTypesLower.Store(k, v)
39 }
40 for k, v := range mixExt {
41 mimeTypes.Store(k, v)
42 }
43
44 extensionsMu.Lock()
45 defer extensionsMu.Unlock()
46 for k, v := range lowerExt {
47 justType, _, err := ParseMediaType(v)
48 if err != nil {
49 panic(err)
50 }
51 var exts []string
52 if ei, ok := extensions.Load(k); ok {
53 exts = ei.([]string)
54 }
55 extensions.Store(justType, append(exts, k))
56 }
57 }
58
59 var builtinTypesLower = map[string]string{
60 ".css": "text/css; charset=utf-8",
61 ".gif": "image/gif",
62 ".htm": "text/html; charset=utf-8",
63 ".html": "text/html; charset=utf-8",
64 ".jpg": "image/jpeg",
65 ".js": "application/x-javascript",
66 ".pdf": "application/pdf",
67 ".png": "image/png",
68 ".svg": "image/svg+xml",
69 ".xml": "text/xml; charset=utf-8",
70 }
71
72 var once sync.Once // guards initMime
73
74 var testInitMime, osInitMime func()
75
76 func initMime() {
77 if fn := testInitMime; fn != nil {
78 fn()
79 } else {
80 setMimeTypes(builtinTypesLower, builtinTypesLower)
81 osInitMime()
82 }
83 }
84
85 // TypeByExtension returns the MIME type associated with the file extension ext.
86 // The extension ext should begin with a leading dot, as in ".html".
87 // When ext has no associated type, TypeByExtension returns "".
88 //
89 // Extensions are looked up first case-sensitively, then case-insensitively.
90 //
91 // The built-in table is small but on unix it is augmented by the local
92 // system's mime.types file(s) if available under one or more of these
93 // names:
94 //
95 // /etc/mime.types
96 // /etc/apache2/mime.types
97 // /etc/apache/mime.types
98 //
99 // On Windows, MIME types are extracted from the registry.
100 //
101 // Text types have the charset parameter set to "utf-8" by default.
102 func TypeByExtension(ext string) string {
103 once.Do(initMime)
104
105 // Case-sensitive lookup.
106 if v, ok := mimeTypes.Load(ext); ok {
107 return v.(string)
108 }
109
110 // Case-insensitive lookup.
111 // Optimistically assume a short ASCII extension and be
112 // allocation-free in that case.
113 var buf [10]byte
114 lower := buf[:0]
115 const utf8RuneSelf = 0x80 // from utf8 package, but not importing it.
116 for i := 0; i < len(ext); i++ {
117 c := ext[i]
118 if c >= utf8RuneSelf {
119 // Slow path.
120 si, _ := mimeTypesLower.Load(strings.ToLower(ext))
121 s, _ := si.(string)
122 return s
123 }
124 if 'A' <= c && c <= 'Z' {
125 lower = append(lower, c+('a'-'A'))
126 } else {
127 lower = append(lower, c)
128 }
129 }
130 si, _ := mimeTypesLower.Load(string(lower))
131 s, _ := si.(string)
132 return s
133 }
134
135 // ExtensionsByType returns the extensions known to be associated with the MIME
136 // type typ. The returned extensions will each begin with a leading dot, as in
137 // ".html". When typ has no associated extensions, ExtensionsByType returns an
138 // nil slice.
139 func ExtensionsByType(typ string) ([]string, error) {
140 justType, _, err := ParseMediaType(typ)
141 if err != nil {
142 return nil, err
143 }
144
145 once.Do(initMime)
146 s, ok := extensions.Load(justType)
147 if !ok {
148 return nil, nil
149 }
150 return append([]string{}, s.([]string)...), nil
151 }
152
153 // AddExtensionType sets the MIME type associated with
154 // the extension ext to typ. The extension should begin with
155 // a leading dot, as in ".html".
156 func AddExtensionType(ext, typ string) error {
157 if !strings.HasPrefix(ext, ".") {
158 return fmt.Errorf("mime: extension %q missing leading dot", ext)
159 }
160 once.Do(initMime)
161 return setExtensionType(ext, typ)
162 }
163
164 func setExtensionType(extension, mimeType string) error {
165 justType, param, err := ParseMediaType(mimeType)
166 if err != nil {
167 return err
168 }
169 if strings.HasPrefix(mimeType, "text/") && param["charset"] == "" {
170 param["charset"] = "utf-8"
171 mimeType = FormatMediaType(mimeType, param)
172 }
173 extLower := strings.ToLower(extension)
174
175 mimeTypes.Store(extension, mimeType)
176 mimeTypesLower.Store(extLower, mimeType)
177
178 extensionsMu.Lock()
179 defer extensionsMu.Unlock()
180 var exts []string
181 if ei, ok := extensions.Load(justType); ok {
182 exts = ei.([]string)
183 }
184 for _, v := range exts {
185 if v == extLower {
186 return nil
187 }
188 }
189 extensions.Store(justType, append(exts, extLower))
190 return nil
191 }