Index: utils/archive/entry.dart |
diff --git a/utils/archive/entry.dart b/utils/archive/entry.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..22387dfa9e3e1c431873b7f464238d27be8e4d57 |
--- /dev/null |
+++ b/utils/archive/entry.dart |
@@ -0,0 +1,221 @@ |
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+#library("entry"); |
+ |
+#import("dart:io"); |
+#import("read_request.dart"); |
+#import("utils.dart"); |
+ |
+/** |
+ * A single file in an archive. |
+ * |
+ * This is accessible via [ArchiveInputStream.onEntry]. |
+ */ |
+class ArchiveEntry { |
+ /** |
+ * The various properties of this archive entry, as sent over from the C |
+ * extension. |
+ */ |
+ final List _properties; |
+ |
+ /** |
+ * The id of the archive to which this entry belongs. Used to read the entry |
+ * data. This will be set to null once there's no longer data available to be |
+ * read for this entry. |
+ */ |
+ int _archiveId; |
+ |
+ /** |
+ * The input stream being used to read data from this entry. This is null |
+ * until [openInputStream] is called. |
+ */ |
+ InputStream _input; |
+ |
+ // TODO(nweiz): Get rid of this once issue 4202 is fixed. |
+ /** |
+ * A future that only exists once [openInputStream] is called, and completes |
+ * once the input stream is closed. |
+ * |
+ * For internal use only. |
+ */ |
+ Future inputComplete; |
+ |
+ ArchiveEntry(this._archiveId, this._properties); |
+ |
+ /** If this entry is a hardlink, this is the destination. Otherwise, null. */ |
+ String get hardlink() => _properties[0]; |
+ |
+ /** The path to this entry in the archive. */ |
+ String get pathname() => _properties[1]; |
+ |
+ /** The path to this entry on disk, */ |
+ String get sourcepath() => _properties[2]; |
+ |
+ /** If this entry is a symlink, this is the destination. Otherwise, null. */ |
+ String get symlink() => _properties[3]; |
+ |
+ /** The group identifier for this entry. */ |
+ int get gid() => _properties[4]; |
+ |
+ /** The user identifier for this entry. */ |
+ int get uid() => _properties[5]; |
+ |
+ /** The permissions bitmask for this entry. */ |
+ int get perm_mask() => _properties[6]; |
+ |
+ /** The String representation of the permissions for this entry. */ |
+ String get strmode() => _properties[7]; |
+ |
+ /** The name of the group this entry belongs to. */ |
+ String get gname() => _properties[8]; |
+ |
+ /** The name of the user this entry belongs to. */ |
+ String get uname() => _properties[9]; |
+ |
+ /** The file flag bits that should be set for this entry. */ |
+ int get fflags_set() => _properties[10]; |
+ |
+ /** The file flag bits that should be cleared for this entry. */ |
+ int get fflags_clear() => _properties[11]; |
+ |
+ /** The textual representation of the file flags for this entry. */ |
+ String get fflags_text() => _properties[12]; |
+ |
+ /** The filetype bitmask for this entry. */ |
+ int get filetype_mask() => _properties[13]; |
+ |
+ /** The filetype and permissions bitmask for this entry. */ |
+ int get mode_mask() => _properties[14]; |
+ |
+ /** The size of this entry in bytes, or null if it's unset. */ |
+ int get size() => _properties[15]; |
+ |
+ /** The ID of the device containing this entry, or null if it's unset. */ |
+ int get dev() => _properties[16]; |
+ |
+ /** The major number of the ID of the device containing this entry. */ |
+ int get devmajor() => _properties[17]; |
+ |
+ /** The minor number of the ID of the device containing this entry. */ |
+ int get devminor() => _properties[18]; |
+ |
+ /** The inode number of this entry, or null if it's unset. */ |
+ int get ino() => _properties[19]; |
+ |
+ /** The number of references to this entry. */ |
+ int get nlink() => _properties[20]; |
+ |
+ /** The device ID of this entry. */ |
+ int get rdev() => _properties[21]; |
+ |
+ /** The major number of the device ID of this entry. */ |
+ int get rdevmajor() => _properties[22]; |
+ |
+ /** The minor number of the device ID of this entry. */ |
+ int get rdevminor() => _properties[23]; |
+ |
+ /** The last time this entry was accessed. */ |
+ Date get atime() => new Date.fromMillisecondsSinceEpoch(_properties[24]); |
+ |
+ /** The nanoseconds at the last time this entry was accessed. */ |
+ int get atime_nsec() => _properties[25]; |
+ |
+ /** The time this entry was created. */ |
+ Date get birthtime() => new Date.fromMillisecondsSinceEpoch(_properties[26]); |
+ |
+ /** The nanoseconds at the time this entry was created. */ |
+ int get birthtime_nsec() => _properties[27]; |
+ |
+ /** The last time an inode property of this entry was changed. */ |
+ Date get ctime() => new Date.fromMillisecondsSinceEpoch(_properties[28]); |
+ |
+ /** |
+ * The nanoseconds at the last time an inode property of this entry was |
+ * changed. |
+ */ |
+ int get ctime_nsec() => _properties[29]; |
+ |
+ /** The last time this entry was modified. */ |
+ Date get mtime() => new Date.fromMillisecondsSinceEpoch(_properties[30]); |
+ |
+ /** The nanoseconds at the last time this entry was modified. */ |
+ int get mtime_nsec() => _properties[31]; |
+ |
+ /** Whether [openInputStream] has been called. */ |
+ bool get isInputOpen() => _input != null; |
+ |
+ /** |
+ * Creates a new input stream for reading the contents of this entry. |
+ * |
+ * The contents of an entry must be consumed before the next entry is read |
+ * from the parent [ArchiveInputStream]. This means that [openInputStream] |
+ * must be called from the [ArchiveInputStream.onEntry] callback, or before |
+ * the future returned by that callback completes. Once the next entry has |
+ * been read, calling [openInputStream] will throw an [ArchiveException]. |
+ * |
+ * Only one input stream may be opened per entry. |
+ */ |
+ InputStream openInputStream() { |
+ if (_archiveId == null) { |
+ throw new UnsupportedOperationException("Archive entry $pathname is no " |
+ "longer being read from the archive."); |
+ } else if (_input != null) { |
+ throw new UnsupportedOperationException("An input stream has already been" |
+ "opened for archive entry $pathname."); |
+ } |
+ |
+ var inputCompleter = new Completer(); |
+ inputComplete = inputCompleter.future; |
+ |
+ _input = new ListInputStream(); |
+ // TODO(nweiz): Report errors once issue 3657 is fixed |
+ var future = _consumeInput().chain((_) { |
+ if (!_input.closed) _input.markEndOfStream(); |
+ // Asynchronously complete to give the InputStream callbacks a chance to |
+ // fire. |
+ return async(); |
+ }).transform((_) => inputCompleter.complete(null)); |
+ |
+ future.handleException((e) { |
+ print(e); |
+ print(future.stackTrace); |
+ }); |
+ return _input; |
+ } |
+ |
+ /** |
+ * Close this entry so that its input stream no longer produces data. |
+ * |
+ * In addition to closing the associated input stream, this will prevent new |
+ * input streams from being opened. |
+ */ |
+ void close() { |
+ _archiveId = null; |
+ if (_input != null) _input.close(); |
+ } |
+ |
+ /** |
+ * Read all data from the archive and write it to [_input]. Returns a future |
+ * that completes once this is done. |
+ * |
+ * This assumes that both [_input] and [_archiveId] are non-null and that |
+ * [_input] is open, although if that changes before this completes it will |
+ * handle it gracefully. |
+ */ |
+ Future _consumeInput() { |
+ var data; |
+ return call(DATA_BLOCK, _archiveId).chain((_data) { |
+ data = _data; |
+ // TODO(nweiz): This async() call is only necessary because of issue 4222. |
+ return async(); |
+ }).chain((_) { |
+ if (_input.closed || _archiveId == null || data == null) { |
+ return new Future.immediate(null); |
+ } |
+ _input.write(data); |
+ return _consumeInput(); |
+ }); |
+ } |
+} |