├── .gitignore ├── .vscode └── launch.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── bin └── system_info.dart ├── example └── example.dart ├── lib ├── src │ ├── core_info.dart │ ├── file_list.dart │ ├── file_path.dart │ ├── file_utils.dart │ ├── fluent.dart │ ├── platform │ │ ├── cpu.dart │ │ ├── cpu_macos.dart │ │ ├── cpu_nix.dart │ │ ├── cpu_windows.dart │ │ ├── kernel.dart │ │ ├── memory.dart │ │ ├── operating_system.dart │ │ ├── user.dart │ │ └── userspace.dart │ ├── processor_architecture.dart │ ├── system_info.dart │ ├── utils.dart │ └── version │ │ └── version.g.dart └── system_info2.dart └── pubspec.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://www.dartlang.org/guides/libraries/private-files 2 | 3 | # Files and directories created by pub 4 | .dart_tool/ 5 | .packages 6 | build/ 7 | # If you're building an application, you may want to check-in your pubspec.lock 8 | pubspec.lock 9 | 10 | # Directory created by dartdoc 11 | # If you don't generate documentation locally you can remove this line. 12 | doc/api/ 13 | 14 | # Avoid committing generated Javascript files: 15 | *.dart.js 16 | *.info.json # Produced by the --dump-info flag. 17 | *.js # When generated by dart2js. Don't specify *.js if your 18 | # project includes source files written in JavaScript. 19 | *.js_ 20 | *.js.deps 21 | *.js.map.failed_tracker 22 | .failed_tracker 23 | .history 24 | 25 | 26 | # Do not remove or rename entries in this file, only add new ones 27 | # See https://github.com/flutter/flutter/issues/128635 for more context. 28 | 29 | # Miscellaneous 30 | *.class 31 | *.lock 32 | *.log 33 | *.pyc 34 | *.swp 35 | .DS_Store 36 | .atom/ 37 | .buildlog/ 38 | .svn/ 39 | 40 | # IntelliJ related 41 | *.iml 42 | *.ipr 43 | *.iws 44 | .idea/ 45 | 46 | # Visual Studio Code related 47 | .classpath 48 | .project 49 | .settings/ 50 | .vscode/* 51 | 52 | # Flutter repo-specific 53 | /bin/cache/ 54 | /bin/internal/bootstrap.bat 55 | /bin/internal/bootstrap.sh 56 | /bin/mingit/ 57 | /dev/benchmarks/mega_gallery/ 58 | /dev/bots/.recipe_deps 59 | /dev/bots/android_tools/ 60 | /dev/devicelab/ABresults*.json 61 | /dev/docs/doc/ 62 | /dev/docs/api_docs.zip 63 | /dev/docs/flutter.docs.zip 64 | /dev/docs/lib/ 65 | /dev/docs/pubspec.yaml 66 | /dev/integration_tests/**/xcuserdata 67 | /dev/integration_tests/**/Pods 68 | /packages/flutter/coverage/ 69 | version 70 | analysis_benchmark.json 71 | 72 | # packages file containing multi-root paths 73 | .packages.generated 74 | 75 | # Flutter/Dart/Pub related 76 | **/doc/api/ 77 | .flutter-plugins 78 | .flutter-plugins-dependencies 79 | **/generated_plugin_registrant.dart 80 | .pub-preload-cache/ 81 | .pub-cache/ 82 | .pub/ 83 | flutter_*.png 84 | linked_*.ds 85 | unlinked.ds 86 | unlinked_spec.ds 87 | 88 | # Android related 89 | **/android/**/gradle-wrapper.jar 90 | .gradle/ 91 | **/android/captures/ 92 | **/android/gradlew 93 | **/android/gradlew.bat 94 | **/android/local.properties 95 | **/android/**/GeneratedPluginRegistrant.java 96 | **/android/key.properties 97 | *.jks 98 | 99 | # iOS/XCode related 100 | **/ios/**/*.mode1v3 101 | **/ios/**/*.mode2v3 102 | **/ios/**/*.moved-aside 103 | **/ios/**/*.pbxuser 104 | **/ios/**/*.perspectivev3 105 | **/ios/**/*sync/ 106 | **/ios/**/.sconsign.dblite 107 | **/ios/**/.tags* 108 | **/ios/**/.vagrant/ 109 | **/ios/**/DerivedData/ 110 | **/ios/**/Icon? 111 | **/ios/**/Pods/ 112 | **/ios/**/.symlinks/ 113 | **/ios/**/profile 114 | **/ios/**/xcuserdata 115 | **/ios/.generated/ 116 | **/ios/Flutter/.last_build_id 117 | **/ios/Flutter/App.framework 118 | **/ios/Flutter/Flutter.framework 119 | **/ios/Flutter/Flutter.podspec 120 | **/ios/Flutter/Generated.xcconfig 121 | **/ios/Flutter/ephemeral 122 | **/ios/Flutter/app.flx 123 | **/ios/Flutter/app.zip 124 | **/ios/Flutter/flutter_assets/ 125 | **/ios/Flutter/flutter_export_environment.sh 126 | **/ios/ServiceDefinitions.json 127 | **/ios/Runner/GeneratedPluginRegistrant.* 128 | 129 | # macOS 130 | **/Flutter/ephemeral/ 131 | **/Pods/ 132 | **/macos/Flutter/GeneratedPluginRegistrant.swift 133 | **/macos/Flutter/ephemeral 134 | **/xcuserdata/ 135 | 136 | # Windows 137 | **/windows/flutter/ephemeral/ 138 | **/windows/flutter/generated_plugin_registrant.cc 139 | **/windows/flutter/generated_plugin_registrant.h 140 | **/windows/flutter/generated_plugins.cmake 141 | 142 | # Linux 143 | **/linux/flutter/ephemeral/ 144 | **/linux/flutter/generated_plugin_registrant.cc 145 | **/linux/flutter/generated_plugin_registrant.h 146 | **/linux/flutter/generated_plugins.cmake 147 | 148 | # Coverage 149 | coverage/ 150 | 151 | # Symbols 152 | app.*.symbols 153 | 154 | # Exceptions to above rules. 155 | !**/ios/**/default.mode1v3 156 | !**/ios/**/default.mode2v3 157 | !**/ios/**/default.pbxuser 158 | !**/ios/**/default.perspectivev3 159 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 160 | !/dev/ci/**/Gemfile.lock 161 | !.vscode/settings.json -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Dart", 6 | "program": "example/example.dart", 7 | "request": "launch", 8 | "type": "dart" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 4.0.0 2 | - upgraded to support dart 3.0 3 | - removed dependency on deprecated file_utils package by coping 4 | the required classes into system_info. 5 | 6 | # 3.0.2 7 | - Updated to kernel.dart processorToArchitecure map 8 | 9 | # 3.0.1 10 | - breaking: changed SysInfo.kernelArchitecture to return a ProcessorArchitecture rather than a string as it provides a more useful abstraction. 11 | - Introduced the new SysInfo.rawKernelArchitecture which provides the same value as the original SysInfo.kernelArchitecture. 12 | 13 | # 3.0.0 14 | - Breaking Change: removed all classes/methods marked as @deprecated in 2.x. 15 | To make your life easier be sure to remove all deprecated warnings before upgrading to 16 | 3.x 17 | - Breaking change, `ProcessorArchitecture.aarch64` renamed to `ProcessorArchitecture.arm64` 18 | - significant refactoring of the code base by breaking out code into seperate dart 19 | files to improve readability. 20 | # 2.0.4 21 | - fixed getFreePhysicalMemory - big thanks to @jingluoguo for the patch 22 | 23 | # 2.0.3 24 | - removed trailing slashes of homepage and documenation. 25 | - fix of the readme. 26 | 27 | # 2.0.2 28 | - released the manual and updated the readme to point to it. 29 | - made processors redundant and replaced with cores. 30 | - Applied lint hard and fixed incorrect camel case usage. 31 | 32 | # 2.0.1 33 | Acknowledged onepub.dev's support. 34 | 35 | # 2.0.0 36 | Forked an created a supported release. 37 | 38 | - 1.0.1 39 | - 1.0.1 40 | - 1.0.0 41 | - Merge pull request #3 from bsutton/master 42 | - Corrected the version no. 43 | - migrate to nnbd 44 | - 0.1.3 45 | - 0.1.2 46 | - 0.1.1 47 | - 0.1.0 48 | - 0.0.16 49 | - 0.0.15 50 | - Merge pull request #1 from martinsik/master 51 | - fixed _getKernelBitness for "macos" 52 | - 0.0.14 53 | - 0.0.12 54 | - 0.0.12 55 | - 0.0.12 56 | - 0.0.11 57 | - 0.0.11 58 | - 0.0.10 59 | - 0.0.9 60 | - 0.0.9 61 | - 0.0.8 62 | - 0.0.7 63 | - 0.0.6 64 | - 0.0.6 65 | - 0.0.5 66 | - 0.0.5 67 | - 0.0.4 68 | - 0.0.3 69 | - 0.0.2 70 | - 0.0.1 71 | - Initial commit 72 | 73 | ## 1.0.1 74 | 75 | - This package is no longer supported because it was flagged by Google Dart developers as being published by an unknown person. Publisher Unknown. As a normal person, I believe that hardly anyone would want to use software from unknown publishers. 76 | 77 | ## 1.0.0 78 | 79 | - The source code has been migrated to null safety. Thanks to the author of this work, Brett Sutton (github.com/bsutton). 80 | 81 | ## 0.1.3 82 | 83 | - Minor changes in processor architecture recognition 84 | 85 | ## 0.1.2 86 | 87 | - The source code has been modified in accordance with Google’s guidelines about the effectiveness of the Dart language 88 | 89 | ## 0.1.1 90 | 91 | - To avoid downgrading the rating on https://pub.dartlang.org, the source code has been changed to fit Google’s ever-changing recommendations on what a good (pedantic) source code should be. 92 | 93 | ## 0.1.0 94 | 95 | - Adaptation to Dart 2.0 96 | 97 | ## 0.0.16 98 | 99 | - Fixed bug in detection of `user space bitness` on Mac OS X 100 | 101 | ## 0.0.15 102 | 103 | - Fixed bug in detection of `bitness of kernel` on Mac OS X 104 | 105 | ## 0.0.14 106 | 107 | - Added statistics `getVirtualMemorySize()` about the current memory usage by the current process 108 | 109 | ## 0.0.12 110 | 111 | - Breaking change, `ProcessorArchitecture.ARM64` renamed to `ProcessorArchitecture.AARCH64` 112 | 113 | ## 0.0.11 114 | 115 | - Fixed bug in detection of `kernel architecture` on Windows. More universal algorithm independent from the architecture name, allows detect any architecture (X86/ AMD64/ IA64/ etc) 116 | - Impoved detection of `bitness of kernel` on Windows. Added suport of `IA64` 117 | - Impoved detection of `bitness of user space` on Windows. Added suport of `IA64` 118 | 119 | ## 0.0.10 120 | 121 | - Implemented `getFreePhysicalMemory()` and `getTotalVirtualMemory()` on Mac OS X 122 | 123 | ## 0.0.9 124 | 125 | - Fixed bug in detection of `bitness of kernel` on Linux 126 | 127 | ## 0.0.8 128 | 129 | - Detection of `bitness of kernel` on Linux are based on the found file formats of `libc.so.*` 130 | 131 | ## 0.0.7 132 | 133 | - Partial support of `processor architecture` statistics 134 | 135 | ## 0.0.6 136 | 137 | - Renamed method `physicalMemoryFreeSize()` to `getFreePhysicalMemory()` 138 | - Renamed method `physicalMemoryTotalSize()` to `getTotalPhysicalMemory()` 139 | - Renamed method `virtualMemoryFreeSize()` to `getFreeVirtualMemory()` 140 | - Renamed method `virtualMemoryTotalSize()` to `getTotalVirtualMemory()` 141 | 142 | ## 0.0.5 143 | 144 | - Partial support of `phycical memory` and `virtual memory` statistics 145 | 146 | ## 0.0.4 147 | 148 | - Fixed a bug when parsing comments in the file `/boot/config` on Linux 149 | 150 | ## 0.0.3 151 | 152 | - Changed the algorithm for detecting the number of physical processor sockets on Windows 153 | 154 | ## 0.0.1 155 | 156 | - Initial release 157 | 158 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Andrew Mezoni, S. Brett Sutton 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the Andrew Mezoni nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | system_info2 2 | ===== 3 | 4 | A fork of system_info as the original maintainer no longer wishes to do so. 5 | 6 | Thanks to onepub.dev for allowing me the time and resources to support this package. 7 | 8 | Provides easy access to useful information about the system (architecture, bitness, kernel, memory, operating system, CPU, user). 9 | 10 | system_info2 lets you discover specific hardware characteristics of the OS you are running on includeing: 11 | 12 | * kernelArchitecture 13 | * Kernel bitness 14 | * Kernel name 15 | * Kernel version 16 | * Operating system name 17 | * Operating system version 18 | * User directory 19 | * User id 20 | * User name 21 | * User space bitness 22 | 23 | **Basic examples:** 24 | 25 | ```dart 26 | if (SysInfo.operatingSystemName == "Ubuntu") { 27 | log.info("We love Ubuntu users"); 28 | } 29 | ``` 30 | 31 | ## Documentation 32 | 33 | You can find the manual at: 34 | 35 | [sysinfo.onepub.dev](https://sysinfo.onepub.dev) 36 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:lint_hard/all.yaml 2 | 3 | linter: 4 | rules: 5 | avoid_print: false -------------------------------------------------------------------------------- /bin/system_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:system_info2/system_info2.dart'; 2 | 3 | const int megaByte = 1024 * 1024; 4 | 5 | void main() { 6 | print('Kernel architecture : ${SysInfo.kernelArchitecture}'); 7 | print('Kernel bitness : ${SysInfo.kernelBitness}'); 8 | print('Kernel name : ${SysInfo.kernelName}'); 9 | print('Kernel version : ${SysInfo.kernelVersion}'); 10 | print('Operating system name : ${SysInfo.operatingSystemName}'); 11 | print('Operating system version: ${SysInfo.operatingSystemVersion}'); 12 | print('User directory : ${SysInfo.userDirectory}'); 13 | print('User id : ${SysInfo.userId}'); 14 | print('User name : ${SysInfo.userName}'); 15 | print('User space bitness : ${SysInfo.userSpaceBitness}'); 16 | final cores = SysInfo.cores; 17 | print('Number of processors : ${cores.length}'); 18 | for (final core in cores) { 19 | print(' Architecture : ${core.architecture}'); 20 | print(' Name : ${core.name}'); 21 | print(' Socket : ${core.socket}'); 22 | print(' Vendor : ${core.vendor}'); 23 | } 24 | print('Total physical memory : ' 25 | '${SysInfo.getTotalPhysicalMemory() ~/ megaByte} MB'); 26 | print('Free physical memory : ' 27 | '${SysInfo.getFreePhysicalMemory() ~/ megaByte} MB'); 28 | print('Total virtual memory : ' 29 | '${SysInfo.getTotalVirtualMemory() ~/ megaByte} MB'); 30 | print('Free virtual memory : ' 31 | '${SysInfo.getFreeVirtualMemory() ~/ megaByte} MB'); 32 | print('Virtual memory size : ' 33 | '${SysInfo.getVirtualMemorySize() ~/ megaByte} MB'); 34 | } 35 | -------------------------------------------------------------------------------- /example/example.dart: -------------------------------------------------------------------------------- 1 | import 'package:system_info2/system_info2.dart'; 2 | 3 | void main() { 4 | print('Kernel architecture : ${SysInfo.kernelArchitecture}'); 5 | print('Raw Kernel architecture : ${SysInfo.rawKernelArchitecture}'); 6 | print('Kernel bitness : ${SysInfo.kernelBitness}'); 7 | print('Kernel name : ${SysInfo.kernelName}'); 8 | print('Kernel version : ${SysInfo.kernelVersion}'); 9 | print('Operating system name : ${SysInfo.operatingSystemName}'); 10 | print('Operating system version: ${SysInfo.operatingSystemVersion}'); 11 | print('User directory : ${SysInfo.userDirectory}'); 12 | print('User id : ${SysInfo.userId}'); 13 | print('User name : ${SysInfo.userName}'); 14 | print('User space bitness : ${SysInfo.userSpaceBitness}'); 15 | final cores = SysInfo.cores; 16 | print('Number of core : ${cores.length}'); 17 | for (final core in cores) { 18 | print(' Architecture : ${core.architecture}'); 19 | print(' Name : ${core.name}'); 20 | print(' Socket : ${core.socket}'); 21 | print(' Vendor : ${core.vendor}'); 22 | } 23 | print('Total physical memory ' 24 | ': ${SysInfo.getTotalPhysicalMemory() ~/ megaByte} MB'); 25 | print('Free physical memory ' 26 | ': ${SysInfo.getFreePhysicalMemory() ~/ megaByte} MB'); 27 | print('Total virtual memory ' 28 | ': ${SysInfo.getTotalVirtualMemory() ~/ megaByte} MB'); 29 | print('Free virtual memory ' 30 | ': ${SysInfo.getFreeVirtualMemory() ~/ megaByte} MB'); 31 | print('Virtual memory size ' 32 | ': ${SysInfo.getVirtualMemorySize() ~/ megaByte} MB'); 33 | } 34 | 35 | const int megaByte = 1024 * 1024; 36 | -------------------------------------------------------------------------------- /lib/src/core_info.dart: -------------------------------------------------------------------------------- 1 | import 'processor_architecture.dart'; 2 | 3 | /// Describes a processor core. 4 | class CoreInfo { 5 | CoreInfo( 6 | {this.architecture = ProcessorArchitecture.unknown, 7 | this.name = '', 8 | this.socket = 0, 9 | this.vendor = ''}); 10 | 11 | final ProcessorArchitecture architecture; 12 | 13 | final String name; 14 | 15 | final int socket; 16 | 17 | final String vendor; 18 | } 19 | -------------------------------------------------------------------------------- /lib/src/file_list.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | import 'dart:io'; 3 | 4 | import 'package:globbing/glob_lister.dart'; 5 | 6 | import 'file_path.dart'; 7 | 8 | class FileList extends Object with ListMixin { 9 | /// Creates file list. 10 | /// 11 | /// Parameters: 12 | /// [directory] 13 | /// Directory whic will be listed. 14 | /// [pattern] 15 | /// Glob pattern of this file list. 16 | /// [caseSensitive] 17 | /// True, if the pattern is case sensitive; otherwise false. 18 | /// [notify] 19 | /// Function that is called whenever an item is added. 20 | FileList(this.directory, String pattern, 21 | {bool? caseSensitive, void Function(String path)? notify}) { 22 | if (caseSensitive == null) { 23 | if (_isWindows) { 24 | caseSensitive = false; 25 | } else { 26 | caseSensitive = true; 27 | } 28 | } 29 | 30 | _caseSensitive = caseSensitive; 31 | _notify = notify; 32 | _pattern = FilePath.expand(pattern); 33 | _files = _getFiles(); 34 | } 35 | 36 | static final bool _isWindows = Platform.isWindows; 37 | 38 | final Directory directory; 39 | 40 | late bool _caseSensitive; 41 | 42 | late List _files; 43 | 44 | void Function(String path)? _notify; 45 | 46 | late String _pattern; 47 | 48 | /// Returns the length. 49 | @override 50 | int get length => _files.length; 51 | 52 | /// Sets the length; 53 | @override 54 | set length(int length) { 55 | throw UnsupportedError('length='); 56 | } 57 | 58 | @override 59 | String operator [](int index) => _files[index]; 60 | 61 | @override 62 | void operator []=(int index, String value) { 63 | throw UnsupportedError('[]='); 64 | } 65 | 66 | bool _exists(String path) { 67 | if (!Directory(path).existsSync()) { 68 | if (!File(path).existsSync()) { 69 | if (!Link(path).existsSync()) { 70 | return false; 71 | } 72 | } 73 | } 74 | 75 | return true; 76 | } 77 | 78 | List _getFiles() { 79 | final lister = GlobLister(_pattern, 80 | caseSensitive: _caseSensitive, 81 | exists: _exists, 82 | isDirectory: _isDirectory, 83 | isWindows: _isWindows, 84 | list: _list); 85 | return lister.list(directory.path, notify: _notify) ?? []; 86 | } 87 | 88 | bool _isDirectory(String path) => Directory(path).existsSync(); 89 | 90 | List _list(String path, bool? followLinks) { 91 | List result; 92 | try { 93 | result = Directory(path) 94 | .listSync(followLinks: followLinks ?? true) 95 | .map((e) => e.path) 96 | .toList(); 97 | // ignore: avoid_catches_without_on_clauses 98 | } catch (e) { 99 | result = []; 100 | } 101 | 102 | return result; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /lib/src/file_path.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:path/path.dart' as pathos; 4 | 5 | // ignore: avoid_classes_with_only_static_members 6 | class FilePath { 7 | static final bool _isWindows = Platform.isWindows; 8 | 9 | /// Returns the expanded [path]. 10 | /// 11 | /// Expands the following parts: 12 | /// - Environment variables (IEEE Std 1003.1-2001), eg. $HOME/dart-sdk/pub 13 | /// - Home directory of the current user, eg ~/dart-sdk/pub 14 | static String expand(String path) { 15 | if (path.isEmpty) { 16 | return path; 17 | } 18 | 19 | path = _expand(path); 20 | if (path[0] != '~') { 21 | return path; 22 | } 23 | 24 | // TODO: add support of '~user' format. 25 | String home; 26 | if (_isWindows) { 27 | final drive = Platform.environment['HOMEDRIVE']; 28 | final path = Platform.environment['HOMEPATH']; 29 | if (drive != null && 30 | drive.isNotEmpty && 31 | path != null && 32 | path.isNotEmpty) { 33 | home = drive + path; 34 | } else { 35 | home = Platform.environment['USERPROFILE'] ?? ''; 36 | } 37 | 38 | home = home.replaceAll(r'\', '/'); 39 | } else { 40 | home = Platform.environment['HOME'] ?? ''; 41 | } 42 | 43 | if (home.isEmpty) { 44 | return path; 45 | } 46 | 47 | if (home.endsWith('/') || home.endsWith(r'\')) { 48 | home = home.substring(0, home.length - 1); 49 | } 50 | 51 | if (path == '~' || path == '~/') { 52 | return home; 53 | } 54 | 55 | if (path.startsWith('~/')) { 56 | return '$home/${path.substring(2)}'; 57 | } 58 | 59 | return path; 60 | } 61 | 62 | /// Returns the full name of the path if possible. 63 | /// 64 | /// Resolves the following segments: 65 | /// - Segments '.' indicating the current directory 66 | /// - Segments '..' indicating the parent directory 67 | /// - Leading '~' character indicating the home directory 68 | /// - Environment variables in IEEE Std 1003.1-2001 format, eg. $HOME/dart-sdk 69 | /// 70 | /// Useful when you get path name in a format incompatible with POSIX, and 71 | /// intend to use it as part of the wildcard patterns. 72 | /// 73 | /// Do not use this method directly on wildcard patterns because it can deform 74 | /// the patterns. 75 | static String fullname(String path) { 76 | if (path.isEmpty) { 77 | return path; 78 | } 79 | 80 | var native = false; 81 | var normalized = false; 82 | if (path.startsWith('..')) { 83 | native = true; 84 | final current = Directory.current.parent.path; 85 | if (path == '..') { 86 | path = current; 87 | normalized = true; 88 | } else if (path.startsWith('../')) { 89 | path = pathos.join(current, path.substring(3)); 90 | } 91 | } else if (path.startsWith('.')) { 92 | native = true; 93 | final current = Directory.current.path; 94 | if (path == '.') { 95 | path = current; 96 | normalized = true; 97 | } else if (path.startsWith('./')) { 98 | path = pathos.join(current, path.substring(2)); 99 | } 100 | } 101 | 102 | if (!native) { 103 | path = FilePath.expand(path); 104 | } 105 | 106 | if (!normalized) { 107 | path = pathos.normalize(path); 108 | } 109 | 110 | if (_isWindows) { 111 | path = path.replaceAll(r'\', '/'); 112 | } 113 | 114 | return path; 115 | } 116 | 117 | static String _expand(String path) { 118 | final sb = StringBuffer(); 119 | final length = path.length; 120 | for (var i = 0; i < length; i++) { 121 | var s = path[i]; 122 | switch (s) { 123 | case r'$': 124 | if (i + 1 < length) { 125 | var pos = i + 1; 126 | final c = path.codeUnitAt(pos); 127 | if ((c >= 65 && c <= 90) || c == 95) { 128 | while (true) { 129 | if (pos == length) { 130 | break; 131 | } 132 | 133 | final c = path.codeUnitAt(pos); 134 | if ((c >= 65 && c <= 90) || (c >= 48 && c <= 57) || c == 95) { 135 | pos++; 136 | continue; 137 | } 138 | 139 | break; 140 | } 141 | } 142 | 143 | if (pos > i + 1) { 144 | final key = path.substring(i + 1, pos); 145 | var value = Platform.environment[key]; 146 | if (value == null) { 147 | value = ''; 148 | } else if (_isWindows) { 149 | value = value.replaceAll(r'\', '/'); 150 | } 151 | 152 | sb.write(value); 153 | i = pos - 1; 154 | } else { 155 | sb.write(s); 156 | } 157 | } else { 158 | sb.write(s); 159 | } 160 | 161 | break; 162 | case '[': 163 | sb.write(s); 164 | if (i + 1 < length) { 165 | s = path[++i]; 166 | sb.write(s); 167 | while (true) { 168 | if (i == length) { 169 | break; 170 | } 171 | 172 | s = path[++i]; 173 | sb.write(s); 174 | if (s == ']') { 175 | break; 176 | } 177 | } 178 | } 179 | 180 | break; 181 | default: 182 | sb.write(s); 183 | break; 184 | } 185 | } 186 | 187 | return sb.toString(); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /lib/src/file_utils.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: exhaustive_cases 2 | 3 | import 'dart:io'; 4 | 5 | import 'package:globbing/glob_filter.dart'; 6 | import 'package:globbing/glob_parser.dart'; 7 | import 'package:path/path.dart' as pathos; 8 | 9 | import 'file_list.dart'; 10 | import 'file_path.dart'; 11 | 12 | // ignore: avoid_classes_with_only_static_members 13 | class FileUtils { 14 | static final bool _isWindows = Platform.isWindows; 15 | 16 | // /// Removes any leading directory components from [name]. 17 | // /// 18 | // /// If [suffix] is specified and it is identical to the end of [name], it is 19 | // /// removed from [name] as well. 20 | // /// 21 | // /// If [name] is null returns null. 22 | // static String basename(String name, {String suffix}) { 23 | // if (name == null) { 24 | // return null; 25 | // } 26 | 27 | // if (name.isEmpty) { 28 | // return ''; 29 | // } 30 | 31 | // final segments = pathos.split(name); 32 | // if (pathos.isAbsolute(name)) { 33 | // if (segments.length == 1) { 34 | // return ''; 35 | // } 36 | // } 37 | 38 | // var result = segments.last; 39 | // if (suffix.isNotEmpty) { 40 | // final index = result.lastIndexOf(suffix); 41 | // if (index != -1) { 42 | // result = result.substring(0, index); 43 | // } 44 | // } 45 | 46 | // return result; 47 | // } 48 | 49 | /// Changes the current directory to [name]. Returns true if the operation was 50 | /// successful; otherwise false. 51 | static bool chdir(String name) { 52 | if (name.isEmpty) { 53 | return false; 54 | } 55 | 56 | name = FilePath.expand(name); 57 | final directory = Directory(name); 58 | if (!directory.existsSync()) { 59 | return false; 60 | } 61 | 62 | try { 63 | Directory.current = directory; 64 | // ignore: avoid_catches_without_on_clauses 65 | } catch (e) { 66 | return false; 67 | } 68 | 69 | return true; 70 | } 71 | 72 | /// Returns true if directory is empty; otherwise false; 73 | static bool dirempty(String name) { 74 | name = FilePath.expand(name); 75 | final directory = Directory(name); 76 | if (!directory.existsSync()) { 77 | return false; 78 | } 79 | 80 | return directory.listSync().isEmpty; 81 | } 82 | 83 | /// Returns a list of files from which will be removed elements 84 | /// that match glob 85 | /// [pattern]. 86 | /// 87 | /// Parameters: 88 | /// [files] 89 | /// List of file paths. 90 | /// [pattern] 91 | /// Pattern of glob filter. 92 | /// [added] 93 | /// Function that is called whenever an item is added. 94 | /// [caseSensitive] 95 | /// True, if the pattern is case sensitive; otherwise false. 96 | /// [removed] 97 | /// Function that is called whenever an item is removed. 98 | static List exclude(List files, String pattern, 99 | {void Function(String path)? added, 100 | bool? caseSensitive, 101 | void Function(String path)? removed}) { 102 | pattern = FilePath.expand(pattern); 103 | if (!pathos.isAbsolute(pattern)) { 104 | pattern = '${getcwd()}/$pattern'; 105 | } 106 | 107 | bool isDirectory(String path) => Directory(path).existsSync(); 108 | 109 | final filter = GlobFilter(pattern, 110 | caseSensitive: caseSensitive, 111 | isDirectory: isDirectory, 112 | isWindows: _isWindows); 113 | 114 | return filter.exclude(files, added: added, removed: removed); 115 | } 116 | 117 | /// Returns the full name of the path if possible. 118 | /// 119 | /// Resolves the following segments: 120 | /// - Segments '.' indicating the current directory 121 | /// - Segments '..' indicating the parent directory 122 | /// - Leading '~' character indicating the home directory 123 | /// - Environment variables in IEEE Std 1003.1-2001 format, eg. $HOME/dart-sdk 124 | /// 125 | /// Useful when you get path name in a format incompatible with POSIX, and 126 | /// intend to use it as part of the wildcard patterns. 127 | /// 128 | /// Do not use this method directly on wildcard patterns because it can deform 129 | /// the patterns. 130 | static String fullpath(String name) { 131 | if (name.startsWith('..')) { 132 | final path = Directory.current.parent.path; 133 | if (name == '..') { 134 | name = path; 135 | } else if (name.startsWith('../')) { 136 | name = pathos.join(path, name.substring(3)); 137 | name = pathos.normalize(name); 138 | } else { 139 | name = pathos.normalize(name); 140 | } 141 | } else if (name.startsWith('.')) { 142 | final path = Directory.current.path; 143 | if (name == '.') { 144 | name = path; 145 | } else if (name.startsWith('./')) { 146 | name = pathos.join(path, name.substring(2)); 147 | name = pathos.normalize(name); 148 | } else { 149 | name = pathos.normalize(name); 150 | } 151 | } else { 152 | name = pathos.normalize(name); 153 | } 154 | 155 | name = FilePath.expand(name); 156 | if (_isWindows) { 157 | name = name.replaceAll(r'\', '/'); 158 | } 159 | 160 | return name; 161 | } 162 | 163 | /// Returns the path of the current directory. 164 | static String getcwd() { 165 | var path = Directory.current.path; 166 | if (_isWindows) { 167 | path = path.replaceAll(r'\', '/'); 168 | } 169 | 170 | return path; 171 | } 172 | 173 | /// Returns a list of files which match the specified glob [pattern]. 174 | /// 175 | /// Parameters: 176 | /// [pattern] 177 | /// Glob pattern of file list. 178 | /// [caseSensitive] 179 | /// True, if the pattern is case sensitive; otherwise false. 180 | /// [notify] 181 | /// Function that is called whenever an item is added. 182 | static List glob(String pattern, 183 | {bool? caseSensitive, void Function(String path)? notify}) { 184 | pattern = FilePath.expand(pattern); 185 | Directory directory; 186 | if (pathos.isAbsolute(pattern)) { 187 | final parser = GlobParser(); 188 | final node = parser.parse(pattern); 189 | final parts = []; 190 | final nodes = node.nodes; 191 | final length = nodes.length; 192 | for (var i = 1; i < length; i++) { 193 | final element = node.nodes[i]; 194 | final strict = element.strict ?? false; 195 | if (strict) { 196 | parts.add(element); 197 | } else { 198 | break; 199 | } 200 | } 201 | 202 | final path = (nodes.first.source ?? '') + parts.join('/'); 203 | directory = Directory(path); 204 | } else { 205 | directory = Directory.current; 206 | } 207 | 208 | return FileList(directory, pattern, 209 | caseSensitive: caseSensitive, notify: notify); 210 | } 211 | 212 | /// Returns a list of paths from which will be removed elements that do not 213 | /// match glob pattern. 214 | /// 215 | /// Parameters: 216 | /// [files] 217 | /// List of file paths. 218 | /// [pattern] 219 | /// Pattern of glob filter. 220 | /// [added] 221 | /// Function that is called whenever an item is added. 222 | /// [caseSensitive] 223 | /// True, if the pattern is case sensitive; otherwise false. 224 | /// [removed] 225 | /// Function that is called whenever an item is removed. 226 | static List include(List files, String pattern, 227 | {void Function(String path)? added, 228 | bool? caseSensitive, 229 | void Function(String path)? removed}) { 230 | pattern = FilePath.expand(pattern); 231 | if (!pathos.isAbsolute(pattern)) { 232 | pattern = '${getcwd()}/$pattern'; 233 | } 234 | 235 | bool isDirectory(String path) => Directory(path).existsSync(); 236 | 237 | final filter = GlobFilter(pattern, 238 | caseSensitive: caseSensitive, 239 | isDirectory: isDirectory, 240 | isWindows: _isWindows); 241 | 242 | return filter.include(files, added: added, removed: removed); 243 | } 244 | 245 | /// Creates listed directories and returns true if the operation was 246 | /// successful; otherwise false. 247 | /// 248 | /// If listed directories exists returns false. 249 | /// 250 | /// If [recursive] is set to true creates all required subdirectories and 251 | /// returns true if not errors occured. 252 | static bool mkdir(List names, {bool recursive = false}) { 253 | if (names.isEmpty) { 254 | return false; 255 | } 256 | 257 | var result = true; 258 | for (var name in names) { 259 | name = FilePath.expand(name); 260 | final directory = Directory(name); 261 | final exists = directory.existsSync(); 262 | if (exists) { 263 | if (!recursive) { 264 | result = false; 265 | } 266 | } else { 267 | try { 268 | directory.createSync(recursive: recursive); 269 | // ignore: avoid_catches_without_on_clauses 270 | } catch (e) { 271 | result = false; 272 | } 273 | } 274 | } 275 | 276 | return result; 277 | } 278 | 279 | /// Moves files [files] to the directory [dir]. Returns true if the operation 280 | /// was successful; otherwise false. 281 | static bool move(List files, String dir) { 282 | if (!testfile(dir, 'directory')) { 283 | return false; 284 | } 285 | 286 | var result = true; 287 | for (final file in files) { 288 | if (file.isEmpty) { 289 | result = false; 290 | continue; 291 | } 292 | 293 | final list = glob(file); 294 | if (list.isEmpty) { 295 | result = false; 296 | continue; 297 | } 298 | 299 | for (final name in list) { 300 | final basename = pathos.basename(name); 301 | if (basename.isEmpty) { 302 | result = false; 303 | continue; 304 | } 305 | 306 | final dest = pathos.join(dir, basename); 307 | if (!rename(name, dest)) { 308 | result = false; 309 | } 310 | } 311 | } 312 | 313 | return result; 314 | } 315 | 316 | /// Renames or moves [src] to [dest]. Returns true if the operation was 317 | /// successful; otherwise false. 318 | static bool rename(String src, String dest) { 319 | src = FilePath.expand(src); 320 | dest = FilePath.expand(dest); 321 | FileSystemEntity? entity; 322 | switch (FileStat.statSync(src).type) { 323 | case FileSystemEntityType.directory: 324 | entity = Directory(src); 325 | break; 326 | case FileSystemEntityType.file: 327 | entity = File(src); 328 | break; 329 | case FileSystemEntityType.link: 330 | entity = Link(src); 331 | break; 332 | } 333 | 334 | if (entity == null) { 335 | return false; 336 | } 337 | 338 | try { 339 | entity.renameSync(dest); 340 | // ignore: avoid_catches_without_on_clauses 341 | } catch (e) { 342 | return false; 343 | } 344 | 345 | return true; 346 | } 347 | 348 | /// Removes the [files] and returns true if the operation was successful; 349 | /// otherwise false. 350 | /// 351 | /// By default, it does not remove directories. 352 | /// 353 | /// If [directory] is set to true removes the directories if they are empty. 354 | /// 355 | /// If [force] is set to true ignores nonexistent files. 356 | /// 357 | /// If [recursive] is set to true remove the directories and their contents 358 | /// recursively. 359 | static bool rm(List files, 360 | {bool directory = false, bool force = false, bool recursive = false}) { 361 | if (files.isEmpty) { 362 | return false; 363 | } 364 | 365 | var result = true; 366 | for (final file in files) { 367 | if (file.isEmpty) { 368 | if (!force) { 369 | result = false; 370 | } 371 | 372 | continue; 373 | } 374 | 375 | final list = glob(file); 376 | if (list.isEmpty) { 377 | if (!force) { 378 | result = false; 379 | } 380 | 381 | continue; 382 | } 383 | 384 | for (final name in list) { 385 | FileSystemEntity? entity; 386 | var isDirectory = false; 387 | if (testfile(name, 'link')) { 388 | entity = Link(name); 389 | } else if (testfile(name, 'file')) { 390 | entity = File(name); 391 | } else if (testfile(name, 'directory')) { 392 | entity = Directory(name); 393 | isDirectory = true; 394 | } 395 | 396 | if (entity == null) { 397 | if (!force) { 398 | result = false; 399 | } 400 | } else { 401 | if (isDirectory) { 402 | if (recursive) { 403 | try { 404 | entity.deleteSync(recursive: recursive); 405 | // ignore: avoid_catches_without_on_clauses 406 | } catch (e) { 407 | result = false; 408 | } 409 | } else if (directory) { 410 | result = rmdir([entity.path], parents: true); 411 | } else { 412 | result = false; 413 | } 414 | } else { 415 | try { 416 | entity.deleteSync(); 417 | // ignore: avoid_catches_without_on_clauses 418 | } catch (e) { 419 | result = false; 420 | } 421 | } 422 | } 423 | } 424 | } 425 | 426 | return result; 427 | } 428 | 429 | /// Removes empty directories. Returns true if the operation was successful; 430 | /// otherwise false. 431 | static bool rmdir(List names, {bool parents = false}) { 432 | if (names.isEmpty) { 433 | return false; 434 | } 435 | 436 | var result = true; 437 | for (final name in names) { 438 | if (name.isEmpty) { 439 | result = false; 440 | continue; 441 | } 442 | 443 | final list = glob(name); 444 | if (list.isEmpty) { 445 | result = false; 446 | continue; 447 | } 448 | 449 | for (final name in list) { 450 | if (testfile(name, 'file')) { 451 | result = false; 452 | continue; 453 | } else if (testfile(name, 'link')) { 454 | result = false; 455 | continue; 456 | } else if (!testfile(name, 'directory')) { 457 | result = false; 458 | continue; 459 | } 460 | 461 | if (dirempty(name)) { 462 | try { 463 | Directory(name).deleteSync(); 464 | // ignore: avoid_catches_without_on_clauses 465 | } catch (e) { 466 | result = false; 467 | } 468 | } else { 469 | if (parents) { 470 | if (!canDelete(name)) { 471 | result = false; 472 | } else { 473 | try { 474 | Directory(name).deleteSync(recursive: true); 475 | // ignore: avoid_catches_without_on_clauses 476 | } catch (e) { 477 | result = false; 478 | } 479 | } 480 | } else { 481 | result = false; 482 | } 483 | } 484 | } 485 | } 486 | 487 | return result; 488 | } 489 | 490 | static bool canDelete(String name) { 491 | final directory = Directory(name); 492 | for (final entry in directory.listSync()) { 493 | if (entry is File) { 494 | return false; 495 | } else if (entry is Link) { 496 | return false; 497 | } else if (entry is Directory) { 498 | if (!canDelete(entry.path)) { 499 | return false; 500 | } 501 | } else { 502 | return false; 503 | } 504 | } 505 | 506 | return true; 507 | } 508 | 509 | /// Creates the symbolic [link] to the [target] and returns true if the 510 | /// operation was successful; otherwise false. 511 | /// 512 | /// If [target] does not exists returns false. 513 | /// 514 | /// IMPORTANT: 515 | /// On the Windows platform, this will only work with directories. 516 | static bool symlink(String target, String link) { 517 | target = FilePath.expand(target); 518 | link = FilePath.expand(link); 519 | if (_isWindows) { 520 | if (!testfile(target, 'directory')) { 521 | return false; 522 | } 523 | } else { 524 | if (!testfile(target, 'exists')) { 525 | return false; 526 | } 527 | } 528 | 529 | final symlink = Link(link); 530 | try { 531 | symlink.createSync(target); 532 | // ignore: avoid_catches_without_on_clauses 533 | } catch (e) { 534 | return false; 535 | } 536 | 537 | return true; 538 | } 539 | 540 | /// Performs specified test on [file] and returns true if success; otherwise 541 | /// returns false; 542 | /// 543 | /// Available test: 544 | /// directory: 545 | /// [file] exists and is a directory. 546 | /// exists: 547 | /// [file] exists. 548 | /// file: 549 | /// [file] exists and is a regular file. 550 | /// link: 551 | /// [file] exists and is a symbolic link. 552 | static bool testfile(String file, String test) { 553 | file = FilePath.expand(file); 554 | switch (test) { 555 | case 'directory': 556 | return Directory(file).existsSync(); 557 | case 'exists': 558 | return FileStat.statSync(file).type != FileSystemEntityType.notFound; 559 | case 'file': 560 | return File(file).existsSync(); 561 | case 'link': 562 | return Link(file).existsSync(); 563 | default: 564 | return false; 565 | } 566 | } 567 | 568 | /// Changes the modification time of the specified [files]. Returns 569 | /// true if the 570 | /// operation was successful; otherwise false. 571 | /// 572 | /// If [create] is set to true creates files that do not exist, reports 573 | /// failure if the files can not be created. 574 | /// 575 | /// If [create] is set to false do not creates files that do not exist and do 576 | /// not reports failure about files that do not exist. 577 | static bool touch(List files, {bool create = true}) { 578 | if (files.isEmpty) { 579 | return false; 580 | } 581 | 582 | var result = true; 583 | for (var file in files) { 584 | if (file.isEmpty) { 585 | result = false; 586 | continue; 587 | } 588 | 589 | file = FilePath.expand(file); 590 | if (_isWindows) { 591 | if (!_touchOnWindows(file, create)) { 592 | result = false; 593 | } 594 | } else { 595 | if (!_touchOnPosix(file, create)) { 596 | result = false; 597 | } 598 | } 599 | } 600 | 601 | return result; 602 | } 603 | 604 | /// Returns true if [file] is newer than all [depends]; otherwise false. 605 | static bool uptodate(String file, [List? depends]) { 606 | if (file.isEmpty) { 607 | return false; 608 | } 609 | 610 | file = FilePath.expand(file); 611 | final stat = FileStat.statSync(file); 612 | if (stat.type == FileSystemEntityType.notFound) { 613 | return false; 614 | } 615 | 616 | if (depends == null) { 617 | return true; 618 | } 619 | 620 | final date = stat.modified; 621 | for (final name in depends) { 622 | final stat = FileStat.statSync(name); 623 | if (stat.type == FileSystemEntityType.notFound) { 624 | return false; 625 | } 626 | 627 | if (date.compareTo(stat.modified) < 0) { 628 | return false; 629 | } 630 | } 631 | 632 | return true; 633 | } 634 | 635 | static int _shell(String command, List arguments, 636 | {String? workingDirectory}) => 637 | Process.runSync(command, arguments, 638 | runInShell: true, workingDirectory: workingDirectory) 639 | .exitCode; 640 | 641 | static bool _touchOnPosix(String name, bool create) { 642 | final arguments = [name]; 643 | if (!create) { 644 | arguments.add('-c'); 645 | } 646 | 647 | return _shell('touch', arguments) == 0; 648 | } 649 | 650 | static bool _touchOnWindows(String name, bool create) { 651 | if (!testfile(name, 'file')) { 652 | if (!create) { 653 | return true; 654 | } else { 655 | final file = File(name); 656 | try { 657 | file.createSync(); 658 | return true; 659 | // ignore: avoid_catches_without_on_clauses 660 | } catch (e) { 661 | if (create) { 662 | return false; 663 | } else { 664 | return true; 665 | } 666 | } 667 | } 668 | } 669 | 670 | final dirName = pathos.dirname(name); 671 | String workingDirectory; 672 | if (dirName.isNotEmpty) { 673 | name = pathos.basename(name); 674 | if (pathos.isAbsolute(dirName)) { 675 | workingDirectory = dirName; 676 | } else { 677 | workingDirectory = '${Directory.current.path}\\$dirName'; 678 | } 679 | } else { 680 | workingDirectory = '.'; 681 | } 682 | 683 | return _shell('copy', ['/b', name, '+', ',', ','], 684 | workingDirectory: workingDirectory) == 685 | 0; 686 | } 687 | } 688 | -------------------------------------------------------------------------------- /lib/src/fluent.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_dynamic_calls, avoid_catches_without_on_clauses 2 | //, strict_raw_type 3 | 4 | import 'dart:io'; 5 | 6 | Fluent fluent(Object? value) => Fluent(value); 7 | 8 | class Fluent { 9 | Fluent(this.value); 10 | 11 | dynamic value; 12 | 13 | List>? get groupsValue { 14 | if (value is List>) { 15 | return value as List>?; 16 | } 17 | 18 | return >[]; 19 | } 20 | 21 | int get intValue { 22 | if (value is int) { 23 | return value as int; 24 | } 25 | 26 | return 0; 27 | } 28 | 29 | // ignore: strict_raw_type 30 | List get listValue { 31 | if (value is List) { 32 | return value as List; 33 | } 34 | 35 | return []; 36 | } 37 | 38 | // ignore: strict_raw_type 39 | Map get mapValue { 40 | if (value is Map) { 41 | return value as Map; 42 | } 43 | 44 | return {}; 45 | } 46 | 47 | String get stringValue { 48 | if (value is String) { 49 | return value as String; 50 | } 51 | 52 | return ''; 53 | } 54 | 55 | Fluent operator [](Object key) { 56 | try { 57 | value = value[key]; 58 | } catch (e) { 59 | value = null; 60 | } 61 | return this; 62 | } 63 | 64 | void elementAt(int index, [Object? defaultValue]) { 65 | try { 66 | value = value[index]; 67 | } catch (e) { 68 | value = null; 69 | } 70 | 71 | if (value == null && defaultValue != null) { 72 | value = defaultValue; 73 | } 74 | } 75 | 76 | void exec(String executable, List arguments, 77 | {bool runInShell = false}) { 78 | try { 79 | final result = 80 | Process.runSync(executable, arguments, runInShell: runInShell); 81 | if (result.exitCode == 0) { 82 | value = result.stdout.toString(); 83 | } 84 | } catch (e) { 85 | value = null; 86 | } 87 | } 88 | 89 | void last() { 90 | if (value is Iterable) { 91 | value = value.last; 92 | } else { 93 | value = null; 94 | } 95 | } 96 | 97 | void listToGroups(String separator) { 98 | final result = >[]; 99 | if (value is! List) { 100 | value = result; 101 | return; 102 | } 103 | 104 | final list = value as List; 105 | Map? map; 106 | for (final element in list) { 107 | final string = element.toString(); 108 | final index = string.indexOf(separator); 109 | if (index != -1) { 110 | if (map == null) { 111 | map = {}; 112 | result.add(map); 113 | } 114 | 115 | final key = string.substring(0, index).trim(); 116 | final value = string.substring(index + 1).trim(); 117 | if (map.containsKey(key)) { 118 | map = {}; 119 | result.add(map); 120 | } 121 | 122 | map[key] = value; 123 | } else { 124 | map = null; 125 | } 126 | } 127 | 128 | value = result; 129 | } 130 | 131 | void listToMap(String separator) { 132 | if (value is! List) { 133 | value = {}; 134 | return; 135 | } 136 | 137 | final list = value as List; 138 | final map = {}; 139 | for (final element in list) { 140 | final string = element.toString(); 141 | final index = string.indexOf(separator); 142 | if (index != -1) { 143 | final key = string.substring(0, index).trim(); 144 | final value = string.substring(index + 1).trim(); 145 | map[key] = value; 146 | } 147 | } 148 | 149 | value = map; 150 | } 151 | 152 | void parseInt([int defaultValue = 0]) { 153 | if (value == null) { 154 | value = defaultValue; 155 | } else { 156 | value = int.tryParse(value.toString()) ?? defaultValue; 157 | } 158 | } 159 | 160 | void replaceAll(String from, String replace) { 161 | value = value.toString().replaceAll(from, replace); 162 | } 163 | 164 | void split(String separtor) { 165 | value = value.toString().split(separtor); 166 | } 167 | 168 | void stringToList() { 169 | if (value == null) { 170 | value = []; 171 | return; 172 | } 173 | 174 | var string = value.toString(); 175 | string = string.replaceAll('\r\n', '\n'); 176 | //string = string.replaceAll('\r', '\n'); 177 | value = string.split('\n'); 178 | } 179 | 180 | void stringToMap(String separator) { 181 | stringToList(); 182 | listToMap(separator); 183 | } 184 | 185 | void trim() { 186 | value = value.toString().trim(); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /lib/src/platform/cpu.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import '../core_info.dart'; 4 | import '../fluent.dart'; 5 | import '../processor_architecture.dart'; 6 | import '../utils.dart'; 7 | import 'cpu_macos.dart'; 8 | import 'cpu_nix.dart'; 9 | import 'cpu_windows.dart'; 10 | 11 | CoreInfo createUnknownProcessor() => CoreInfo(); 12 | 13 | List getCores() { 14 | switch (Platform.operatingSystem) { 15 | case 'android': 16 | case 'linux': 17 | return getNixCores(); 18 | case 'macos': 19 | return getMacOSCores(); 20 | case 'windows': 21 | return getWindowsCores(); 22 | default: 23 | notSupportedError(); 24 | } 25 | } 26 | 27 | ProcessorArchitecture getProcessorArchitecture( 28 | String name, Map group) { 29 | final uppercaseName = name.toUpperCase(); 30 | var architecture = ProcessorArchitecture.unknown; 31 | if (uppercaseName.startsWith('AMD')) { 32 | architecture = ProcessorArchitecture.x86; 33 | final flags = (fluent(group['flags'])..split(' ')).listValue; 34 | if (flags.contains('lm')) { 35 | architecture = ProcessorArchitecture.x86_64; 36 | } 37 | } else if (uppercaseName.startsWith('Intel')) { 38 | architecture = ProcessorArchitecture.x86; 39 | final flags = (fluent(group['flags'])..split(' ')).listValue; 40 | if (flags.contains('lm')) { 41 | architecture = ProcessorArchitecture.x86_64; 42 | } 43 | 44 | if (flags.contains('ia64')) { 45 | architecture = ProcessorArchitecture.ia64; 46 | } 47 | } else if (uppercaseName.startsWith('ARM')) { 48 | architecture = ProcessorArchitecture.arm; 49 | final features = (fluent(group['Features'])..split(' ')).listValue; 50 | if (features.contains('fp')) { 51 | architecture = ProcessorArchitecture.arm64; 52 | } 53 | } else if (uppercaseName.toUpperCase().startsWith('AARCH64')) { 54 | architecture = ProcessorArchitecture.arm64; 55 | } else if (uppercaseName.startsWith('MIPS')) { 56 | architecture = ProcessorArchitecture.mips; 57 | } 58 | return architecture; 59 | } 60 | -------------------------------------------------------------------------------- /lib/src/platform/cpu_macos.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | 3 | import '../core_info.dart'; 4 | import '../fluent.dart'; 5 | import '../processor_architecture.dart'; 6 | import '../utils.dart'; 7 | import 'cpu.dart'; 8 | 9 | UnmodifiableListView getMacOSCores() { 10 | final data = (fluent(exec('sysctl', ['machdep.cpu'])) 11 | ..trim() 12 | ..stringToMap(':')) 13 | .mapValue; 14 | var architecture = ProcessorArchitecture.unknown; 15 | if (data['machdep.cpu.vendor'] == 'GenuineIntel') { 16 | architecture = ProcessorArchitecture.x86; 17 | final extfeatures = 18 | (fluent(data['machdep.cpu.extfeatures'])..split(' ')).listValue; 19 | if (extfeatures.contains('EM64T')) { 20 | architecture = ProcessorArchitecture.x86_64; 21 | } 22 | } 23 | 24 | final numberOfCores = 25 | (fluent(data['machdep.cpu.core_count'])..parseInt()).intValue; 26 | final processors = []; 27 | for (var i = 0; i < numberOfCores; i++) { 28 | final name = fluent(data['machdep.cpu.brand_string']).stringValue; 29 | final vendor = fluent(data['machdep.cpu.vendor']).stringValue; 30 | final processor = 31 | CoreInfo(architecture: architecture, name: name, vendor: vendor); 32 | processors.add(processor); 33 | } 34 | 35 | if (processors.isEmpty) { 36 | processors.add(createUnknownProcessor()); 37 | } 38 | 39 | return UnmodifiableListView(processors); 40 | } 41 | -------------------------------------------------------------------------------- /lib/src/platform/cpu_nix.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | 3 | import '../core_info.dart'; 4 | import '../fluent.dart'; 5 | import '../utils.dart'; 6 | import 'cpu.dart'; 7 | 8 | UnmodifiableListView getNixCores() { 9 | final cores = []; 10 | final groups = (fluent(exec('cat', ['/proc/cpuinfo'])) 11 | ..trim() 12 | ..stringToList() 13 | ..listToGroups(':')) 14 | .groupsValue!; 15 | 16 | final processorGroups = groups.where((e) => e.keys.contains('processor')); 17 | String? cpuImplementer = ''; 18 | String? cpuPart = ''; 19 | String? hardware = ''; 20 | String? processorName = ''; 21 | for (final group in groups) { 22 | if (cpuPart!.isEmpty) { 23 | cpuPart = fluent(group['CPU part']).stringValue; 24 | } 25 | 26 | if (hardware!.isEmpty) { 27 | hardware = fluent(group['Hardware']).stringValue; 28 | } 29 | 30 | if (cpuImplementer!.isEmpty) { 31 | cpuImplementer = fluent(group['CPU implementer']).stringValue; 32 | } 33 | 34 | if (processorName!.isEmpty) { 35 | processorName = fluent(group['Processor']).stringValue; 36 | } 37 | } 38 | 39 | for (final group in processorGroups) { 40 | int? socket = 0; 41 | if (fluent(group['physical id']).stringValue.isNotEmpty) { 42 | socket = (fluent(group['physical id'])..parseInt()).intValue; 43 | } else { 44 | socket = (fluent(group['processor'])..parseInt()).intValue; 45 | } 46 | 47 | var vendor = fluent(group['vendor_id']).stringValue; 48 | const modelFields = ['model name', 'cpu model']; 49 | String? name = ''; 50 | for (final field in modelFields) { 51 | name = fluent(group[field]).stringValue; 52 | if (name.isNotEmpty) { 53 | break; 54 | } 55 | } 56 | 57 | if (name!.isEmpty) { 58 | name = processorName; 59 | } 60 | 61 | final architecture = getProcessorArchitecture(name!, group); 62 | 63 | if (vendor.isEmpty) { 64 | switch (cpuImplementer!.toLowerCase()) { 65 | case '0x51': 66 | vendor = 'Qualcomm'; 67 | break; 68 | default: 69 | } 70 | } 71 | 72 | final processor = CoreInfo( 73 | architecture: architecture, name: name, socket: socket, vendor: vendor); 74 | cores.add(processor); 75 | } 76 | 77 | if (cores.isEmpty) { 78 | cores.add(createUnknownProcessor()); 79 | } 80 | 81 | return UnmodifiableListView(cores); 82 | } 83 | -------------------------------------------------------------------------------- /lib/src/platform/cpu_windows.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | 3 | import '../core_info.dart'; 4 | import '../fluent.dart'; 5 | import '../processor_architecture.dart'; 6 | import '../utils.dart'; 7 | import 'cpu.dart'; 8 | 9 | UnmodifiableListView getWindowsCores() { 10 | final groups = wmicGetValueAsGroups('CPU', 11 | ['Architecture', 'DataWidth', 'Manufacturer', 'Name', 'NumberOfCores'])!; 12 | final numberOfSockets = groups.length; 13 | final cores = []; 14 | for (var i = 0; i < numberOfSockets; i++) { 15 | final data = groups[i]; 16 | final numberOfCores = (fluent(data['NumberOfCores'])..parseInt()).intValue; 17 | var architecture = ProcessorArchitecture.unknown; 18 | switch ((fluent(data['Architecture'])..parseInt()).intValue) { 19 | case 0: 20 | architecture = ProcessorArchitecture.x86; 21 | break; 22 | case 1: 23 | architecture = ProcessorArchitecture.mips; 24 | break; 25 | case 5: 26 | switch ((fluent(data['DataWidth'])..parseInt()).intValue) { 27 | case 32: 28 | architecture = ProcessorArchitecture.arm; 29 | break; 30 | case 64: 31 | architecture = ProcessorArchitecture.arm64; 32 | break; 33 | } 34 | 35 | break; 36 | case 9: 37 | architecture = ProcessorArchitecture.x86_64; 38 | break; 39 | } 40 | 41 | for (var socket = 0; socket < numberOfCores; socket++) { 42 | final name = fluent(data['Name']).stringValue; 43 | final vendor = fluent(data['Manufacturer']).stringValue; 44 | final core = CoreInfo( 45 | architecture: architecture, 46 | name: name, 47 | socket: socket, 48 | vendor: vendor); 49 | cores.add(core); 50 | } 51 | } 52 | 53 | if (cores.isEmpty) { 54 | cores.add(createUnknownProcessor()); 55 | } 56 | 57 | return UnmodifiableListView(cores); 58 | } 59 | -------------------------------------------------------------------------------- /lib/src/platform/kernel.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:path/path.dart' as pathos; 4 | 5 | import '../file_utils.dart'; 6 | import '../fluent.dart'; 7 | import '../processor_architecture.dart'; 8 | import '../utils.dart'; 9 | import 'operating_system.dart'; 10 | import 'userspace.dart'; 11 | 12 | int getKernelBitness() { 13 | switch (Platform.operatingSystem) { 14 | case 'android': 15 | case 'linux': 16 | if (getUserSpaceBitness() == 64) { 17 | return 64; 18 | } 19 | 20 | final paths = []; 21 | final path = resolveLink('/etc/ld.so.conf'); 22 | if (path != null) { 23 | parseLdConf(path, paths, {}); 24 | } 25 | 26 | paths.add('/lib'); 27 | paths.add('/lib64'); 28 | for (final path in paths) { 29 | final files = FileUtils.glob(pathos.join(path, 'libc.so.*')); 30 | for (final filePath in files) { 31 | final resolvedFilePath = resolveLink(filePath); 32 | if (resolvedFilePath == null) { 33 | continue; 34 | } 35 | 36 | final file = File(resolvedFilePath); 37 | if (file.existsSync()) { 38 | final fileType = 39 | (fluent(exec('file', ['-b', file.path]))..trim()).stringValue; 40 | if (fileType.startsWith('ELF 64-bit')) { 41 | return 64; 42 | } 43 | } 44 | } 45 | } 46 | 47 | return 32; 48 | case 'macos': 49 | if ((fluent(exec('uname', ['-m']))..trim()).stringValue == 'x86_64') { 50 | return 64; 51 | } 52 | 53 | return 32; 54 | case 'windows': 55 | final wow64 = 56 | fluent(Platform.environment['PROCESSOR_ARCHITEW6432']).stringValue; 57 | if (wow64.isNotEmpty) { 58 | return 64; 59 | } 60 | 61 | switch (Platform.environment['PROCESSOR_ARCHITECTURE']) { 62 | case 'AMD64': 63 | case 'IA64': 64 | return 64; 65 | } 66 | 67 | return 32; 68 | default: 69 | notSupportedError(); 70 | } 71 | } 72 | 73 | /// Converts the value return by [getRawKernelArchitecture] into an 74 | /// high level type. 75 | /// Note that we only support a limited set of raw architecture types 76 | /// as per the [ProcessorArchitecture] enum. 77 | ProcessorArchitecture getKernalArchitecture() { 78 | var processorArchitecture = 79 | processorToArchitecure[getRawKernelArchitecture()]; 80 | return processorArchitecture ??= ProcessorArchitecture.unknown; 81 | } 82 | 83 | /// Returns the low level kernel archtecture as reported by the OS 84 | /// 85 | /// The following list was taken from https://stackoverflow.com/questions/45125516/possible-values-for-uname-m 86 | /// 87 | /// Thanks to Jonathon Reinhart for compiling the list! 88 | /// 89 | /// Current known values are: 90 | /// 91 | /// alpha 92 | /// arc 93 | /// arm 94 | /// aarch64_be (arm64) 95 | /// aarch64 (arm64) 96 | /// armv7l 97 | /// armv8b (arm64 compat) 98 | /// armv8l (arm64 compat) 99 | /// blackfin 100 | /// c6x 101 | /// cris 102 | /// frv 103 | /// h8300 104 | /// hexagon 105 | /// ia64 106 | /// m32r 107 | /// m68k 108 | /// metag 109 | /// microblaze 110 | /// mips (native or compat) 111 | /// mips64 (mips) 112 | /// mn10300 113 | /// nios2 114 | /// openrisc 115 | /// parisc (native or compat) 116 | /// parisc64 (parisc) 117 | /// ppc (powerpc native or compat) 118 | /// ppc64 (powerpc) 119 | /// ppcle (powerpc native or compat) 120 | /// ppc64le (powerpc) 121 | /// s390 (s390x compat) 122 | /// s390x 123 | /// score 124 | /// sh 125 | /// sh64 (sh) 126 | /// sparc (native or compat) 127 | /// sparc64 (sparc) 128 | /// tile 129 | /// unicore32 130 | /// i386 (x86) 131 | /// i686 (x86 compat) 132 | /// x86_64 (x64) 133 | /// xtensa 134 | String getRawKernelArchitecture() { 135 | switch (Platform.operatingSystem) { 136 | case 'android': 137 | case 'linux': 138 | case 'macos': 139 | return (fluent(exec('uname', ['-m']))..trim()).stringValue; 140 | case 'windows': 141 | final wow64 = 142 | fluent(Platform.environment['PROCESSOR_ARCHITEW6432']).stringValue; 143 | if (wow64.isNotEmpty) { 144 | return wow64; 145 | } 146 | 147 | return fluent(Platform.environment['PROCESSOR_ARCHITECTURE']).stringValue; 148 | default: 149 | notSupportedError(); 150 | } 151 | } 152 | 153 | String getKernelName() { 154 | switch (Platform.operatingSystem) { 155 | case 'android': 156 | case 'linux': 157 | case 'macos': 158 | return (fluent(exec('uname', ['-s']))..trim()).stringValue; 159 | case 'windows': 160 | return fluent(Platform.environment['OS']).stringValue; 161 | default: 162 | notSupportedError(); 163 | } 164 | } 165 | 166 | String getKernelVersion() { 167 | switch (Platform.operatingSystem) { 168 | case 'android': 169 | case 'linux': 170 | case 'macos': 171 | return (fluent(exec('uname', ['-r']))..trim()).stringValue; 172 | case 'windows': 173 | return getOperatingSystemVersion(); 174 | default: 175 | notSupportedError(); 176 | } 177 | } 178 | 179 | final processorToArchitecure = { 180 | // 'alpha', ProcessorArchitecture.alpah, 181 | // 'arc' 182 | 'arm': ProcessorArchitecture.arm, 183 | // Returned by Apple Silicon M2 when running uname -m 184 | 'arm64': ProcessorArchitecture.arm64, 185 | 'aarch64_be': ProcessorArchitecture.arm64, 186 | 'aarch64': ProcessorArchitecture.arm64, 187 | 'armv7l': ProcessorArchitecture.arm, 188 | 'armv8b': ProcessorArchitecture.arm64, 189 | 'armv8l': ProcessorArchitecture.arm64, 190 | // 'blackfin' 191 | // 'c6x' 192 | // 'cris' 193 | // 'frv' 194 | // 'h8300' 195 | // 'hexagon' 196 | 'ia64': ProcessorArchitecture.ia64, 197 | // 'm32r' 198 | // 'm68k' 199 | // 'metag' 200 | // 'microblaze' 201 | 'mips': ProcessorArchitecture.mips, 202 | 'mips64': ProcessorArchitecture.mips, 203 | // 'mn10300' 204 | // 'nios2' 205 | // 'openrisc' 206 | // 'parisc' (native or compat) 207 | // 'parisc64' (parisc) 208 | // 'ppc' (powerpc native or compat) 209 | // 'ppc64' (powerpc) 210 | // 'ppcle' (powerpc native or compat) 211 | // 'ppc64le' (powerpc) 212 | // 's390' (s390x compat) 213 | // 's390x' 214 | // 'score' 215 | // 'sh' 216 | // 'sh64' (sh) 217 | // 'sparc' (native or compat) 218 | // 'sparc64' (sparc) 219 | // 'tile' 220 | // 'unicore32' 221 | 'i386': ProcessorArchitecture.x86, 222 | 'i686': ProcessorArchitecture.x86, 223 | 'x86_64': ProcessorArchitecture.x86_64 224 | // 'xtensa' 225 | }; 226 | -------------------------------------------------------------------------------- /lib/src/platform/memory.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import '../fluent.dart'; 4 | import '../utils.dart'; 5 | 6 | int getFreePhysicalMemory() { 7 | switch (Platform.operatingSystem) { 8 | case 'android': 9 | case 'linux': 10 | final data = (fluent(exec('cat', ['/proc/meminfo'])) 11 | ..trim() 12 | ..stringToMap(':')) 13 | .mapValue; 14 | final value = (fluent(data['MemFree']) 15 | ..split(' ') 16 | ..elementAt(0) 17 | ..parseInt()) 18 | .intValue; 19 | return value * 1024; 20 | case 'macos': 21 | return getFreeVirtualMemory(); 22 | case 'windows': 23 | final data = wmicGetValueAsMap('OS', ['FreePhysicalMemory'])!; 24 | final value = (fluent(data['FreePhysicalMemory'])..parseInt()).intValue; 25 | return value * 1024; 26 | default: 27 | notSupportedError(); 28 | } 29 | } 30 | 31 | int getAvailablePhysicalMemory() { 32 | switch (Platform.operatingSystem) { 33 | case 'android': 34 | case 'linux': 35 | final data = (fluent(exec('cat', ['/proc/meminfo'])) 36 | ..trim() 37 | ..stringToMap(':')) 38 | .mapValue; 39 | final value = (fluent(data['MemAvailable']) 40 | ..split(' ') 41 | ..elementAt(0) 42 | ..parseInt()) 43 | .intValue; 44 | return value * 1024; 45 | case 'macos': 46 | return getFreeVirtualMemory(); 47 | case 'windows': 48 | final data = wmicGetValueAsMap('OS', ['FreePhysicalMemory'])!; 49 | final value = (fluent(data['FreePhysicalMemory'])..parseInt()).intValue; 50 | return value * 1024; 51 | default: 52 | notSupportedError(); 53 | } 54 | } 55 | 56 | int getFreeVirtualMemory() { 57 | switch (Platform.operatingSystem) { 58 | case 'android': 59 | case 'linux': 60 | final data = (fluent(exec('cat', ['/proc/meminfo'])) 61 | ..trim() 62 | ..stringToMap(':')) 63 | .mapValue; 64 | final physical = (fluent(data['MemFree']) 65 | ..split(' ') 66 | ..elementAt(0) 67 | ..parseInt()) 68 | .intValue; 69 | final swap = (fluent(data['SwapFree']) 70 | ..split(' ') 71 | ..elementAt(0) 72 | ..parseInt()) 73 | .intValue; 74 | return (physical + swap) * 1024; 75 | case 'macos': 76 | final data = (fluent(exec('vm_stat', [])) 77 | ..trim() 78 | ..stringToMap(':')) 79 | .mapValue; 80 | final free = (fluent(data['Pages free']) 81 | ..replaceAll('.', '') 82 | ..parseInt()) 83 | .intValue; 84 | final pageSize = (fluent(exec('sysctl', ['-n', 'hw.pagesize'])) 85 | ..trim() 86 | ..parseInt()) 87 | .intValue; 88 | return free * pageSize; 89 | case 'windows': 90 | final data = wmicGetValueAsMap('OS', ['FreeVirtualMemory'])!; 91 | final free = (fluent(data['FreeVirtualMemory'])..parseInt()).intValue; 92 | return free * 1024; 93 | default: 94 | notSupportedError(); 95 | } 96 | } 97 | 98 | int getTotalPhysicalMemory() { 99 | switch (Platform.operatingSystem) { 100 | case 'android': 101 | case 'linux': 102 | final data = (fluent(exec('cat', ['/proc/meminfo'])) 103 | ..trim() 104 | ..stringToMap(':')) 105 | .mapValue; 106 | final value = (fluent(data['MemTotal']) 107 | ..split(' ') 108 | ..elementAt(0) 109 | ..parseInt()) 110 | .intValue; 111 | return value * 1024; 112 | case 'macos': 113 | final pageSize = (fluent(exec('sysctl', ['-n', 'hw.pagesize'])) 114 | ..trim() 115 | ..parseInt()) 116 | .intValue; 117 | final size = (fluent(exec('sysctl', ['-n', 'hw.memsize'])) 118 | ..trim() 119 | ..parseInt()) 120 | .intValue; 121 | return size * pageSize; 122 | case 'windows': 123 | final data = 124 | wmicGetValueAsMap('ComputerSystem', ['TotalPhysicalMemory'])!; 125 | final value = (fluent(data['TotalPhysicalMemory'])..parseInt()).intValue; 126 | return value; 127 | default: 128 | notSupportedError(); 129 | } 130 | } 131 | 132 | int getTotalVirtualMemory() { 133 | switch (Platform.operatingSystem) { 134 | case 'android': 135 | case 'linux': 136 | final data = (fluent(exec('cat', ['/proc/meminfo'])) 137 | ..trim() 138 | ..stringToMap(':')) 139 | .mapValue; 140 | final physical = (fluent(data['MemTotal']) 141 | ..split(' ') 142 | ..elementAt(0) 143 | ..parseInt()) 144 | .intValue; 145 | final swap = (fluent(data['SwapTotal']) 146 | ..split(' ') 147 | ..elementAt(0) 148 | ..parseInt()) 149 | .intValue; 150 | return (physical + swap) * 1024; 151 | case 'macos': 152 | final data = (fluent(exec('vm_stat', [])) 153 | ..trim() 154 | ..stringToMap(':')) 155 | .mapValue; 156 | final free = (fluent(data['Pages free']) 157 | ..replaceAll('.', '') 158 | ..parseInt()) 159 | .intValue; 160 | final active = (fluent(data['Pages active']) 161 | ..replaceAll('.', '') 162 | ..parseInt()) 163 | .intValue; 164 | final inactive = (fluent(data['Pages inactive']) 165 | ..replaceAll('.', '') 166 | ..parseInt()) 167 | .intValue; 168 | final speculative = (fluent(data['Pages speculative']) 169 | ..replaceAll('.', '') 170 | ..parseInt()) 171 | .intValue; 172 | final wired = (fluent(data['Pages wired down']) 173 | ..replaceAll('.', '') 174 | ..parseInt()) 175 | .intValue; 176 | final pageSize = (fluent(exec('sysctl', ['-n', 'hw.pagesize'])) 177 | ..trim() 178 | ..parseInt()) 179 | .intValue; 180 | return (free + active + inactive + speculative + wired) * pageSize; 181 | case 'windows': 182 | final data = wmicGetValueAsMap('OS', ['TotalVirtualMemorySize'])!; 183 | final value = 184 | (fluent(data['TotalVirtualMemorySize'])..parseInt()).intValue; 185 | return value * 1024; 186 | default: 187 | notSupportedError(); 188 | } 189 | } 190 | 191 | int getVirtualMemorySize() { 192 | switch (Platform.operatingSystem) { 193 | case 'android': 194 | case 'linux': 195 | case 'macos': 196 | final data = (fluent(exec('ps', ['-o', 'vsz', '-p', '$pid'])) 197 | ..trim() 198 | ..stringToList()) 199 | .listValue; 200 | final size = (fluent(data.elementAt(1))..parseInt()).intValue; 201 | return size * 1024; 202 | case 'windows': 203 | final data = wmicGetValueAsMap('Process', ['VirtualSize'], 204 | where: ["Handle='$pid'"])!; 205 | final value = (fluent(data['VirtualSize'])..parseInt()).intValue; 206 | return value; 207 | default: 208 | notSupportedError(); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /lib/src/platform/operating_system.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import '../fluent.dart'; 4 | import '../utils.dart'; 5 | 6 | String getOperatingSystemName() { 7 | switch (Platform.operatingSystem) { 8 | case 'android': 9 | case 'linux': 10 | final data = (fluent(exec('lsb_release', ['-a'])) 11 | ..trim() 12 | ..stringToMap(':')) 13 | .mapValue; 14 | return fluent(data['Distributor ID']).stringValue; 15 | case 'macos': 16 | final data = (fluent(exec('sw_vers', [])) 17 | ..trim() 18 | ..stringToMap(':')) 19 | .mapValue; 20 | return fluent(data['ProductName']).stringValue; 21 | case 'windows': 22 | final data = wmicGetValueAsMap('OS', ['Caption'])!; 23 | return fluent(data['Caption']).stringValue; 24 | default: 25 | notSupportedError(); 26 | } 27 | } 28 | 29 | String getOperatingSystemVersion() { 30 | switch (Platform.operatingSystem) { 31 | case 'android': 32 | case 'linux': 33 | final data = (fluent(exec('lsb_release', ['-a'])) 34 | ..trim() 35 | ..stringToMap(':')) 36 | .mapValue; 37 | return fluent(data['Release']).stringValue; 38 | case 'macos': 39 | final data = (fluent(exec('sw_vers', [])) 40 | ..trim() 41 | ..stringToMap(':')) 42 | .mapValue; 43 | return fluent(data['ProductVersion']).stringValue; 44 | case 'windows': 45 | final data = wmicGetValueAsMap('OS', ['Version'])!; 46 | return fluent(data['Version']).stringValue; 47 | default: 48 | notSupportedError(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/src/platform/user.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import '../fluent.dart'; 4 | import '../system_info.dart'; 5 | import '../utils.dart'; 6 | 7 | String getUserDirectory() { 8 | switch (Platform.operatingSystem) { 9 | case 'android': 10 | case 'linux': 11 | case 'macos': 12 | return fluent(Platform.environment['HOME']).stringValue; 13 | case 'windows': 14 | return fluent(Platform.environment['USERPROFILE']).stringValue; 15 | default: 16 | notSupportedError(); 17 | } 18 | } 19 | 20 | String getUserId() { 21 | switch (Platform.operatingSystem) { 22 | case 'android': 23 | case 'linux': 24 | case 'macos': 25 | return (fluent(exec('id', ['-u']))..trim()).stringValue; 26 | case 'windows': 27 | final data = wmicGetValueAsMap('UserAccount', ['SID'], 28 | where: ["Name='${SysInfo.userName}'"])!; 29 | return fluent(data['SID']).stringValue; 30 | default: 31 | notSupportedError(); 32 | } 33 | } 34 | 35 | String getUserName() { 36 | switch (Platform.operatingSystem) { 37 | case 'android': 38 | case 'linux': 39 | case 'macos': 40 | return (fluent(exec('whoami', []))..trim()).stringValue; 41 | case 'windows': 42 | final data = wmicGetValueAsMap('ComputerSystem', ['UserName'])!; 43 | return (fluent(data['UserName']) 44 | ..split(r'\') 45 | ..last()) 46 | .stringValue; 47 | default: 48 | notSupportedError(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/src/platform/userspace.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import '../fluent.dart'; 4 | import '../utils.dart'; 5 | import 'kernel.dart'; 6 | 7 | int getUserSpaceBitness() { 8 | switch (Platform.operatingSystem) { 9 | case 'android': 10 | case 'linux': 11 | return (fluent(exec('getconf', ['LONG_BIT'])) 12 | ..trim() 13 | ..parseInt()) 14 | .intValue; 15 | case 'macos': 16 | if (Platform.version.contains('macos_ia32')) { 17 | return 32; 18 | } else if (Platform.version.contains('macos_x64')) { 19 | return 64; 20 | } else { 21 | return getKernelBitness(); 22 | } 23 | case 'windows': 24 | final wow64 = 25 | fluent(Platform.environment['PROCESSOR_ARCHITEW6432']).stringValue; 26 | if (wow64.isNotEmpty) { 27 | return 32; 28 | } 29 | 30 | switch (Platform.environment['PROCESSOR_ARCHITECTURE']) { 31 | case 'AMD64': 32 | case 'IA64': 33 | return 64; 34 | } 35 | 36 | return 32; 37 | default: 38 | notSupportedError(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/src/processor_architecture.dart: -------------------------------------------------------------------------------- 1 | class ProcessorArchitecture { 2 | const ProcessorArchitecture(this.name); 3 | 4 | static const ProcessorArchitecture arm64 = ProcessorArchitecture('ARM64'); 5 | 6 | static const ProcessorArchitecture arm = ProcessorArchitecture('ARM'); 7 | 8 | static const ProcessorArchitecture ia64 = ProcessorArchitecture('IA64'); 9 | 10 | static const ProcessorArchitecture mips = ProcessorArchitecture('MIPS'); 11 | 12 | static const ProcessorArchitecture x86 = ProcessorArchitecture('X86'); 13 | 14 | static const ProcessorArchitecture x86_64 = ProcessorArchitecture('X86_64'); 15 | 16 | static const ProcessorArchitecture unknown = ProcessorArchitecture('UNKNOWN'); 17 | 18 | final String name; 19 | 20 | @override 21 | String toString() => name; 22 | } 23 | -------------------------------------------------------------------------------- /lib/src/system_info.dart: -------------------------------------------------------------------------------- 1 | import 'core_info.dart'; 2 | import 'platform/cpu.dart'; 3 | import 'platform/kernel.dart'; 4 | import 'platform/memory.dart' as pm; 5 | import 'platform/operating_system.dart'; 6 | import 'platform/user.dart'; 7 | import 'platform/userspace.dart'; 8 | import 'processor_architecture.dart'; 9 | 10 | abstract class SysInfo { 11 | SysInfo._internal(); 12 | 13 | /// Returns the architecture of the kernel by obtaining 14 | /// the [rawKernelArchitecture] 15 | /// and converting it to a high level ProcessorArchitecture. 16 | /// 17 | /// print(SysInfo.kernelArchitecture); 18 | /// => ProcessorArchitecture.x86 19 | static final ProcessorArchitecture kernelArchitecture = 20 | getKernalArchitecture(); 21 | 22 | /// Returns the raw architecture of the kernel as reported by the OS 23 | /// 24 | /// print(SysInfo.rawKernelArchitecture); 25 | /// => i686 26 | static final String rawKernelArchitecture = getRawKernelArchitecture(); 27 | 28 | /// Returns the bintness of kernel. 29 | /// 30 | /// print(SysInfo.kernelBitness); 31 | /// => 32 32 | static final int kernelBitness = getKernelBitness(); 33 | 34 | /// Returns the name of kernel. 35 | /// 36 | /// print(SysInfo.kernelName); 37 | /// => Linux 38 | static final String kernelName = getKernelName(); 39 | 40 | /// Returns the version of kernel. 41 | /// 42 | /// print(SysInfo.kernelVersion); 43 | /// => 32 44 | static final String kernelVersion = getKernelVersion(); 45 | 46 | /// Returns the name of operating system. 47 | /// 48 | /// print(SysInfo.operatingSystemName); 49 | /// => Ubuntu 50 | static final String operatingSystemName = getOperatingSystemName(); 51 | 52 | /// Returns the version of operating system. 53 | /// 54 | /// print(SysInfo.operatingSystemVersion); 55 | /// => 14.04 56 | static final String operatingSystemVersion = getOperatingSystemVersion(); 57 | 58 | /// Returns the information about the processors. 59 | /// 60 | /// print(SysInfo.processors.first.vendor); 61 | /// => GenuineIntel 62 | static final List cores = getCores(); 63 | 64 | /// Returns the path of user home directory. 65 | /// 66 | /// print(SysInfo.userDirectory); 67 | /// => /home/andrew 68 | static final String userDirectory = getUserDirectory(); 69 | 70 | /// Returns the identifier of current user. 71 | /// 72 | /// print(SysInfo.userId); 73 | /// => 1000 74 | static final String userId = getUserId(); 75 | 76 | /// Returns the name of current user. 77 | /// 78 | /// print(SysInfo.userName); 79 | /// => 'Andrew' 80 | static final String userName = getUserName(); 81 | 82 | /// Returns the bitness of the user space. 83 | /// 84 | /// print(SysInfo.userSpaceBitness); 85 | /// => 32 86 | static final int userSpaceBitness = getUserSpaceBitness(); 87 | 88 | /// Returns the amount of free physical memory in bytes. 89 | /// 90 | /// This is the amount of physical memory that is unused (or empty). 91 | /// This is different from available memory, often lesser 92 | /// 93 | /// print(SysInfo.getFreePhysicalMemory()); 94 | /// => 3755331584 95 | static int getFreePhysicalMemory() => pm.getFreePhysicalMemory(); 96 | 97 | 98 | /// Returns the amount of available physical memory in bytes. 99 | /// 100 | /// This is the amount of physical memory that can be used by the system. 101 | /// Most "diagnostic apps" show this as the "free memory" 102 | /// 103 | /// print(SysInfo.getAvailablePhysicalMemory()); 104 | /// => 3755331584 105 | static int getAvailablePhysicalMemory() => pm.getAvailablePhysicalMemory(); 106 | 107 | /// Returns the amount of free virtual memory in bytes. 108 | /// 109 | /// print(SysInfo.getFreeVirtualMemory()); 110 | /// => 3755331584 111 | static int getFreeVirtualMemory() => pm.getFreeVirtualMemory(); 112 | 113 | /// Returns the amount of total physical memory in bytes. 114 | /// 115 | /// print(SysInfo.getTotalPhysicalMemory()); 116 | /// => 3755331584 117 | static int getTotalPhysicalMemory() => pm.getTotalPhysicalMemory(); 118 | 119 | /// Returns the amount of total virtual memory in bytes. 120 | /// 121 | /// print(SysInfo.getTotalVirtualMemory()); 122 | /// => 3755331584 123 | static int getTotalVirtualMemory() => pm.getTotalVirtualMemory(); 124 | 125 | /// Returns the amount of virtual memory in bytes used by the proccess. 126 | /// 127 | /// print(SysInfo.getVirtualMemorySize()); 128 | /// => 123456 129 | static int getVirtualMemorySize() => pm.getVirtualMemorySize(); 130 | } 131 | -------------------------------------------------------------------------------- /lib/src/utils.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_catches_without_on_clauses 2 | 3 | import 'dart:io'; 4 | 5 | import 'package:path/path.dart' as pathos; 6 | 7 | import 'file_utils.dart'; 8 | import 'fluent.dart'; 9 | 10 | String? exec(String executable, List arguments, 11 | {bool runInShell = false}) { 12 | try { 13 | final result = 14 | Process.runSync(executable, arguments, runInShell: runInShell); 15 | if (result.exitCode == 0) { 16 | return result.stdout.toString(); 17 | } 18 | } catch (e) { 19 | // 20 | } 21 | 22 | return null; 23 | } 24 | 25 | String? resolveLink(String path) { 26 | while (true) { 27 | if (!FileUtils.testfile(path, 'link')) { 28 | break; 29 | } 30 | 31 | try { 32 | // ignore: parameter_assignments 33 | path = Link(path).resolveSymbolicLinksSync(); 34 | } catch (e) { 35 | return null; 36 | } 37 | } 38 | 39 | return path; 40 | } 41 | 42 | void parseLdConf(String path, List paths, Set processed) { 43 | final _path = resolveLink(path); 44 | if (_path == null) { 45 | return; 46 | } 47 | 48 | final file = File(_path); 49 | if (!file.existsSync()) { 50 | return; 51 | } 52 | 53 | final dir = pathos.dirname(_path); 54 | for (var line in file.readAsLinesSync()) { 55 | line = line.trim(); 56 | final index = line.indexOf('#'); 57 | if (index != -1) { 58 | line = line.substring(0, index); 59 | } 60 | 61 | if (line.isEmpty) { 62 | continue; 63 | } 64 | 65 | var include = false; 66 | if (line.startsWith('include ')) { 67 | line = line.substring(8); 68 | include = true; 69 | } 70 | 71 | if (pathos.isRelative(line)) { 72 | line = pathos.join(dir, line); 73 | } 74 | 75 | if (include) { 76 | for (final path in FileUtils.glob(line)) { 77 | if (!processed.contains(path)) { 78 | processed.add(path); 79 | parseLdConf(path, paths, processed); 80 | } 81 | } 82 | } else { 83 | paths.add(line); 84 | } 85 | } 86 | } 87 | 88 | String? _wmicGetValue(String section, List fields, 89 | {List? where}) { 90 | final arguments = [section]; 91 | if (where != null) { 92 | arguments 93 | ..add('where') 94 | ..addAll(where); 95 | } 96 | 97 | arguments 98 | ..add('get') 99 | ..addAll(fields.join(', ').split(' ')) 100 | ..add('/VALUE'); 101 | return exec('wmic', arguments); 102 | } 103 | 104 | List>? wmicGetValueAsGroups( 105 | String section, List fields, 106 | {List? where}) { 107 | final string = _wmicGetValue(section, fields, where: where); 108 | return (fluent(string) 109 | ..stringToList() 110 | ..listToGroups('=')) 111 | .groupsValue; 112 | } 113 | 114 | Map? wmicGetValueAsMap(String section, List fields, 115 | {List? where}) { 116 | final string = _wmicGetValue(section, fields, where: where); 117 | return (fluent(string) 118 | ..stringToList() 119 | ..listToMap('=')) 120 | .mapValue as Map?; 121 | } 122 | 123 | Never notSupportedError() { 124 | throw UnsupportedError('Unsupported operating system.'); 125 | } 126 | -------------------------------------------------------------------------------- /lib/src/version/version.g.dart: -------------------------------------------------------------------------------- 1 | /// GENERATED BY pub_release do not modify. 2 | /// system_info2 version 3 | String packageVersion = '4.0.0'; 4 | -------------------------------------------------------------------------------- /lib/system_info2.dart: -------------------------------------------------------------------------------- 1 | export 'src/core_info.dart'; 2 | export 'src/processor_architecture.dart'; 3 | export 'src/system_info.dart'; 4 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: system_info2 2 | version: 4.0.0 3 | homepage: https://sysinfo.onepub.dev 4 | documentation: https://sysinfo.onepub.dev 5 | description: Provides easy access to useful information about the system (architecture, bitness, kernel, memory, operating system, CPU, user). 6 | repository: https://github.com/onepub-dev/system_info 7 | environment: 8 | sdk: '>=2.19.0 <4.0.0' 9 | dependencies: 10 | globbing: ^1.0.0 11 | path: ^1.8.0 12 | dev_dependencies: 13 | lint_hard: ^3.0.0 14 | --------------------------------------------------------------------------------