File Coverage

blib/lib/File/Namaste.pm
Criterion Covered Total %
statement 93 102 91.1
branch 32 54 59.2
condition 18 39 46.1
subroutine 11 12 91.6
pod 1 6 16.6
total 155 213 72.7


line stmt bran cond sub pod time code
1             package File::Namaste;
2              
3 2     2   59780 use 5.006;
  2         9  
  2         153  
4 2     2   11 use strict;
  2         6  
  2         62  
5 2     2   18 use warnings;
  2         10  
  2         336  
6              
7             require Exporter;
8             our @ISA = qw(Exporter);
9              
10             our $VERSION;
11             $VERSION = sprintf "%d.%02d", q$Name: Release-1-04 $ =~ /Release-(\d+)-(\d+)/;
12              
13             our @EXPORT = qw();
14             #our @EXPORT_OK = qw();
15             our @EXPORT_OK = qw(
16             nam_get nam_add nam_elide
17             );
18             our %EXPORT_TAGS = (all => [ @EXPORT_OK ]);
19             #our @EXPORT_OK = qw();
20              
21 2     2   2051 use File::Spec::Functions; # we want catfile()
  2         1922  
  2         930  
22              
23             # Default setting for tranformations is non-portable for Unix.
24             our $portable_default = grep(/Win32|OS2/i, @File::Spec::ISA);
25              
26             # Instead of "use File::Value;" for a small dependency, use this
27             # abbreviated version of "raw" File::Value::file_value() (that we
28             # also use commonly in test scripts).
29             #
30 11     11 0 17 sub filval { my( $file, $value )=@_; # $file must begin with >, <, or >>
31 11 100       45 if ($file =~ /^\s*>>?/) {
32 3 50       242 open(OUT, $file) or return "$file: $!";
33 3         25 my $r = print OUT $value;
34 3 50       143 close(OUT); return ($r ? '' : "write failed: $!");
  3         28  
35             } # If we get here, we're doing file-to-value case.
36 8 50       241 open(IN, $file) or return "$file: $!";
37 8         29 local $/; $_[1] = ; # slurp mode (entire file)
  8         139  
38 8         77 close(IN); return '';
  8         29  
39             }
40              
41             # xxx not yet doing unicode or i18n
42             # only first arg required
43             # return tvalue given fvalue
44 3     3 0 7 sub nam_tvalue { my( $fvalue, $portable, $max, $ellipsis )=@_;
45              
46 3 50       10 defined($portable) or $portable = $portable_default;
47 3         6 my $tvalue = $fvalue;
48 3         10 $tvalue =~ s,/,=,g;
49 3         12 $tvalue =~ s,\s+, ,g;
50 2     2   8860 $tvalue =~ s,\p{IsC},?,g; # control characters
  2         27  
  2         34  
  3         7  
51              
52 3 50       9 $portable and # more portable (Win32) mapping
53             $tvalue =~ tr {"*/:<>?|\\}{.};
54              
55 3         10 return nam_elide($tvalue, $max, $ellipsis);
56             }
57              
58             # Ordered list of labels, mostly kernel element names
59             # yyy should this be coming from ERC module, or is that creating
60             # a big dependency hurdle for a sliver of functionality?
61             our @namaste_labels = qw(
62             dir_type
63             who
64             what
65             when
66             where
67             how
68             why
69             huh
70             );
71              
72 0     0 0 0 sub num2label { my $num = shift;
73              
74 0         0 my $last = $#namaste_labels;
75 0         0 $num =~ s/^(\d+).*/$1/; # forgive, eg, 3=foo
76 0 0 0     0 $num =~ /\D/ || $num < 0 || $num > $last and
      0        
77             return $namaste_labels[$last]; # last label
78             # last label doubles as unknown (huh?) if number is bad
79 0         0 return $namaste_labels[$num]; # normal return
80             }
81              
82              
83             # xxx should create shadow tag files with highly deterministic names?
84             # easier for a machine to fine a specific element
85             my $dtname = ".dir_type"; # canonical name of directory type file
86             # xxx .=dir_type
87             # xxx .=how
88             # xxx .=huh
89             # xxx .=what
90             # xxx .=when
91             # xxx .=where
92             # xxx .=who
93             # xxx .=why
94              
95             # xxx to do
96             # N means create N=...
97             # .N means create or add to .=wh{o,at,en,ere}
98             # N. means do both N and .N
99              
100             # $num and $fvalue required
101             # returns empty string on success, otherwise a diagnostic
102 3     3 0 4936 sub nam_add { my( $dir, $portable, $num, $fvalue, $max, $ellipsis )=@_;
103              
104 3 50 33     18 return 0
105             if (! defined($num) || ! defined($fvalue));
106              
107 3   50     14 $dir ||= "";
108             #$dir = catfile($dir, "") # add portable separator
109             # if $dir; # (eg, slash) if there's a dir name
110              
111             #my $fname = $dir . $dtname; # path to .dir_type, if needed
112 3         85 my $fname = catfile($dir, $dtname); # path to .dir_type, if needed
113 3         12 my $tvalue = nam_tvalue($fvalue, $portable, $max, $ellipsis);
114             # ".0" means set .dir_type also; "." means only set .dir_type
115 3 50 33     21 if ($num =~ s/^\.0/0/ || $num eq ".") {
116             # "append only" supports multi-typing in .dir_type, so
117             # caller must remove .dir_type to re-set (see "nam" script)
118             # right now $fname contains catfile($dir, $dtname)
119 0         0 my $ret = filval(">>$fname", $fvalue);
120 0 0 0     0 return $ret # return if error or only .dir_type
121             if $ret || $num eq ".";
122             }
123              
124             #$fname = "$dir$num=$tvalue";
125 3         19 $fname = catfile($dir, "$num=$tvalue");
126             #nam_tvalue($fvalue, $portable, $max, $ellipsis);
127             # why is this sometimes null?
128              
129 3         15 return filval(">$fname", $fvalue);
130             }
131              
132 2     2   53858 use File::Glob ':glob'; # standard use of module, which we need
  2         5  
  2         8439  
133             # as vanilla glob won't match whitespace
134              
135             # first arg is directory, remaining args give numbers to fetch;
136             # no args means return all
137             # args can be file globs
138             # returns array of number/fname/value triples (every third elem is number)
139             sub nam_get {
140              
141 5     5 0 2468 my $dir = shift @_;
142              
143 5   50     16 $dir ||= "";
144             #$dir = catfile($dir, "") # add portable separator
145             # if $dir; # (eg, slash) if there's a dir name
146             #my $dir_type = $dir . $dtname; # path to .dir_type, if needed
147 5         28 my $dir_type = catfile($dir, $dtname); # path to .dir_type, if needed
148              
149 5         9 my (@in, @out);
150 5 100       13 if ($#_ < 0) { # if no args, get all files starting
151             # Surprisingly, with bsd_glob a / separator works in Win32
152 3         288 @in = bsd_glob("$dir/[0-9]=*"); # so no need for catfile()
153             # @in = bsd_glob(catfile($dir, '[0-9]=*')); # "=..."
154 3 50       41 -e $dir_type and # since we're getting all,
155             unshift @in, $dir_type; # if it exists, add .dir_type
156             }
157             else { # else do globs for each arg
158 2         9 while (defined(my $n = shift @_)) { # next number
159 2 50 33     19 if (($n =~ s/^\.0/0/ || $n eq ".") && -e $dir_type) {
      33        
160             # if requested and it exists, add .dir_type
161 0         0 push @in, $dir_type;
162             next # next if only .dir_type
163 0 0       0 if $n eq ".";
164             }
165 2         131 push @in, bsd_glob("$dir/$n=*");
166             #push @in, bsd_glob(catfile($dir, $n . '=*'));
167             }
168             }
169             # Now create the output array.
170 5         8 my ($number, $fname, $fvalue, $status, $regex);
171 5         17 while (defined($fname = shift(@in))) {
172              
173 8         25 $status = filval("<$fname", $fvalue);
174              
175             #($number) = ($fname =~ m{^$dir(\d*)=});
176             #$regex = catfile($dir, '(\d*)=');
177              
178             # # ask for a dummy file 'x' in order to get a dir separator
179             # #
180             # $regex = catfile($dir, 'x'); # separator might be \ or /
181             # temporary crap to flush out bug in Windows version
182             #$regex =~ s,/,\\,g;
183             #$fname =~ s,/,\\,g;
184             # $regex =~ s/\\/\\\\/g; # preserve any \ separators for regex
185             #
186             # # replace dummy file with pattern we want, leaving separator
187             # #
188             # $regex =~ s/x$/(\\d*)=/; # replace with literal pattern
189             #print "xxx regex=$regex\n";
190             # ($number) = ($fname =~ m{^$regex});
191             #print "xxx number=$number, fname=$fname\n";
192 8         70 ($number) = ($fname =~ m{^$dir/(\d*)=});
193              
194             # if there's no number matched, it may be for .dir_type,
195             # in which case use "." for number, else give up with ""
196             #
197             # $regex = catfile($dir, $dtname);
198             #$regex =~ s,/,\\,g;
199             # $regex =~ s/\\/\\\\/g; # preserve any \ separators for regex
200             #print "xxx dtname=$dtname, regex=$regex\n";
201             #$number = ($fname =~ m{^$dir$dtname} ? "." : "")
202             # yyy matching on $dtname is imperfect if it contains
203             # a '.' -- eg, ".dir_type" matches "adir_type"
204             # contains a
205             #$number = ($fname =~ m{^$regex} ? "." : "")
206              
207             # \Q prevents chars in $dtname (eg, '.') being used in regex
208 8 0       21 $number = ($fname =~ m{^$dir/\Q$dtname\E} ? "." : "")
    50          
209             if (! defined($number));
210              
211 8 50       48 push @out, $number, $fname, ($status ? $status : $fvalue);
212             }
213 5         35 return @out;
214             }
215              
216             # xxx unicode friendly??
217             our $max_default = 16; # is there some sense to this? xxx use
218             # fraction of display width maybe?
219              
220 13     13 1 4760 sub nam_elide { my( $s, $max, $ellipsis )=@_;
221              
222 13 50       33 $s or return undef;
223             # $max can be zero (0) so that nam_add() can ask for no elision.
224 13 100       32 defined($max) or $max = $max_default;
225 13 50       80 $max !~ /^(\d+)([esmESM]*)([+-]\d+%?)?$/ and
226             return undef;
227 13         41 my ($maxlen, $where, $tweak) = ($1, $2, $3);
228              
229 13   100     45 $where ||= "e";
230 13         24 $where = lc($where);
231              
232 13 100 66     54 $ellipsis ||= ($where eq "m" ? "..." : "..");
233 13         19 my $elen = length($ellipsis);
234              
235 13         16 my ($side, $offset, $percent); # xxx only used for "m"?
236 13 100       33 if (defined($tweak)) {
237 1         7 ($side, $offset, $percent) = ($tweak =~ /^([+-])(\d+)(%?)$/);
238             }
239 13   100     44 $side ||= ""; $offset ||= 0; $percent ||= "";
  13   100     44  
  13   100     42  
240             # XXXXX finish this! print "side=$side, n=$offset, p=$percent\n";
241              
242 13         23 my $slen = length($s);
243 13 100 66     97 return $s
244             if ($slen <= $maxlen || $maxlen == 0); # doesn't need elision
245              
246 10         14 my $re; # we will create a regex to edit the string
247             # length of orig string after that will be left after edit
248 10         12 my $left = $maxlen - $elen;
249              
250 10         15 my $retval = $s;
251             # Example: if $left is 5, then
252             # if "e" then s/^(.....).*$/$1$ellipsis/
253             # if "s" then s/^.*(.....)$/$ellipsis$1/
254             # if "m" then s/^.*(...).*(..)$/$1$ellipsis$2/
255             # In order to make '.' match \n, we use s///s ('s' modifier).
256 10 100       21 if ($where eq "m") {
257             # if middle, we split the string
258 4         10 my $half = int($left / 2);
259 4 50       11 $half += 1 # bias larger half to front if $left is odd
260             if ($half > $left - $half); # xxx test
261 4         16 $re = "^(" . ("." x $half) . ").*("
262             . ("." x ($left - $half)) . ")\$";
263             # $left - $half might be zero, but this still works
264 4         84 $retval =~ s/$re/$1$ellipsis$2/s;
265             }
266             else {
267 6         14 my $dots = "." x $left;
268 6 100       21 $re = ($where eq "e" ? "^($dots).*\$" : "^.*($dots)\$");
269 6 100       12 if ($where eq "e") {
270 4         76 $retval =~ s/$re/$1$ellipsis/s;
271             }
272             else { # else "s"
273 2         39 $retval =~ s/$re/$ellipsis$1/s;
274             }
275             }
276 10         58 return $retval;
277             }
278              
279             1;
280              
281             __END__