perl 4.0 patch 12: patch #11, continued
authorLarry Wall <lwall@netlabs.com>
Tue, 5 Nov 1991 06:26:52 +0000 (06:26 +0000)
committerLarry Wall <lwall@netlabs.com>
Tue, 5 Nov 1991 06:26:52 +0000 (06:26 +0000)
See patch #11.

c2ph.SH [new file with mode: 0644]
c2ph.doc [new file with mode: 0644]
cflags.SH
lib/chat2.pl [new file with mode: 0644]
patchlevel.h

diff --git a/c2ph.SH b/c2ph.SH
new file mode 100644 (file)
index 0000000..4bf52be
--- /dev/null
+++ b/c2ph.SH
@@ -0,0 +1,1101 @@
+case $CONFIG in
+'')
+    if test ! -f config.sh; then
+       ln ../config.sh . || \
+       ln ../../config.sh . || \
+       ln ../../../config.sh . || \
+       (echo "Can't find config.sh."; exit 1)
+    fi
+    . config.sh
+    ;;
+esac
+: This forces SH files to create target in same directory as SH file.
+: This is so that make depend always knows where to find SH derivatives.
+case "$0" in
+*/*) cd `expr X$0 : 'X\(.*\)/'` ;;
+esac
+echo "Extracting c2ph (with variable substitutions)"
+: This section of the file will have variable substitutions done on it.
+: Move anything that needs config subs from !NO!SUBS! section to !GROK!THIS!.
+: Protect any dollar signs and backticks that you do not want interpreted
+: by putting a backslash in front.  You may delete these comments.
+$spitshell >c2ph <<!GROK!THIS!
+#!$bin/perl
+#
+!GROK!THIS!
+
+: In the following dollars and backticks do not need the extra backslash.
+$spitshell >>c2ph <<'!NO!SUBS!'
+#
+#   c2ph (aka pstruct)
+#   Tom Christiansen, <tchrist@convex.com>
+#   
+#   As pstruct, dump C structures as generated from 'cc -g -S' stabs.
+#   As c2ph, do this PLUS generate perl code for getting at the structures.
+#
+#   See the usage message for more.  If this isn't enough, read the code.
+#
+
+$RCSID = '$RCSfile: c2ph.SH,v $$Revision: 4.0.1.1 $$Date: 91/11/05 16:02:29 $';
+
+
+######################################################################
+
+# some handy data definitions.   many of these can be reset later.
+
+$bitorder = 'b';  # ascending; set to B for descending bit fields
+
+%intrinsics = 
+%template = (
+    'char',                    'c',
+    'unsigned char',           'C',
+    'short',                   's',
+    'short int',               's',
+    'unsigned short',          'S',
+    'unsigned short int',      'S',
+    'short unsigned int',      'S',
+    'int',                     'i',
+    'unsigned int',            'I',
+    'long',                    'l',
+    'long int',                        'l',
+    'unsigned long',           'L',
+    'unsigned long',           'L',
+    'long unsigned int',       'L',
+    'unsigned long int',       'L',
+    'long long',               'q',
+    'long long int',           'q',
+    'unsigned long long',      'Q',
+    'unsigned long long int',  'Q',
+    'float',                   'f',
+    'double',                  'd',
+    'pointer',                 'p',
+    'null',                    'x',
+    'neganull',                        'X',
+    'bit',                     $bitorder,
+); 
+
+&buildscrunchlist;
+delete $intrinsics{'neganull'};
+delete $intrinsics{'bit'};
+delete $intrinsics{'null'};
+
+# use -s to recompute sizes
+%sizeof = (
+    'char',                    '1',
+    'unsigned char',           '1',
+    'short',                   '2',
+    'short int',               '2',
+    'unsigned short',          '2',
+    'unsigned short int',      '2',
+    'short unsigned int',      '2',
+    'int',                     '4',
+    'unsigned int',            '4',
+    'long',                    '4',
+    'long int',                        '4',
+    'unsigned long',           '4',
+    'unsigned long int',       '4',
+    'long unsigned int',       '4',
+    'long long',               '8',
+    'long long int',           '8',
+    'unsigned long long',      '8',
+    'unsigned long long int',  '8',
+    'float',                   '4',
+    'double',                  '8',
+    'pointer',                 '4',
+);
+
+($type_width, $member_width, $offset_width, $size_width) = (20, 20, 6, 5);
+
+($offset_fmt, $size_fmt) = ('d', 'd');
+
+$indent = 2;
+
+$CC = 'cc';
+$CFLAGS = '-g -S';
+$DEFINES = '';
+
+$perl++ if $0 =~ m#/?c2ph$#;
+
+require 'getopts.pl';
+
+eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_]+=)(.*)/ && shift;
+
+&Getopts('aixdpvtnws:') || &usage(0);
+
+$opt_d && $debug++;
+$opt_t && $trace++;
+$opt_p && $perl++;
+$opt_v && $verbose++;
+$opt_n && ($perl = 0);
+
+if ($opt_w) {
+    ($type_width, $member_width, $offset_width) = (45, 35, 8);
+} 
+if ($opt_x) {
+    ($offset_fmt, $offset_width, $size_fmt, $size_width) = ( 'x', '08', 'x', 04 );
+}
+
+eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_]+=)(.*)/ && shift;
+
+sub PLUMBER {
+    select(STDERR);
+    print "oops, apperent pager foulup\n";
+    $isatty++;
+    &usage(1);
+} 
+
+sub usage {
+    local($oops) = @_;
+    unless (-t STDOUT) {
+       select(STDERR);
+    } elsif (!$oops) {
+       $isatty++;
+       $| = 1;
+       print "hit <RETURN> for further explanation: ";
+       <STDIN>;
+       open (PIPE, "|". ($ENV{PAGER} || 'more'));
+       $SIG{PIPE} = PLUMBER;
+       select(PIPE);
+    } 
+
+    print "usage: $0 [-dpnP] [var=val] [files ...]\n";
+
+    exit unless $isatty;
+
+    print <<EOF;
+
+Options:
+
+-w     wide; short for: type_width=45 member_width=35 offset_width=8
+-x     hex; short for:  offset_fmt=x offset_width=08 size_fmt=x size_width=04
+
+-n     do not generate perl code  (default when invoked as pstruct)
+-p     generate perl code         (default when invoked as c2ph)
+-v     generate perl code, with C decls as comments
+
+-i     do NOT recompute sizes for intrinsic datatypes
+-a     dump information on intrinsics also
+
+-t     trace execution
+-d     spew reams of debugging output
+
+-slist  give comma-separated list a structures to dump
+
+
+Var Name        Default Value    Meaning
+
+EOF
+
+    &defvar('CC', 'which_compiler to call');
+    &defvar('CFLAGS', 'how to generate *.s files with stabs');
+    &defvar('DEFINES','any extra cflags or cpp defines, like -I, -D, -U');
+
+    print "\n";
+
+    &defvar('type_width', 'width of type field   (column 1)');
+    &defvar('member_width', 'width of member field (column 2)');
+    &defvar('offset_width', 'width of offset field (column 3)');
+    &defvar('size_width', 'width of size field   (column 4)');
+
+    print "\n";
+
+    &defvar('offset_fmt', 'sprintf format type for offset');
+    &defvar('size_fmt', 'sprintf format type for size');
+
+    print "\n";
+
+    &defvar('indent', 'how far to indent each nesting level');
+
+   print <<'EOF';
+
+    If any *.[ch] files are given, these will be catted together into
+    a temporary *.c file and sent through:
+           $CC $CFLAGS $DEFINES 
+    and the resulting *.s groped for stab information.  If no files are
+    supplied, then stdin is read directly with the assumption that it
+    contains stab information.  All other liens will be ignored.  At
+    most one *.s file should be supplied.
+
+EOF
+    close PIPE;
+    exit 1;
+} 
+
+sub defvar {
+    local($var, $msg) = @_;
+    printf "%-16s%-15s  %s\n", $var, eval "\$$var", $msg;
+} 
+
+$recurse = 1;
+
+if (@ARGV) {
+    if (grep(!/\.[csh]$/,@ARGV)) {
+       warn "Only *.[csh] files expected!\n";
+       &usage;
+    } 
+    elsif (grep(/\.s$/,@ARGV)) {
+       if (@ARGV > 1) { 
+           warn "Only one *.s file allowed!\n";
+           &usage;
+       }
+    } 
+    elsif (@ARGV == 1 && $ARGV[0] =~ /\.c$/) {
+       local($dir, $file) = $ARGV[0] =~ m#(.*/)?(.*)$#;
+       $chdir = "cd $dir; " if $dir;
+       &system("$chdir$CC $CFLAGS $DEFINES $file") && exit 1;
+       $ARGV[0] =~ s/\.c$/.s/;
+    } 
+    else {
+       $TMP = "/tmp/c2ph.$$.c";
+       &system("cat @ARGV > $TMP") && exit 1;
+       &system("cd /tmp; $CC $CFLAGS $DEFINES $TMP") && exit 1;
+       unlink $TMP;
+       $TMP =~ s/\.c$/.s/;
+       @ARGV = ($TMP);
+    } 
+}
+
+if ($opt_s) {
+    for (split(/[\s,]+/, $opt_s)) {
+       $interested{$_}++;
+    } 
+} 
+
+
+$| = 1 if $debug;
+
+main: {
+
+    if ($trace) {
+       if (-t && !@ARGV) { 
+           print STDERR "reading from your keyboard: ";
+       } else {
+           print STDERR "reading from " . (@ARGV ? "@ARGV" : "<STDIN>").": ";
+       }
+    }
+
+STAB: while (<>) {
+       if ($trace && !($. % 10)) {
+           $lineno = $..'';
+           print STDERR $lineno, "\b" x length($lineno);
+       } 
+       next unless /^\s*\.stabs\s+/;
+       $line = $_;
+       s/^\s*\.stabs\s+//; 
+       &stab; 
+    }
+    print STDERR "$.\n" if $trace;
+    unlink $TMP if $TMP;
+
+    &compute_intrinsics if $perl && !$opt_i;
+
+    print STDERR "resolving types\n" if $trace;
+
+    &resolve_types;
+    &adjust_start_addrs;
+
+    $sum = 2 + $type_width + $member_width;
+    $pmask1 = "%-${type_width}s %-${member_width}s"; 
+    $pmask2 = "%-${sum}s %${offset_width}${offset_fmt}%s %${size_width}${size_fmt}%s";
+
+    if ($perl) {
+       # resolve template -- should be in stab define order, but even this isn't enough.
+       print STDERR "\nbuilding type templates: " if $trace;
+       for $i (reverse 0..$#type) {
+           next unless defined($name = $type[$i]);
+           next unless defined $struct{$name};
+           $build_recursed = 0;
+           &build_template($name) unless defined $template{&psou($name)} ||
+                                       $opt_s && !$interested{$name};
+       } 
+       print STDERR "\n\n" if $trace;
+    }
+
+    print STDERR "dumping structs: " if $trace;
+
+
+    foreach $name (sort keys %struct) {
+       next if $opt_s && !$interested{$name};
+       print STDERR "$name " if $trace;
+
+       undef @sizeof;
+       undef @typedef;
+       undef @offsetof;
+       undef @indices;
+       undef @typeof;
+
+       $mname = &munge($name);
+
+       $fname = &psou($name);
+
+       print "# " if $perl && $verbose;
+       $pcode = '';
+       print "$fname {\n" if !$perl || $verbose; 
+       $template{$fname} = &scrunch($template{$fname}) if $perl;
+       &pstruct($name,$name,0); 
+       print "# " if $perl && $verbose;
+       print "}\n" if !$perl || $verbose; 
+       print "\n" if $perl && $verbose;
+
+       if ($perl) {
+           print "$pcode";
+
+           printf("\nsub %-32s { %4d; }\n\n", "${mname}'struct", $countof{$name});
+
+           print <<EOF;
+sub ${mname}'typedef { 
+    local(\$${mname}'index) = shift;
+    defined \$${mname}'index 
+       ? \$${mname}'typedef[\$${mname}'index] 
+       : \$${mname}'typedef;
+}
+EOF
+
+           print <<EOF;
+sub ${mname}'sizeof { 
+    local(\$${mname}'index) = shift;
+    defined \$${mname}'index 
+       ? \$${mname}'sizeof[\$${mname}'index] 
+       : \$${mname}'sizeof;
+}
+EOF
+
+           print <<EOF;
+sub ${mname}'offsetof { 
+    local(\$${mname}'index) = shift;
+    defined \$${mname}index 
+       ? \$${mname}'offsetof[\$${mname}'index] 
+       : \$${mname}'sizeof;
+}
+EOF
+
+           print <<EOF;
+sub ${mname}'typeof { 
+    local(\$${mname}'index) = shift;
+    defined \$${mname}index 
+       ? \$${mname}'typeof[\$${mname}'index] 
+       : '$name';
+}
+EOF
+    
+
+           print "\$${mname}'typedef = '" . &scrunch($template{$fname}) 
+               . "';\n";
+
+           print "\$${mname}'sizeof = $sizeof{$name};\n\n";
+
+
+           print "\@${mname}'indices = (", &squishseq(@indices), ");\n";
+
+           print "\n";
+
+           print "\@${mname}'typedef[\@${mname}'indices] = (",
+                       join("\n\t", '', @typedef), "\n    );\n\n";
+           print "\@${mname}'sizeof[\@${mname}'indices] = (",
+                       join("\n\t", '', @sizeof), "\n    );\n\n";
+           print "\@${mname}'offsetof[\@${mname}'indices] = (",
+                       join("\n\t", '', @offsetof), "\n    );\n\n";
+           print "\@${mname}'typeof[\@${mname}'indices] = (",
+                       join("\n\t", '', @typeof), "\n    );\n\n";
+
+           $template_printed{$fname}++;
+           $size_printed{$fname}++;
+       } 
+       print "\n";
+    }
+
+    print STDERR "\n" if $trace;
+
+    unless ($perl && $opt_a) { 
+       print "\n1;\n";
+       exit;
+    }
+
+
+
+    foreach $name (sort bysizevalue keys %intrinsics) {
+       next if $size_printed{$name};
+       print '$',&munge($name),"'sizeof = ", $sizeof{$name}, ";\n";
+    }
+
+    print "\n";
+
+    sub bysizevalue { $sizeof{$a} <=> $sizeof{$b}; }
+
+
+    foreach $name (sort keys %intrinsics) {
+       print '$',&munge($name),"'typedef = '", $template{$name}, "';\n";
+    }
+
+    print "\n1;\n";
+       
+    exit;
+}
+
+########################################################################################
+
+
+sub stab {
+    next unless /:[\$\w]+(\(\d+,\d+\))?=[\*\$\w]+/;  # (\d+,\d+) is for sun
+    s/"//                                              || next;
+    s/",([x\d]+),([x\d]+),([x\d]+),.*//                || next;
+
+    next if /^\s*$/;
+
+    $size = $3 if $3;
+
+
+    $line = $_;
+
+    if (($name, $pdecl) = /^([\$ \w]+):[tT]((\d+)(=[rufs*](\d+))+)$/) {
+       print "$name is a typedef for some funky pointers: $pdecl\n" if $debug;
+       &pdecl($pdecl);
+       next;
+    }
+
+
+
+    if (/(([ \w]+):t(\d+|\(\d+,\d+\)))=r?(\d+|\(\d+,\d+\))(;\d+;\d+;)?/) {  
+       local($ident) = $2;
+       push(@intrinsics, $ident);
+       $typeno = &typeno($3);
+       $type[$typeno] = $ident;
+       print STDERR "intrinsic $ident in new type $typeno\n" if $debug; 
+       next;
+    }
+
+    if (($name, $typeordef, $typeno, $extra, $struct, $_) 
+       = /^([\$ \w]+):([ustT])(\d+|\(\d+,\d+\))(=[rufs*](\d+))?(.*)$/) 
+    {
+       $typeno = &typeno($typeno);  # sun foolery
+    } 
+    elsif (/^[\$\w]+:/) {
+       next; # variable
+    }
+    else { 
+       warn "can't grok stab: <$_> in: $line " if $_;
+       next;
+    } 
+
+    #warn "got size $size for $name\n";
+    $sizeof{$name} = $size if $size;
+
+    s/;[-\d]*;[-\d]*;$//;  # we don't care about ranges
+
+    $typenos{$name} = $typeno;
+
+    unless (defined $type[$typeno]) {
+       &panic("type 0??") unless $typeno;
+       $type[$typeno] = $name unless defined $type[$typeno];
+       printf "new type $typeno is $name" if $debug;
+       if ($extra =~ /\*/ && defined $type[$struct]) {
+           print ", a typedef for a pointer to " , $type[$struct] if $debug;
+       }
+    } else {
+       printf "%s is type %d", $name, $typeno if $debug;
+       print ", a typedef for " , $type[$typeno] if $debug;
+    } 
+    print "\n" if $debug;
+    #next unless $extra =~ /[su*]/;
+
+    #$type[$struct] = $name;
+
+    if ($extra =~ /[us*]/) {
+       &sou($name, $extra);
+       $_ = &sdecl($name, $_, 0);
+    }
+    elsif (/^=ar/) {
+       print "it's a bare array typedef -- that's pretty sick\n" if $debug;
+       $_ = "$typeno$_";
+       $scripts = '';
+       $_ = &adecl($_,1);
+
+    }
+    elsif (s/((\w+):t(\d+|\(\d+,\d+\)))?=r?(;\d+;\d+;)?//) {  # the ?'s are for gcc
+       push(@intrinsics, $2);
+       $typeno = &typeno($3);
+       $type[$typeno] = $2;
+       print STDERR "intrinsic $2 in new type $typeno\n" if $debug; 
+    }
+    elsif (s/^=e//) { # blessed by thy compiler; mine won't do this
+       &edecl;
+    } 
+    else {
+       warn "Funny remainder for $name on line $_ left in $line " if $_;
+    } 
+}
+
+sub typeno {  # sun thinks types are (0,27) instead of just 27
+    local($_) = @_;
+    s/\(\d+,(\d+)\)/$1/;
+    $_;
+} 
+
+sub pstruct {
+    local($what,$prefix,$base) = @_; 
+    local($field, $fieldname, $typeno, $count, $offset, $entry); 
+    local($fieldtype);
+    local($type, $tname); 
+    local($mytype, $mycount, $entry2);
+    local($struct_count) = 0;
+    local($pad, $revpad, $length, $prepad, $lastoffset, $lastlength, $fmt);
+    local($bits,$bytes);
+    local($template);
+
+
+    local($mname) = &munge($name);
+
+    sub munge { 
+       local($_) = @_;
+       s/[\s\$\.]/_/g;
+       $_;
+    }
+
+    local($sname) = &psou($what);
+
+    $nesting++;
+
+    for $field (split(/;/, $struct{$what})) {
+       $pad = $prepad = 0;
+       $entry = ''; 
+       ($fieldname, $typeno, $count, $offset, $length) = split(/,/, $field); 
+
+       $type = $type[$typeno];
+
+       $type =~ /([^[]*)(\[.*\])?/;
+       $mytype = $1;
+       $count .= $2;
+       $fieldtype = &psou($mytype);
+
+       local($fname) = &psou($name);
+
+       if ($build_templates) {
+
+           $pad = ($offset - ($lastoffset + $lastlength))/8 
+               if defined $lastoffset;
+
+           if (! $finished_template{$sname}) {
+               if ($isaunion{$what}) {
+                   $template{$sname} .= 'X' x $revpad . ' '    if $revpad;
+               } else {
+                   $template{$sname} .= 'x' x $pad    . ' '    if $pad;
+               }
+           }
+
+           $template = &fetch_template($type) x 
+                           ($count ? &scripts2count($count) : 1);
+
+           if (! $finished_template{$sname}) {
+               $template{$sname} .= $template;
+           }
+
+           $revpad = $length/8 if $isaunion{$what};
+
+           ($lastoffset, $lastlength) = ($offset, $length);
+
+       } else { 
+           print '# ' if $perl && $verbose;
+           $entry = sprintf($pmask1,
+                       ' ' x ($nesting * $indent) . $fieldtype,
+                       "$prefix.$fieldname" . $count); 
+
+           $entry =~ s/(\*+)( )/$2$1/; 
+
+           printf $pmask2,
+                   $entry,
+                   ($base+$offset)/8,
+                   ($bits = ($base+$offset)%8) ? ".$bits" : "  ",
+                   $length/8,
+                   ($bits = $length % 8) ? ".$bits": ""
+                       if !$perl || $verbose;
+
+
+           if ($perl && $nesting == 1) {
+               $template = &scrunch(&fetch_template($type) x 
+                               ($count ? &scripts2count($count) : 1));
+               push(@sizeof, int($length/8) .",\t# $fieldname");
+               push(@offsetof, int($offset/8) .",\t# $fieldname");
+               push(@typedef, "'$template', \t# $fieldname");
+               $type =~ s/(struct|union) //;
+               push(@typeof, "'$type" . ($count ? $count : '') .
+                   "',\t# $fieldname");
+           }
+
+           print '  ', ' ' x $indent x $nesting, $template
+                               if $perl && $verbose;
+
+           print "\n" if !$perl || $verbose;
+
+       }    
+       if ($perl) {
+           local($mycount) = defined $struct{$mytype} ? $countof{$mytype} : 1;
+           $mycount *= &scripts2count($count) if $count;
+           if ($nesting==1 && !$build_templates) {
+               $pcode .= sprintf("sub %-32s { %4d; }\n", 
+                       "${mname}'${fieldname}", $struct_count);
+               push(@indices, $struct_count);
+           }
+           $struct_count += $mycount;
+       } 
+
+
+       &pstruct($type, "$prefix.$fieldname", $base+$offset) 
+               if $recurse && defined $struct{$type}; 
+    }
+
+    $countof{$what} = $struct_count unless defined $countof{$whati};
+
+    $template{$sname} .= '$' if $build_templates;
+    $finished_template{$sname}++;
+
+    if ($build_templates && !defined $sizeof{$name}) {
+       local($fmt) = &scrunch($template{$sname});
+       print STDERR "no size for $name, punting with $fmt..." if $debug;
+       eval '$sizeof{$name} = length(pack($fmt, ()))';
+       if ($@) {
+           chop $@;
+           warn "couldn't get size for \$name: $@";
+       } else {
+           print STDERR $sizeof{$name}, "\n" if $debUg;
+       }
+    } 
+
+    --$nesting;
+}
+
+
+sub psize {
+    local($me) = @_; 
+    local($amstruct) = $struct{$me} ?  'struct ' : '';
+
+    print '$sizeof{\'', $amstruct, $me, '\'} = '; 
+    printf "%d;\n", $sizeof{$me}; 
+}
+
+sub pdecl {
+    local($pdecl) = @_;
+    local(@pdecls);
+    local($tname);
+
+    warn "pdecl: $pdecl\n" if $debug;
+
+    $pdecl =~ s/\(\d+,(\d+)\)/$1/g;
+    $pdecl =~ s/\*//g; 
+    @pdecls = split(/=/, $pdecl); 
+    $typeno = $pdecls[0];
+    $tname = pop @pdecls;
+
+    if ($tname =~ s/^f//) { $tname = "$tname&"; } 
+    #else { $tname = "$tname*"; } 
+
+    for (reverse @pdecls) {
+       $tname  .= s/^f// ? "&" : "*"; 
+       #$tname =~ s/^f(.*)/$1&/;
+       print "type[$_] is $tname\n" if $debug;
+       $type[$_] = $tname unless defined $type[$_];
+    } 
+}
+
+
+
+sub adecl {
+    ($arraytype, $unknown, $lower, $upper) = ();
+    #local($typeno);
+    # global $typeno, @type
+    local($_, $typedef) = @_;
+
+    while (s/^((\d+)=)?ar(\d+);//) {
+       ($arraytype, $unknown) = ($2, $3); 
+       if (s/^(\d+);(\d+);//) {
+           ($lower, $upper) = ($1, $2); 
+           $scripts .= '[' .  ($upper+1) . ']'; 
+       } else {
+           warn "can't find array bounds: $_"; 
+       } 
+    }
+    if (s/^([\d*f=]*),(\d+),(\d+);//) {
+       ($start, $length) = ($2, $3); 
+       local($whatis) = $1;
+       if ($whatis =~ /^(\d+)=/) {
+           $typeno = $1;
+           &pdecl($whatis);
+       } else {
+           $typeno = $whatis;
+       }
+    } elsif (s/^(\d+)(=[*suf]\d*)//) {
+       local($whatis) = $2; 
+
+       if ($whatis =~ /[f*]/) {
+           &pdecl($whatis); 
+       } elsif ($whatis =~ /[su]/) {  # 
+           print "$prefix.$fieldname is an array$scripts anon structs; disgusting\n" 
+               if $debug;
+           #$type[$typeno] = $name unless defined $type[$typeno];
+           ##printf "new type $typeno is $name" if $debug;
+           $typeno = $1;
+           $type[$typeno] = "$prefix.$fieldname";
+           local($name) = $type[$typeno];
+           &sou($name, $whatis);
+           $_ = &sdecl($name, $_, $start+$offset);
+           1;
+           $start = $start{$name};
+           $offset = $sizeof{$name};
+           $length = $offset;
+       } else {
+           warn "what's this? $whatis in $line ";
+       } 
+    } elsif (/^\d+$/) {
+       $typeno = $_;
+    } else {
+       warn "bad array stab: $_ in $line ";
+       next STAB;
+    } 
+    #local($wasdef) = defined($type[$typeno]) && $debug;
+    #if ($typedef) { 
+       #print "redefining $type[$typeno] to " if $wasdef;
+       #$type[$typeno] = "$whatis$scripts"; # unless defined $type[$typeno];
+       #print "$type[$typeno]\n" if $wasdef;
+    #} else {
+       #$type[$arraytype] = $type[$typeno] unless defined $type[$arraytype];
+    #}
+    $type[$arraytype] = "$type[$typeno]$scripts" if defined $type[$typeno];
+    print "type[$arraytype] is $type[$arraytype]\n" if $debug;
+    print "$prefix.$fieldname is an array of $type[$arraytype]\n" if $debug;
+    $_;
+}
+
+
+
+sub sdecl {
+    local($prefix, $_, $offset) = @_;
+
+    local($fieldname, $scripts, $type, $arraytype, $unknown,
+    $whatis, $pdecl, $upper,$lower, $start,$length) = ();
+    local($typeno,$sou);
+
+
+SFIELD:
+    while (/^([^;]+);/) {
+       $scripts = '';
+       warn "sdecl $_\n" if $debug;
+       if (s/^([\$\w]+)://) { 
+           $fieldname = $1;
+       } elsif (s/(\d+)=([us])(\d+|\(\d+,\d+\))//) { # 
+           $typeno = &typeno($1);
+           $type[$typeno] = "$prefix.$fieldname";
+           local($name) = "$prefix.$fieldname";
+           &sou($name,$2);
+           $_ = &sdecl("$prefix.$fieldname", $_, $start+$offset);
+           $start = $start{$name};
+           $offset += $sizeof{$name};
+           #print "done with anon, start is $start, offset is $offset\n";
+           #next SFIELD;
+       } else  {
+           warn "weird field $_ of $line" if $debug;
+           next STAB;
+           #$fieldname = &gensym;
+           #$_ = &sdecl("$prefix.$fieldname", $_, $start+$offset);
+       }
+
+       if (/^\d+=ar/) {
+           $_ = &adecl($_);
+       }
+       elsif (s/^(\d+|\(\d+,\d+\))?,(\d+),(\d+);//) {
+           ($start, $length) =  ($2, $3); 
+           &panic("no length?") unless $length;
+           $typeno = &typeno($1) if $1;
+       }
+       elsif (s/^((\d+|\(\d+,\d+\))(=[*f](\d+|\(\d+,\d+\)))+),(\d+),(\d+);//) {
+           ($pdecl, $start, $length) =  ($1,$5,$6); 
+           &pdecl($pdecl); 
+       }
+       elsif (s/(\d+)=([us])(\d+|\(\d+,\d+\))//) { # the dratted anon struct
+           ($typeno, $sou) = ($1, $2);
+           $typeno = &typeno($typeno);
+           if (defined($type[$typeno])) {
+               warn "now how did we get type $1 in $fieldname of $line?";
+           } else {
+               print "anon type $typeno is $prefix.$fieldname\n" if $debug;
+               $type[$typeno] = "$prefix.$fieldname" unless defined $type[$typeno];
+           };
+           local($name) = "$prefix.$fieldname";
+           &sou($name,$sou);
+           print "anon ".($isastruct{$name}) ? "struct":"union"." for $prefix.$fieldname\n" if $debug;
+           $type[$typeno] = "$prefix.$fieldname";
+           $_ = &sdecl("$prefix.$fieldname", $_, $start+$offset); 
+           $start = $start{$name};
+           $length = $sizeof{$name};
+       }
+       else {
+           warn "can't grok stab for $name ($_) in line $line "; 
+           next STAB; 
+       }
+
+       &panic("no length for $prefix.$fieldname") unless $length;
+       $struct{$name} .= join(',', $fieldname, $typeno, $scripts, $start, $length) . ';';
+    }
+    if (s/;\d*,(\d+),(\d+);//) {
+       local($start, $size) = ($1, $2); 
+       $sizeof{$prefix} = $size;
+       print "start of $prefix is $start, size of $sizeof{$prefix}\n" if $debug; 
+       $start{$prefix} = $start; 
+    } 
+    $_;
+}
+
+sub edecl {
+    s/;$//;
+    $enum{$name} = $_;
+    $_ = '';
+} 
+
+sub resolve_types {
+    local($sou);
+    for $i (0 .. $#type) {
+       next unless defined $type[$i];
+       $_ = $type[$i];
+       unless (/\d/) {
+           print "type[$i] $type[$i]\n" if $debug;
+           next;
+       }
+       print "type[$i] $_ ==> " if $debug;
+       s/^(\d+)(\**)\&\*(\**)/"$2($3".&type($1) . ')()'/e;
+       s/^(\d+)\&/&type($1)/e; 
+       s/^(\d+)/&type($1)/e; 
+       s/(\*+)([^*]+)(\*+)/$1$3$2/;
+       s/\((\*+)(\w+)(\*+)\)/$3($1$2)/;
+       s/^(\d+)([\*\[].*)/&type($1).$2/e;
+       #s/(\d+)(\*|(\[[\[\]\d\*]+]\])+)/&type($1).$2/ge;
+       $type[$i] = $_;
+       print "$_\n" if $debug;
+    }
+}
+sub type { &psou($type[$_[0]] || "<UNDEFINED>"); } 
+
+sub adjust_start_addrs {
+    for (sort keys %start) {
+       ($basename = $_) =~ s/\.[^.]+$//;
+       $start{$_} += $start{$basename};
+       print "start: $_ @ $start{$_}\n" if $debug;
+    }
+}
+
+sub sou {
+    local($what, $_) = @_;
+    /u/ && $isaunion{$what}++;
+    /s/ && $isastruct{$what}++;
+}
+
+sub psou {
+    local($what) = @_;
+    local($prefix) = '';
+    if ($isaunion{$what})  {
+       $prefix = 'union ';
+    } elsif ($isastruct{$what})  {
+       $prefix = 'struct ';
+    }
+    $prefix . $what;
+}
+
+sub scrunch {
+    local($_) = @_;
+
+    study;
+
+    s/\$//g;
+    s/  / /g;
+    1 while s/(\w) \1/$1$1/g;
+
+    # i wanna say this, but perl resists my efforts:
+    #     s/(\w)(\1+)/$2 . length($1)/ge;
+
+    &quick_scrunch;
+
+    s/ $//;
+
+    $_;
+}
+
+sub buildscrunchlist {
+    $scrunch_code = "sub quick_scrunch {\n";
+    for (values %intrinsics) {
+        $scrunch_code .= "\ts/($_{2,})/'$_' . length(\$1)/ge;\n";
+    } 
+    $scrunch_code .= "}\n";
+    print "$scrunch_code" if $debug;
+    eval $scrunch_code;
+    &panic("can't eval scrunch_code $@ \nscrunch_code") if $@;
+} 
+
+sub fetch_template {
+    local($mytype) = @_;
+    local($fmt);
+    local($count) = 1;
+
+    &panic("why do you care?") unless $perl;
+
+    if ($mytype =~ s/(\[\d+\])+$//) {
+       $count .= $1;
+    } 
+
+    if ($mytype =~ /\*/) {
+       $fmt = $template{'pointer'};
+    } 
+    elsif (defined $template{$mytype}) {
+       $fmt = $template{$mytype};
+    } 
+    elsif (defined $struct{$mytype}) {
+       if (!defined $template{&psou($mytype)}) {
+           &build_template($mytype) unless $mytype eq $name;
+       } 
+       elsif ($template{&psou($mytype)} !~ /\$$/) {
+           #warn "incomplete template for $mytype\n";
+       } 
+       $fmt = $template{&psou($mytype)} || '?';
+    } 
+    else {
+       warn "unknown fmt for $mytype\n";
+       $fmt = '?';
+    } 
+
+    $fmt x $count . ' ';
+}
+
+sub compute_intrinsics {
+    local($TMP) = "/tmp/c2ph-i.$$.c";
+    open (TMP, ">$TMP") || die "can't open $TMP: $!";
+    select(TMP);
+
+    print STDERR "computing intrinsic sizes: " if $trace;
+
+    undef %intrinsics;
+
+    print <<'EOF';
+main() {
+    char *mask = "%d %s\n";
+EOF
+
+    for $type (@intrinsics) {
+       next if $type eq 'void';
+       print <<"EOF";
+    printf(mask,sizeof($type), "$type");
+EOF
+    } 
+
+    print <<'EOF';
+    printf(mask,sizeof(char *), "pointer");
+    exit(0);
+}
+EOF
+    close TMP;
+
+    select(STDOUT);
+    open(PIPE, "cd /tmp && $CC $TMP && /tmp/a.out|");
+    while (<PIPE>) {
+       chop;
+       split(' ',$_,2);;
+       print "intrinsic $_[1] is size $_[0]\n" if $debug;
+       $sizeof{$_[1]} = $_[0];
+       $intrinsics{$_[1]} = $template{$_[0]};
+    } 
+    close(PIPE) || die "couldn't read intrinsics!";
+    unlink($TMP, '/tmp/a.out');
+    print STDERR "done\n" if $trace;
+} 
+
+sub scripts2count {
+    local($_) = @_;
+
+    s/^\[//;
+    s/\]$//;
+    s/\]\[/*/g;
+    $_ = eval;
+    &panic("$_: $@") if $@;
+    $_;
+}
+
+sub system {
+    print STDERR "@_\n" if $trace;
+    system @_;
+} 
+
+sub build_template { 
+    local($name) = @_;
+
+    &panic("already got a template for $name") if defined $template{$name};
+
+    local($build_templates) = 1;
+
+    local($lparen) = '(' x $build_recursed;
+    local($rparen) = ')' x $build_recursed;
+
+    print STDERR "$lparen$name$rparen " if $trace;
+    $build_recursed++;
+    &pstruct($name,$name,0);
+    print STDERR "TEMPLATE for $name is ", $template{&psou($name)}, "\n" if $debug;
+    --$build_recursed;
+}
+
+
+sub panic {
+
+    select(STDERR);
+
+    print "\npanic: @_\n";
+
+    exit 1 if $] <= 4.003;  # caller broken
+
+    local($i,$_);
+    local($p,$f,$l,$s,$h,$a,@a,@sub);
+    for ($i = 0; ($p,$f,$l,$s,$h,$w) = caller($i); $i++) {
+       @a = @DB'args;
+       for (@a) {
+           if (/^StB\000/ && length($_) == length($_main{'_main'})) {
+               $_ = sprintf("%s",$_);
+           }
+           else {
+               s/'/\\'/g;
+               s/([^\0]*)/'$1'/ unless /^-?[\d.]+$/;
+               s/([\200-\377])/sprintf("M-%c",ord($1)&0177)/eg;
+               s/([\0-\37\177])/sprintf("^%c",ord($1)^64)/eg;
+           }
+       }
+       $w = $w ? '@ = ' : '$ = ';
+       $a = $h ? '(' . join(', ', @a) . ')' : '';
+       push(@sub, "$w&$s$a from file $f line $l\n");
+       last if $signal;
+    }
+    for ($i=0; $i <= $#sub; $i++) {
+       last if $signal;
+       print $sub[$i];
+    }
+    exit 1;
+} 
+
+sub squishseq {
+    local($num);
+    local($last) = -1e8;
+    local($string);
+    local($seq) = '..';
+
+    while (defined($num = shift)) {
+        if ($num == ($last + 1)) {
+            $string .= $seq unless $inseq++;
+            $last = $num;
+            next;
+        } elsif ($inseq) {
+            $string .= $last unless $last == -1e8;
+        }
+
+        $string .= ',' if defined $string;
+        $string .= $num;
+        $last = $num;
+        $inseq = 0;
+    }
+    $string .= $last if $inseq && $last != -e18;
+    $string;
+}
+!NO!SUBS!
+$eunicefix c2ph
+rm -f pstruct
+ln c2ph pstruct
diff --git a/c2ph.doc b/c2ph.doc
new file mode 100644 (file)
index 0000000..0c3eaee
--- /dev/null
+++ b/c2ph.doc
@@ -0,0 +1,191 @@
+Article 484 of comp.lang.perl:
+Xref: netlabs comp.lang.perl:484 comp.lang.c:983 alt.sources:134
+Path: netlabs!psinntp!iggy.GW.Vitalink.COM!lll-winken!sun-barr!cronkite.Central.Sun.COM!spdev!texsun!convex!tchrist
+From: tchrist@convex.com (Tom Christiansen)
+Newsgroups: comp.lang.perl,comp.lang.c,alt.sources
+Subject: pstruct -- a C structure formatter; AKA c2ph, a C to perl header translator
+Keywords: C perl tranlator
+Message-ID: <1991Jul25.081021.8104@convex.com>
+Date: 25 Jul 91 08:10:21 GMT
+Sender: usenet@convex.com (news access account)
+Followup-To: comp.lang.perl
+Organization: CONVEX Computer Corporation, Richardson, Tx., USA
+Lines: 1208
+Nntp-Posting-Host: pixel.convex.com
+
+Once upon a time, I wrote a program called pstruct.  It was a perl 
+program that tried to parse out C structures and display their member
+offsets for you.  This was especially useful for people looking at
+binary dumps or poking around the kernel.  
+
+Pstruct was not a pretty program.  Neither was it particularly robust.
+The problem, you see, was that the C compiler was much better at parsing
+C than I could ever hope to be.  
+
+So I got smart:  I decided to be lazy and let the C compiler parse the C,
+which would spit out debugger stabs for me to read.  These were much
+easier to parse.  It's still not a pretty program, but at least it's more
+robust.  
+
+Pstruct takes any .c or .h files, or preferably .s ones, since that's
+the format it is going to massage them into anyway, and spits out
+listings like this:
+
+struct tty {
+  int                          tty.t_locker                         000      4
+  int                          tty.t_mutex_index                    004      4
+  struct tty *                 tty.t_tp_virt                        008      4
+  struct clist                 tty.t_rawq                           00c     20
+    int                        tty.t_rawq.c_cc                      00c      4
+    int                        tty.t_rawq.c_cmax                    010      4
+    int                        tty.t_rawq.c_cfx                     014      4
+    int                        tty.t_rawq.c_clx                     018      4
+    struct tty *               tty.t_rawq.c_tp_cpu                  01c      4
+    struct tty *               tty.t_rawq.c_tp_iop                  020      4
+    unsigned char *            tty.t_rawq.c_buf_cpu                 024      4
+    unsigned char *            tty.t_rawq.c_buf_iop                 028      4
+  struct clist                 tty.t_canq                           02c     20
+    int                        tty.t_canq.c_cc                      02c      4
+    int                        tty.t_canq.c_cmax                    030      4
+    int                        tty.t_canq.c_cfx                     034      4
+    int                        tty.t_canq.c_clx                     038      4
+    struct tty *               tty.t_canq.c_tp_cpu                  03c      4
+    struct tty *               tty.t_canq.c_tp_iop                  040      4
+    unsigned char *            tty.t_canq.c_buf_cpu                 044      4
+    unsigned char *            tty.t_canq.c_buf_iop                 048      4
+  struct clist                 tty.t_outq                           04c     20
+    int                        tty.t_outq.c_cc                      04c      4
+    int                        tty.t_outq.c_cmax                    050      4
+    int                        tty.t_outq.c_cfx                     054      4
+    int                        tty.t_outq.c_clx                     058      4
+    struct tty *               tty.t_outq.c_tp_cpu                  05c      4
+    struct tty *               tty.t_outq.c_tp_iop                  060      4
+    unsigned char *            tty.t_outq.c_buf_cpu                 064      4
+    unsigned char *            tty.t_outq.c_buf_iop                 068      4
+  (*int)()                     tty.t_oproc_cpu                      06c      4
+  (*int)()                     tty.t_oproc_iop                      070      4
+  (*int)()                     tty.t_stopproc_cpu                   074      4
+  (*int)()                     tty.t_stopproc_iop                   078      4
+  struct thread *              tty.t_rsel                           07c      4
+
+  etc.
+
+
+Actually, this was generated by a particular set of options.  You can control
+the formatting of each column, whether you prefer wide or fat, hex or decimal,
+leading zeroes or whatever.
+
+All you need to be able to use this is a C compiler than generates
+BSD/GCC-style stabs.  The -g option on native BSD compilers and GCC
+should get this for you.   
+
+To learn more, just type a bogus option, like -\?, and a long usage message
+will be provided.  There are a fair number of possibilities.
+
+If you're only a C programmer, than this is the end of the message for you.
+You can quit right now, and if you care to, save off the source and run it
+when you feel like it.  Or not.
+
+
+
+But if you're a perl programmer, then for you I have something much more
+wondrous than just a structure offset printer.
+
+You see, if you call pstruct by its other incybernation, c2ph, you have a code
+generator that translates C code into perl code!  Well, structure and union 
+declarations at least, but that's quite a bit.  
+
+Prior to this point, anyone programming in perl who wanted to interact
+with C programs, like the kernel, was forced to guess the layouts of the C
+strutures, and then hardwire these into his program.  Of course, when you
+took your wonderfully to a system where the sgtty structure was laid out
+differently, you program broke.  Which is a shame.
+
+We've had Larry's h2ph translator, which helped, but that only works on
+cpp symbols, not real C, which was also very much needed.  What I offer
+you is a symbolic way of getting at all the C structures.  I've couched
+them in terms of packages and functions.  Consider the following program:
+
+    #!/usr/local/bin/perl
+
+    require 'syscall.ph';
+    require 'sys/time.ph';
+    require 'sys/resource.ph';
+
+    $ru = "\0" x &rusage'sizeof();
+
+    syscall(&SYS_getrusage, &RUSAGE_SELF, $ru)      && die "getrusage: $!";
+
+    @ru = unpack($t = &rusage'typedef(), $ru);
+
+    $utime =  $ru[ &rusage'ru_utime + &timeval'tv_sec  ]
+          + ($ru[ &rusage'ru_utime + &timeval'tv_usec ]) / 1e6;
+
+    $stime =  $ru[ &rusage'ru_stime + &timeval'tv_sec  ]
+          + ($ru[ &rusage'ru_stime + &timeval'tv_usec ]) / 1e6;
+
+    printf "you have used %8.3fs+%8.3fu seconds.\n", $utime, $stime;
+
+
+As you see, the name of the package is the name of the structure.  Regular
+fields are just their own names.  Plus the follwoing  accessor functions are
+provided for your convenience:
+
+    struct     This takes no arguments, and is merely the number of first-level
+               elements in the structure.  You would use this for indexing
+               into arrays of structures, perhaps like this
+
+                   
+                   $usec = $u[ &user'u_utimer
+                               + (&ITIMER_VIRTUAL * &itimerval'struct)
+                               + &itimerval'it_value
+                               + &timeval'tv_usec
+                             ];
+
+    sizeof     Returns the bytes in the structure, or the member if 
+               you pass it an argument, such as
+
+                       &rusage'sizeof(&rusage'ru_utime)
+
+    typedef    This is the perl format definition for passing to pack and
+               unpack.  If you ask for the typedef of a nothing, you get 
+               the whole structure, otherwise you get that of the member
+               you ask for.  Padding is taken care of, as is the magic to 
+               guarantee that a union is unpacked into all its aliases.
+               Bitfields are not quite yet supported however.
+
+    offsetof   This function is the byte offset into the array of that
+               member.  You may wish to use this for indexing directly
+               into the packed structure with vec() if you're too lazy
+               to unpack it.
+
+    typeof     Not to be confused with the typedef accessor function, this
+               one returns the C type of that field.  This would allow 
+               you to print out a nice structured pretty print of some
+               structure without knoning anything about it beforehand.
+               No args to this one is a noop.  Someday I'll post such 
+               a thing to dump out your u structure for you.
+
+
+The way I see this being used is like basically this:
+
+       % h2ph <some_include_file.h  >  /usr/lib/perl/tmp.ph
+       % c2ph  some_include_file.h  >> /usr/lib/perl/tmp.ph
+       % install 
+
+It's a little tricker with c2ph because you have to get the includes right.
+I can't know this for your system, but it's not usually too terribly difficult.  
+
+The code isn't pretty as I mentioned  -- I never thought it would be a 1000-
+line program when I started, or I might not have begun. :-)  But I would have
+been less cavalier in how the parts of the program communicated with each 
+other, etc.  It might also have helped if I didn't have to divine the makeup
+of the stabs on the fly, and then account for micro differences between my 
+compiler and gcc. 
+
+Anyway, here it is.  Should run on perl v4 or greater.  Maybe less.
+
+
+--tom
+
+
index d01bad8..df07083 100644 (file)
--- a/cflags.SH
+++ b/cflags.SH
@@ -6,7 +6,7 @@ case $CONFIG in
        ln ../../../config.sh . || \
        (echo "Can't find config.sh."; exit 1)
     fi
-    . config.sh
+    . ./config.sh
     ;;
 esac
 : This forces SH files to create target in same directory as SH file.
diff --git a/lib/chat2.pl b/lib/chat2.pl
new file mode 100644 (file)
index 0000000..916b975
--- /dev/null
@@ -0,0 +1,333 @@
+## chat.pl: chat with a server
+## V2.01.alpha.7 91/06/16
+## Randal L. Schwartz
+
+package chat;
+
+$sockaddr = 'S n a4 x8';
+chop($thishost = `hostname`); $thisaddr = (gethostbyname($thishost))[4];
+$thisproc = pack($sockaddr, 2, 0, $thisaddr);
+
+# *S = symbol for current I/O, gets assigned *chatsymbol....
+$next = "chatsymbol000000"; # next one
+$nextpat = "^chatsymbol"; # patterns that match next++, ++, ++, ++
+
+
+## $handle = &chat'open_port("server.address",$port_number);
+## opens a named or numbered TCP server
+
+sub open_port { ## public
+       local($server, $port) = @_;
+
+       local($serveraddr,$serverproc);
+
+       *S = ++$next;
+       if ($server =~ /^(\d+)+\.(\d+)\.(\d+)\.(\d+)$/) {
+               $serveraddr = pack('C4', $1, $2, $3, $4);
+       } else {
+               local(@x) = gethostbyname($server);
+               return undef unless @x;
+               $serveraddr = $x[4];
+       }
+       $serverproc = pack($sockaddr, 2, $port, $serveraddr);
+       unless (socket(S, 2, 1, 6)) {
+               # XXX hardwired $AF_SOCKET, $SOCK_STREAM, 'tcp'
+               # but who the heck would change these anyway? (:-)
+               ($!) = ($!, close(S)); # close S while saving $!
+               return undef;
+       }
+       unless (bind(S, $thisproc)) {
+               ($!) = ($!, close(S)); # close S while saving $!
+               return undef;
+       }
+       unless (connect(S, $serverproc)) {
+               ($!) = ($!, close(S)); # close S while saving $!
+               return undef;
+       }
+       select((select(S), $| = 1)[0]);
+       $next; # return symbol for switcharound
+}
+
+## ($host, $port, $handle) = &chat'open_listen([$port_number]);
+## opens a TCP port on the current machine, ready to be listened to
+## if $port_number is absent or zero, pick a default port number
+## process must be uid 0 to listen to a low port number
+
+sub open_listen { ## public
+
+       *S = ++$next;
+       local($thisport) = shift || 0;
+       local($thisproc_local) = pack($sockaddr, 2, $thisport, $thisaddr);
+       local(*NS) = "__" . time;
+       unless (socket(NS, 2, 1, 6)) {
+               # XXX hardwired $AF_SOCKET, $SOCK_STREAM, 'tcp'
+               # but who the heck would change these anyway? (:-)
+               ($!) = ($!, close(NS));
+               return undef;
+       }
+       unless (bind(NS, $thisproc_local)) {
+               ($!) = ($!, close(NS));
+               return undef;
+       }
+       unless (listen(NS, 1)) {
+               ($!) = ($!, close(NS));
+               return undef;
+       }
+       select((select(NS), $| = 1)[0]);
+       local($family, $port, @myaddr) =
+               unpack("S n C C C C x8", getsockname(NS));
+       $S{"needs_accept"} = *NS; # so expect will open it
+       (@myaddr, $port, $next); # returning this
+}
+
+## $handle = &chat'open_proc("command","arg1","arg2",...);
+## opens a /bin/sh on a pseudo-tty
+
+sub open_proc { ## public
+       local(@cmd) = @_;
+
+       *S = ++$next;
+       local(*TTY) = "__TTY" . time;
+       local($pty,$tty) = &_getpty(S,TTY);
+       die "Cannot find a new pty" unless defined $pty;
+       local($pid) = fork;
+       die "Cannot fork: $!" unless defined $pid;
+       unless ($pid) {
+               close STDIN; close STDOUT; close STDERR;
+               setpgrp(0,$$);
+               if (open(DEVTTY, "/dev/tty")) {
+                   ioctl(DEVTTY,0x20007471,0);         # XXX s/b &TIOCNOTTY
+                   close DEVTTY;
+               }
+               open(STDIN,"<&TTY");
+               open(STDOUT,">&TTY");
+               open(STDERR,">&STDOUT");
+               die "Oops" unless fileno(STDERR) == 2;  # sanity
+               close(S);
+               exec @cmd;
+               die "Cannot exec @cmd: $!";
+       }
+       close(TTY);
+       $next; # return symbol for switcharound
+}
+
+# $S is the read-ahead buffer
+
+## $return = &chat'expect([$handle,] $timeout_time,
+##     $pat1, $body1, $pat2, $body2, ... )
+## $handle is from previous &chat'open_*().
+## $timeout_time is the time (either relative to the current time, or
+## absolute, ala time(2)) at which a timeout event occurs.
+## $pat1, $pat2, and so on are regexs which are matched against the input
+## stream.  If a match is found, the entire matched string is consumed,
+## and the corresponding body eval string is evaled.
+##
+## Each pat is a regular-expression (probably enclosed in single-quotes
+## in the invocation).  ^ and $ will work, respecting the current value of $*.
+## If pat is 'TIMEOUT', the body is executed if the timeout is exceeded.
+## If pat is 'EOF', the body is executed if the process exits before
+## the other patterns are seen.
+##
+## Pats are scanned in the order given, so later pats can contain
+## general defaults that won't be examined unless the earlier pats
+## have failed.
+##
+## The result of eval'ing body is returned as the result of
+## the invocation.  Recursive invocations are not thought
+## through, and may work only accidentally. :-)
+##
+## undef is returned if either a timeout or an eof occurs and no
+## corresponding body has been defined.
+## I/O errors of any sort are treated as eof.
+
+$nextsubname = "expectloop000000"; # used for subroutines
+
+sub expect { ## public
+       if ($_[0] =~ /$nextpat/) {
+               *S = shift;
+       }
+       local($endtime) = shift;
+
+       local($timeout,$eof) = (1,1);
+       local($caller) = caller;
+       local($rmask, $nfound, $timeleft, $thisbuf);
+       local($cases, $pattern, $action, $subname);
+       $endtime += time if $endtime < 600_000_000;
+
+       if (defined $S{"needs_accept"}) { # is it a listen socket?
+               local(*NS) = $S{"needs_accept"};
+               delete $S{"needs_accept"};
+               $S{"needs_close"} = *NS;
+               unless(accept(S,NS)) {
+                       ($!) = ($!, close(S), close(NS));
+                       return undef;
+               }
+               select((select(S), $| = 1)[0]);
+       }
+
+       # now see whether we need to create a new sub:
+
+       unless ($subname = $expect_subname{$caller,@_}) {
+               # nope.  make a new one:
+               $expect_subname{$caller,@_} = $subname = $nextsubname++;
+
+               $cases .= <<"EDQ"; # header is funny to make everything elsif's
+sub $subname {
+       LOOP: {
+               if (0) { ; }
+EDQ
+               while (@_) {
+                       ($pattern,$action) = splice(@_,0,2);
+                       if ($pattern =~ /^eof$/i) {
+                               $cases .= <<"EDQ";
+               elsif (\$eof) {
+                       package $caller;
+                       $action;
+               }
+EDQ
+                               $eof = 0;
+                       } elsif ($pattern =~ /^timeout$/i) {
+                       $cases .= <<"EDQ";
+               elsif (\$timeout) {
+                       package $caller;
+                       $action;
+               }
+EDQ
+                               $timeout = 0;
+                       } else {
+                               $pattern =~ s#/#\\/#g;
+                       $cases .= <<"EDQ";
+               elsif (\$S =~ /$pattern/) {
+                       \$S = \$';
+                       package $caller;
+                       $action;
+               }
+EDQ
+                       }
+               }
+               $cases .= <<"EDQ" if $eof;
+               elsif (\$eof) {
+                       undef;
+               }
+EDQ
+               $cases .= <<"EDQ" if $timeout;
+               elsif (\$timeout) {
+                       undef;
+               }
+EDQ
+               $cases .= <<'ESQ';
+               else {
+                       $rmask = "";
+                       vec($rmask,fileno(S),1) = 1;
+                       ($nfound, $rmask) =
+                               select($rmask, undef, undef, $endtime - time);
+                       if ($nfound) {
+                               $nread = sysread(S, $thisbuf, 1024);
+                               if ($nread > 0) {
+                                       $S .= $thisbuf;
+                               } else {
+                                       $eof++, redo LOOP; # any error is also eof
+                               }
+                       } else {
+                               $timeout++, redo LOOP; # timeout
+                       }
+                       redo LOOP;
+               }
+       }
+}
+ESQ
+               eval $cases; die "$cases:\n$@" if $@;
+       }
+       $eof = $timeout = 0;
+       do $subname();
+}
+
+## &chat'print([$handle,] @data)
+## $handle is from previous &chat'open().
+## like print $handle @data
+
+sub print { ## public
+       if ($_[0] =~ /$nextpat/) {
+               *S = shift;
+       }
+       print S @_;
+}
+
+## &chat'close([$handle,])
+## $handle is from previous &chat'open().
+## like close $handle
+
+sub close { ## public
+       if ($_[0] =~ /$nextpat/) {
+               *S = shift;
+       }
+       close(S);
+       if (defined $S{"needs_close"}) { # is it a listen socket?
+               local(*NS) = $S{"needs_close"};
+               delete $S{"needs_close"};
+               close(NS);
+       }
+}
+
+## @ready_handles = &chat'select($timeout, @handles)
+## select()'s the handles with a timeout value of $timeout seconds.
+## Returns an array of handles that are ready for I/O.
+## Both user handles and chat handles are supported (but beware of
+## stdio's buffering for user handles).
+
+sub select { ## public
+       local($timeout) = shift;
+       local(@handles) = @_;
+       local(%handlename) = ();
+       local(%ready) = ();
+       local($caller) = caller;
+       local($rmask) = "";
+       for (@handles) {
+               if (/$nextpat/o) { # one of ours... see if ready
+                       local(*SYM) = $_;
+                       if (length($SYM)) {
+                               $timeout = 0; # we have a winner
+                               $ready{$_}++;
+                       }
+                       $handlename{fileno($_)} = $_;
+               } else {
+                       $handlename{fileno(/'/ ? $_ : "$caller\'$_")} = $_;
+               }
+       }
+       for (sort keys %handlename) {
+               vec($rmask, $_, 1) = 1;
+       }
+       select($rmask, undef, undef, $timeout);
+       for (sort keys %handlename) {
+               $ready{$handlename{$_}}++ if vec($rmask,$_,1);
+       }
+       sort keys %ready;
+}
+
+# ($pty,$tty) = $chat'_getpty(PTY,TTY):
+# internal procedure to get the next available pty.
+# opens pty on handle PTY, and matching tty on handle TTY.
+# returns undef if can't find a pty.
+
+sub _getpty { ## private
+       local($_PTY,$_TTY) = @_;
+       $_PTY =~ s/^([^']+)$/(caller)[$[]."'".$1/e;
+       $_TTY =~ s/^([^']+)$/(caller)[$[]."'".$1/e;
+       local($pty,$tty);
+       for $bank (112..127) {
+               next unless -e sprintf("/dev/pty%c0", $bank);
+               for $unit (48..57) {
+                       $pty = sprintf("/dev/pty%c%c", $bank, $unit);
+                       open($_PTY,"+>$pty") || next;
+                       select((select($_PTY), $| = 1)[0]);
+                       ($tty = $pty) =~ s/pty/tty/;
+                       open($_TTY,"+>$tty") || next;
+                       select((select($_TTY), $| = 1)[0]);
+                       system "stty nl>$tty";
+                       return ($pty,$tty);
+               }
+       }
+       undef;
+}
+
+1;
index 98702f8..bc5f1c8 100644 (file)
@@ -1 +1 @@
-#define PATCHLEVEL 11
+#define PATCHLEVEL 12