var xpi = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
var path = this.name;
path = path.replace(/\//g, "\\"); // HACK!
xpi.initWithPath(path);
var Cif = Components.interfaces.nsIFile;
writer.open(xpi, Cif.PR_RDWR | Cif.PR_CREATE_FILE | Cif.PR_TRUNCATE);
Any ideas?
John.
> var Cif = Components.interfaces.nsIFile;
> writer.open(xpi, Cif.PR_RDWR | Cif.PR_CREATE_FILE | Cif.PR_TRUNCATE);
Those PR_* constants don't exist on Components.interfaces.nsIFile
(Components.interfaces.nsIFile.PR_CREATE_FILE is undefined), so you're
effectively passing in 0.
Open() throws NS_ERROR_FILE_NOT_FOUND if the file doesn't exist and
you don't pass in PR_CREATE_FILE:
http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/modules/libjar/zipwriter/src/nsZipWriter.cpp&rev=1.2&mark=281,282#264
I don't think the flags are defined anywhere that's easily accessible
from JS. You probably need to just redefine them in JS based on their
definitions in C++:
http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/nsprpub/pr/include/prio.h&rev=3.40#612
Some examples:
http://mxr.mozilla.org/seamonkey/search?string=PR_&find=\.js&findi=&filter=&hitlimit=&tree=seamonkey
Gavin
Please, can't error messages say what they mean?
>
> I don't think the flags are defined anywhere that's easily accessible
> from JS.
Bug 433295 has been added to the database
Thanks Gavin.
Given the current methods used to propagate exceptions through
XPCOM/xpconnect? Not really - at least not without a lot of overhead.
There's almost always going to be room for improvement for how
detailed the exception messages are. I think there's a balance to be
had between the performance and footprint overhead of propagating
unique textual descriptions for every possible exceptional
circumstance, and the confusion caused by difficult to debug error
messages. I think you're right that in a lot of cases the current
setup is suboptimal (e.g. trying to find the source of a generic
NS_ERROR_FAILURE can often be frustrating).
In this particular case, the error code made it fairly obvious what
the problem was once you looked at the implementation of the method
you were calling. Learning how to find that implementation using
LXR/MXR perhaps isn't as easy as it should be, but it's certainly a
skill worth learning for those cases when you encounter mysterious
behavior.
Gavin
If that is so, why not just avoid all of the overhead of exceptions?
Simply abort execution and print the stack trace for all exceptions.
Then with a simple tool to map the stack trace to LXR/MXR, I will be
quickly lead to the test that failed. This seems more sensible than
error messages that are designed incomplete.
John.
> var xpi = Components.classes["@mozilla.org/file/local;1"]
> .createInstance(Components.interfaces.nsILocalFile);
> var path = this.name;
> path = path.replace(/\//g, "\\"); // HACK!
Why might you need to do that? Unless someone's passing you bogus data,
all internal file paths should use native path separators and all URIs
should use forward slashes and the conversion functions switch as necessary.
--
Warning: May contain traces of nuts.
That _is_ what the error means: you've asked it to open a file
(without CREATE it means to open an existing one) and the file doesn't
exist. You get something very similar from open(2). A better API
might have separated open and create, but without that I don't think
you're going to get much of a better error.
Mike
If the message had been:
"The file "+file.path+" was not found (or its directory does not exist)
and the flags "+(PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE)+" didn't permit
creating it."
then I could have cut a half-hour off my coding. (not count whining in
the newsgroup). When I got NS_ERROR_FILE_NOT_FOUND what could I do? Well
first I had to check that the file name input was what I expected by
comparing my input to the file system. Then I had to change my code to
print the file name just before the open() call to verify that the
string I sent to open() was the same one as I gave my program. Then I
had to re-read the documentation for nsIFile because I still can't
believe that it won't take file names with forward slashes. Then I
re-read the example code for nsIZipWriter but since it is incomplete I
edited the wiki. I didn't read the exception parts because, after a few
thousand examples of exception documentation like:
NS_ERROR_FILE_NOT_FOUND: the file was not found.
I eventually learned that this is a waste time. Finally I went back and
printed everything in the method. When PR_RDWR came back zero, I fixed it.
The method returns an int, which could be an index in to large string
table either online or downloaded by developers and populated by
build-processing of the C++ code. The C++ code could replace
return NS_ERROR_FILE_NOT_FOUND;
with
return NS_ERROR("The file %s...", mFile->Path, aIoFlags);
When I send file names from my command line I use forward slashes. On
every other cross platform tool I have used, forward slashes work for
file names in code. In these tools, backslashes often are the quoting
character. (Forward slashes work fine windows). Since my command line
tools are cross platform, I have to write platform-dependent code in
mozilla to convert my platform independent strings in to platform
dependent strings for their platform-dependent API.
John.
That is referring to the entry names passed when adding a file/directory
to the zip. The zip format specifies that / is the standard directory
separator there.
When initialising an nsILocalFile with a path I believe you are supposed
to use native path separators, though I guess it might auto fix when not.
I'm intrigued by your comment on the zipwriter documentation: "The
argument nyZipFilePath isn't a path but rather it has to be an nsIFile
instance. It's not clear what state that object should be in". What
states are you referring to?
I'd guess not, based on trying it.
> I'm intrigued by your comment on the zipwriter documentation: "The
> argument nyZipFilePath isn't a path but rather it has to be an nsIFile
> instance. It's not clear what state that object should be in". What
> states are you referring to?
>
I'm referring to the complete set of states of nsIFile, whatever they
may be, which are unknown to me. Because if I knew those states, maybe I
could puzzle out what the example code was doing. In others words "I
don't know".
Try to imagine not knowing anything about nsIFile. Then read
http://developer.mozilla.org/en/docs/nsIFile
Now explain nsIFile. I believe you will be able to tell me much about
what nsIFile is not, and lots of obscure rules about UTF.
Now I guess that nsIFile represents a full path to a file or directory.
It is largely equivalent to a string file name, except that it has to be
initialized in an OS-dependent fashion. Like a string file name, nothing
stored nsIFile relates to physical storage. Operations on nsIFile like
"create" and "copyTo" do change the state of physical storage, but not
the state of nsIFile object.
John.
> The method returns an int, which could be an index in to large string
> table either online or downloaded by developers and populated by
> build-processing of the C++ code. The C++ code could replace
> return NS_ERROR_FILE_NOT_FOUND;
> with
> return NS_ERROR("The file %s...", mFile->Path, aIoFlags);
This proposed behavior doesn't correspond with the role nsresult plays
in XPCOM. The type is an *indicator* of XPCOM method success/failure, as
different from a *description* of XPCOM method failure. Method author
may or may not use different codes in different situations. It is
generally not safe to make any conclusions based on nsresult with a
notable exception of passing it to NS_SUCCEED/NS_FAILED.
It is also important that nsresult is a 32bit integer, so
return NS_ERROR_FILE_NOT_FOUND;
is roughly equivalent to a single machine instruction
mov eax, NS_ERROR_FILE_NOT_FOUND
and has no side effect. At the same time,
return NS_ERROR("The file %s...", mFile->Path, aIoFlags);
involves string manipulations call, and is several orders of magnitude
slower. In addition, it will allocate a string, which will require a
huge framework and extra overhead to be freed properly.
This issue is best addressed by C++ exceptions, which are in the agenda
on a post-mozilla-2 timeframe.
--
Sergey Yanovich
Ok thanks, I've tried to improve on the summary to that page to try to
introduce the object better.
> Now I guess that nsIFile represents a full path to a file or directory.
> It is largely equivalent to a string file name, except that it has to be
> initialized in an OS-dependent fashion. Like a string file name, nothing
> stored nsIFile relates to physical storage. Operations on nsIFile like
> "create" and "copyTo" do change the state of physical storage, but not
> the state of nsIFile object.
Yes an nsIFile does just represent a location in the filesystem, which
thankfully for all modern systems is a string path, however in most
cases you need not use OS dependant code for initialising it, you can
get an nsIFile from the directory service or a file picker, both of
which are cross platform (mostly) and will let you use the filesystem
without worrying about platform specific path representations.
Its worth it. NS_ERROR can be a macro that returns a 32bit integer key
for a table created by a compile time tool. Developer builds can get
real error messages; user builds get a key and a method that links that
key to an online copy of the message table. The user message would not
have values for paths and flags, but it would be better than "NS_ERROR".
> This issue is best addressed by C++ exceptions, which are in the agenda
> on a post-mozilla-2 timeframe.
As in Mozilla-2 or as in Mozilla-3?
It represents a location where a file can be. Or you can think of it as
a file identifier. You're correct that that API documentation could say
this more explicitly. Patches very much accepted!
> It is largely equivalent to a string file name
This was not the case in the past on Mac OS. It is the case now on all
of our supported platforms. There is no guarantee that it will be the
case in the future.
An nsIFile is exactly what the documentation says:
47 * This is the only correct cross-platform way to specify a file.
48 * Strings are not such a way. If you grew up on windows or unix, you
49 * may think they are. Welcome to reality.
As I said, now that Macs also use Unix, strings would also work. For
the time being. It's not clear whether baking in such an assumption is
a good idea.
It seems that the only problem here is the "file == string" mindset of
the reader of this API, but that could be just me.
> except that it has to be initialized in an OS-dependent fashion.
If you create one by contract, this is correct, since the information
needed to identify a file differs on different OSes. But creating an
nsIFile by contract is not the right way to go, in general. The right
way to get one is via the directory service, I would say.
-Boris
Much better, thanks!
Do all platforms support file:// URLS?
John.
Define "support"? On OS9, as I recall, there were files that could not be
specified with a file:// URI. That is, the mapping from file:// URIs (and file
paths in general) to actual files was one-to-many, not one-to-one.
-Boris
Well, not quite -- numerous properties and methods (not just .exists) behave differently based on whether the file exists or not. For example, normalize() will, on Linux and I think OS X, use realpath and perhaps even produce a path entirely outside the file's original path, such that |(oldFile = file.clone(), file.normalize(), oldFile.parent != file.parent)| is true. On Windows we do string munging to remove '.' and '..' components. This is part of the reason, for example, that I can't use normalize() at the code at the following location to remove canceling components from HTTP paths:
(The other part of the reason is that on Windows nsILocalFile.appendRelativePath flatly contradicts its API and allows paths containing '.' and '..'.)
Another hazard is isDirectory(), which does an existence-and-is-directory check rather than a string check on at least some platforms. There are a bunch of hazards like this which I've encountered in practice, and the only way to know what will happen is to test or read the source code of every implementation. The IDL documentation is incomplete and inexact at times to the point of being useless.
> An nsIFile is exactly what the documentation says:
>
> 47 * This is the only correct cross-platform way to specify a file.
> 48 * Strings are not such a way. If you grew up on windows or unix, you
> 49 * may think they are. Welcome to reality.
>
> As I said, now that Macs also use Unix, strings would also work. For
> the time being. It's not clear whether baking in such an assumption is
> a good idea.
I like the idea of an object encapsulating the string to define helper methods in only one location. Given that files as a shared resource are inherently racy, I think the only reasonable thing is for files to be represented by wrappers around strings (and never, except for .exists or other non-string-based information like .lastModifiedTime, have behavior which depends upon the file existing).
> It seems that the only problem here is the "file == string" mindset of
> the reader of this API, but that could be just me.
The only problem as I see it is that the API tries to have it both ways and never specifies precisely which way it is in all the method and property descriptions. I'd have to reimplement stuff if it's specified to be existence-based, but that would be better than having to reimplement anyway just because it's not clear everyone does the right thing.
I sooo look forward to fully specifying and exactly implementing the behaviors of nsIFile and nsILocalFile when we get the chance to break ABI compatibility. As it is right now, I don't really believe it's possible to safely and securely trust the checks our Windows file implementation makes.
Jeff
Well, ok. That's a problem too, as is the fact that the implementations don't
agree with the documentation. We do have bugs covering this.
> I sooo look forward to fully specifying and exactly implementing the
> behaviors of nsIFile and nsILocalFile
Amen.
-Boris