File Coverage

blib/lib/Mac/FileSpec/Unixish.pm
Criterion Covered Total %
statement 7 21 33.3
branch 0 2 0.0
condition n/a
subroutine 3 8 37.5
pod 0 3 0.0
total 10 34 29.4


line stmt bran cond sub pod time code
1              
2             # Time-stamp: "2004-12-29 19:01:48 AST"
3             require 5;
4             package Mac::FileSpec::Unixish;
5              
6 2     2   16714 use strict;
  2         7  
  2         109  
7 2         2692 use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION
8 2     2   12 $Debug $Pretend_Non_Mac $Pretend_Mac);
  2         4  
9             require Exporter;
10             @ISA = qw(Exporter);
11             @EXPORT = qw(nativize unixify);
12             @EXPORT_OK = qw(nativize unixify under_macos);
13              
14             $VERSION = "1.12";
15             $Debug = 0;
16              
17             $Pretend_Non_Mac ||= 0; # hardcode to 1 for testing non-Mac things on a Mac
18             $Pretend_Mac ||= 0; # hardcode to 1 for testing Mac things on a non-Mac
19             # Don't set both of those to 1 at once
20             #==========================================================================
21              
22             =head1 NAME
23              
24             Mac::FileSpec::Unixish -- Unixish-compatability in file specifications
25              
26             =head1 SYNOPSIS
27              
28             use Mac::FileSpec::Unixish; # exports 'unixify' and 'nativize'
29            
30             @input = map( unixify($_), @ARGV);
31             foreach $item (@input) {
32             my $_native_item = nativize($item);
33             next unless
34             $item =~ m<([^/]+)$>s # assumes / is the path separator
35             and -f $_native_item;
36             printf("File %s is %d bytes long...\n", $1, -s _ );
37             open(IN, "<$_native_item")
38             || die "Can't open $_native_item : $!\n";
39             print "First line: ", scalar();
40             close(IN);
41             }
42              
43             =head1 DESCRIPTION
44              
45             Mac::FileSpec::Unixish provides two functions, C and
46             C (both of which are exported by default), that will allow
47             you to denote and manipulate pathspecs in Unixish style, and let you
48             convert these pathspecs to and from the native OS's format for
49             conveying such things. It currently assumes that if you are not
50             running under MacOS (as reported in C<$^O>), you must be on a Unix
51             box. If you want better, I suggest using File::Spec. (In essence, I
52             wrote Mac::FileSpec::Unixish as a cheap hack to get around using
53             File::Spec.)
54              
55             Using this library, you can, in your code, refer to files using a
56             Unixish notation, a la:
57              
58             $foo = "../analyses/ziz.txt";
59             open(OUT, '>' . nativize($foo) ) || die "Couldn't open $foo \: $!";
60              
61             Under Unix, C will be simply "../analyses/ziz.txt"
62             (C and C are nearly no-ops under Unixes); but under
63             MacOS it will be "::analyses:ziz.txt".
64              
65             Incidentally, C is always eq C<$item>, for
66             all (defined, non-undef) values of C<$item>, regardless of whether or
67             not this is performed under MacOS. In other words, this:
68              
69             @items = map(unixify($_), @ARGV);
70             foreach $one (@items) {
71             print "$one => ", -s nativize($one), " bytes\n";
72             my $one_copy = $one;
73             $one_copy =~ s/[^/]+$//s;
74             print " in the directory $one_copy";
75             }
76              
77             will work equally well under MacOS as under Unix, regardless of the
78             fact that items in @ARGV will be in "foo:bar:baz" format if run under
79             MacOS, and "/foo/bar/baz" format if run under Unix.
80              
81             This portability is the entire point of this library.
82              
83             (This code will work even if run under MacOS and if @ARGV contains a
84             pathspec like "Sean:reports:by week:5/5/98". C encodes those
85             slashes (as "\e2f", if you're curious) so that they won't be
86             misunderstood as path separators in the Unixish representation -- see
87             "GUTS", below, for the gory details.)
88              
89             This library also provides (but does not by default export) a function
90             Mac::FileSpec::Unixish::under_macos(), which returns true if you're
91             running under MacOS, and false otherwise. You can use that in cases
92             like:
93              
94             my $home =
95             Mac::FileSpec::Unixish::under_macos() ? '/Sean/' : '~/' ;
96              
97             =head2 PURPOSE
98              
99             This library exists so that a careful programmer who knows what
100             filespecs are legal and meaningful both under Mac and under Unix, can
101             write code that manipulates files and filenawes, and have this code
102             work equally well under MacOS and under Unix.
103              
104             That's all this library is for, and that's all it does.
105              
106             This library doesn't overload anything, so I go thinking that
107             you can go
108              
109             open(OUT, '>../foo/bar.txt");
110              
111             under MacOS.
112              
113             Proper use of this library means that I time you pass a file
114             specification to any file operation (from C to C<-s> to
115             C), you should pass the Unixish designation thru C
116             -- and I time you get a file spec from the OS (thru C<@ARGV> or
117             C or whatever), that you
118             pass it thru C to get the Unixish representation.
119              
120             C and C are the only two functions this module
121             exports.
122              
123             This library doesn't try to interpret Unixish pathspecs with B
124             semantics other than the above-described -- to wit, "~"s in filespecs
125             (as in C<~/.plan> or C<~luser/.plan>) aren't expanded, since there is
126             no equivalent meaning under MacOS.
127              
128             And if you say "/tmp/", you I get "tmp:" under MacOS -- and this
129             is probably I what you want.
130              
131             This (coupled with the fact that MacOS has nothing like "/", save as a
132             notational (or notional) base for the mounted volumes) almost
133             definitely means that B
134             like "/tmp/" or "/usr/home/luser" or "/Sean/System Folder",
135             or pathspecs based on ~ or ~luser>. In other words, your pathspecs
136             should either come from outside the program (as from %ENV, @ARGV, or
137             things you devise based on them), or should be relative.
138              
139             You have been warned!
140              
141             =head2 GUTS
142              
143             Here are some of the icky details of how this module works.
144              
145             "Unixish" path specification means pathspecs expressed with the
146             meanings that Unix assigns to '/', '.', and '..' -- with the
147             additional bit of semantics that the escape character (ASCII 0x1B,
148             a.k.a. C<"\e">) and two hex ([a-fA-F0-9]) characters after it denote
149             the one character with that hex code.
150              
151             In other words, it's just like URL-encoding, but with C instead
152             of C<%>. I included this quoting mechanism so it would be possible to
153             denote, in Unixish notation, Mac filenames with "/"s in them.
154             Example:
155              
156             "Foovol:stuff:05/98" -> "/Foovol/stuff/05\e2f98"
157              
158             But actual hardcoding of "\e2f" is unwise, since if you have:
159              
160             open(OUT, '>' . nativize("/Foovol/stuff/05\e2f98"));
161              
162             This will Do What You Want only if you're under MacOS, but under Unix
163             it will instead try to write to C.
164              
165             As mentioned above, C is always $item, for
166             all values of $item, and regardless of whether or not this is
167             performed under MacOS.
168              
169             But the inverse (i.e., whether C) is not
170             necessarily true! In a rather dramatic case, C happens
171             to yield "" under MacOS, for many, many reasons. Other, more mundane
172             cases include the fact that "../foo" and "./../foo" and, for that
173             matter, ".././foo" are all synonyms for the same thing, and the
174             (notational if not meaningful) distinction between them I be
175             smashed -- under MacOS, they'd all end up "::foo".
176              
177             =head2 A Note on Trailers
178              
179             Note that when a trailing MacOS ":" means 'directory' (e.g.,
180             "Sean:reports:", it is represented as a trailing '/' in the Unixish
181             representation, and vice versa. When I'm writing code, I always
182             use a trailer (a trailing ":" or "/") when accessing a directory (as
183             is C or C
184             ). Now, this is generally unnecessary; C
185             ":foo:bar:")> and C do the same thing,
186             just as C and C
187             "foo/bar")> do the same thing on (absolutely all?) Unix boxes.
188              
189             However, when accessing the root drive of a MacOS volume, the "root"
190             directory of a volume, like "foo", you should use the trailer --
191             C, not C.
192              
193             It's odd to note that MacOS seems inconsistent about using the
194             trailer. If you drop the Finder icon for the volume "foo" onto a
195             droplet, it'll see "foo:" in @ARGV -- with the trailer. But if you
196             drop the Finder icon for the directory "foo:bar" (or any other
197             non-volume-root directory I've tested this on) onto a droplet, it'll
198             see "foo:bar" in @ARGV -- no trailer.
199              
200             =head1 COPYRIGHT
201              
202             Copyright 1998-2000, Sean M. Burke C, all rights
203             reserved.
204              
205             You may use and redistribute this library under the same terms as Perl itself.
206              
207             =head1 AUTHOR
208              
209             Sean M. Burke C
210              
211             =cut
212              
213             #--------------------------------------------------------------------------
214              
215             sub nativize {
216             # Convert a unixish filespec to one that has
217             # the equivalent meaning for the native OS.
218             my($spec) = $_[0];
219              
220             print " spec: $spec\n" if $Debug;
221              
222             return undef unless defined($spec);
223             return '' if $spec eq '';
224              
225             my($is_abs) = $spec =~ s<^/+><>s;
226             my($is_dir) = $spec =~ s<>s;
227              
228             my(@bits) = ($spec =~
229             m<
230             ( [^\/]+ )
231             >xsg
232             );
233              
234             print " bits: ", map("<$_>", @bits), "\n" if $Debug;
235              
236             my(@bits_out) = ();
237              
238             foreach my $bit (@bits) {
239             if($bit eq '..') {
240             push @bits_out, "\eUP";
241             # \eUP is my internal symbol for up-dir
242             } elsif ($bit eq '.') { # a HERE
243             # do nothing
244             } else {
245             push @bits_out, $bit;
246             }
247             }
248              
249             my($out) = join(':', @bits_out);
250              
251             print " bits_out: ", map("<$_>", @bits_out), "\n" if $Debug;
252             print " out1 = <$out>\n" if $Debug;
253              
254             $out =~ s<( # Match...
255             :? # a possible leading ':'
256             (?: # and one or more of a
257             \eUP # \eUP
258             \:? # possibly followed by ':'
259             )+
260             )
261             ><&_parse_ups($1)>exsg;
262              
263             print " out2 = <$out>\n" if $Debug;
264              
265             $out =
266             ($is_abs
267             || substr($out,0,1) eq ':'
268             # So that '::foo' (from '../foo' ) doesn't => ':::foo'
269             ? '' : ':') .
270             $out .
271             ($is_dir ? ':' : '')
272             ;
273              
274             print " out3 = <$out>\n" if $Debug;
275             $out = &_e_decode($out);
276             print " out4 = <$out>\n" if $Debug;
277              
278             return $out;
279             }
280              
281             #--------------------------------------------------------------------------
282              
283             sub unixify {
284             # Convert from native format into a unixish (with \e-quoting) spec
285             my($spec) = $_[0];
286              
287             print " spec: $spec\n" if $Debug;
288              
289             return undef unless defined($spec);
290             return '' if $spec eq '';
291              
292             my($is_abs) = $spec !~ m<^:>s;
293              
294             my(@bits) = split( /(\:+)/ , $spec, -1);
295             print " bits: ", map("<$_>", @bits), "\n" if $Debug;
296              
297             my($out) = '';
298             foreach my $bit (@bits) {
299             # print " Bit: <$bit>\n";
300             if( $bit eq '') { # Caused by a leading ':'
301             # Do nothing.
302             } elsif ( $bit eq ':' ) {
303             $out .= '/';
304             } elsif( $bit =~ /^\:+$/s ) {
305             $out .= join('..', ('/') x length($bit))
306             } else {
307             # It's an item -- \e-quote as necessary
308             $out .= &_e_encode($bit);
309             }
310             }
311             $out =
312             ($is_abs ? '/' : '.') . $out;
313             print " out: <$out>\n" if $Debug;
314             return $out;
315             }
316              
317             sub under_macos { 1 }
318              
319             #==========================================================================
320              
321             # And if I'm not on a Mac, override &nativize and &unixify
322             # to just \e-decode and \e-encode.
323              
324             if( ($^O ne 'MacOS' || $Pretend_Non_Mac)
325             && !$Pretend_Mac
326             ) {
327 0     0 0 0 eval "
  1     1 0 616  
  0     0 0    
328             sub nativize { &_e_decode(\@_) }
329             sub unixify { &_e_encode(\@_) }
330             sub under_macos { 0 }
331             ";
332             }
333              
334             #==========================================================================
335             # Internal routines
336              
337             sub _parse_ups {
338             # Return a string of 1 + as many ":"s as there were
339             # \e's in the input string.
340 0     0     my($in) = $_[0];
341 0           my($out) = ':' x (1 + $in =~ tr/\e//);
342 0 0         print " UP-path string <$in> => <$out>\n" if $Debug > 1;
343 0           return $out;
344             }
345              
346             sub _e_encode {
347 0     0     my($thing) = $_[0];
348 0           $thing =~ s<([/\e])><"\e".(unpack('H2',$1))>eg;
  0            
349 0           return $thing;
350             }
351              
352             sub _e_decode {
353 0     0     my($thing) = $_[0];
354 0           $thing =~ s/\e([a-fA-F0-9][a-fA-F0-9])/pack('C', hex($1))/eg;
  0            
355 0           return $thing;
356             }
357              
358             #==========================================================================
359             1;
360              
361             __END__