http://www.perlmonks.org?node_id=270750

Hi, Monks. I've been trying to add a MS Windows shell context entry to enable me to alternate-mouse click on a file or folder in Windows Explorer GUI, and have the file name as Cygwin sees it placed on the Clipboard. Because the Perl tool I wrote to help me accomplish this was used it for enhancing my enjoyment of Cygwin, this qualifies in my book as a CUFP.

This (context-menu access to the Cygwin-mode filename) has been invented before, but TTBOMK nobody has shown how to make it portable across different people's systems without editing the .reg file manually.

BTW, to get this to work I used what's already defined in my master Windows ENV, a variable %CYGROOT% which points to the MSWin32 mode path spec of the top of my Cygwin installation tree.

Substitute your own different software installation needs and you may agree that this could be pretty useful once in a while.

Update:
Except that you cannot get Win95, 98, prob. ME to import such a key value-data type into the Registry. I have just found out that the inferior REGEDIT tool supplied with these inferior versions of Windows is incapable of doing this. The later NT4/5-derived Windows can do so.

There's really some Perl coming soon, I promise. I just dislike very incomplete and puzzling explanations that people sometimes give about software I am unfamiliar with, that leaves me totally in the dark about why they bothered. So here's the rest of the set-up...

There are several "DATA TYPES" (Microsoft terminology) possible for MS Win Registry entries. The type most of us would call a "C string" is called a REG_SZ and is formatted as a simple string (but with some complex quoting and escaping rules). There's a logically and semantically (since "SZ" is is Microsoft-speak for "null-terminated string") related Registry data type called a REG_EXPAND_SZ that can contain a level of "indirection" in the form of a system variable that can be resolved at the time of use of the entry. For example:


   "SoMeThIng"="%WINDIR%\\system32"
could in our naive imagination be a Registry .REG -file line indicating a directive to insert a value-data pair into a key, except that REG_EXPAND_SZ data must be presented as what MS calls a *binary* data type (subtype "2"), so the value must be formatted in a kind of hexidecimal format, comma-delimited, two tokens per byte (padded with zeros), with a terminating null byte of course (and further explanation is obviously far beyond the scope of this article, and the author won't be held responsible for anyone's misuse of the incomplete information given thus far). So the line above will not work but indicates the end result I wanted to achieve. As a real working .REG -file entry the example above must be rendered as:

  "SoMeThIng"=hex(2):22,25,57,49,4e,44,49,52,25,5c,5c,73,79,73,74,65,6d,33,32,22,00
Finally we get to the Perl. Perl does what's needed here with such elegance and efficiency, <sigh>. Given a "Registry psuedo-code" string that *describes* what we want the "decoded" string to look like, like that shown earlier, Perl can generate the kind of hex format needed for inclusion of the line in a REGEDIT .REG file:

UPDATE: Ooops. I forget to chomp() the newline before encoding it when taking data from <STDIN>. Fixed now. Well, classic CRLF vs. LF gotcha. NOW is fixed.

UPDATE: Ooops, again. Not showing sterling recall of my sprintf() style ;-). Need to pad to two tokens per byte, format: "%02x". Debugging continues.

UPDATE (July 06, 2003): Apparently, REGEDIT5 (e.g., on Win XP) exports such REG_EXPAND_SZ data with a null byte 00 after every real byte. These tools here will clearly fail to create or decode such a stream and therefore are limited to handling REGEDIT4 -style .REG files.

#!/usr/bin/perl -w # "REGexpandSZgen" - generate the formatted hex string from a normal # ascii input string- for a REGEDIT file; to enter a REG_EXPAND_SZ # data-type entry into the MS Windows Registry. use strict; my $encme = pop(@ARGV) || <STDIN>; $encme =~s% (\x{0d}|\x{0a}{1,2})\Z %%x; if($encme =~m{^("[^"]+"|\@)\=(.*)$}) { # " does not appear where "entry"= @ $encme = [$1, $2]; } my $cvtd = [ map { sprintf "%02x",ord } split('', ref $encme ? $encme->[1]:$encme) ]; if(not $ARGV[0]) { print '', (ref $encme ? "$encme->[0]=":""), "hex(2):", join(',',@$cvtd,'00'), "\n"; } else { # any prepended arg causes us to # debug / test / verify the string. print "\n IN: ",(ref $encme ? $encme->[1]:$encme),"\nOUT: " , pack("c*", map(hex, @$cvtd[0..(@$cvtd - 0x1)])) , "\n"; } __END__ This Program is Free Software, (C)2003 Soren Andersen <somian -AT- pobox -DOT- com> it may be used under the same terms as Perl itself.


I needed a debugging tool, so I whipped up a decoder too...

#! /usr/bin/perl -wl # "REGdecodeSZ" - decode Microsoft .REG -style null-terminated # strings encoded as comma-delimited sequences of hex bytes. use strict; my ($instr, @barr); chomp($instr = <STDIN>) unless( $instr = $ARGV[0x0] ); my $to = 0x1 * (@barr=split q/,/ , $instr); print join '', ( map { chr hex } @barr[ 0x0 .. ($to ? $to - 0x2 : $to) ] ); __END__ This Program is Free Software, (C)2003 Soren Andersen <somian -AT- pobox -DOT- com> and it may be used under the same terms as Perl itself.

I haven't shown all the pieces -- particularly the .REG file which actually modifies the Registry, because that isn't Perl and doesn't really pertain to the broader concept. The reader who is left wanting a look at the rest of the pieces of this, my excuse-for-the-evening to crank out some Perl hacks, is encouraged to check the Cygwin mailing List archives (from the Cygwin site) for the recent and many past discussions relating to this specific idea.