Commit | Line | Data |
28f2d4d1 |
1 | #!/opt/bin/perl |
2 | use strict; |
3 | use warnings; |
4 | |
5 | use Cwd; |
6 | use Getopt::Std; |
7 | use File::Basename; |
8 | use FindBin; |
9 | |
10 | my $Opts = {}; |
e23621c7 |
11 | getopts( 'r:p:e:vudn', $Opts ); |
28f2d4d1 |
12 | |
e23621c7 |
13 | my $Cwd = cwd(); |
28f2d4d1 |
14 | my $Verbose = 1; |
74182dbd |
15 | my $ExcludeRe = $Opts->{e} ? qr/$Opts->{e}/i : undef; |
28f2d4d1 |
16 | my $Debug = $Opts->{v} || 0; |
17 | my $RunDiff = $Opts->{d} || 0; |
18 | my $PkgDir = $Opts->{p} || cwd(); |
e23621c7 |
19 | my $Repo = $Opts->{r} or die "Need repository!\n". usage(); |
20 | my $NoBranch = $Opts->{n} || 0; |
28f2d4d1 |
21 | |
22 | ### strip trailing slashes; |
e23621c7 |
23 | $Repo =~ s|/$||; |
28f2d4d1 |
24 | |
25 | my $CPV = $Debug ? '-v' : ''; |
26 | my $TestBin = 'ptardiff'; |
27 | my $PkgDirRe = quotemeta( $PkgDir .'/' ); |
e23621c7 |
28 | my $BranchName = basename( $PkgDir ) . '.' . $$; |
29 | my $OrigRepo = $Repo; |
28f2d4d1 |
30 | |
e23621c7 |
31 | ### establish working directory, either branch or full copy |
32 | if ( $NoBranch ) { |
33 | ### create a copy of the repo directory |
34 | my $RepoCopy = "$Repo-$BranchName"; |
35 | print "Copying repository to $RepoCopy ..." if $Verbose; |
36 | |
ab06b353 |
37 | ### --archive == -dPpR, but --archive is not portable, and neither |
38 | ### is -d, so settling for -PpR |
39 | system( "cp -PpR -f $Repo $RepoCopy" ) |
e23621c7 |
40 | and die "Copying master repo to $RepoCopy failed: $?"; |
28f2d4d1 |
41 | |
e23621c7 |
42 | ### Going forward, use the copy in place of the original repo |
43 | $Repo = $RepoCopy; |
28f2d4d1 |
44 | |
e23621c7 |
45 | print "done\n" if $Verbose; |
46 | } |
47 | else { |
48 | ### create a git branch for the new package |
49 | print "Setting up a branch from blead called '$BranchName'..." if $Verbose; |
50 | chdir $Repo or die "Could not chdir to $Repo: $!"; |
51 | unless ( -d '.git' ) { |
52 | die "\n$Repo is not a git repository\n"; |
53 | } |
54 | my $status = `git status`; |
55 | unless ( $status =~ /nothing to commit/ims ) { |
56 | die "\nWorking directory not clean. Stopping.\n"; |
57 | } |
1e3c652e |
58 | system( "git checkout -b $BranchName blead" ) |
e23621c7 |
59 | and die "Could not create branch '$BranchName': $?"; |
28f2d4d1 |
60 | |
61 | print "done\n" if $Verbose; |
62 | } |
63 | |
e23621c7 |
64 | ### chdir there |
65 | chdir $PkgDir or die "Could not chdir to $PkgDir: $!"; |
66 | |
28f2d4d1 |
67 | ### copy over all files under lib/ |
e23621c7 |
68 | my @LibFiles; |
28f2d4d1 |
69 | { print "Copying libdir..." if $Verbose; |
e23621c7 |
70 | die "Can't (yet) copy from a repository (found .git or .svn)" |
71 | if -d '.git' || -d '.svn'; |
28f2d4d1 |
72 | die "No lib/ directory found\n" unless -d 'lib'; |
73 | system( "cp -fR $CPV lib $Repo" ) and die "Copy of lib/ failed: $?"; |
e23621c7 |
74 | |
75 | @LibFiles = map { chomp; $_ } |
76 | ### should we get rid of this file? |
77 | grep { $ExcludeRe && $_ =~ $ExcludeRe |
17633433 |
78 | ? do { warn "Removing $Repo/$_\n"; |
79 | system("rm $Repo/$_") and die "rm '$Repo/$_' failed: $?"; |
e23621c7 |
80 | undef |
81 | } |
82 | : 1 |
58118e0c |
83 | } `find lib -type f` |
e23621c7 |
84 | or die "Could not detect library files\n"; |
85 | |
28f2d4d1 |
86 | print "done\n" if $Verbose; |
87 | } |
88 | |
89 | ### find the directory to put the t/ and bin/ files under |
90 | my $RelTopDir; # topdir from the repo root |
91 | my $TopDir; # full path to the top dir |
92 | my $ModName; # name of the module |
93 | my @ModFiles; # the .PMs in this package |
94 | { print "Creating top level dir..." if $Verbose; |
95 | |
96 | ### make sure we get the shortest file, so we dont accidentally get |
97 | ### a subdir |
98 | @ModFiles = sort { length($a) <=> length($b) } |
99 | map { chomp; $_ } |
100 | grep { $ExcludeRe ? $_ !~ $ExcludeRe : 1 } |
101 | grep /\.p(?:m|od)$/, |
102 | `find $PkgDir/lib -type f` |
103 | or die "No TopDir detected\n"; |
104 | |
105 | $RelTopDir = $ModFiles[0]; |
106 | $RelTopDir =~ s/^$PkgDirRe//; |
107 | $RelTopDir =~ s/\.p(m|od)$//; |
108 | $TopDir = "$Repo/$RelTopDir"; |
109 | |
110 | ### create the dir if it's not there yet |
111 | unless( -d $TopDir ) { |
112 | system( "mkdir $TopDir" ) and die "Creating dir $TopDir failed: $?"; |
113 | } |
114 | |
115 | ### the module name, like Foo::Bar |
116 | ### slice syntax not elegant, but we need to remove the |
117 | ### leading 'lib/' entry |
118 | ### stupid temp vars! stupid perl! it doesn't do @{..}[0..-1] :( |
119 | { my @list = @{[split '/', $RelTopDir]}; |
120 | $ModName = join '::', @list[1 .. $#list]; |
121 | } |
122 | |
123 | ### the .pm files in this package |
124 | @ModFiles = map { s|^$PkgDirRe||; $_ } @ModFiles |
125 | or die "Could not detect modfiles\n"; |
126 | |
127 | print "done\n" if $Verbose; |
128 | } |
129 | |
130 | my $TopDirRe = quotemeta( $TopDir . '/' ); |
131 | |
132 | ### copy over t/ and bin/ directories to the $TopDir |
133 | my @TestFiles; |
134 | { print "Copying t/* files to $TopDir..." if $Verbose; |
135 | |
136 | -d 't' |
137 | ? system( "cp -fR $CPV t $TopDir" ) && die "Copy of t/ failed: $?" |
138 | : warn "No t/ directory found\n"; |
139 | |
15e5e866 |
140 | @TestFiles = map { chomp; s|^$TopDirRe||; s|//|/|g; $_ } |
28f2d4d1 |
141 | ### should we get rid of this file? |
142 | grep { $ExcludeRe && $_ =~ $ExcludeRe |
143 | ? do { warn "Removing $_\n"; |
58118e0c |
144 | system("rm $TopDir/$_") and die "rm '$_' failed: $?"; |
28f2d4d1 |
145 | undef |
146 | } |
147 | : 1 |
58118e0c |
148 | } `find t -type f` |
28f2d4d1 |
149 | or die "Could not detect testfiles\n"; |
150 | |
151 | print "done\n" if $Verbose; |
152 | } |
153 | |
e23621c7 |
154 | my $BinDir; |
28f2d4d1 |
155 | my @BinFiles; |
e23621c7 |
156 | my $TopBinDir; |
28f2d4d1 |
157 | BIN: { |
e23621c7 |
158 | $BinDir = -d 'bin' ? 'bin' : |
159 | -d 'scripts' ? 'scripts' : undef ; |
160 | unless ($BinDir) { |
161 | print "No bin/ or scripts/ directory found\n" if $Verbose; |
28f2d4d1 |
162 | last BIN; |
163 | } |
e23621c7 |
164 | my $TopBinDir = "$TopDir/$BinDir/"; |
165 | print "Copying $BinDir/* files to $TopBinDir..." if $Verbose; |
166 | |
167 | my $CopyCmd = "cp -fR $CPV $BinDir $TopDir"; |
168 | print "Running '$CopyCmd'..." if $Verbose; |
28f2d4d1 |
169 | |
e23621c7 |
170 | system($CopyCmd) && die "Copy of $BinDir failed: $?"; |
28f2d4d1 |
171 | |
15e5e866 |
172 | @BinFiles = map { chomp; s|^$TopDirRe||; s|//|/|g; $_ } |
28f2d4d1 |
173 | ### should we get rid of this file? |
174 | grep { $ExcludeRe && $_ =~ $ExcludeRe |
175 | ? do { warn "Removing $_\n"; |
58118e0c |
176 | system("rm $TopDir/$_") and die "rm '$_' failed: $?"; |
28f2d4d1 |
177 | undef |
178 | } |
179 | : 1 |
58118e0c |
180 | } `find $BinDir -type f` |
28f2d4d1 |
181 | or die "Could not detect binfiles\n"; |
182 | |
183 | print "done\n" if $Verbose; |
184 | } |
185 | |
186 | ### add files where they are required |
187 | my @NewFiles; |
e23621c7 |
188 | my @ChangedFiles; |
28f2d4d1 |
189 | { for my $bin ( map { basename( $_ ) } @BinFiles ) { |
190 | print "Registering $bin with system files...\n"; |
191 | |
192 | ### fix installperl, so these files get installed by other utils |
193 | ### ./installperl: return if $name =~ |
194 | ### /^(?:cpan|instmodsh|prove|corelist|ptar|ptardiff|config_data)\z/; |
195 | { my $file = 'installperl'; |
196 | |
197 | ### not there already? |
198 | unless( `grep $TestBin $Repo/$file| grep $bin` ) { |
199 | print " Adding $bin to $file..." if $Verbose; |
200 | |
201 | ### double \\| required --> once for in this script, once |
202 | ### for the cli |
203 | system("$^X -pi -e 's/($TestBin\\|)/$bin|\$1/' $Repo/$file") |
204 | and die "Could not add $bin to $file: $?"; |
205 | print "done\n" if $Verbose; |
e23621c7 |
206 | push @ChangedFiles, $file; |
28f2d4d1 |
207 | } else { |
208 | print " $bin already mentioned in $file\n" if $Verbose; |
209 | } |
210 | } |
211 | |
212 | ### fix utils.lst, so the new tools are mentioned |
213 | { my $file = 'utils.lst'; |
214 | |
215 | ### not there already? |
216 | unless( `grep $bin $Repo/$file` ) { |
217 | print " Adding $bin to $file..." if $Verbose; |
218 | |
219 | ### double \\| required --> once for in this script, once |
220 | ### for the cli |
221 | system("$^X -pi -e 's!($TestBin)!\$1\nutils/$bin!' $Repo/$file") |
222 | and die "Could not add $bin to $file: $?"; |
223 | print "done\n" if $Verbose; |
e23621c7 |
224 | push @ChangedFiles, $file; |
28f2d4d1 |
225 | } else { |
226 | print " $bin already mentioned in $file\n" if $Verbose; |
227 | } |
228 | } |
229 | |
230 | ### make a $bin.PL file and fix it up |
231 | { my $src = "utils/${TestBin}.PL"; |
232 | my $file = "utils/${bin}.PL"; |
233 | |
234 | ### not there already? |
235 | unless( -e "$Repo/$file" ) { |
236 | print " Creating $file..." if $Verbose; |
237 | |
238 | ### important part of the template looks like this |
239 | ### (we'll need to change it): |
240 | # my $script = File::Spec->catfile( |
241 | # File::Spec->catdir( |
242 | # File::Spec->updir, qw[lib Archive Tar bin] |
243 | # ), "module-load.pl"); |
244 | |
245 | ### copy another template file |
246 | system( "cp -f $Repo/$src $Repo/$file" ) |
247 | and die "Could not create $file from $src: $?"; |
248 | |
249 | ### change the 'updir' path |
250 | ### make sure to escape the \[ character classes |
e23621c7 |
251 | my $updir = join ' ', (split('/', $RelTopDir), $BinDir); |
28f2d4d1 |
252 | system( "$^X -pi -e'". |
253 | 's/^(.*?File::Spec->updir, qw\[).+?(\].*)$/'. |
254 | "\$1 $updir \$2/' $Repo/$file" |
255 | ) and die "Could not fix updir for $bin in $file: $?"; |
256 | |
257 | |
258 | ### change the name of the file from $TestBin to $bin |
259 | system( "$^X -pi -e's/$TestBin/$bin/' $Repo/$file" ) |
260 | and die "Could not update $file with '$bin' as name: $?"; |
261 | |
262 | print "done\n" if $Verbose; |
263 | |
264 | } else { |
265 | print " $file already exists\n" if $Verbose; |
266 | } |
267 | |
268 | ### we've may just have created a new file, it will have to |
269 | ### go into the manifest |
270 | push @NewFiles, $file; |
271 | } |
272 | |
e23621c7 |
273 | ### add an entry to utils/Makefile.SH for $bin |
274 | { my $file = "utils/Makefile.SH"; |
28f2d4d1 |
275 | |
276 | ### not there already? |
277 | unless( `grep $bin $Repo/$file` ) { |
278 | print " Adding $bin entries to $file..." if $Verbose; |
279 | |
280 | ### $bin appears on 4 lines in this file, so replace all 4 |
281 | ### first, pl = |
282 | system( "$^X -pi -e'/^pl\\s+=/ && s/(${TestBin}.PL)/". |
283 | "\$1 ${bin}.PL/' $Repo/$file" |
284 | ) and die "Could not add $bin to the pl = entry: $?"; |
285 | |
286 | ### next, plextract = |
287 | system( "$^X -pi -e'/^plextract\\s+=/ " . |
288 | "&& s/(${TestBin})/\$1 $bin/' $Repo/$file" |
289 | ) and die "Could not add $bin to the plextract = entry: $?"; |
290 | |
291 | ### third, plextractexe = |
292 | system( "$^X -pi -e'/^plextractexe\\s+=/ " . |
293 | "&& s!(\./${TestBin})!\$1 ./$bin!' $Repo/$file" |
294 | ) and die "Could not add $bin to the plextractexe = entry: $?"; |
295 | |
296 | ### last, the make directive $bin: |
297 | system( "$^X -pi -e'/^(${TestBin}:.+)/; \$x=\$1 or next;" . |
298 | "\$x =~ s/$TestBin/$bin/g;" . '$_.=$/.$x.$/;' . |
299 | "' $Repo/$file" |
300 | ) and die "Could not add $bin as a make directive: $?"; |
301 | |
e23621c7 |
302 | push @ChangedFiles, $file; |
28f2d4d1 |
303 | print "done\n" if $Verbose; |
304 | } else { |
305 | print " $bin already added to $file\n" if $Verbose; |
306 | } |
307 | } |
308 | |
309 | ### add entries to win32/Makefile and win32/makefile.mk |
310 | ### they contain the following lines: |
311 | # ./win32/makefile.mk: ..\utils\ptardiff \ |
312 | # ./win32/makefile.mk: xsubpp instmodsh prove ptar ptardiff |
313 | for my $file ( qw[win32/Makefile win32/makefile.mk] ) { |
314 | unless ( `grep $bin $Repo/$file` ) { |
315 | print " Adding $bin entries to $file..." if $Verbose; |
316 | |
317 | system( "$^X -pi -e'/^(.+?utils.${TestBin}.+)/;". |
318 | '$x=$1 or next;' . |
319 | "\$x =~ s/$TestBin/$bin/g;" . '$_.=$x.$/;' . |
320 | "' $Repo/$file" |
321 | ) and die "Could not add $bin to UTILS section in $file: $?\n"; |
322 | |
323 | system( "$^X -pi -e's/( $TestBin)/\$1 $bin/' $Repo/$file" ) |
324 | and die "Could not add $bin to $file: $?\n"; |
325 | |
e23621c7 |
326 | push @ChangedFiles, $file; |
28f2d4d1 |
327 | print "done\n" if $Verbose; |
328 | } else { |
329 | print " $bin already added to $file\n" if $Verbose; |
330 | } |
331 | } |
332 | |
333 | ### we need some entries in a vms specific file as well.. |
334 | ### except, i dont understand how it works or what it does, and it |
335 | ### looks all a bit odd... so lets just print a warning... |
336 | ### the entries look something like this: |
337 | # ./vms/descrip_mms.template:utils4 = [.utils]enc2xs.com |
338 | # [.utils]piconv.com [.utils]cpan.com [.utils]prove.com |
339 | # [.utils]ptar.com [.utils]ptardiff.com [.utils]shasum.com |
340 | # ./vms/descrip_mms.template:[.utils]ptardiff.com : [.utils]ptardiff.PL |
341 | # $(ARCHDIR)Config.pm |
342 | { my $file = 'vms/descrip_mms.template'; |
343 | |
344 | unless( `grep $bin $Repo/$file` ) { |
345 | print $/.$/; |
346 | print " WARNING! You should add entries like the following\n" |
347 | . " to $file (Using $TestBin as an example)\n" |
348 | . " Unfortunately I dont understand what these entries\n" |
349 | . " do, so I wont change them automatically:\n\n"; |
350 | |
351 | print `grep -nC1 $TestBin $Repo/$file`; |
352 | print $/.$/; |
353 | |
354 | } else { |
355 | print " $bin already added to $file\n" if $Verbose; |
356 | } |
357 | } |
358 | } |
359 | } |
360 | |
361 | ### binary files must be encoded! |
362 | ### XXX use the new 'uupacktool.pl' |
363 | { my $pack = "$Repo/uupacktool.pl"; |
364 | |
365 | ### pack.pl encodes binary files for us |
366 | -e $pack or die "Need $pack to encode binary files!"; |
367 | |
368 | ### chdir, so uupacktool writes relative files properly |
369 | ### into it's header... |
370 | my $curdir = cwd(); |
371 | chdir($Repo) or die "Could not chdir to '$Repo': $!"; |
372 | |
373 | for my $aref ( \@ModFiles, \@TestFiles, \@BinFiles ) { |
374 | for my $file ( @$aref ) { |
375 | my $full = -e $file ? $file : |
376 | -e "$RelTopDir/$file" ? "$RelTopDir/$file" : |
377 | die "Can not find $file in $Repo or $TopDir\n"; |
378 | |
379 | if( -f $full && -s _ && -B _ ) { |
380 | print "Binary file $file needs encoding\n" if $Verbose; |
381 | |
382 | my $out = $full . '.packed'; |
383 | |
384 | ### does the file exist already? |
385 | ### and doesn't have +w |
386 | if( -e $out && not -w _ ) { |
387 | system("chmod +w $out") |
388 | and die "Could not set chmod +w to '$out': $!"; |
389 | } |
390 | |
391 | ### -D to remove the original |
392 | system("$^X $pack -D -p $full $out") |
393 | and die "Could not encode $full to $out"; |
394 | |
395 | |
396 | $file .= '.packed'; |
397 | } |
398 | } |
399 | } |
400 | |
401 | chdir($curdir) or die "Could not chdir back to '$curdir': $!"; |
402 | } |
403 | |
404 | ### update the manifest |
405 | { my $file = $Repo . '/MANIFEST'; |
406 | my @manifest; |
407 | { open my $fh, "<$file" or die "Could not open $file: $!"; |
408 | @manifest = <$fh>; |
409 | close $fh; |
410 | } |
411 | |
412 | ### fill it with files from our package |
413 | my %pkg_files; |
414 | for ( @ModFiles ) { |
415 | $pkg_files{$_} = "$_\t$ModName\n"; |
416 | } |
417 | |
418 | for ( @TestFiles ) { |
419 | $pkg_files{"$RelTopDir/$_"} = "$RelTopDir/$_\t$ModName tests\n" |
420 | } |
421 | |
422 | for ( @BinFiles ) { |
423 | $pkg_files{"$RelTopDir/$_"} = "$RelTopDir/$_\tthe ". |
424 | basename($_) ." utility\n"; |
425 | } |
426 | |
427 | for ( @NewFiles ) { |
428 | $pkg_files{$_} = "$_\tthe ". |
429 | do { m/(.+?)\.PL$/; basename($1) } . |
430 | " utility\n" |
431 | } |
432 | |
433 | ### remove all the files that are already in the manifest; |
434 | delete $pkg_files{ [split]->[0] } for @manifest; |
435 | |
436 | print "Adding the following entries to the MANIFEST:\n" if $Verbose; |
437 | print "\t$_" for sort values %pkg_files; |
438 | print $/.$/; |
439 | |
440 | push @manifest, values %pkg_files; |
441 | |
74182dbd |
442 | { chmod 0644, $file; |
28f2d4d1 |
443 | open my $fh, ">$file" or die "Could not open $file for writing: $!"; |
444 | #print $fh sort { lc $a cmp lc $b } @manifest; |
445 | ### XXX stolen from pod/buildtoc:sub do_manifest |
446 | print $fh |
447 | map { $_->[0] } |
448 | sort { $a->[1] cmp $b->[1] || $a->[0] cmp $b->[0] } |
449 | map { my $f = lc $_; $f =~ s/[^a-z0-9\s]//g; [ $_, $f ] } |
450 | @manifest; |
451 | |
452 | close $fh; |
453 | } |
e23621c7 |
454 | push @ChangedFiles, 'MANIFEST'; |
28f2d4d1 |
455 | } |
456 | |
e23621c7 |
457 | |
28f2d4d1 |
458 | ### would you like us to show you a diff? |
459 | if( $RunDiff ) { |
e23621c7 |
460 | if ( $NoBranch ) { |
28f2d4d1 |
461 | |
e23621c7 |
462 | my $diff = $Repo; $diff =~ s/$$/patch/; |
28f2d4d1 |
463 | |
e23621c7 |
464 | ### weird RV ;( |
465 | my $master = basename( $OrigRepo ); |
466 | my $repo = basename( $Repo ); |
467 | my $chdir = dirname( $OrigRepo ); |
1df59df4 |
468 | |
e23621c7 |
469 | ### the .patch file is added by an rsync from the APC |
470 | ### but isn't actually in the p4 repo, so exclude it |
471 | my $cmd = "cd $chdir; diff -ruN --exclude=.patch $master $repo > $diff"; |
1df59df4 |
472 | |
e23621c7 |
473 | print "Running: '$cmd'\n"; |
1df59df4 |
474 | |
e23621c7 |
475 | print "Generating diff..." if $Verbose; |
28f2d4d1 |
476 | |
e23621c7 |
477 | system( $cmd ); |
478 | #and die "Could not write diff to '$diff': $?"; |
479 | die "Could not write diff to '$diff'" unless -e $diff && -s _; |
480 | |
481 | print "done\n" if $Verbose; |
482 | print "\nDiff can be applied with patch -p1 in $OrigRepo\n\n"; |
483 | print " Diff written to: $diff\n\n" if $Verbose; |
484 | } |
485 | else { |
486 | my $diff = "$Repo/$BranchName"; $diff =~ s/$$/patch/; |
487 | my $cmd = "cd $Repo; git diff > $diff"; |
488 | |
489 | print "Running: '$cmd'\n"; |
490 | |
491 | print "Generating diff..." if $Verbose; |
492 | |
493 | system( $cmd ); |
494 | #and die "Could not write diff to '$diff': $?"; |
495 | die "Could not write diff to '$diff'" unless -e $diff && -s _; |
496 | |
497 | print "done\n" if $Verbose; |
498 | print " Diff written to: $diff\n\n" if $Verbose; |
499 | } |
28f2d4d1 |
500 | } |
501 | |
e23621c7 |
502 | |
503 | # add files to git index |
504 | unless ( $NoBranch ) { |
505 | chdir $Repo; |
506 | system( "git add $CPV $_" ) |
507 | for ( @LibFiles, @NewFiles, @ChangedFiles, |
508 | map { "$RelTopDir/$_" } @TestFiles, @BinFiles ); |
509 | } |
510 | |
511 | # return to original directory |
512 | chdir $Cwd; |
513 | |
28f2d4d1 |
514 | sub usage { |
515 | my $me = basename($0); |
516 | return qq[ |
517 | |
518 | Usage: $me -r PERL_REPO_DIR [-p PACKAGE_DIR] [-v] [-d] [-e REGEX] |
519 | |
520 | Options: |
e23621c7 |
521 | -r Path to perl-core git repository |
28f2d4d1 |
522 | -v Run verbosely |
523 | -e Perl regex matching files that shouldn't be included |
524 | -d Create a diff as patch file |
525 | -p Path to the package to add. Defaults to cwd() |
e23621c7 |
526 | -n No branching; repository is not a git repo |
28f2d4d1 |
527 | |
528 | \n]; |
529 | |
530 | } |