566 lines
15 KiB
Perl
Executable File
566 lines
15 KiB
Perl
Executable File
#!/usr/annex/perl
|
|
#
|
|
# kcons, the perl version.
|
|
# kgrab merged into this.
|
|
# kfree too
|
|
# klist also
|
|
# ksteal as well
|
|
#
|
|
# $Header: /proj/irix6.5.7m/isms/eoe/cmd/ktools/RCS/ksteal,v 1.7 1996/01/23 08:15:03 ack Exp $
|
|
# $Revision: 1.7 $
|
|
# $Modified: Fri Apr 21 14:49:48 1995 by cwilson $
|
|
# Set up a session between this system and the indicated target
|
|
# (debug) system's console port. The session is composed of
|
|
# a TELNET process.
|
|
#
|
|
# If invoked as kgrab,
|
|
# Set up a session between this system and the indicated target
|
|
# (debug) system. The session is composed of a TELNET
|
|
# process for each console port on the target, and a shell
|
|
# window for each debug port on the target. The shells for
|
|
# the debug ports will have the KDBXPORT environment variable
|
|
# set to the appropriate path (ie: /dev/annex/foo.d0).
|
|
#
|
|
#
|
|
# -lock locks the port
|
|
# -cu uses cu (if possible) to talk to the system.
|
|
# -nodebug|-nodbg -- we won't bring up the debug ports if running as kgrab.
|
|
# -q quiet mode. Mostly for quietly stealing a tap session
|
|
#
|
|
|
|
$REVISION = '$Revision: 1.7 $'; # ";" <-- this fixes indenting and highlighting
|
|
$TELNET = 'telnet';
|
|
$RCFILE = "$ENV{'HOME'}/.kgrabrc"; # for kgrab
|
|
$KWHO = '/usr/annex/kwho';
|
|
$HOSTNAME = `hostname -s`;chop $HOSTNAME;
|
|
$CONSOLE = 0; # default console to grab.
|
|
$| = 1;
|
|
$PORTSFILE = '/usr/annex/ktools.map';
|
|
|
|
# we can now require this, since the ktools are an inst image
|
|
# that has pre-reqs on eoe2.sw.gifts_perl or perl.sw.perl
|
|
require 'newgetopt.pl';
|
|
|
|
|
|
&NGetOpt( "c:s", "lock", "cu", "steal", "grab", "f", "a:s",
|
|
"nodebug", "nodbg", "debug", "dbg", "DEBUG", "noyp", "q");
|
|
$opt_nodbg += $opt_nodebug;
|
|
$opt_dbg += $opt_debug;
|
|
|
|
$REVISION =~ s/.*(\d\.\d+).*/$1/;
|
|
|
|
`/etc/chkconfig yp`;
|
|
$KWHO .= ' -noyp' if $opt_noyp || $? ;
|
|
print (stderr "Sorry, you can't use the ktools with the -noyp option until you copy\n" .
|
|
"the ktools configuration file, ktools.map, to your system.\n"), exit 1 if $opt_noyp && !-e $PORTSFILE;
|
|
|
|
$CONSOLE = $ARGV[1] if $ARGV[1];
|
|
|
|
&checkclasses; # validate classes, if any
|
|
|
|
&kfree if ($0 =~ /kfree$/);
|
|
|
|
&klist if ($0 =~ /klist$/);
|
|
|
|
&Usage, exit(1) if $#ARGV > 1 || $#ARGV == -1;
|
|
|
|
&kgrab_setup if ($0 =~ /kgrab/ || $opt_grab);
|
|
|
|
|
|
# if attempting to use cu, make sure there's a device to talk to
|
|
#
|
|
if ($opt_cu) {
|
|
# verify device entry
|
|
$dev = `grep $ARGV[0] /usr/annex/cu_device_names`;
|
|
|
|
# verify rtelnetd is running
|
|
if ($dev) {
|
|
$rt = `ps -elf|grep rtelnetd`;
|
|
}
|
|
if (!$dev || !$rt) {
|
|
print stderr "cu device doesn't exist or rtelenetd not running.\n";
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
@kwho = `$KWHO $ARGV[0]`;
|
|
|
|
&tapcheck if $#kwho; # check to see if there's a tap session running
|
|
|
|
&ksteal if ($0 =~ /ksteal$/ || $opt_steal);
|
|
|
|
if (($sysinfo = &ypmatch($ARGV[0]))) {
|
|
&Doit($ARGV[0]);
|
|
&checkversion;
|
|
exit(0);
|
|
}
|
|
else {
|
|
print "$0: Unknown system: $ARGV[0]\n";
|
|
&Usage, exit(1);
|
|
}
|
|
|
|
#
|
|
# ypmatch:
|
|
# do the ypmatch thing
|
|
#
|
|
sub ypmatch {
|
|
local($foo) = $opt_noyp ? `grep ^$_[0] $PORTSFILE` : `ypmatch -k $_[0] ktools 2>&1`;
|
|
|
|
if ($opt_noyp) {
|
|
if ($foo !~ /$_[0]/) { return 0; }
|
|
$foo =~ s/(.*)$_[0]\s*(.*)/$2/;
|
|
} else {
|
|
if ($foo !~ /$_[0]:/) { return 0; }
|
|
$foo =~ s/(.*)$_[0]: (.*)/$2/;
|
|
}
|
|
$foo;
|
|
}
|
|
|
|
#
|
|
# ypcat:
|
|
# do the ypcat thing
|
|
#
|
|
sub ypcat {
|
|
local(@foo) = $opt_noyp ? `cat $PORTSFILE` : `ypcat -k ktools 2>&1`;
|
|
|
|
@foo;
|
|
}
|
|
|
|
#
|
|
# slurpMachInfo:
|
|
# a single point to futz with splitting the info from the maps.
|
|
# presumes that $_[0] holds the info string.
|
|
#
|
|
sub slurpMachInfo {
|
|
|
|
($M_desc, $M_mach_type, $M_mktg_mach_type, $M_chassis, $M_gfx, $M_mem,
|
|
$M_special, $M_loc, $M_asset, $M_serial, $M_notes, $M_owner,
|
|
$M_owner_uname, $M_owner_ext, $M_ports, $M_annex, $M_reset,
|
|
$M_consoles, $M_dbg_consoles, $M_access) = split('`', $_[0]);
|
|
|
|
@M_consoles = split(',', $M_consoles);
|
|
@M_dbg_consoles = split(',', $M_dbg_consoles);
|
|
grep(s/\s//g, @M_consoles);
|
|
grep(s/\s//g, @M_dbg_consoles);
|
|
|
|
$M_notes =~ s/^\s+//;
|
|
$M_full_owner = '';
|
|
$M_full_owner = "$M_owner ($M_owner_uname), $M_owner_ext" if
|
|
( length($M_owner) && length($M_owner_uname) && length($M_owner_ext));
|
|
|
|
}
|
|
|
|
#
|
|
# Doit:
|
|
# start the telnet session
|
|
#
|
|
sub Doit {
|
|
$machine = $ARGV[0];
|
|
&slurpMachInfo($sysinfo);
|
|
grep(s/r//, @M_consoles);
|
|
&PrintNote if $M_notes;
|
|
|
|
$ARGV[0] .= ".con.$CONSOLE";# if $CONSOLE;
|
|
if (grep(/$ARGV[0]/, @kwho)) {
|
|
print "$ARGV[0] appears to be in use:\n", @kwho;
|
|
exit(1) ;
|
|
}
|
|
# print "$TELNET $annex 50$Console{$_}\n";
|
|
|
|
if ($0 =~ /kgrab/) {
|
|
# tell the annex box who's connected.
|
|
foreach ( defined($ARGV[1]) ? $M_consoles[$CONSOLE] :
|
|
@M_consoles) {
|
|
&NA($_, $M_annex);
|
|
}
|
|
if ($opt_dbg) {
|
|
foreach ( defined($ARGV[1]) ? $M_dbg_consoles[$CONSOLE] :
|
|
@M_dbg_consoles) {
|
|
&NA($_, $M_annex);
|
|
}
|
|
}
|
|
foreach ( defined($ARGV[1]) ? $CONSOLE :
|
|
(0..$#M_consoles)) {
|
|
if ($CMD eq "xwsh") {
|
|
$conargs = "-icontitle $machine" . "_c_$_ -title $machine" . "_cons_$_";
|
|
$dbgargs = "-icontitle $machine" . "_debug_$_ -title $machine" . "_debug_$_";
|
|
*x = $machine . '_con_' . $_;
|
|
$conargs .= " $x";
|
|
|
|
*x = $machine . '_dbg_' . $_;
|
|
$dbgargs .= " $x";
|
|
|
|
}
|
|
elsif ($CMD eq "xterm") {
|
|
$conargs = "-n $machine" . "_c_$_ -title $machine" . "_c_$_";
|
|
$dbgargs = "-n $machine" . "_debug_$_ -title $machine" . "_debug_$_";
|
|
}
|
|
print "$CMD $iconic $con_geom[$_] $conargs -e $TELNET $M_annex 50$M_consoles[$_] &\n" if $opt_DEBUG;
|
|
system "$CMD $iconic $con_geom[$_] $conargs -e $TELNET $M_annex 50$M_consoles[$_] &\n";
|
|
$ENV{'KDBXPORT'} = "/dev/annex/$machine.dbg.$_";
|
|
print "$CMD $iconic $dbg_geom[$_] $dbgargs -e $ENV{'SHELL'} &\n" if !$opt_nodbg && $opt_DEBUG;
|
|
system "$CMD $iconic $dbg_geom[$_] $dbgargs -e $ENV{'SHELL'} &\n" if $opt_dbg;
|
|
}
|
|
}
|
|
else { # kcons
|
|
# change the xwsh title bar && icon if we've got an xwsh
|
|
if ($ENV{'TERM'} eq 'iris-ansi') {
|
|
print "P1.ykcons:$machine\\P3.ykcons:$machine\\";
|
|
}
|
|
$M_consoles[$CONSOLE] =~ s/^r//;
|
|
&NA($M_consoles[$CONSOLE], $M_annex);
|
|
if (!$opt_cu) {
|
|
print "$TELNET $M_annex 50$M_consoles[$CONSOLE]\n" if $opt_DEBUG;
|
|
system("$TELNET $M_annex 50$M_consoles[$CONSOLE];echo 'P1.y$HOSTNAME\\P3.y$HOSTNAME\\'");
|
|
}
|
|
else {
|
|
print "cu -lannex/$machine.con.$CONSOLE\n" if $opt_DEBUG;
|
|
system("cu -lannex/$machine.con.$CONSOLE;echo 'P1.y$HOSTNAME\\P3.y$HOSTNAME\\'");
|
|
}
|
|
}
|
|
}
|
|
|
|
# talk to na to set the username for the port
|
|
#
|
|
#
|
|
sub NA {
|
|
local ($port, $annex) = @_;
|
|
$user = $opt_lock ? '[' . $ENV{'USER'} . ']' : $ENV{'USER'};
|
|
system $opt_DEBUG ? "/bin/echo 'set port=$port\@$annex user_name \"$user\"' | /usr/annex/na" :
|
|
"/bin/echo 'set port=$port\@$annex user_name \"$user\"' | /usr/annex/na 1>/dev/null 2>&1";
|
|
}
|
|
|
|
# usage:
|
|
#
|
|
sub Usage {
|
|
print "Usage: $0 sysname\nWhere sysname is one of:\n";
|
|
&ListMachines;
|
|
}
|
|
|
|
# checkversion:
|
|
# check to make sure that we've got the latest version of this
|
|
#
|
|
sub checkversion {
|
|
$nis_version = &ypmatch("kcons_version");
|
|
if (!$nis_version) {
|
|
printf(stderr "\n$0: unable to find kcons_version in ktools NIS map.\n");
|
|
} else {
|
|
if ($nis_version > $REVISION) {
|
|
printf(stderr "\n NOTICE: A newer version of $0 is available. \n Please upgrade at your earliest convenience\n");
|
|
printf(stderr " for added functionality and bug fixes.\n Install from babylon:/usr/dist/misc/ktools\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
# checkclasses: verify that there's a classes map to check against, and that
|
|
# the KTOOLS_CLASS envariable has valid values.
|
|
#
|
|
sub checkclasses {
|
|
chop ($x = &ypmatch('classes'));
|
|
if (!length($x)) {
|
|
die "No classes found in ktools NIS map.\n";
|
|
}
|
|
|
|
foreach $class (split ('`', $x)) {
|
|
$Class{$class} = &ypmatch($class . '_config');
|
|
}
|
|
|
|
if ($ENV{KTOOLS_CLASS}) {
|
|
foreach (split(':', $ENV{KTOOLS_CLASS})) {
|
|
if (!$Class{$_}) {
|
|
print "ERROR: Unknown class: $ENV{KTOOLS_CLASS}. Valid classes are:\n";
|
|
&PrintClasses;
|
|
exit 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
########################################
|
|
#
|
|
sub PrintClasses {
|
|
$^ = 'ch';
|
|
$~ = 'cd';
|
|
|
|
foreach (keys Class) {
|
|
($C_desc, @x) = split('`', $Class{$_});
|
|
$x = join(',', @x);
|
|
$C_name = $_;
|
|
write;
|
|
}
|
|
}
|
|
|
|
########################################
|
|
#
|
|
sub ListMachines {
|
|
$~ = 'mach';
|
|
@ypinfo = &ypcat;
|
|
foreach (@ypinfo) {
|
|
/^(\S+)\s+(.*)/;
|
|
$ktools{$1} = $2;
|
|
}
|
|
if (defined ($_[0])) {
|
|
@classes = $_[0];
|
|
} elsif (defined ($ENV{'KTOOLS_CLASS'})) {
|
|
@classes = split(':', $ENV{'KTOOLS_CLASS'});
|
|
}
|
|
|
|
if (defined @classes) {
|
|
foreach $class (@classes) {
|
|
$annexes = (split('`', $ktools{$class . '_config'}))[4];
|
|
foreach $annex (split(',', $annexes)) {
|
|
push( machines, split('`', $ktools{$annex . '_machines'}));
|
|
}
|
|
}
|
|
} else {
|
|
foreach (split('`', $ktools{'annexes'})) {
|
|
if ($opt_a) {
|
|
push( machines, split('`', $ktools{$_ . '_machines'}))
|
|
if $opt_a eq $_;
|
|
} else {
|
|
push( machines, split('`', $ktools{$_ . '_machines'}));
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach $machine (sort @machines) {
|
|
$machine =~ s/\s//g;
|
|
$M_mach = $machine;
|
|
&slurpMachInfo($ktools{$machine});
|
|
write;
|
|
}
|
|
}
|
|
|
|
# tapcheck: check to see if a tap session is running
|
|
# if so, steal it.
|
|
#
|
|
sub tapcheck {
|
|
if ((($who) = grep(/$ARGV[0].con.$CONSOLE/, @kwho)) && $who =~ /tapper/) {
|
|
print STDERR "Tap session for $who found, stealing.\n" if $opt_DEBUG;
|
|
$opt_f = 1;
|
|
$opt_steal = 1;
|
|
$opt_q = 1 if !$opt_DEBUG;
|
|
@kwho = (); # so kcons later succeeds
|
|
&ksteal;
|
|
}
|
|
}
|
|
|
|
# ksteal: Steal a session from someone else
|
|
#
|
|
sub ksteal {
|
|
if (@kwho) { # somebody's using the desired machine
|
|
@x = grep(/$ARGV[0].con.$CONSOLE/, @kwho);
|
|
$host = substr($x[0], 19, 15); $host =~ s/\s//g;
|
|
$person = substr($x[0], 37, 11); $person =~ s/\s//g;
|
|
$idle = substr($x[0], 65, 11); $idle =~ s/\s//g;
|
|
if ($person =~ /\[(\w+)\]/) {
|
|
$person = $1;
|
|
print "$ARGV[0] is being used by $person@$host, idle $idle \n";
|
|
print "However, it has been locked. Please contact $person directly\n";
|
|
print "and have them release the port(s).\n";
|
|
exit 1;
|
|
}
|
|
print "$ARGV[0] is being used by $person@$host, idle $idle \n" if !$opt_q;
|
|
if (($hour, $min) = $idle =~ /(\d+):(\d+)/) {
|
|
$waylong = 1 if $hour >= 2;
|
|
}
|
|
$waylong = $opt_f if $opt_f;
|
|
if (!$waylong) {
|
|
print "Can't steal $ARGV[0], idle time less than 2 hours.\n" if !$opt_q;
|
|
exit (1);
|
|
}
|
|
}
|
|
&ResetEm;
|
|
grep(s/$ARGV[0]//, @kwho); # remove old status info from kwho
|
|
$stole = $opt_f ? "forcefully stole" : "stole";
|
|
`echo "$ENV{'USER'} $stole $ARGV[0] from you at $DATE, idle $idle\n" | Mail -s 'Annex Box Notice' $person@$host` if !$opt_q;
|
|
}
|
|
|
|
#########################################
|
|
#
|
|
sub PrintNote {
|
|
$~ = note;
|
|
print "NOTES:\n";
|
|
write;
|
|
}
|
|
#########################################
|
|
#
|
|
sub ResetEm {
|
|
print "Stealing $ARGV[0]\n" if !$opt_q;
|
|
$sysinfo = &ypmatch($ARGV[0]);
|
|
&slurpMachInfo($sysinfo);
|
|
|
|
# $consoles = (split('`', $sysinfo))[17]; $consoles =~ s/,\s*$//;
|
|
# $annex = (split('`', $sysinfo))[15];
|
|
|
|
$M_consoles =~ s/r//g;
|
|
if ($opt_DEBUG) {
|
|
print "/bin/echo 'reset $M_consoles@$M_annex' | /usr/annex/na 1>/dev/null 2>&1\n";
|
|
system "/bin/echo 'reset $M_consoles@$M_annex' | /usr/annex/na";
|
|
} else {
|
|
system "/bin/echo 'reset $M_consoles@$M_annex' | /usr/annex/na 1>/dev/null 2>&1";
|
|
}
|
|
}
|
|
|
|
#########################################
|
|
#
|
|
sub klist {
|
|
if (defined $opt_c && !length($opt_c)) {
|
|
&PrintClasses;
|
|
exit 0;
|
|
} else {
|
|
if (length($opt_c)) {
|
|
print "\n Machines hooked up to the annex boxes:\n" . ' ' x 15 . "class = $opt_c\n" . '=' x 50 . "\n";
|
|
} else {
|
|
print "\n Machines hooked up to the annex boxes:\n" . ' ' x 15 . "KTOOLS_CLASS = $ENV{KTOOLS_CLASS}\n" . '=' x 50 . "\n";
|
|
}
|
|
&ListMachines($opt_c);
|
|
&checkversion;
|
|
exit 0;
|
|
}
|
|
}
|
|
|
|
#########################################
|
|
#
|
|
sub kfree {
|
|
$~ = 'mach';
|
|
@kwho = `$KWHO`;
|
|
@ypinfo = &ypcat;
|
|
foreach (@ypinfo) {
|
|
/^(\S+)\s+(.*)/;
|
|
$ktools{$1} = $2;
|
|
}
|
|
if (length($opt_c)) {
|
|
@classes = $opt_c;
|
|
} else {
|
|
@classes = split(':', $ENV{'KTOOLS_CLASS'});
|
|
}
|
|
|
|
if (defined @classes) {
|
|
foreach $class (@classes) {
|
|
foreach $annex (split(',', (split('`', $ktools{$class . '_config'}))[4])) {
|
|
push( machines, split('`', $ktools{$annex . '_machines'}));
|
|
}
|
|
}
|
|
} else {
|
|
foreach (split('`', $ktools{'annexes'})) {
|
|
push( machines, split('`', $ktools{$_ . '_machines'}));
|
|
}
|
|
}
|
|
|
|
print " Unused Machines";
|
|
if (length($opt_c)) {
|
|
print " (class = $opt_c):\n";
|
|
} else {
|
|
print $ENV{'KTOOLS_CLASS'} ? " (KTOOLS_CLASS = $ENV{'KTOOLS_CLASS'}):\n" : ":\n";
|
|
}
|
|
print '=' x 60 . "\n";
|
|
foreach $machine (sort @machines) {
|
|
$machine =~ s/\s//g;
|
|
$M_mach = $machine;
|
|
&slurpMachInfo($ktools{$machine});
|
|
if (!grep(/$M_mach/, @kwho)) {
|
|
write;
|
|
}
|
|
}
|
|
&checkversion;
|
|
exit 0;
|
|
}
|
|
|
|
#########################################
|
|
#
|
|
sub kgrab_setup {
|
|
#customizable defaults:
|
|
$con1_geom = ""; $dbg1_geom = "";
|
|
$con2_geom = ""; $dbg2_geom = "";
|
|
$con3_geom = ""; $dbg3_geom = "";
|
|
$con4_geom = ""; $dbg4_geom = "";
|
|
$con5_geom = ""; $dbg5_geom = "";
|
|
$con6_geom = ""; $dbg6_geom = "";
|
|
$con7_geom = ""; $dbg7_geom = "";
|
|
$con8_geom = ""; $dbg8_geom = "";
|
|
$CMD = "xwsh";
|
|
$openiconic = 0;
|
|
#
|
|
|
|
#pick up any customizations from the user:
|
|
if ( -r $RCFILE) {
|
|
open (RCFILE, $RCFILE);
|
|
while (<RCFILE>) {
|
|
s/set /\$/; $_ .= ';';
|
|
eval "$_";
|
|
}
|
|
}
|
|
|
|
$opt_dbg = $debug if $debug;
|
|
@con_geom = ($con1_geom, $con2_geom, $con3_geom, $con4_geom,
|
|
$con5_geom, $con6_geom, $con7_geom, $con8_geom);
|
|
|
|
@dbg_geom = ($dbg1_geom, $dbg2_geom, $dbg3_geom, $dbg4_geom,
|
|
$dbg5_geom, $dbg6_geom, $dbg7_geom, $dbg8_geom);
|
|
$iconic = '-iconic' if $openiconic; #
|
|
}
|
|
|
|
#########################################
|
|
#
|
|
sub Keys {
|
|
local ($sub) = $_[1];
|
|
local (@keys) = eval "keys %$_[0]";
|
|
local (@return);
|
|
foreach (@keys) {
|
|
push (@return, $1) if /$sub$;(\S+)/;
|
|
}
|
|
@return;
|
|
}
|
|
|
|
sub Values {
|
|
local ($sub) = $_[1];
|
|
local (@keys) = &Keys($_[0], $sub);
|
|
local (@return);
|
|
foreach (@keys) {
|
|
eval "push (\@return, \$$_[0]{$sub, $_})";
|
|
}
|
|
@return;
|
|
}
|
|
|
|
sub Defined {
|
|
local ($sub) = $_[1];
|
|
local (@keys) = &Keys($_[0], $sub);
|
|
foreach (@keys) {
|
|
return 1 if eval "defined \$$_[0]{$sub, $_}";
|
|
}
|
|
return 0;
|
|
}
|
|
format mach =
|
|
@<<<<<<<<< @<<<<< @<<<<<<<<<< @<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
|
$M_mach, $M_mach_type, $M_mktg_mach_type, $M_mem, $M_desc
|
|
~ @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
|
"$M_loc $M_full_owner"
|
|
.
|
|
|
|
format note =
|
|
~^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
|
$M_notes
|
|
~^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
|
$M_notes
|
|
~^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
|
$M_notes
|
|
~^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
|
$M_notes
|
|
~^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
|
$M_notes
|
|
~^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
|
$M_notes
|
|
.
|
|
|
|
format ch =
|
|
Class name Class description Class owner, email, extension
|
|
---------- ----------------- -----------------------------
|
|
.
|
|
|
|
format cd =
|
|
@<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
|
$_, $C_desc, $x
|
|
.
|
|
|