Annotation of loncom/debugging_tools/check_authoring_spaces.pl, revision 1.3
1.1 raeburn 1: #!/usr/bin/perl
2: #
3: # The LearningOnline Network
4: #
5: # Compare last modification dates for files in Authoring Space with last
6: # modification dates for corresponding files in Resource Space.
7: # If file in Authoring Space is older than file in Resource Space, and
8: # file is not a binary file, check if files are the same.
9: # If files are not the same include in list for potentially overwriting
10: # file in Authoring space with file in Resource space.
11: #
1.3 ! raeburn 12: # $Id: check_authoring_spaces.pl,v 1.2 2017/10/10 15:07:45 raeburn Exp $
1.1 raeburn 13: #
14: # Copyright Michigan State University Board of Trustees
15: #
16: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
17: #
18: # LON-CAPA is free software; you can redistribute it and/or modify
19: # it under the terms of the GNU General Public License as published by
20: # the Free Software Foundation; either version 2 of the License, or
21: # (at your option) any later version.
22: #
23: # LON-CAPA is distributed in the hope that it will be useful,
24: # but WITHOUT ANY WARRANTY; without even the implied warranty of
25: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26: # GNU General Public License for more details.
27: #
28: # You should have received a copy of the GNU General Public License
29: # along with LON-CAPA; if not, write to the Free Software
30: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31: #
32: # /home/httpd/html/adm/gpl.txt
33: #
34: # http://www.lon-capa.org/
35: #
36: #################################################
37:
38: use strict;
39: use lib '/home/httpd/lib/perl/';
40: use LONCAPA::Configuration;
41: use LONCAPA qw(:DEFAULT :match);
42: use Apache::lonlocal;
43: use File::Compare;
44: use File::Copy;
45:
46: my ($lonusersdir,$londocroot,$londaemons);
47:
48: BEGIN {
49: my $perlvar=&LONCAPA::Configuration::read_conf();
50: if (ref($perlvar) eq 'HASH') {
51: $lonusersdir = $perlvar->{'lonUsersDir'};
52: $londocroot = $perlvar->{'lonDocRoot'};
53: $londaemons = $perlvar->{'lonDaemons'};
54: }
55: undef($perlvar);
56: }
57:
58: my $lang = &Apache::lonlocal::choose_language();
59: &Apache::lonlocal::get_language_handle(undef,$lang);
60:
61: if ($< != 0) {
62: print &mt('You must be root in order to check Authoring Spaces.')."\n".
63: &mt('Stopping')."\n";
64: exit;
65: }
66:
67: if ($lonusersdir eq '') {
68: print &mt('Could not determine location of [_1] directory.',"'lonUsersDir'")."\n".
69: &mt('Stopping')."\n";
70: exit;
71: }
72:
73: if ($londocroot eq '') {
74: print &mt('Could not determine location of [_1] directory.',"'lonDocRoot'")."\n".
75: &mt('Stopping')."\n";
76: exit;
77: }
78:
79: if ($londaemons eq '') {
80: print &mt('Could not determine location of [_1] directory.',"'lonDaemons'")."\n".
81: &mt('Stopping')."\n";
82: exit;
83: }
84:
85: # Abort if more than one argument.
86:
87: my $parameter=$ARGV[0];
88: $parameter =~ s/^\s+//;
89: $parameter =~ s/\s+$//;
90:
91: my (undef,undef,$uid,$gid) = getpwnam('www');
92:
93: if ((@ARGV > 1) || (($parameter ne '') && ($parameter !~ /^(copy|undo)$/))) {
94: print &mt('usage: [_1]','check_authoring_spaces.pl [copy|undo]')."\n\n".
95: &mt('You should enter either no arguments, or just one argument -- either copy or undo.')."\n".
96: &mt("copy - to copy files from Resources Space [_1] to Authoring Space [_2]",
97: "'$londocroot/res/'","'$londocroot/priv/'")."\n".
98: &mt('undo - to reverse those changes and restore overwritten files in Authoring Space back from: [_1] to [_2].',
99: "'/home/httpd/overwritten","'$londocroot/priv'")."\n".
100: &mt('no argument to do a dry run of the copy option, without actually copying anything.')."\n";
101: exit;
102: }
103:
104: print "\n".&mt("Comparing last modification date for files in published authors' Authoring Spaces with files in Resource Space.")."\n".
105: "--------------------------------------------------------------------------------------------------------------\n\n".
106: &mt('If run without an argument, the script will report what it would do when copying Resource Space files to Authoring Space, i.e., from [_1] to [_2], for which: (a) the last modification time for the file in /priv predates the last modification time for the corresponding file in /res, and (b) the contents of the files differ, and (c) the file is not a binary file.',
107: "'$londocroot/res'","'$londocroot/priv/'")."\n\n";
108:
109: my (undef,undef,$uid,$gid) = getpwnam('www');
110: my ($action) = ($parameter=~/^(copy|undo)$/);
111: if ($action eq '') {
112: $action = 'dryrun';
113: }
114:
115: if ($action eq 'dryrun') {
116: print "\n\n".
117: &mt('Running in exploratory mode ...')."\n\n".
118: &mt('Run with argument [_1] to actually copy files from Resource Space ([_2]) to Authoring Space ([_3]), i.e., [_4]',
119: "'copy'","'$londocroot/res'","'$londocroot/priv'","\n\nperl check_authoring_spaces.pl copy")."\n\n\n".
120: &mt('Run with argument [_1] to restore previously overwritten Authoring Spaces back to [_2], i.e., [_3]',
121: "'undo'","'$londocroot/priv'","\n\nperl check_authoring_spaces.pl undo")."\n\n\n".
122: &mt('Continue? ~[y/N~] ');
123: if (!&get_user_selection()) {
124: exit;
125: } else {
126: print "\n";
127: }
128: } else {
129: print "\n *** ".&mt('Running in a mode where changes will be made.')." ***\n";
130: if ($action eq 'copy') {
131: print "\n".
132: &mt('Mode is [_1] -- files will be copied to [_2].',
133: "'$action'","'$londocroot/priv'")."\n";
134: } else {
135: print "\n".
136: &mt('Mode is [_1] -- files will be copied back to [_2].',
137: "'$action'","'$londocroot/priv'")."\n";
138: }
139: print &mt('Continue? ~[y/N~] ');
140: if (!&get_user_selection()) {
141: exit;
142: } else {
143: print "\n";
144: }
145: }
146:
147: my $logfh;
148: if ($action ne 'dryrun') {
149: if (!open($logfh,">>$londaemons/logs/check_authoring_spaces.log")) {
150: print &mt('Could not open log file: [_1] for writing.',
151: "'$londaemons/logs/check_authoring_spaces.log'")."\n".
152: &mt('Stopping.')."\n";
153: exit;
154: } else {
155: &start_logging($logfh,$action);
156: }
157: }
158:
159: # Authors hosted on this server
160: my %allauthors;
161: my %pubusers;
162: my @allskipped;
163:
164: my @machinedoms;
165: my ($dir,$output);
166:
167: if ($lonusersdir) {
168: if (opendir($dir,$lonusersdir)) {
169: my @contents = (grep(!/^\.{1,2}$/,readdir($dir)));
170: closedir($dir);
171: foreach my $item (@contents) {
172: if (-d "$lonusersdir/$item") {
173: if ($item =~ /^$match_domain$/) {
174: my $domain = $item;
175: unless (grep(/^\Q$domain\E$/,@machinedoms)) {
176: push(@machinedoms,$domain);
177: }
178: }
179: }
180: }
181: } else {
182: $output = &mt('Could not open [_1].',"'$lonusersdir'")."\n";
183: print $output;
184: unless ($action eq 'dryrun') {
185: &stop_logging($logfh,$output);
186: }
187: print &mt('Stopping')."\n";
188: exit;
189: }
190: }
191:
192: if ($action eq 'undo') {
193: my (%allcopied,@allskipped);
194: if (-d "$londaemons/logs/checked_authoring_spaces") {
195: if (opendir($dir,"$londaemons/logs/checked_authoring_spaces")) {
196: my @contents = (grep(!/^\.{1,2}$/,readdir($dir)));
197: closedir($dir);
198: foreach my $dom (@contents) {
199: if ((grep(/^\Q$dom\E/,@machinedoms)) && (-d "$londaemons/logs/checked_authoring_spaces/$dom")) {
200: my $domdir;
201: if (opendir($domdir,"$londaemons/logs/checked_authoring_spaces/$dom")) {
202: my @unames = (grep(!/^\.{1,2}$/,readdir($domdir)));
203: closedir($domdir);
204: foreach my $uname (@unames) {
205: my %oldfiles;
206: my $skipped;
207: &descend_preserved_tree('',$londaemons,$dom,$uname,\%oldfiles);
208: print &mt('User: [_1], in domain: [_2] has [quant,_3,file].',$uname,$dom,scalar(keys(%oldfiles)))."\n".
209: &mt('Continue? ~[y/N~] ');
210: if (!&get_user_selection()) {
211: print &mt('Enter [_1] to skip this user.','1')."\n".
212: &mt('Enter [_1] to stop.','2')."\n".
213: &mt('Your input: ');
214: my $choice=<STDIN>;
215: chomp($choice);
216: $choice =~ s/^\s+//;
217: $choice =~ s/\s+$//;
218: if ($choice == 1) {
219: my $output = &mt('Skipping user: [_1].',"'$uname'")."\n";
220: print $output;
221: print $logfh $output;
222: push(@allskipped,$uname);
223: next;
224: }
225: if ($choice == 2) {
226: print &mt('Stopped.')."\n";
227: my $output = &mt('Stopped at user: [_1].',"'$uname'")."\n";
228: &stop_logging($logfh,$output);
229: exit;
230: } else {
231: print &mt('Invalid response:')." $choice\n";
232: my $output = &mt('Skipping user: [_1].',"'$uname'")."\n";
233: print $output;
234: print $logfh $output;
235: push(@allskipped,$uname);
236: next;
237: }
238: }
239: foreach my $key (sort(keys(%oldfiles))) {
240: my $output;
241: unless ($key eq '') {
242: my $source_path="$londaemons/logs/checked_authoring_spaces/$dom/$uname/$key";
243: my $target_path="$londocroot/priv/$dom/$uname/$key";
244: if (-e $source_path) {
245: if (File::Copy::copy($source_path,$target_path)) {
246: chown($uid,$gid,$target_path);
247: system("touch -r $source_path $target_path");
248: $output .= &mt('Copied [_1] to [_2].',
249: "'$source_path'","'$target_path'")."\n";
250: push(@{$allcopied{$dom}{$uname}},$key);
251: my $logfile;
252: my $logname = $target_path.'.log';
253: if (-e $logname) {
254: if (open($logfile,">>$logname")) {
255: print $logfile
256: "\n\n================= Retrieve ".localtime()." ================\n".
257: "Version: new\nSource: $source_path\nTarget: $target_path\n".
258: "Copied sucessfully.\n\n";
259: close($logfile);
260: } else {
261: $output .= &mt('Could not open logfile [_1] to log retrieval.',$logname)."\n";
262: }
263: } else {
264: $output .= &mt('Logfile [_1] does not exist.',$logname)."\n";
265: }
266: }
267: } else {
268: $output .= &mt('Source file [_1] does not exist.',$source_path)."\n";
269: }
270: }
271: print $logfh $output;
272: }
273: }
274: }
275: }
276: }
277: }
278: } else {
279: print &mt('Directory: [_1] does not exist',"$londaemons/logs/checked_authoring_spaces");
280: }
281: my ($copyinfo,$skipcount);
282: if (keys(%allcopied) == 0) {
283: $copyinfo = &mt('None')."\n";
284: } else {
285: foreach my $dom (sort(keys(%allcopied))) {
286: if (ref($allcopied{$dom}) eq 'HASH') {
287: $copyinfo .= "\n ".&mt('Domain: [_1], number of authors: [_2]',
288: "'$dom'",scalar(keys(%{$allcopied{$dom}})));
289: }
290: }
291: }
292:
293: $skipcount = scalar(@allskipped);
294:
295: print "\n";
296: my $output;
297: if ($skipcount) {
298: $output = &mt('You skipped: [_1].',$skipcount)."\n".
299: join("\n",sort(@allskipped))."\n\n";
300: }
301: $output .= &mt('Copied back ... [_1]',$copyinfo)."\n";
302: print $output;
303: print "\n".&mt('Done.')."\n";
304: print $logfh $output;
305: &stop_logging($logfh);
306: exit;
307: } elsif (($londocroot ne '') && (-d "$londocroot/res")) {
308: if (-d "$londocroot/res") {
309: my ($dir,$domdir);
310: if (opendir($dir,"$londocroot/res")) {
311: my @contents = (grep(!/^\.{1,2}$/,readdir($dir)));
312: closedir($dir);
313: foreach my $dom (@contents) {
314: if ((grep(/^\Q$dom\E/,@machinedoms)) && (-d "$londocroot/res/$dom")) {
315: if (opendir($domdir,"$londocroot/res/$dom")) {
316: my @unames = (grep(!/^\.{1,2}$/,readdir($domdir)));
317: closedir($domdir);
318: foreach my $uname (@unames) {
319: if ($uname =~ /^$match_username$/) {
320: push(@{$pubusers{$uname}},$dom);
321: }
322: }
323: }
324: }
325: }
326: }
327: }
328:
1.2 raeburn 329: my (%allcopied,$numcopied,$numchecked);
1.1 raeburn 330:
331: # Iterate over directories in /home/httpd/html/res
1.2 raeburn 332: $numchecked = 0;
1.1 raeburn 333: foreach my $uname (sort(keys(%pubusers))) {
334: if (ref($pubusers{$uname}) eq 'ARRAY') {
335: foreach my $dom (@{$pubusers{$uname}}) {
336: my %allfiles;
1.2 raeburn 337: &descend_res_tree('',$londocroot,$dom,$uname,\%allfiles,\$numchecked);
1.1 raeburn 338: if (keys(%allfiles)) {
339: print &mt('User: [_1], in domain: [_2] has [quant,_3,file].',$uname,$dom,scalar(keys(%allfiles)))."\n".
340: &mt('Continue? ~[y/N~] ');
341: if (!&get_user_selection()) {
342: print &mt('Enter [_1] to skip this user.','1')."\n".
343: &mt('Enter [_1] to stop.','2')."\n".
344: &mt('Your input: ');
345: my $choice=<STDIN>;
346: chomp($choice);
347: $choice =~ s/^\s+//;
348: $choice =~ s/\s+$//;
349: if ($choice == 1) {
350: my $output = &mt('Skipping user: [_1].',"'$uname:$dom'")."\n";
351: print $output;
352: unless ($action eq 'dryrun') {
353: print $logfh $output;
354: }
355: push(@allskipped,"$uname:$dom");
356: next;
357: }
358: if ($choice == 2) {
359: print &mt('Stopped.')."\n";
360: my $output = &mt('Stopped at user: [_1].',"'$uname'")."\n";
361: &stop_logging($logfh,$output);
362: exit;
363: } else {
364: print &mt('Invalid response:')." $choice\n";
365: my $output = &mt('Skipping user: [_1].',"'$uname:$dom'")."\n";
366: print $output;
367: unless ($action eq 'dryrun') {
368: print $logfh $output;
369: }
370: push(@allskipped,$uname);
371: next;
372: }
373: }
374: foreach my $key (sort(keys(%allfiles))) {
375: if ($key ne '') {
376: my $source_path="$londocroot/res/$dom/$uname/$key";
377: my $target_path="$londocroot/priv/$dom/$uname/$key";
378: if ($action eq 'copy') {
379: my $output;
380: if (!-e "$londaemons/logs/checked_authoring_spaces") {
381: mkdir("$londaemons/logs/checked_authoring_spaces",0755);
382: chown($uid,$gid,"$londaemons/logs/checked_authoring_spaces");
383: }
384: if (!-e "$londaemons/logs/checked_authoring_spaces/$dom") {
385: mkdir("$londaemons/logs/checked_authoring_spaces/$dom",0755);
386: chown($uid,$gid,"$londaemons/logs/checked_authoring_spaces/$dom");
387: }
388: if (!-e "$londaemons/logs/checked_authoring_spaces/$dom/$uname") {
389: mkdir("$londaemons/logs/checked_authoring_spaces/$dom/$uname",0755);
390: chown($uid,$gid,"$londaemons/logs/checked_authoring_spaces/$dom/$uname");
391: }
392: if (-e "$londaemons/logs/checked_authoring_spaces/$dom/$uname") {
393: my $saveold_path = "$londaemons/logs/checked_authoring_spaces/$dom/$uname/$key";
394: if ($key =~ m{/}) {
395: my @subdirs = split(/\//,$key);
396: my $file = pop(@subdirs);
397: my $path = "$londaemons/logs/checked_authoring_spaces/$dom/$uname";
398: while (@subdirs) {
1.3 ! raeburn 399: my $dir = shift(@subdirs);
1.1 raeburn 400: $path .= '/'.$dir;
401: if (!-e $path) {
402: mkdir($path,0755);
403: chown($uid,$gid,$path);
404: }
405: }
406: }
407: if (-e $target_path) {
408: if (File::Copy::copy($target_path,$saveold_path)) {
409: chown($uid,$gid,$saveold_path);
410: system("touch -r $target_path $saveold_path");
411: $output .= &mt('Copied [_1] to [_2].',
412: "'$target_path'","'$saveold_path'")."\n";
413: if (-e $source_path) {
414: if (File::Copy::copy($source_path,$target_path)) {
415: chown($uid,$gid,$target_path);
416: system("touch -r $source_path $target_path");
417: $output .= &mt('Copied [_1] to [_2].',
418: "'$source_path'","'$target_path'")."\n";
419: push(@{$allcopied{$dom}{$uname}},$key);
1.2 raeburn 420: $numcopied ++;
1.1 raeburn 421: my $logfile;
422: my $logname = $target_path.'.log';
423: if (-e $logname) {
424: if (open($logfile,">>$logname")) {
425: print $logfile
426: "\n\n================= Retrieve ".localtime()." ================\n".
427: "Version: new\nSource: $source_path\nTarget: $target_path\n".
428: "Copied sucessfully.\n\n";
429: close($logfile);
430: } else {
431: $output .= &mt('Could not open logfile [_1] to log retrieval.',$logname)."\n";
432: }
433: } else {
434: $output .= &mt('Logfile [_1] does not exist.',$logname)."\n";
435: }
436: } else {
437: $output .= &mt('Failed to copy [_1] to [_2].',
438: "'$source_path'","'$target_path'")."\n";
439: }
440: } else {
441: $output .= &mt('Source file [_1] does not exist.',$source_path),"\n";
442: }
443: } else {
444: $output .= &mt('Failed to copy [_1] to [_2].',
445: "'$target_path'","'$saveold_path'")."\n";
446: }
447: } else {
448: $output .= &mt('Target file [_1] does not exist.',$target_path);
449: }
450: } else {
451: $output .= &mt('Directory needed to preserve pre-dated file from Authoring Space (prior to overwriting) not available.')."\n";
452: }
453: print $output;
454: print $logfh $output;
455: } elsif ($action eq 'dryrun') {
456: push(@{$allcopied{$dom}{$uname}},$key);
1.2 raeburn 457: $numcopied ++;
1.1 raeburn 458: print &mt('Would copy [_1] to [_2].',"'$source_path'","'$target_path'")."\n";
459: }
460: }
461: }
462: }
463: }
464: }
465: }
466:
467: my ($copyinfo,$skipcount);
468: if (keys(%allcopied) == 0) {
469: $copyinfo = &mt('None')."\n";
470: } else {
471: foreach my $dom (sort(keys(%allcopied))) {
472: if (ref($allcopied{$dom}) eq 'HASH') {
1.2 raeburn 473: $copyinfo .= "\n ".&mt('Domain: [_1], number of authors: [_2], number for copying: [_3], total number of files checked: [_4]',
474: "'$dom'",scalar(keys(%{$allcopied{$dom}})),$numcopied,$numchecked);
1.1 raeburn 475: }
476: }
477: }
478:
479: $skipcount = scalar(@allskipped);
480:
481: print "\n";
482: if ($action ne 'dryrun') {
483: my $output = &mt('You skipped: [_1].',$skipcount)."\n".
484: join("\n",sort(@allskipped))."\n\n".
485: &mt('Copied ... [_1]',$copyinfo)."\n";
486: print $output;
487: print $logfh $output;
488: &stop_logging($logfh);
489: } else {
490: if ($skipcount) {
491: print &mt('You would have skipped: [_1].',$skipcount)."\n".
492: join("\n",sort(@allskipped))."\n\n";
493: }
494: print &mt('You would have copied ... [_1]',$copyinfo);
495: }
496: print "\n\n".&mt('Done.')."\n";
497: }
498:
499: sub get_user_selection {
500: my ($defaultrun) = @_;
501: my $do_action = 0;
502: my $choice = <STDIN>;
503: chomp($choice);
504: $choice =~ s/(^\s+|\s+$)//g;
505: my $yes = &mt('y');
506: if ($defaultrun) {
507: if (($choice eq '') || ($choice =~ /^\Q$yes\E/i)) {
508: $do_action = 1;
509: }
510: } else {
511: if ($choice =~ /^\Q$yes\E/i) {
512: $do_action = 1;
513: }
514: }
515: return $do_action;
516: }
517:
518: sub start_logging {
519: my ($fh,$action) = @_;
520: my $start = localtime(time);
521: print $fh "*****************************************************\n".
522: &mt('[_1] - mode is [_2].',
523: 'check_authoring_spaces.pl',"'$action'")."\n".
524: &mt('Started -- time: [_1]',$start)."\n".
525: "*****************************************************\n\n";
526: return;
527: }
528:
529: sub stop_logging {
530: my ($fh) = @_;
531: my $end = localtime(time);
532: print $fh "*****************************************************\n".
533: &mt('Ended -- time: [_1]',$end)."\n".
534: "*****************************************************\n\n\n";
535: close($fh);
536: return;
537: }
538:
539: sub descend_res_tree {
1.2 raeburn 540: my ($dir,$londocroot,$dom,$uname,$allfiles,$numchecked) = @_;
1.1 raeburn 541: my $path = "$londocroot/res/$dom/$uname";
542: if ($dir ne '') {
543: $path .= "/$dir";
544: }
545: if (-d $path) {
546: opendir(DIR,"$path");
547: my @contents = grep(!/^\./,readdir(DIR));
548: closedir(DIR);
549: foreach my $item (@contents) {
550: if (-d "$path/$item") {
551: my $newdir;
552: if ($dir eq '') {
553: $newdir = $item;
554: } else {
555: $newdir = $dir.'/'.$item;
556: }
1.2 raeburn 557: &descend_res_tree($newdir,$londocroot,$dom,$uname,$allfiles,$numchecked);
1.1 raeburn 558: } else {
559: my $newpath;
560: if ($dir eq '') {
561: $newpath = $item;
562: } else {
563: $newpath = "$dir/$item";
564: }
565: if (-f "$londocroot/res/$dom/$uname/$newpath") {
566: next if ($item =~ /\.(tmp|subscription|meta)$/);
567: next if (-B "$londocroot/res/$dom/$uname/$newpath");
568: my $resfile = "$londocroot/res/$dom/$uname/$newpath";
569: my $cstrfile = "$londocroot/priv/$dom/$uname/$newpath";
570: if (-f $cstrfile) {
571: my $lastmodres = (stat($resfile))[9];
572: my $lastmodcstr = (stat($cstrfile))[9];
573: my $delta = $lastmodres - $lastmodcstr;
1.2 raeburn 574: if (ref($numchecked)) {
575: $$numchecked ++;
576: }
1.1 raeburn 577: if ($delta > 0) {
578: if (&File::Compare::compare($resfile,$cstrfile)) {
579: $allfiles->{$newpath} = $delta;
580: }
581: }
582: }
583: }
584: }
585: }
586: }
587: }
588:
589: sub descend_preserved_tree {
590: my ($dir,$londaemons,$dom,$uname,$allfiles) = @_;
591: my $path = "$londaemons/logs/checked_authoring_spaces/$dom/$uname";
592: if ($dir ne '') {
593: $path .= "/$dir";
594: }
595: if (-d $path) {
596: opendir(DIR,"$path");
597: my @contents = grep(!/^\./,readdir(DIR));
598: closedir(DIR);
599: foreach my $item (@contents) {
600: if (-d "$path/$item") {
601: my $newdir;
602: if ($dir eq '') {
603: $newdir = $item;
604: } else {
605: $newdir = $dir.'/'.$item;
606: }
607: &descend_preserved_tree($newdir,$londaemons,$dom,$uname,$allfiles);
608: } elsif (-f "$path/$item") {
609: my $newpath;
610: if ($dir eq '') {
611: $newpath = $item;
612: } else {
613: $newpath = "$dir/$item";
614: }
615: $allfiles->{$newpath} = 1;
616: }
617: }
618: }
619: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>