package LatexIndent::UTF8CmdLineArgsFileOperation; use strict; use warnings; use feature qw( say state ); use utf8; use Config qw( %Config ); use Encode qw(find_encoding decode encode ); use Exporter qw/import/; our @EXPORT_OK = qw/commandlineargs_with_encode @new_args copy_with_encode exist_with_encode open_with_encode zero_with_encode read_yaml_with_encode isdir_with_encode mkdir_with_encode/; our $encodingObject; if ($^O eq 'MSWin32') { my $encoding_sys = 'cp' . Win32::GetACP(); $encodingObject = find_encoding( $encoding_sys ); # Check if the encoding is valid. unless ( ref($encodingObject) ) { $encodingObject = find_encoding( 'utf-8' ); } } else { $encodingObject = find_encoding( 'utf-8' ); } sub copy_with_encode { use File::Copy; my ( $source, $destination ) = @_; if ( $FindBin::Script =~ /\.exe$/ ) { require Win32::Unicode::File; Win32::Unicode::File->import(qw(copyW)); copyW( $source, $destination, 1 ); } else { $source = $encodingObject->encode($source); $destination = $encodingObject->encode($destination); copy( $source, $destination ); } } sub exist_with_encode { my ($filename) = @_; if ( $FindBin::Script =~ /\.exe$/ ) { require Win32::Unicode::File; Win32::Unicode::File->import(qw(statW)); return statW($filename); } else { $filename = $encodingObject->encode($filename); return -e $filename; } } sub zero_with_encode { my ($filename) = @_; if ( $FindBin::Script =~ /\.exe$/ ) { require Win32::Unicode::File; Win32::Unicode::File->import(qw(file_size)); my $size = file_size($filename); if ($size) { return 0; } else { return 1; } } else { $filename = $encodingObject->encode($filename); return -z $filename; } } sub open_with_encode { my $mode = shift; my $filename = shift; my $fh; if ( $FindBin::Script =~ /\.exe$/ ) { require Win32::Unicode::File; Win32::Unicode::File->import; $fh = Win32::Unicode::File->new; if ( open $fh, $mode, $filename ) { return $fh; } else { return undef; } } else { $filename = $encodingObject->encode($filename); if ( open( $fh, $mode, $filename ) ) { return $fh; } else { return undef; } } } sub read_yaml_with_encode { use YAML::Tiny; my $filename = shift; my $fh = open_with_encode( '<:encoding(UTF-8)', $filename ) or return undef; my $yaml_string = join( "", <$fh> ); return YAML::Tiny->read_string($yaml_string); } sub isdir_with_encode { my $path = shift; if ( $FindBin::Script =~ /\.exe$/ ) { require Win32::Unicode::File; Win32::Unicode::File->import(qw(file_type)); return file_type( 'd', $path ); } else { $path = $encodingObject->encode($path); return -d $path; } } sub mkdir_with_encode { my $path = shift; if ( $FindBin::Script =~ /\.exe$/ ) { require Win32::Unicode::Dir; Win32::Unicode::Dir->import(qw(mkdirW)); mkdirW($path); } else { require File::Path; File::Path->import(qw(make_path)); $path = $encodingObject->encode($path); make_path($path); } } #https://stackoverflow.com/a/63868721 #https://stackoverflow.com/a/44489228 sub commandlineargs_with_encode { if ( $FindBin::Script =~ /\.exe$/ ) { require Win32::API; import Win32::API qw( ReadMemory ); #use open ':std', ':encoding('.do { require Win32; "cp".Win32::GetConsoleOutputCP() }.')'; use constant ptr_size => $Config{ptrsize}; use constant ptr_pack_format => ptr_size == 8 ? 'Q' : ptr_size == 4 ? 'L' : die("Unrecognized ptrsize\n"); use constant ptr_win32api_type => ptr_size == 8 ? 'Q' : ptr_size == 4 ? 'N' : die("Unrecognized ptrsize\n"); sub lstrlenw { my ($ptr) = @_; state $lstrlenw = Win32::API->new( 'kernel32', 'lstrlenW', ptr_win32api_type, 'i' ) or die($^E); return $lstrlenw->Call($ptr); } sub decode_lpcwstr { my ($ptr) = @_; return undef if !$ptr; my $num_chars = lstrlenw($ptr) or return ''; return decode( 'UTF-16le', ReadMemory( $ptr, $num_chars * 2 ) ); } # Returns true on success. Returns false and sets $^E on error. sub localfree { my ($ptr) = @_; state $localfree = Win32::API->new( 'kernel32', 'LocalFree', ptr_win32api_type, ptr_win32api_type ) or die($^E); return $localfree->Call($ptr) == 0; } sub getcommandline { state $getcommandline = Win32::API->new( 'kernel32', 'GetCommandLineW', '', ptr_win32api_type ) or die($^E); return decode_lpcwstr( $getcommandline->Call() ); } # Returns a reference to an array on success. Returns undef and sets $^E on error. sub commandlinetoargv { my ($cmd_line) = @_; state $commandlinetoargv = Win32::API->new( 'shell32', 'CommandLineToArgvW', 'PP', ptr_win32api_type ) or die($^E); my $cmd_line_encoded = encode( 'UTF-16le', $cmd_line . "\0" ); my $num_args_buf = pack( 'i', 0 ); # Allocate space for an "int". my $arg_ptrs_ptr = $commandlinetoargv->Call( $cmd_line_encoded, $num_args_buf ) or return undef; my $num_args = unpack( 'i', $num_args_buf ); my @args = map { decode_lpcwstr($_) } unpack ptr_pack_format . '*', ReadMemory( $arg_ptrs_ptr, ptr_size * $num_args ); localfree($arg_ptrs_ptr); return \@args; } my $cmd_line = getcommandline(); our @new_args = $cmd_line; $cmd_line =~ s/(^(.*?)\.pl\"? )//g; $cmd_line =~ s/(^(.*?)\.exe\"? )//g; my $args = commandlinetoargv($cmd_line) or die("CommandLineToArgv: $^E\n"); @ARGV = @{$args}; } else { @ARGV = map { decode( $encodingObject, $_ ) } @ARGV; our @new_args = @ARGV; } } 1;