Initial commit.
diff --git a/pkgs/io/.gitignore b/pkgs/io/.gitignore
new file mode 100644
index 0000000..dd3394e
--- /dev/null
+++ b/pkgs/io/.gitignore
@@ -0,0 +1,3 @@
+.pub/
+.packages
+pubspec.lock
diff --git a/pkgs/io/.travis.yml b/pkgs/io/.travis.yml
new file mode 100644
index 0000000..ccd3815
--- /dev/null
+++ b/pkgs/io/.travis.yml
@@ -0,0 +1,20 @@
+language: dart
+sudo: false
+dart:
+ - dev
+ - stable
+cache:
+ directories:
+ - $HOME/.pub-cache
+dart_task:
+ - test: --platform vm
+ install_dartium: true
+ - dartanalyzer
+ - dartfmt
+matrix:
+ # Only run dartfmt checks with stable.
+ exclude:
+ - dart: dev
+ dart_task: dartfmt
+ - dart: dev
+ dart_task: dartanalyzer
diff --git a/pkgs/io/AUTHORS b/pkgs/io/AUTHORS
new file mode 100644
index 0000000..ff09364
--- /dev/null
+++ b/pkgs/io/AUTHORS
@@ -0,0 +1,7 @@
+# Below is a list of people and organizations that have contributed
+# to the project. Names should be added to the list like so:
+#
+# Name/Organization <email address>
+
+Google Inc.
+
diff --git a/pkgs/io/CHANGELOG.md b/pkgs/io/CHANGELOG.md
new file mode 100644
index 0000000..9334d24
--- /dev/null
+++ b/pkgs/io/CHANGELOG.md
@@ -0,0 +1,7 @@
+## 0.1.0
+
+- Initial commit of `FutureOr<bool> String isExecutable(path)`:
+
+```dart
+
+```
\ No newline at end of file
diff --git a/pkgs/io/LICENSE b/pkgs/io/LICENSE
new file mode 100644
index 0000000..e76eb0c
--- /dev/null
+++ b/pkgs/io/LICENSE
@@ -0,0 +1,26 @@
+Copyright 2017, the Dart project authors. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of package:dom nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/pkgs/io/README.md b/pkgs/io/README.md
new file mode 100644
index 0000000..e470cf5
--- /dev/null
+++ b/pkgs/io/README.md
@@ -0,0 +1,12 @@
+# io
+
+Contains utilities for the Dart VM's `dart:io`.
+
+## Usage
+
+### Files
+
+#### `isExecutable`
+
+Returns whether a provided file path is considered _executable_ on the host
+operating system.
diff --git a/pkgs/io/analysis_options.yaml b/pkgs/io/analysis_options.yaml
new file mode 100644
index 0000000..cf2c891
--- /dev/null
+++ b/pkgs/io/analysis_options.yaml
@@ -0,0 +1,54 @@
+analyzer:
+ strong-mode:
+ implicit-casts: false
+ implicit-dynamic: false
+
+linter:
+ rules:
+ # Error Rules
+ - avoid_empty_else
+ - comment_references
+ - control_flow_in_finally
+ - empty_statements
+ - hash_and_equals
+ - invariant_booleans
+ - iterable_contains_unrelated_type
+ - list_remove_unrelated_type
+ - no_adjacent_strings_in_list
+ - no_duplicate_case_values
+ - test_types_in_equals
+ - throw_in_finally
+ - unrelated_type_equality_checks
+ - valid_regexps
+
+ # Style Rules
+ - annotate_overrides
+ - avoid_init_to_null
+ - avoid_return_types_on_setters
+ - camel_case_types
+ - cascade_invocations
+ - constant_identifier_names
+ - directives_ordering
+ - empty_catches
+ - empty_constructor_bodies
+ - implementation_imports
+ - library_names
+ - library_prefixes
+ - non_constant_identifier_names
+ - omit_local_variable_types
+ - only_throw_errors
+ - prefer_adjacent_string_concatenation
+ - prefer_collection_literals
+ - prefer_const_constructors
+ - prefer_contains
+ - prefer_final_fields
+ - prefer_final_locals
+ - prefer_initializing_formals
+ - prefer_interpolation_to_compose_strings
+ - prefer_is_empty
+ - prefer_is_not_empty
+ - recursive_getters
+ - slash_for_doc_comments
+ - type_init_formals
+ - unnecessary_brace_in_string_interps
+ - unnecessary_this
diff --git a/pkgs/io/lib/io.dart b/pkgs/io/lib/io.dart
new file mode 100644
index 0000000..35a6663
--- /dev/null
+++ b/pkgs/io/lib/io.dart
@@ -0,0 +1,6 @@
+// Copyright 2017, 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.
+
+export 'src/permissions.dart'
+ show FilePermissionRole, FilePermission, hasPermission, isExecutable;
diff --git a/pkgs/io/lib/src/permissions.dart b/pkgs/io/lib/src/permissions.dart
new file mode 100644
index 0000000..d72afd2
--- /dev/null
+++ b/pkgs/io/lib/src/permissions.dart
@@ -0,0 +1,72 @@
+// Copyright 2017, 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.
+
+import 'dart:async';
+import 'dart:io';
+
+/// What type of permission is granted to a file based on file permission roles.
+enum _FilePermission {
+ execute,
+ write,
+ read,
+ setGid,
+ setUid,
+ sticky,
+}
+
+/// What type of role is assigned to a file.
+enum _FilePermissionRole {
+ world,
+ group,
+ user,
+}
+
+/// Returns whether file [stat] has [permission] for a [role] type.
+bool _hasPermission(
+ FileStat stat,
+ _FilePermission permission, {
+ _FilePermissionRole role: _FilePermissionRole.world,
+}) {
+ final index = _permissionBitIndex(permission, role);
+ return (stat.mode & (1 << index)) != 0;
+}
+
+int _permissionBitIndex(_FilePermission permission, _FilePermissionRole role) {
+ switch (permission) {
+ case _FilePermission.setUid:
+ return 11;
+ case _FilePermission.setGid:
+ return 10;
+ case _FilePermission.sticky:
+ return 9;
+ default:
+ return (role.index * 3) + permission.index;
+ }
+}
+
+/// Returns whether [path] is considered an executable file on this OS.
+///
+/// May optionally define how to implement [getStat] or whether to execute based
+/// on whether this is the windows platform ([isWindows]) - if not set it is
+/// automatically extracted from `dart:io#Platform`.
+///
+/// **NOTE**: On windows this always returns `true`.
+FutureOr<bool> isExecutable(
+ String path, {
+ bool isWindows,
+ FutureOr<FileStat> getStat(String path): FileStat.stat,
+}) {
+ // Windows has no concept of executable.
+ if (isWindows ?? Platform.isWindows) return true;
+ final stat = getStat(path);
+ if (stat is FileStat) {
+ return _isExecutable(stat);
+ }
+ return (stat as Future<FileStat>).then(_isExecutable);
+}
+
+bool _isExecutable(FileStat stat) =>
+ stat.type == FileSystemEntityType.FILE &&
+ _FilePermissionRole.values.any(
+ (role) => _hasPermission(stat, _FilePermission.execute, role: role));
diff --git a/pkgs/io/pubspec.yaml b/pkgs/io/pubspec.yaml
new file mode 100644
index 0000000..999e752
--- /dev/null
+++ b/pkgs/io/pubspec.yaml
@@ -0,0 +1,12 @@
+name: io
+description: >
+ Utilities for the Dart VM Runtime.
+version: 0.1.0
+author: Dart Team <misc@dartlang.org>
+homepage: https://github.com/dart-lang/io
+
+environment:
+ sdk: ">=1.22.0 <2.0.0"
+
+dev_dependencies:
+ test: ^0.12.0
diff --git a/pkgs/io/test/_files/is_executable.sh b/pkgs/io/test/_files/is_executable.sh
new file mode 100755
index 0000000..f1f641a
--- /dev/null
+++ b/pkgs/io/test/_files/is_executable.sh
@@ -0,0 +1 @@
+#!/usr/bin/env bash
diff --git a/pkgs/io/test/_files/is_not_executable.sh b/pkgs/io/test/_files/is_not_executable.sh
new file mode 100644
index 0000000..f1f641a
--- /dev/null
+++ b/pkgs/io/test/_files/is_not_executable.sh
@@ -0,0 +1 @@
+#!/usr/bin/env bash
diff --git a/pkgs/io/test/permissions_test.dart b/pkgs/io/test/permissions_test.dart
new file mode 100644
index 0000000..51fbf6b
--- /dev/null
+++ b/pkgs/io/test/permissions_test.dart
@@ -0,0 +1,35 @@
+// Copyright 2017, 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.
+
+import 'package:io/io.dart';
+import 'package:test/test.dart';
+
+void main() {
+ group('isExecutable', () {
+ const files = 'test/_files';
+ const shellIsExec = '$files/is_executable.sh';
+ const shellNotExec = '$files/is_not_executable.sh';
+
+ group('on shell scripts', () {
+ test('should return true for "is_executable.sh"', () async {
+ expect(await isExecutable(shellIsExec), isTrue);
+ });
+
+ test('should return false for "is_not_executable.sh"', () async {
+ expect(await isExecutable(shellNotExec), isFalse);
+ });
+ }, testOn: '!windows');
+ group('on shell scripts [windows]', () {
+ test('should always return true', () {
+ test('should return true for "is_executable.sh"', () async {
+ expect(await isExecutable(shellIsExec, isWindows: true), isTrue);
+ });
+
+ test('should return true for "is_not_executable.sh"', () async {
+ expect(await isExecutable(shellNotExec, isWindows: true), isTrue);
+ });
+ });
+ });
+ });
+}