Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(694)

Side by Side Diff: src/cmd/godoc/httpzip.go

Issue 4750047: code review 4750047: godoc: implement http.FileSystem for zip files (Closed)
Patch Set: diff -r 85ab018b6c97 https://go.googlecode.com/hg/ Created 12 years, 8 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/cmd/godoc/godoc.go ('k') | src/cmd/godoc/main.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2011 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 // This file provides an implementation of the http.FileSystem
6 // interface based on the contents of a .zip file.
7 //
8 // Assumptions:
9 //
10 // - The file paths stored in the zip file must use a slash ('/') as path
11 // separator; and they must be relative (i.e., they must not start with
12 // a '/' - this is usually the case if the file was created w/o special
13 // options).
14 // - The zip file system treats the file paths found in the zip internally
15 // like absolute paths w/o a leading '/'; i.e., the paths are considered
16 // relative to the root of the file system.
17 // - All path arguments to file system methods must be absolute paths.
18
19 // TODO(gri) Should define a commonly used FileSystem API that is the same
20 // for http and godoc. Then we only need one zip-file based file
21 // system implementation.
22
23 package main
24
25 import (
26 "archive/zip"
27 "fmt"
28 "http"
29 "io"
30 "os"
31 "path"
32 "sort"
33 "strings"
34 )
35
36 // We cannot import syscall on app engine.
37 // TODO(gri) Once we have a truly abstract FileInfo implementation
38 // this won't be needed anymore.
39 const (
40 S_IFDIR = 0x4000 // == syscall.S_IFDIR
41 S_IFREG = 0x8000 // == syscall.S_IFREG
42 )
43
44 // httpZipFile is the zip-file based implementation of http.File
45 type httpZipFile struct {
46 info os.FileInfo
47 io.ReadCloser // nil for directory
48 list zipList
49 }
50
51 func (f *httpZipFile) Close() os.Error {
52 if f.info.IsRegular() {
53 return f.ReadCloser.Close()
54 }
55 f.list = nil
56 return nil
57 }
58
59 func (f *httpZipFile) Stat() (*os.FileInfo, os.Error) {
60 return &f.info, nil
61 }
62
63 func (f *httpZipFile) Readdir(count int) ([]os.FileInfo, os.Error) {
64 println("Readdir", f.info.Name)
65 if f.info.IsRegular() {
66 return nil, fmt.Errorf("Readdir called for regular file: %s", f. info.Name)
67 }
68
69 var list []os.FileInfo
70 dirname := zipPath(f.info.Name) + "/"
71 prevname := ""
72 for i, e := range f.list {
73 if count == 0 {
74 f.list = f.list[i:]
75 break
76 }
77 if !strings.HasPrefix(e.Name, dirname) {
78 f.list = nil
79 break // not in the same directory anymore
80 }
81 name := e.Name[len(dirname):] // local name
82 var mode uint32
83 var size, mtime_ns int64
84 if i := strings.IndexRune(name, '/'); i >= 0 {
85 // We infer directories from files in subdirectories.
86 // If we have x/y, return a directory entry for x.
87 name = name[0:i] // keep local directory name only
88 mode = S_IFDIR
89 // no size or mtime_ns for directories
90 } else {
91 mode = S_IFREG
92 size = int64(e.UncompressedSize)
93 mtime_ns = e.Mtime_ns()
94 }
95 // If we have x/y and x/z, don't return two directory entries fo r x.
96 // TODO(gri): It should be possible to do this more efficiently
97 // by determining the (fs.list) range of local directory entries
98 // (via two binary searches).
99 if name != prevname {
100 list = append(list, os.FileInfo{
101 Name: name,
102 Mode: mode,
103 Size: size,
104 Mtime_ns: mtime_ns,
105 })
106 prevname = name
107 count--
108 }
109 }
110
111 if count >= 0 && len(list) == 0 {
112 return nil, os.EOF
113 }
114
115 return list, nil
116 }
117
118 func (f *httpZipFile) Read(buf []byte) (int, os.Error) {
119 if f.info.IsRegular() {
120 return f.ReadCloser.Read(buf)
121 }
122 return 0, fmt.Errorf("Read called for directory: %s", f.info.Name)
123 }
124
125 func (f *httpZipFile) Seek(offset int64, whence int) (int64, os.Error) {
126 return 0, fmt.Errorf("Seek not implemented for zip file entry: %s", f.in fo.Name)
127 }
128
129 // httpZipFS is the zip-file based implementation of http.FileSystem
130 type httpZipFS struct {
131 *zip.ReadCloser
132 list zipList
133 root string
134 }
135
136 func (fs *httpZipFS) Open(abspath string) (http.File, os.Error) {
137 name := path.Join(fs.root, abspath)
138 index := fs.list.lookup(name)
139 if index < 0 {
140 return nil, fmt.Errorf("file not found: %s", abspath)
141 }
142
143 if f := fs.list[index]; f.Name == name {
144 // exact match found - must be a file
145 rc, err := f.Open()
146 if err != nil {
147 return nil, err
148 }
149 return &httpZipFile{
150 os.FileInfo{
151 Name: abspath,
152 Mode: S_IFREG,
153 Size: int64(f.UncompressedSize),
154 Mtime_ns: f.Mtime_ns(),
155 },
156 rc,
157 nil,
158 }, nil
159 }
160
161 // not an exact match - must be a directory
162 println("opened directory", abspath, len(fs.list[index:]))
163 return &httpZipFile{
164 os.FileInfo{
165 Name: abspath,
166 Mode: S_IFDIR,
167 // no size or mtime_ns for directories
168 },
169 nil,
170 fs.list[index:],
171 }, nil
172 }
173
174 func (fs *httpZipFS) Close() os.Error {
175 fs.list = nil
176 return fs.ReadCloser.Close()
177 }
178
179 func NewHttpZipFS(rc *zip.ReadCloser, root string) http.FileSystem {
180 list := make(zipList, len(rc.File))
181 copy(list, rc.File) // sort a copy of rc.File
182 sort.Sort(list)
183 return &httpZipFS{rc, list, zipPath(root)}
184 }
OLDNEW
« no previous file with comments | « src/cmd/godoc/godoc.go ('k') | src/cmd/godoc/main.go » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b