46 lines
2.6 KiB
Dart
46 lines
2.6 KiB
Dart
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:path/path.dart' as p;
|
|
|
|
import '../l10n/tr.dart';
|
|
import '../services/platform_info.dart';
|
|
|
|
/// 관리자 지시 (10/20, 28 재현 - P0 데이터): Windows 의 rsync (MSYS2 · Cygwin 런타임) 는 Windows 프로그램이 넘긴 명령 줄을
|
|
/// 스스로 다시 읽는다 - ' 를 따옴표로 짝지어 (--exclude=it's.txt --exclude=Bob's.txt 가 한 인수로 합쳐져 거르기가 사라지고
|
|
/// 지킬 파일이 지워짐, ' 가 든 경로는 합쳐져 실패) * ? [ ] { } 를 작업 폴더의 파일로 펼친다 (--exclude *.log 가 x.log y.log 로).
|
|
/// 앱이 만든 인수를 응답 파일 (@파일) 에 큰따옴표로 감싸 넣고 MSYS=noglob 으로 띄우면 그대로 간다 (실제 rsync 3.5.1 로 확인 -
|
|
/// MSYS=noglob 만으로는 ' 를 막지 못하고, CYGWIN=noglob 은 소용없다).
|
|
/// 응답 파일의 경로에 빈칸 · ' 가 있으면 @ 가 먹히지 않아, 그 파일이 든 폴더를 작업 폴더로 하고 "@args.txt" 로 넘긴다.
|
|
/// 큰따옴표 (") 가 든 인수는 이 방법으로도 그대로 넘기지 못한다 - 실행하지 않고 이유를 알린다 (Windows 파일 이름에는 " 가 없다).
|
|
class RsyncLaunch {
|
|
final List<String> args;
|
|
final String? workingDirectory;
|
|
final Map<String, String>? environment;
|
|
final Directory? _dir;
|
|
RsyncLaunch._(this.args, this.workingDirectory, this.environment, this._dir);
|
|
|
|
/// 다 쓴 응답 파일을 지운다 (rsync 가 끝난 뒤)
|
|
void dispose() {
|
|
try {
|
|
_dir?.deleteSync(recursive: true);
|
|
} catch (_) {}
|
|
}
|
|
}
|
|
|
|
/// Windows rsync 에 그대로 넘길 수 없는 인수 (큰따옴표가 듦)
|
|
List<String> msysUnsafeArgs(List<String> args) => [for (final a in args) if (a.contains('"')) a];
|
|
|
|
/// [args] 를 rsync 에 그대로 넘기도록 준비한다 (Windows 만 응답 파일 · MSYS=noglob, 그 밖은 그대로). 끝나면 [RsyncLaunch.dispose]
|
|
Future<RsyncLaunch> prepareRsyncLaunch(List<String> args, {bool? windows}) async {
|
|
if (!(windows ?? PlatformInfo.host.isWindows)) return RsyncLaunch._(args, null, null, null);
|
|
final bad = msysUnsafeArgs(args);
|
|
if (bad.isNotEmpty) {
|
|
throw StateError(trf('큰따옴표 (") 가 든 인수는 Windows 의 rsync 에 그대로 넘길 수 없어 실행하지 않았습니다: {0}', [bad.join(' · ')]));
|
|
}
|
|
final dir = await Directory.systemTemp.createTemp('jj_rsargs_');
|
|
await File(p.join(dir.path, 'args.txt')).writeAsString(args.map((a) => '"$a"').join(' '), encoding: utf8);
|
|
final msys = Platform.environment['MSYS'];
|
|
return RsyncLaunch._(['@args.txt'], dir.path, {'MSYS': msys == null || msys.trim().isEmpty ? 'noglob' : '$msys noglob'}, dir);
|
|
}
|