5 # Tom Christiansen, <tchrist@convex.com>
7 # As pstruct, dump C structures as generated from 'cc -g -S' stabs.
8 # As c2ph, do this PLUS generate perl code for getting at the structures.
10 # See the usage message for more. If this isn't enough, read the code.
13 $RCSID = '$RCSfile: pstruct,v $$Revision: 4.0.1.1 $$Date: 92/06/08 15:19:40 $';
16 ######################################################################
18 # some handy data definitions. many of these can be reset later.
20 $bitorder = 'b'; # ascending; set to B for descending bit fields
28 'unsigned short', 'S',
29 'unsigned short int', 'S',
30 'short unsigned int', 'S',
37 'long unsigned int', 'L',
38 'unsigned long int', 'L',
41 'unsigned long long', 'Q',
42 'unsigned long long int', 'Q',
52 delete $intrinsics{'neganull'};
53 delete $intrinsics{'bit'};
54 delete $intrinsics{'null'};
56 # use -s to recompute sizes
62 'unsigned short', '2',
63 'unsigned short int', '2',
64 'short unsigned int', '2',
70 'unsigned long int', '4',
71 'long unsigned int', '4',
74 'unsigned long long', '8',
75 'unsigned long long int', '8',
81 ($type_width, $member_width, $offset_width, $size_width) = (20, 20, 6, 5);
83 ($offset_fmt, $size_fmt) = ('d', 'd');
91 $perl++ if $0 =~ m#/?c2ph$#;
95 eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_]+=)(.*)/ && shift;
97 &Getopts('aixdpvtnws:') || &usage(0);
102 $opt_v && $verbose++;
103 $opt_n && ($perl = 0);
106 ($type_width, $member_width, $offset_width) = (45, 35, 8);
109 ($offset_fmt, $offset_width, $size_fmt, $size_width) = ( 'x', '08', 'x', 04 );
112 eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_]+=)(.*)/ && shift;
116 print "oops, apperent pager foulup\n";
128 print "hit <RETURN> for further explanation: ";
130 open (PIPE, "|". ($ENV{PAGER} || 'more'));
131 $SIG{PIPE} = PLUMBER;
135 print "usage: $0 [-dpnP] [var=val] [files ...]\n";
143 -w wide; short for: type_width=45 member_width=35 offset_width=8
144 -x hex; short for: offset_fmt=x offset_width=08 size_fmt=x size_width=04
146 -n do not generate perl code (default when invoked as pstruct)
147 -p generate perl code (default when invoked as c2ph)
148 -v generate perl code, with C decls as comments
150 -i do NOT recompute sizes for intrinsic datatypes
151 -a dump information on intrinsics also
154 -d spew reams of debugging output
156 -slist give comma-separated list a structures to dump
159 Var Name Default Value Meaning
163 &defvar('CC', 'which_compiler to call');
164 &defvar('CFLAGS', 'how to generate *.s files with stabs');
165 &defvar('DEFINES','any extra cflags or cpp defines, like -I, -D, -U');
169 &defvar('type_width', 'width of type field (column 1)');
170 &defvar('member_width', 'width of member field (column 2)');
171 &defvar('offset_width', 'width of offset field (column 3)');
172 &defvar('size_width', 'width of size field (column 4)');
176 &defvar('offset_fmt', 'sprintf format type for offset');
177 &defvar('size_fmt', 'sprintf format type for size');
181 &defvar('indent', 'how far to indent each nesting level');
185 If any *.[ch] files are given, these will be catted together into
186 a temporary *.c file and sent through:
188 and the resulting *.s groped for stab information. If no files are
189 supplied, then stdin is read directly with the assumption that it
190 contains stab information. All other liens will be ignored. At
191 most one *.s file should be supplied.
199 local($var, $msg) = @_;
200 printf "%-16s%-15s %s\n", $var, eval "\$$var", $msg;
206 if (grep(!/\.[csh]$/,@ARGV)) {
207 warn "Only *.[csh] files expected!\n";
210 elsif (grep(/\.s$/,@ARGV)) {
212 warn "Only one *.s file allowed!\n";
216 elsif (@ARGV == 1 && $ARGV[0] =~ /\.c$/) {
217 local($dir, $file) = $ARGV[0] =~ m#(.*/)?(.*)$#;
218 $chdir = "cd $dir; " if $dir;
219 &system("$chdir$CC $CFLAGS $DEFINES $file") && exit 1;
220 $ARGV[0] =~ s/\.c$/.s/;
223 $TMP = "/tmp/c2ph.$$.c";
224 &system("cat @ARGV > $TMP") && exit 1;
225 &system("cd /tmp; $CC $CFLAGS $DEFINES $TMP") && exit 1;
233 for (split(/[\s,]+/, $opt_s)) {
245 print STDERR "reading from your keyboard: ";
247 print STDERR "reading from " . (@ARGV ? "@ARGV" : "<STDIN>").": ";
252 if ($trace && !($. % 10)) {
254 print STDERR $lineno, "\b" x length($lineno);
256 next unless /^\s*\.stabs\s+/;
261 print STDERR "$.\n" if $trace;
264 &compute_intrinsics if $perl && !$opt_i;
266 print STDERR "resolving types\n" if $trace;
271 $sum = 2 + $type_width + $member_width;
272 $pmask1 = "%-${type_width}s %-${member_width}s";
273 $pmask2 = "%-${sum}s %${offset_width}${offset_fmt}%s %${size_width}${size_fmt}%s";
276 # resolve template -- should be in stab define order, but even this isn't enough.
277 print STDERR "\nbuilding type templates: " if $trace;
278 for $i (reverse 0..$#type) {
279 next unless defined($name = $type[$i]);
280 next unless defined $struct{$name};
282 &build_template($name) unless defined $template{&psou($name)} ||
283 $opt_s && !$interested{$name};
285 print STDERR "\n\n" if $trace;
288 print STDERR "dumping structs: " if $trace;
291 foreach $name (sort keys %struct) {
292 next if $opt_s && !$interested{$name};
293 print STDERR "$name " if $trace;
301 $mname = &munge($name);
303 $fname = &psou($name);
305 print "# " if $perl && $verbose;
307 print "$fname {\n" if !$perl || $verbose;
308 $template{$fname} = &scrunch($template{$fname}) if $perl;
309 &pstruct($name,$name,0);
310 print "# " if $perl && $verbose;
311 print "}\n" if !$perl || $verbose;
312 print "\n" if $perl && $verbose;
317 printf("\nsub %-32s { %4d; }\n\n", "${mname}'struct", $countof{$name});
320 sub ${mname}'typedef {
321 local(\$${mname}'index) = shift;
322 defined \$${mname}'index
323 ? \$${mname}'typedef[\$${mname}'index]
324 : \$${mname}'typedef;
329 sub ${mname}'sizeof {
330 local(\$${mname}'index) = shift;
331 defined \$${mname}'index
332 ? \$${mname}'sizeof[\$${mname}'index]
338 sub ${mname}'offsetof {
339 local(\$${mname}'index) = shift;
340 defined \$${mname}index
341 ? \$${mname}'offsetof[\$${mname}'index]
347 sub ${mname}'typeof {
348 local(\$${mname}'index) = shift;
349 defined \$${mname}index
350 ? \$${mname}'typeof[\$${mname}'index]
356 print "\$${mname}'typedef = '" . &scrunch($template{$fname})
359 print "\$${mname}'sizeof = $sizeof{$name};\n\n";
362 print "\@${mname}'indices = (", &squishseq(@indices), ");\n";
366 print "\@${mname}'typedef[\@${mname}'indices] = (",
367 join("\n\t", '', @typedef), "\n );\n\n";
368 print "\@${mname}'sizeof[\@${mname}'indices] = (",
369 join("\n\t", '', @sizeof), "\n );\n\n";
370 print "\@${mname}'offsetof[\@${mname}'indices] = (",
371 join("\n\t", '', @offsetof), "\n );\n\n";
372 print "\@${mname}'typeof[\@${mname}'indices] = (",
373 join("\n\t", '', @typeof), "\n );\n\n";
375 $template_printed{$fname}++;
376 $size_printed{$fname}++;
381 print STDERR "\n" if $trace;
383 unless ($perl && $opt_a) {
390 foreach $name (sort bysizevalue keys %intrinsics) {
391 next if $size_printed{$name};
392 print '$',&munge($name),"'sizeof = ", $sizeof{$name}, ";\n";
397 sub bysizevalue { $sizeof{$a} <=> $sizeof{$b}; }
400 foreach $name (sort keys %intrinsics) {
401 print '$',&munge($name),"'typedef = '", $template{$name}, "';\n";
409 ########################################################################################
413 next unless /:[\$\w]+(\(\d+,\d+\))?=[\*\$\w]+/; # (\d+,\d+) is for sun
415 s/",([x\d]+),([x\d]+),([x\d]+),.*// || next;
424 if (($name, $pdecl) = /^([\$ \w]+):[tT]((\d+)(=[rufs*](\d+))+)$/) {
425 print "$name is a typedef for some funky pointers: $pdecl\n" if $debug;
432 if (/(([ \w]+):t(\d+|\(\d+,\d+\)))=r?(\d+|\(\d+,\d+\))(;\d+;\d+;)?/) {
434 push(@intrinsics, $ident);
435 $typeno = &typeno($3);
436 $type[$typeno] = $ident;
437 print STDERR "intrinsic $ident in new type $typeno\n" if $debug;
441 if (($name, $typeordef, $typeno, $extra, $struct, $_)
442 = /^([\$ \w]+):([ustT])(\d+|\(\d+,\d+\))(=[rufs*](\d+))?(.*)$/)
444 $typeno = &typeno($typeno); # sun foolery
446 elsif (/^[\$\w]+:/) {
450 warn "can't grok stab: <$_> in: $line " if $_;
454 #warn "got size $size for $name\n";
455 $sizeof{$name} = $size if $size;
457 s/;[-\d]*;[-\d]*;$//; # we don't care about ranges
459 $typenos{$name} = $typeno;
461 unless (defined $type[$typeno]) {
462 &panic("type 0??") unless $typeno;
463 $type[$typeno] = $name unless defined $type[$typeno];
464 printf "new type $typeno is $name" if $debug;
465 if ($extra =~ /\*/ && defined $type[$struct]) {
466 print ", a typedef for a pointer to " , $type[$struct] if $debug;
469 printf "%s is type %d", $name, $typeno if $debug;
470 print ", a typedef for " , $type[$typeno] if $debug;
472 print "\n" if $debug;
473 #next unless $extra =~ /[su*]/;
475 #$type[$struct] = $name;
477 if ($extra =~ /[us*]/) {
479 $_ = &sdecl($name, $_, 0);
482 print "it's a bare array typedef -- that's pretty sick\n" if $debug;
488 elsif (s/((\w+):t(\d+|\(\d+,\d+\)))?=r?(;\d+;\d+;)?//) { # the ?'s are for gcc
489 push(@intrinsics, $2);
490 $typeno = &typeno($3);
492 print STDERR "intrinsic $2 in new type $typeno\n" if $debug;
494 elsif (s/^=e//) { # blessed by thy compiler; mine won't do this
498 warn "Funny remainder for $name on line $_ left in $line " if $_;
502 sub typeno { # sun thinks types are (0,27) instead of just 27
509 local($what,$prefix,$base) = @_;
510 local($field, $fieldname, $typeno, $count, $offset, $entry);
512 local($type, $tname);
513 local($mytype, $mycount, $entry2);
514 local($struct_count) = 0;
515 local($pad, $revpad, $length, $prepad, $lastoffset, $lastlength, $fmt);
520 local($mname) = &munge($name);
528 local($sname) = &psou($what);
532 for $field (split(/;/, $struct{$what})) {
535 ($fieldname, $typeno, $count, $offset, $length) = split(/,/, $field);
537 $type = $type[$typeno];
539 $type =~ /([^[]*)(\[.*\])?/;
542 $fieldtype = &psou($mytype);
544 local($fname) = &psou($name);
546 if ($build_templates) {
548 $pad = ($offset - ($lastoffset + $lastlength))/8
549 if defined $lastoffset;
551 if (! $finished_template{$sname}) {
552 if ($isaunion{$what}) {
553 $template{$sname} .= 'X' x $revpad . ' ' if $revpad;
555 $template{$sname} .= 'x' x $pad . ' ' if $pad;
559 $template = &fetch_template($type) x
560 ($count ? &scripts2count($count) : 1);
562 if (! $finished_template{$sname}) {
563 $template{$sname} .= $template;
566 $revpad = $length/8 if $isaunion{$what};
568 ($lastoffset, $lastlength) = ($offset, $length);
571 print '# ' if $perl && $verbose;
572 $entry = sprintf($pmask1,
573 ' ' x ($nesting * $indent) . $fieldtype,
574 "$prefix.$fieldname" . $count);
576 $entry =~ s/(\*+)( )/$2$1/;
581 ($bits = ($base+$offset)%8) ? ".$bits" : " ",
583 ($bits = $length % 8) ? ".$bits": ""
584 if !$perl || $verbose;
587 if ($perl && $nesting == 1) {
588 $template = &scrunch(&fetch_template($type) x
589 ($count ? &scripts2count($count) : 1));
590 push(@sizeof, int($length/8) .",\t# $fieldname");
591 push(@offsetof, int($offset/8) .",\t# $fieldname");
592 push(@typedef, "'$template', \t# $fieldname");
593 $type =~ s/(struct|union) //;
594 push(@typeof, "'$type" . ($count ? $count : '') .
598 print ' ', ' ' x $indent x $nesting, $template
599 if $perl && $verbose;
601 print "\n" if !$perl || $verbose;
605 local($mycount) = defined $struct{$mytype} ? $countof{$mytype} : 1;
606 $mycount *= &scripts2count($count) if $count;
607 if ($nesting==1 && !$build_templates) {
608 $pcode .= sprintf("sub %-32s { %4d; }\n",
609 "${mname}'${fieldname}", $struct_count);
610 push(@indices, $struct_count);
612 $struct_count += $mycount;
616 &pstruct($type, "$prefix.$fieldname", $base+$offset)
617 if $recurse && defined $struct{$type};
620 $countof{$what} = $struct_count unless defined $countof{$whati};
622 $template{$sname} .= '$' if $build_templates;
623 $finished_template{$sname}++;
625 if ($build_templates && !defined $sizeof{$name}) {
626 local($fmt) = &scrunch($template{$sname});
627 print STDERR "no size for $name, punting with $fmt..." if $debug;
628 eval '$sizeof{$name} = length(pack($fmt, ()))';
631 warn "couldn't get size for \$name: $@";
633 print STDERR $sizeof{$name}, "\n" if $debUg;
643 local($amstruct) = $struct{$me} ? 'struct ' : '';
645 print '$sizeof{\'', $amstruct, $me, '\'} = ';
646 printf "%d;\n", $sizeof{$me};
654 warn "pdecl: $pdecl\n" if $debug;
656 $pdecl =~ s/\(\d+,(\d+)\)/$1/g;
658 @pdecls = split(/=/, $pdecl);
659 $typeno = $pdecls[0];
660 $tname = pop @pdecls;
662 if ($tname =~ s/^f//) { $tname = "$tname&"; }
663 #else { $tname = "$tname*"; }
665 for (reverse @pdecls) {
666 $tname .= s/^f// ? "&" : "*";
667 #$tname =~ s/^f(.*)/$1&/;
668 print "type[$_] is $tname\n" if $debug;
669 $type[$_] = $tname unless defined $type[$_];
676 ($arraytype, $unknown, $lower, $upper) = ();
678 # global $typeno, @type
679 local($_, $typedef) = @_;
681 while (s/^((\d+)=)?ar(\d+);//) {
682 ($arraytype, $unknown) = ($2, $3);
683 if (s/^(\d+);(\d+);//) {
684 ($lower, $upper) = ($1, $2);
685 $scripts .= '[' . ($upper+1) . ']';
687 warn "can't find array bounds: $_";
690 if (s/^([\d*f=]*),(\d+),(\d+);//) {
691 ($start, $length) = ($2, $3);
693 if ($whatis =~ /^(\d+)=/) {
699 } elsif (s/^(\d+)(=[*suf]\d*)//) {
702 if ($whatis =~ /[f*]/) {
704 } elsif ($whatis =~ /[su]/) { #
705 print "$prefix.$fieldname is an array$scripts anon structs; disgusting\n"
707 #$type[$typeno] = $name unless defined $type[$typeno];
708 ##printf "new type $typeno is $name" if $debug;
710 $type[$typeno] = "$prefix.$fieldname";
711 local($name) = $type[$typeno];
712 &sou($name, $whatis);
713 $_ = &sdecl($name, $_, $start+$offset);
715 $start = $start{$name};
716 $offset = $sizeof{$name};
719 warn "what's this? $whatis in $line ";
724 warn "bad array stab: $_ in $line ";
727 #local($wasdef) = defined($type[$typeno]) && $debug;
729 #print "redefining $type[$typeno] to " if $wasdef;
730 #$type[$typeno] = "$whatis$scripts"; # unless defined $type[$typeno];
731 #print "$type[$typeno]\n" if $wasdef;
733 #$type[$arraytype] = $type[$typeno] unless defined $type[$arraytype];
735 $type[$arraytype] = "$type[$typeno]$scripts" if defined $type[$typeno];
736 print "type[$arraytype] is $type[$arraytype]\n" if $debug;
737 print "$prefix.$fieldname is an array of $type[$arraytype]\n" if $debug;
744 local($prefix, $_, $offset) = @_;
746 local($fieldname, $scripts, $type, $arraytype, $unknown,
747 $whatis, $pdecl, $upper,$lower, $start,$length) = ();
752 while (/^([^;]+);/) {
754 warn "sdecl $_\n" if $debug;
755 if (s/^([\$\w]+)://) {
757 } elsif (s/(\d+)=([us])(\d+|\(\d+,\d+\))//) { #
758 $typeno = &typeno($1);
759 $type[$typeno] = "$prefix.$fieldname";
760 local($name) = "$prefix.$fieldname";
762 $_ = &sdecl("$prefix.$fieldname", $_, $start+$offset);
763 $start = $start{$name};
764 $offset += $sizeof{$name};
765 #print "done with anon, start is $start, offset is $offset\n";
768 warn "weird field $_ of $line" if $debug;
770 #$fieldname = &gensym;
771 #$_ = &sdecl("$prefix.$fieldname", $_, $start+$offset);
777 elsif (s/^(\d+|\(\d+,\d+\))?,(\d+),(\d+);//) {
778 ($start, $length) = ($2, $3);
779 &panic("no length?") unless $length;
780 $typeno = &typeno($1) if $1;
782 elsif (s/^((\d+|\(\d+,\d+\))(=[*f](\d+|\(\d+,\d+\)))+),(\d+),(\d+);//) {
783 ($pdecl, $start, $length) = ($1,$5,$6);
786 elsif (s/(\d+)=([us])(\d+|\(\d+,\d+\))//) { # the dratted anon struct
787 ($typeno, $sou) = ($1, $2);
788 $typeno = &typeno($typeno);
789 if (defined($type[$typeno])) {
790 warn "now how did we get type $1 in $fieldname of $line?";
792 print "anon type $typeno is $prefix.$fieldname\n" if $debug;
793 $type[$typeno] = "$prefix.$fieldname" unless defined $type[$typeno];
795 local($name) = "$prefix.$fieldname";
797 print "anon ".($isastruct{$name}) ? "struct":"union"." for $prefix.$fieldname\n" if $debug;
798 $type[$typeno] = "$prefix.$fieldname";
799 $_ = &sdecl("$prefix.$fieldname", $_, $start+$offset);
800 $start = $start{$name};
801 $length = $sizeof{$name};
804 warn "can't grok stab for $name ($_) in line $line ";
808 &panic("no length for $prefix.$fieldname") unless $length;
809 $struct{$name} .= join(',', $fieldname, $typeno, $scripts, $start, $length) . ';';
811 if (s/;\d*,(\d+),(\d+);//) {
812 local($start, $size) = ($1, $2);
813 $sizeof{$prefix} = $size;
814 print "start of $prefix is $start, size of $sizeof{$prefix}\n" if $debug;
815 $start{$prefix} = $start;
828 for $i (0 .. $#type) {
829 next unless defined $type[$i];
832 print "type[$i] $type[$i]\n" if $debug;
835 print "type[$i] $_ ==> " if $debug;
836 s/^(\d+)(\**)\&\*(\**)/"$2($3".&type($1) . ')()'/e;
837 s/^(\d+)\&/&type($1)/e;
838 s/^(\d+)/&type($1)/e;
839 s/(\*+)([^*]+)(\*+)/$1$3$2/;
840 s/\((\*+)(\w+)(\*+)\)/$3($1$2)/;
841 s/^(\d+)([\*\[].*)/&type($1).$2/e;
842 #s/(\d+)(\*|(\[[\[\]\d\*]+]\])+)/&type($1).$2/ge;
844 print "$_\n" if $debug;
847 sub type { &psou($type[$_[0]] || "<UNDEFINED>"); }
849 sub adjust_start_addrs {
850 for (sort keys %start) {
851 ($basename = $_) =~ s/\.[^.]+$//;
852 $start{$_} += $start{$basename};
853 print "start: $_ @ $start{$_}\n" if $debug;
858 local($what, $_) = @_;
859 /u/ && $isaunion{$what}++;
860 /s/ && $isastruct{$what}++;
866 if ($isaunion{$what}) {
868 } elsif ($isastruct{$what}) {
881 1 while s/(\w) \1/$1$1/g;
883 # i wanna say this, but perl resists my efforts:
884 # s/(\w)(\1+)/$2 . length($1)/ge;
893 sub buildscrunchlist {
894 $scrunch_code = "sub quick_scrunch {\n";
895 for (values %intrinsics) {
896 $scrunch_code .= "\ts/($_{2,})/'$_' . length(\$1)/ge;\n";
898 $scrunch_code .= "}\n";
899 print "$scrunch_code" if $debug;
901 &panic("can't eval scrunch_code $@ \nscrunch_code") if $@;
909 &panic("why do you care?") unless $perl;
911 if ($mytype =~ s/(\[\d+\])+$//) {
915 if ($mytype =~ /\*/) {
916 $fmt = $template{'pointer'};
918 elsif (defined $template{$mytype}) {
919 $fmt = $template{$mytype};
921 elsif (defined $struct{$mytype}) {
922 if (!defined $template{&psou($mytype)}) {
923 &build_template($mytype) unless $mytype eq $name;
925 elsif ($template{&psou($mytype)} !~ /\$$/) {
926 #warn "incomplete template for $mytype\n";
928 $fmt = $template{&psou($mytype)} || '?';
931 warn "unknown fmt for $mytype\n";
938 sub compute_intrinsics {
939 local($TMP) = "/tmp/c2ph-i.$$.c";
940 open (TMP, ">$TMP") || die "can't open $TMP: $!";
943 print STDERR "computing intrinsic sizes: " if $trace;
949 char *mask = "%d %s\n";
952 for $type (@intrinsics) {
953 next if $type eq 'void';
955 printf(mask,sizeof($type), "$type");
960 printf(mask,sizeof(char *), "pointer");
967 open(PIPE, "cd /tmp && $CC $TMP && /tmp/a.out|");
971 print "intrinsic $_[1] is size $_[0]\n" if $debug;
972 $sizeof{$_[1]} = $_[0];
973 $intrinsics{$_[1]} = $template{$_[0]};
975 close(PIPE) || die "couldn't read intrinsics!";
976 unlink($TMP, '/tmp/a.out');
977 print STDERR "done\n" if $trace;
987 &panic("$_: $@") if $@;
992 print STDERR "@_\n" if $trace;
999 &panic("already got a template for $name") if defined $template{$name};
1001 local($build_templates) = 1;
1003 local($lparen) = '(' x $build_recursed;
1004 local($rparen) = ')' x $build_recursed;
1006 print STDERR "$lparen$name$rparen " if $trace;
1008 &pstruct($name,$name,0);
1009 print STDERR "TEMPLATE for $name is ", $template{&psou($name)}, "\n" if $debug;
1018 print "\npanic: @_\n";
1020 exit 1 if $] <= 4.003; # caller broken
1023 local($p,$f,$l,$s,$h,$a,@a,@sub);
1024 for ($i = 0; ($p,$f,$l,$s,$h,$w) = caller($i); $i++) {
1027 if (/^StB\000/ && length($_) == length($_main{'_main'})) {
1028 $_ = sprintf("%s",$_);
1032 s/([^\0]*)/'$1'/ unless /^-?[\d.]+$/;
1033 s/([\200-\377])/sprintf("M-%c",ord($1)&0177)/eg;
1034 s/([\0-\37\177])/sprintf("^%c",ord($1)^64)/eg;
1037 $w = $w ? '@ = ' : '$ = ';
1038 $a = $h ? '(' . join(', ', @a) . ')' : '';
1039 push(@sub, "$w&$s$a from file $f line $l\n");
1042 for ($i=0; $i <= $#sub; $i++) {
1051 local($last) = -1e8;
1055 while (defined($num = shift)) {
1056 if ($num == ($last + 1)) {
1057 $string .= $seq unless $inseq++;
1061 $string .= $last unless $last == -1e8;
1064 $string .= ',' if defined $string;
1069 $string .= $last if $inseq && $last != -e18;