On x86 compilers without fastcall, simulate it when invoking traces and un-simulate...
[wine-gecko.git] / netwerk / base / src / nsDirectoryIndexStream.cpp
blob961403f034b3e5d2bcf86f54796625372756d67b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set sw=4 sts=4 et cin: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Bradley Baetz <bbaetz@cs.mcgill.ca>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
43 The converts a filesystem directory into an "HTTP index" stream per
44 Lou Montulli's original spec:
46 http://www.mozilla.org/projects/netlib/dirindexformat.html
50 #include "nsEscape.h"
51 #include "nsDirectoryIndexStream.h"
52 #include "nsXPIDLString.h"
53 #include "prio.h"
54 #include "prlog.h"
55 #include "prlong.h"
56 #ifdef PR_LOGGING
57 static PRLogModuleInfo* gLog;
58 #endif
60 #include "nsISimpleEnumerator.h"
61 #include "nsICollation.h"
62 #include "nsILocale.h"
63 #include "nsILocaleService.h"
64 #include "nsCollationCID.h"
65 #include "nsIPlatformCharset.h"
66 #include "nsReadableUtils.h"
67 #include "nsURLHelper.h"
68 #include "nsNetUtil.h"
69 #include "nsCRT.h"
70 #include "nsNativeCharsetUtils.h"
72 // NOTE: This runs on the _file transport_ thread.
73 // The problem is that now that we're actually doing something with the data,
74 // we want to do stuff like i18n sorting. However, none of the collation stuff
75 // is threadsafe.
76 // So THIS CODE IS ASCII ONLY!!!!!!!! This is no worse than the current
77 // behaviour, though. See bug 99382.
78 // When this is fixed, #define THREADSAFE_I18N to get this code working
80 //#define THREADSAFE_I18N
82 nsDirectoryIndexStream::nsDirectoryIndexStream()
83 : mOffset(0), mStatus(NS_OK), mPos(0)
85 #ifdef PR_LOGGING
86 if (! gLog)
87 gLog = PR_NewLogModule("nsDirectoryIndexStream");
88 #endif
90 PR_LOG(gLog, PR_LOG_DEBUG,
91 ("nsDirectoryIndexStream[%p]: created", this));
94 static int PR_CALLBACK compare(nsIFile* aElement1,
95 nsIFile* aElement2,
96 void* aData)
98 if (!NS_IsNativeUTF8()) {
99 // don't check for errors, because we can't report them anyway
100 nsAutoString name1, name2;
101 aElement1->GetLeafName(name1);
102 aElement2->GetLeafName(name2);
104 // Note - we should do the collation to do sorting. Why don't we?
105 // Because that is _slow_. Using TestProtocols to list file:///dev/
106 // goes from 3 seconds to 22. (This may be why nsXULSortService is
107 // so slow as well).
108 // Does this have bad effects? Probably, but since nsXULTree appears
109 // to use the raw RDF literal value as the sort key (which ammounts to an
110 // strcmp), it won't be any worse, I think.
111 // This could be made faster, by creating the keys once,
112 // but CompareString could still be smarter - see bug 99383 - bbaetz
113 // NB - 99393 has been WONTFIXed. So if the I18N code is ever made
114 // threadsafe so that this matters, we'd have to pass through a
115 // struct { nsIFile*, PRUint8* } with the pre-calculated key.
116 return Compare(name1, name2);
119 nsCAutoString name1, name2;
120 aElement1->GetNativeLeafName(name1);
121 aElement2->GetNativeLeafName(name2);
123 return Compare(name1, name2);
126 nsresult
127 nsDirectoryIndexStream::Init(nsIFile* aDir)
129 nsresult rv;
130 PRBool isDir;
131 rv = aDir->IsDirectory(&isDir);
132 if (NS_FAILED(rv)) return rv;
133 NS_PRECONDITION(isDir, "not a directory");
134 if (!isDir)
135 return NS_ERROR_ILLEGAL_VALUE;
137 #ifdef PR_LOGGING
138 if (PR_LOG_TEST(gLog, PR_LOG_DEBUG)) {
139 nsCAutoString path;
140 aDir->GetNativePath(path);
141 PR_LOG(gLog, PR_LOG_DEBUG,
142 ("nsDirectoryIndexStream[%p]: initialized on %s",
143 this, path.get()));
145 #endif
147 // Sigh. We have to allocate on the heap because there are no
148 // assignment operators defined.
149 nsCOMPtr<nsISimpleEnumerator> iter;
150 rv = aDir->GetDirectoryEntries(getter_AddRefs(iter));
151 if (NS_FAILED(rv)) return rv;
153 // Now lets sort, because clients expect it that way
154 // XXX - should we do so here, or when the first item is requested?
155 // XXX - use insertion sort instead?
157 PRBool more;
158 nsCOMPtr<nsISupports> elem;
159 while (NS_SUCCEEDED(iter->HasMoreElements(&more)) && more) {
160 rv = iter->GetNext(getter_AddRefs(elem));
161 if (NS_SUCCEEDED(rv)) {
162 nsCOMPtr<nsIFile> file = do_QueryInterface(elem);
163 if (file)
164 mArray.AppendObject(file); // addrefs
168 #ifdef THREADSAFE_I18N
169 nsCOMPtr<nsILocaleService> ls = do_GetService(NS_LOCALESERVICE_CONTRACTID,
170 &rv);
171 if (NS_FAILED(rv)) return rv;
173 nsCOMPtr<nsILocale> locale;
174 rv = ls->GetApplicationLocale(getter_AddRefs(locale));
175 if (NS_FAILED(rv)) return rv;
177 nsCOMPtr<nsICollationFactory> cf = do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID,
178 &rv);
179 if (NS_FAILED(rv)) return rv;
181 nsCOMPtr<nsICollation> coll;
182 rv = cf->CreateCollation(locale, getter_AddRefs(coll));
183 if (NS_FAILED(rv)) return rv;
185 mArray.Sort(compare, coll);
186 #else
187 mArray.Sort(compare, nsnull);
188 #endif
190 mBuf.AppendLiteral("300: ");
191 nsCAutoString url;
192 rv = net_GetURLSpecFromFile(aDir, url);
193 if (NS_FAILED(rv)) return rv;
194 mBuf.Append(url);
195 mBuf.Append('\n');
197 mBuf.AppendLiteral("200: filename content-length last-modified file-type\n");
199 return NS_OK;
202 nsDirectoryIndexStream::~nsDirectoryIndexStream()
204 PR_LOG(gLog, PR_LOG_DEBUG,
205 ("nsDirectoryIndexStream[%p]: destroyed", this));
208 nsresult
209 nsDirectoryIndexStream::Create(nsIFile* aDir, nsIInputStream** aResult)
211 nsDirectoryIndexStream* result = new nsDirectoryIndexStream();
212 if (! result)
213 return NS_ERROR_OUT_OF_MEMORY;
215 nsresult rv;
216 rv = result->Init(aDir);
217 if (NS_FAILED(rv)) {
218 delete result;
219 return rv;
222 *aResult = result;
223 NS_ADDREF(*aResult);
224 return NS_OK;
227 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDirectoryIndexStream, nsIInputStream)
229 // The below routines are proxied to the UI thread!
230 NS_IMETHODIMP
231 nsDirectoryIndexStream::Close()
233 mStatus = NS_BASE_STREAM_CLOSED;
234 return NS_OK;
237 NS_IMETHODIMP
238 nsDirectoryIndexStream::Available(PRUint32* aLength)
240 if (NS_FAILED(mStatus))
241 return mStatus;
243 // If there's data in our buffer, use that
244 if (mOffset < (PRInt32)mBuf.Length()) {
245 *aLength = mBuf.Length() - mOffset;
246 return NS_OK;
249 // Returning one byte is not ideal, but good enough
250 *aLength = (mPos < mArray.Count()) ? 1 : 0;
251 return NS_OK;
254 NS_IMETHODIMP
255 nsDirectoryIndexStream::Read(char* aBuf, PRUint32 aCount, PRUint32* aReadCount)
257 if (mStatus == NS_BASE_STREAM_CLOSED) {
258 *aReadCount = 0;
259 return NS_OK;
261 if (NS_FAILED(mStatus))
262 return mStatus;
264 PRUint32 nread = 0;
266 // If anything is enqueued (or left-over) in mBuf, then feed it to
267 // the reader first.
268 while (mOffset < (PRInt32)mBuf.Length() && aCount != 0) {
269 *(aBuf++) = char(mBuf.CharAt(mOffset++));
270 --aCount;
271 ++nread;
274 // Room left?
275 if (aCount > 0) {
276 mOffset = 0;
277 mBuf.Truncate();
279 // Okay, now we'll suck stuff off of our iterator into the mBuf...
280 while (PRUint32(mBuf.Length()) < aCount) {
281 PRBool more = mPos < mArray.Count();
282 if (!more) break;
284 // don't addref, for speed - an addref happened when it
285 // was placed in the array, so it's not going to go stale
286 nsIFile* current = mArray.ObjectAt(mPos);
287 ++mPos;
289 #ifdef PR_LOGGING
290 if (PR_LOG_TEST(gLog, PR_LOG_DEBUG)) {
291 nsCAutoString path;
292 current->GetNativePath(path);
293 PR_LOG(gLog, PR_LOG_DEBUG,
294 ("nsDirectoryIndexStream[%p]: iterated %s",
295 this, path.get()));
297 #endif
299 // rjc: don't return hidden files/directories!
300 // bbaetz: why not?
301 nsresult rv;
302 #ifndef XP_UNIX
303 PRBool hidden = PR_FALSE;
304 current->IsHidden(&hidden);
305 if (hidden) {
306 PR_LOG(gLog, PR_LOG_DEBUG,
307 ("nsDirectoryIndexStream[%p]: skipping hidden file/directory",
308 this));
309 continue;
311 #endif
313 PRInt64 fileSize = 0;
314 current->GetFileSize( &fileSize );
316 PRInt64 fileInfoModifyTime = 0;
317 current->GetLastModifiedTime( &fileInfoModifyTime );
318 fileInfoModifyTime *= PR_USEC_PER_MSEC;
320 mBuf.AppendLiteral("201: ");
322 // The "filename" field
323 char* escaped = nsnull;
324 if (!NS_IsNativeUTF8()) {
325 nsAutoString leafname;
326 rv = current->GetLeafName(leafname);
327 if (NS_FAILED(rv)) return rv;
328 if (!leafname.IsEmpty())
329 escaped = nsEscape(NS_ConvertUTF16toUTF8(leafname).get(), url_Path);
330 } else {
331 nsCAutoString leafname;
332 rv = current->GetNativeLeafName(leafname);
333 if (NS_FAILED(rv)) return rv;
334 if (!leafname.IsEmpty())
335 escaped = nsEscape(leafname.get(), url_Path);
337 if (escaped) {
338 mBuf += escaped;
339 mBuf.Append(' ');
340 nsMemory::Free(escaped);
343 // The "content-length" field
344 mBuf.AppendInt(fileSize, 10);
345 mBuf.Append(' ');
347 // The "last-modified" field
348 PRExplodedTime tm;
349 PR_ExplodeTime(fileInfoModifyTime, PR_GMTParameters, &tm);
351 char buf[64];
352 PR_FormatTimeUSEnglish(buf, sizeof(buf), "%a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ", &tm);
353 mBuf.Append(buf);
356 // The "file-type" field
357 PRBool isFile = PR_TRUE;
358 current->IsFile(&isFile);
359 if (isFile) {
360 mBuf.AppendLiteral("FILE ");
362 else {
363 PRBool isDir;
364 rv = current->IsDirectory(&isDir);
365 if (NS_FAILED(rv)) return rv;
366 if (isDir) {
367 mBuf.AppendLiteral("DIRECTORY ");
369 else {
370 PRBool isLink;
371 rv = current->IsSymlink(&isLink);
372 if (NS_FAILED(rv)) return rv;
373 if (isLink) {
374 mBuf.AppendLiteral("SYMBOLIC-LINK ");
379 mBuf.Append('\n');
382 // ...and once we've either run out of directory entries, or
383 // filled up the buffer, then we'll push it to the reader.
384 while (mOffset < (PRInt32)mBuf.Length() && aCount != 0) {
385 *(aBuf++) = char(mBuf.CharAt(mOffset++));
386 --aCount;
387 ++nread;
391 *aReadCount = nread;
392 return NS_OK;
395 NS_IMETHODIMP
396 nsDirectoryIndexStream::ReadSegments(nsWriteSegmentFun writer, void * closure, PRUint32 count, PRUint32 *_retval)
398 return NS_ERROR_NOT_IMPLEMENTED;
401 NS_IMETHODIMP
402 nsDirectoryIndexStream::IsNonBlocking(PRBool *aNonBlocking)
404 *aNonBlocking = PR_FALSE;
405 return NS_OK;