package my_gtk; # $Id$ use diagnostics; use strict; use vars qw(@ISA %EXPORT_TAGS @EXPORT_OK $border); @ISA = qw(Exporter); %EXPORT_TAGS = ( helpers => [ qw(create_okcancel createScrolledWindow create_menu create_notebook create_packtable create_hbox create_vbox create_adjustment create_box_with_title create_treeitem) ], wrappers => [ qw(gtksignal_connect gtkradio gtkpack gtkpack_ gtkpack__ gtkpack2 gtkpack2_ gtkpack2__ gtkpowerpack gtkset_editable gtksetstyle gtkset_tip gtkappenditems gtkappend gtkset_shadow_type gtkset_layout gtkset_relief gtkadd gtkput gtktext_insert gtkset_usize gtksize gtkset_justify gtkset_active gtkset_sensitive gtkset_visibility gtkset_modal gtkset_border_width gtkmove gtkresize gtkshow gtkhide gtkdestroy gtkcolor gtkset_mousecursor gtkset_mousecursor_normal gtkset_mousecursor_wait gtkset_background gtkset_default_fontset gtkctree_children gtkxpm gtkpng create_pix_text get_text_coord fill_tiled gtkicons_labels_widget write_on_pixmap gtkcreate_xpm gtkcreate_png gtkbuttonset create_pixbutton gtkroot gtkentry) ], ask => [ qw(ask_warn ask_okcancel ask_yesorno ask_from_entry ask_browse_tree_info ask_browse_tree_info_given_widgets ask_dir) ], various => [ qw(add_icon_path) ], ); $EXPORT_TAGS{all} = [ map { @$_ } values %EXPORT_TAGS ]; @EXPORT_OK = map { @$_ } values %EXPORT_TAGS; use ugtk qw(:helpers :wrappers :various); use common; use log; add_icon_path(@ugtk::icon_paths, "$ENV{SHARE_PATH}/libDrakX/pixmaps", '/usr/lib/libDrakX/icons', 'standalone/icons'); my $forgetTime = 1000; #- in milli-seconds $border = 5; #-############################################################################### #- OO stuff #-############################################################################### sub new { my ($type, $title, %opts) = @_; Gtk->set_locale; my $o = bless { %opts }, $type; $o->_create_window($title); while (my $e = shift @tempory::objects) { $e->destroy } foreach (@interactive::objects) { $_->{rwindow}->set_modal(0) if $_->{rwindow}->can('set_modal'); } push @interactive::objects, $o if !$opts{no_interactive_objects}; $o->{rwindow}->set_position('center_always') if $::isStandalone; $o->{rwindow}->set_modal(1) if $my_gtk::grab || $o->{grab}; if ($::isWizard && !$my_gtk::pop_it) { $o->{isWizard} = 1; $o->{window} = new Gtk::VBox(0,0); $o->{window}->set_border_width($::Wizard_splash ? 0 : 10); $o->{rwindow} = $o->{window}; if (!defined($::WizardWindow)) { $::WizardWindow = new Gtk::Window; $::WizardWindow->set_position('center_always'); $::WizardWindow->signal_connect(delete_event => sub { die 'wizcancel' }); $::WizardTable = new Gtk::Table(2, 2, 0); $::WizardWindow->add($::WizardTable); my $draw1 = new Gtk::DrawingArea; $draw1->set_usize(540,100); my $draw2 = new Gtk::DrawingArea; $draw2->set_usize(100,300); my ($im_up, $_mask_up) = gtkcreate_png($::Wizard_pix_up || "wiz_default_up.png"); my ($y1, $x1) = $im_up->get_size; my ($im_left, $_mask_left) = gtkcreate_png($::Wizard_pix_left || "wiz_default_left.png"); my ($y2, $x2) = $im_left->get_size; my $style = $draw1->style->copy; $style->font(Gtk::Gdk::Font->fontset_load(N("-adobe-utopia-regular-r-*-*-25-*-*-*-p-*-iso8859-*,*-r-*"))); $draw1->signal_connect(expose_event => sub { for (my $i = 0; $i < 540/$y1; $i++) { $draw1->window->draw_pixmap($draw1->style->bg_gc('normal'), $im_up, 0, 0, 0, $y1*$i, $x1, $y1); $draw1->window->draw_string( $style->font, $draw1->style->white_gc, 40, 62, ($::Wizard_title)); } }); $draw2->signal_connect(expose_event => sub { for (my $i = 0; $i < 300/$y2; $i++) { $draw2->window->draw_pixmap($draw2->style->bg_gc('normal'), $im_left, 0, 0, 0, $y2*$i, $x2, $y2); } }); $::WizardTable->attach($draw1, 0, 2, 0, 1, 'fill', 'fill', 0, 0); #- $::WizardTable->attach($draw2, 0, 1, 1, 2, 'fill', 'fill', 0, 0); $::WizardTable->set_usize(540,420); $::WizardWindow->show_all; flush(); } $::WizardTable->attach($o->{window}, 0, 2, 1, 2, ['fill', 'expand'], ['fill', 'expand'], 0, 0); } if ($::isEmbedded && !$my_gtk::pop_it && !eval { $::Plug && $::Plug->child }) { $o->{isEmbedded} = 1; $o->{window} = new Gtk::HBox(0,0); $o->{rwindow} = $o->{window}; $::Plug ||= new Gtk::Plug($::XID); $::Plug->show; flush(); $::Plug->add($o->{window}); } $o; } sub main { my ($o, $completed, $canceled) = @_; gtkset_mousecursor_normal(); my $timeout = Gtk->timeout_add(1000, sub { gtkset_mousecursor_normal(); 1 }); my $_b = MDK::Common::Func::before_leaving { Gtk->timeout_remove($timeout) }; $o->show; do { Gtk->main; } while ($o->{retval} ? $completed && !$completed->() : $canceled && !$canceled->()); $o->destroy; $o->{retval} } sub show($) { my ($o) = @_; $o->{window}->show; $o->{rwindow}->show; } sub destroy($) { my ($o) = @_; $o->{rwindow} and $o->{rwindow}->destroy; gtkset_mousecursor_wait(); flush(); } sub DESTROY { goto &destroy } sub sync { my ($o) = @_; show($o); flush(); } sub flush { gtkflush() } sub exit { gtkset_mousecursor_normal(); #- for restoring a normal in any case flush(); c::_exit($_[1]) #- workaround } #- in case "exit" above was not called by the program END { print "BUG in $0 : my_gtk->exit was *NOT* called !! \n"; &exit() } #-############################################################################### #- createXXX functions #- these functions return a widget #-############################################################################### sub create_okcancel { my ($w, $ok, $cancel, $spread, @other) = @_; $spread ||= $::isWizard ? "end" : "spread"; $cancel = $::isWizard ? N("<- Previous") : N("Cancel") if !defined $cancel && !defined $ok; $ok = $::isWizard ? ($::Wizard_finished ? N("Finish") : N("Next ->")) : N("Ok") if !defined $ok; my $b1 = gtksignal_connect($w->{ok} = new Gtk::Button($ok), clicked => $w->{ok_clicked} || sub { $w->{retval} = 1; Gtk->main_quit }); my $b2 = $cancel && gtksignal_connect($w->{cancel} = new Gtk::Button($cancel), clicked => $w->{cancel_clicked} || sub { log::l("default cancel_clicked"); undef $w->{retval}; Gtk->main_quit }); $::isWizard and gtksignal_connect($w->{wizcancel} = new Gtk::Button(N("Cancel")), clicked => sub { die 'wizcancel' }); my @l = grep { $_ } $::isWizard ? ($w->{wizcancel}, if_(!$::Wizard_no_previous, $b2, $b1)) : ($b1, $b2); push @l, map { gtksignal_connect(new Gtk::Button($_->[0]), clicked => $_->[1]) } @other; $_->can_default($::isWizard) foreach @l; gtkadd(create_hbox($spread), @l); } sub _create_window($$) { my ($o, $title) = @_; my $w = new Gtk::Window; my $gc = Gtk::Gdk::GC->new(gtkroot()); !$::isStandalone && !$::live && !$::g_auto_install and $my_gtk::shape_width = 3; #- $gc->set_foreground(gtkcolor(8448, 17664, 40191)); #- in hex : 33, 69, 157 $gc->set_foreground(gtkcolor(5120, 10752, 22784)); #- in hex : 20, 42, 89 #- $gc->set_foreground(gtkcolor(16896, 16896, 16896)); #- in hex : 66, 66, 66 my $inner = gtkadd(gtkset_shadow_type(new Gtk::Frame(undef), 'out'), my $f = gtkset_border_width(gtkset_shadow_type(new Gtk::Frame(undef), 'none'), 3) ); my $table; if ($::isStandalone || $::live || $::g_auto_install || $::noShadow) { gtkadd($w, $inner) if !$::noBorder } else { my $sqw = $my_gtk::shape_width; gtkadd($w, $table = new Gtk::Table(2, 2, 0)); $table->attach($inner, 0, 1, 0, 1, 1|4, 1|4, 0, 0); $table->attach(gtksignal_connect(gtkset_usize(new Gtk::DrawingArea, $sqw, 1), expose_event => sub { $_[0]->window->draw_rectangle($_[0]->style->bg_gc('normal'), 1, 0, 0, $sqw, $sqw); $_[0]->window->draw_rectangle($gc, 1, 0, $sqw, $sqw, $_[0]->allocation->[3]); }), 1, 2, 0, 1, 'fill', 'fill', 0, 0); $table->attach(gtksignal_connect(gtkset_usize(new Gtk::DrawingArea, 1, $sqw), expose_event => sub { $_[0]->window->draw_rectangle($_[0]->style->bg_gc('normal'), 1, 0, 0, $sqw, $sqw); $_[0]->window->draw_rectangle($gc, 1, $sqw, 0, $_[0]->allocation->[2], $sqw); }), 0, 1, 1, 2, 'fill', 'fill', 0, 0); $table->attach(gtksignal_connect(gtkset_usize(new Gtk::DrawingArea, $sqw, $sqw), expose_event => sub { $_[0]->window->draw_rectangle($gc, 1, 0, 0, $sqw, $sqw); }), 1, 2, 1, 2, 'fill', 'fill', 0, 0); $table->show_all; } $w->set_name("Title"); $w->set_title($title); $w->signal_connect(expose_event => sub { eval { $interactive::objects[-1]{rwindow} == $w and $w->window->XSetInputFocus } }) if $my_gtk::force_focus || $o->{force_focus}; $w->signal_connect(delete_event => sub { if ($::isWizard) { $w->destroy; die 'wizcancel' } else { Gtk->main_quit } }); $w->set_uposition(@{$my_gtk::force_position || $o->{force_position}}) if $my_gtk::force_position || $o->{force_position}; my $focusing; $w->signal_connect(focus => sub { return 1 if $focusing; $focusing = 1; Gtk->idle_add(sub { $w->ensure_focus($_[0]); $focusing = 0; 0 }, $_[1]); }) if $w->can('ensure_focus'); if ($::o->{mouse}{unsafe}) { $w->set_events("pointer_motion_mask"); my $signal; $signal = $w->signal_connect(motion_notify_event => sub { delete $::o->{mouse}{unsafe}; log::l("unsetting unsafe mouse"); $w->signal_disconnect($signal); }); } $w->signal_connect(key_press_event => sub { my $d = ${{ 0xffbe => 'help', 0xffbf => 'screenshot' }}{$_[1]{keyval}}; if ($d eq "help") { require install_gtk; install_gtk::create_big_help($::o); } elsif ($::isInstall && $d eq 'screenshot') { common::take_screenshot(); } elsif (chr($_[1]{keyval}) eq 'e' && $_[1]{state} & 8) { log::l("Switching to " . ($::expert ? "beginner" : "expert")); $::expert = !$::expert; } }); $w->signal_connect(size_allocate => sub { my ($wi, $he) = @{$_[1]}[2,3]; my ($X, $Y, $Wi, $He) = @{$my_gtk::force_center || $o->{force_center}}; $w->set_uposition(max(0, $X + ($Wi - $wi) / 2), max(0, $Y + ($He - $he) / 2)); if (!$::isStandalone && !$::live && !$::g_auto_install && !$::noShadow) { my $sqw = $my_gtk::shape_width; #square width my $wia = int(($wi+7)/8); my $s = "\xFF" x ($wia*$he); my $wib = $wia*8; my $dif = $wib-$wi; foreach my $y (0..$sqw-1) { vec($s, $wib-1-$dif-$_+$wib*$y, 1) = 0x0 foreach 0..$sqw-1 } foreach my $y (0..$sqw-1) { vec($s, (($he-1)*$wib)-$wib*$y+$_, 1) = 0x0 foreach 0..$sqw-1 } $w->realize; my $b = Gtk::Gdk::Bitmap->create_from_data($w->window, $s, $wib, $he); $w->window->shape_combine_mask($b, 0, 0); } }) if ($my_gtk::force_center || $o->{force_center}) && !($my_gtk::force_position || $o->{force_position}); $o->{window} = $::noBorder ? $w : $f; $o->{rwindow} = $w; $table and $table->draw(undef); } #-############################################################################### #- ask_XXX #- just give a title and some args, and it will return the value given by the user #-############################################################################### sub ask_warn { my $w = my_gtk->new(shift @_); $w->_ask_warn(@_); main($w) } sub ask_yesorno { my $w = my_gtk->new(shift @_); $w->_ask_okcancel(@_, N("Yes"), N("No")); main($w) } sub ask_okcancel { my $w = my_gtk->new(shift @_); $w->_ask_okcancel(@_, N("Is this correct?"), N("Ok"), N("Cancel")); main($w) } sub ask_from_entry { my $w = my_gtk->new(shift @_); $w->_ask_from_entry(@_); main($w) } sub ask_dir { my $w = my_gtk->new(shift @_); $w->_ask_dir(@_); main($w) } sub _ask_from_entry($$@) { my ($o, @msgs) = @_; my $entry = new Gtk::Entry; my $f = sub { $o->{retval} = $entry->get_text; Gtk->main_quit }; $o->{ok_clicked} = $f; $o->{cancel_clicked} = sub { undef $o->{retval}; Gtk->main_quit }; gtkadd($o->{window}, gtkpack($o->create_box_with_title(@msgs), gtksignal_connect($entry, 'activate' => $f), ($o->{hide_buttons} ? () : create_okcancel($o))), ); $entry->grab_focus; } sub _ask_warn($@) { my ($o, @msgs) = @_; gtkadd($o->{window}, gtkpack($o->create_box_with_title(@msgs), gtksignal_connect(my $w = new Gtk::Button(N("Ok")), "clicked" => sub { Gtk->main_quit }), ), ); $w->grab_focus; } sub _ask_okcancel($@) { my ($o, @msgs) = @_; my ($ok, $cancel) = splice @msgs, -2; gtkadd($o->{window}, gtkpack(create_box_with_title($o, @msgs), create_okcancel($o, $ok, $cancel), ) ); $o->{ok}->grab_focus; } sub _ask_file { my ($o, $title, $path) = @_; my $f = $o->{rwindow} = new Gtk::FileSelection($title); $f->set_filename($path); $f->ok_button->signal_connect(clicked => sub { $o->{retval} = $f->get_filename; Gtk->main_quit }); $f->cancel_button->signal_connect(clicked => sub { Gtk->main_quit }); $f->hide_fileop_buttons; $f; } sub _ask_dir { my $f = _ask_file(@_); $f->file_list->parent->hide; $f->selection_entry->parent->hide; } sub ask_browse_tree_info { my ($common) = @_; my $w = my_gtk->new($common->{title}); my $tree = Gtk::CTree->new(3, 0); $tree->set_selection_mode('browse'); $tree->set_column_auto_resize($_, 1) foreach 1..2; $tree->set_column_width(0, 200); gtkadd($w->{window}, gtkpack_(new Gtk::VBox(0,5), 0, $common->{message}, 1, gtkpack(new Gtk::HBox(0,0), createScrolledWindow($tree), gtkadd(gtkset_usize(new Gtk::Frame(N("Info")), $::windowwidth - 490, 0), createScrolledWindow(my $info = new Gtk::Text), )), 0, my $l = new Gtk::HBox(0,15), 0, gtkpack(new Gtk::HBox(0,10), my $go = gtksignal_connect(new Gtk::Button($common->{ok}), "clicked" => sub { $w->{retval} = 1; Gtk->main_quit }), $common->{cancel} ? gtksignal_connect(new Gtk::Button($common->{cancel}), "clicked" => sub { $w->{retval} = 0; Gtk->main_quit }) : (), ) )); gtkpack__($l, my $toolbar = new Gtk::Toolbar('horizontal', 'icons')); if ($common->{auto_deps}) { gtkpack__($l, gtksignal_connect(gtkset_active(new Gtk::CheckButton($common->{auto_deps}), $common->{state}{auto_deps}), clicked => sub { invbool \$common->{state}{auto_deps} })); } $l->pack_end(my $status = new Gtk::Label, 0, 1, 20); $w->{window}->set_usize(map { $_ - 2 * $my_gtk::border - 4 } $::windowwidth, $::windowheight); $go->grab_focus; $w->{rwindow}->show_all; my @toolbar = (ftout => [ N("Expand Tree"), sub { $tree->expand_recursive(undef) } ], ftin => [ N("Collapse Tree"), sub { $tree->collapse_recursive(undef) } ], reload => [ N("Toggle between flat and group sorted"), sub { invbool(\$common->{state}{flat}); $common->{rebuild_tree}->() } ]); foreach my $ic (@{$common->{icons} || []}) { push @toolbar, ($ic->{icon} => [ $ic->{help}, sub { if ($ic->{code}) { my $_w = $ic->{wait_message} && $common->{wait_message}->('', $ic->{wait_message}); $ic->{code}(); $common->{rebuild_tree}->(); } } ]); } my %toolbar = @toolbar; $toolbar->set_button_relief("none"); foreach (grep_index { $::i % 2 == 0 } @toolbar) { gtksignal_connect($toolbar->append_item(undef, $toolbar{$_}[0], undef, gtkpng("$_.png")), clicked => $toolbar{$_}[1]); } $toolbar->set_style("icons"); my $widgets = { w => $w, tree => $tree, info => $info, status => $status }; ask_browse_tree_info_given_widgets($common, $widgets); } sub ask_browse_tree_info_given_widgets { my ($common, $w) = @_; my ($curr, $parent, $prev_label, $idle); my (%wtree, %ptree, %pix); my $update_size = sub { my $new_label = $common->{get_status}(); $prev_label ne $new_label and $w->{status}->set($prev_label = $new_label); }; my $set_node_state_flat = sub { my ($node, $state) = @_; $state eq 'XXX' and return; $pix{$state} ||= [ gtkcreate_png($state) ]; $w->{tree}->node_set_pixmap($node, 1, $pix{$state}[0], $pix{$state}[1]); }; my $set_node_state_tree; $set_node_state_tree = sub { my ($node, $state) = @_; $state eq 'XXX' and return; $pix{$state} ||= [ gtkcreate_png($state) ]; if ($node->{state} ne $state) { if ($node->row->is_leaf) { my $parent = $node->row->parent; my $stats = $parent->{state_stats} ||= {}; --$stats->{$node->{state}}; ++$stats->{$state}; my @list = grep { $stats->{$_} > 0 } keys %$stats; my $new_state = @list == 1 ? $list[0] : 'semiselected'; $parent->{state} ne $new_state and $set_node_state_tree->($parent, $new_state); } $w->{tree}->node_set_pixmap($node, 1, $pix{$state}[0], $pix{$state}[1]); $node->{state} = $state; #- hack to to get this features efficiently. } }; my $set_node_state = $common->{state}{flat} ? $set_node_state_flat : $set_node_state_tree; my $set_leaf_state = sub { my ($leaf, $state) = @_; $set_node_state->($_, $state) foreach @{$ptree{$leaf}}; }; my $add_parent; $add_parent = sub { my ($root, $state) = @_; $root or return undef; if (my $w = $wtree{$root}) { return $w } my $s; foreach (split '\|', $root) { my $s2 = $s ? "$s|$_" : $_; $wtree{$s2} ||= do { my $n = $w->{tree}->insert_node($s ? $add_parent->($s, $state) : undef, undef, [$_, '', ''], 5, (undef) x 4, 0, 0); $n; }; $s = $s2; } $set_node_state->($wtree{$s}, $state); #- use this state by default as tree is building. $wtree{$s}; }; my $add_node = sub { my ($leaf, $root, $options) = @_; my $state = $common->{node_state}($leaf) or return; if ($leaf) { my $node = $w->{tree}->insert_node($add_parent->($root, $state), undef, [$leaf, '', ''], 5, (undef) x 4, 1, 0); $set_node_state->($node, $state); push @{$ptree{$leaf}}, $node; } else { my $parent = $add_parent->($root, $state); #- hackery for partial displaying of trees, used in rpmdrake: #- if leaf is void, we may create the parent and one child (to have the [+] in front of the parent in the ctree) #- though we use '' as the label of the child; then rpmdrake will connect on tree_expand, and whenever #- the first child has '' as the label, it will remove the child and add all the "right" children $options->{nochild} or $w->{tree}->insert_node($parent, undef, ['', '', ''], 5, (undef) x 4, 1, 0); } }; $common->{delete_all} = sub { foreach (values %ptree) { delete $_->{state} foreach @$_; } foreach (values %wtree) { delete $_->{state}; delete $_->{state_stats}; } %ptree = %wtree = (); $w->{tree}->freeze; $w->{tree}->clear; $w->{tree}->thaw; }; $common->{rebuild_tree} = sub { $common->{delete_all}->(); $set_node_state = $common->{state}{flat} ? $set_node_state_flat : $set_node_state_tree; $w->{tree}->freeze; $common->{build_tree}($add_node, $common->{state}{flat}, $common->{tree_mode}); $w->{tree}->thaw; &$update_size; }; $common->{delete_category} = sub { my ($cat) = @_; exists $wtree{$cat} or return; $w->{tree}->freeze; foreach (keys %ptree) { my @to_remove; foreach my $node (@{$ptree{$_}}) { my $category; my $parent = $node; while ($parent->row->parent) { $parent = $parent->row->parent; my $parent_name = ($w->{tree}->node_get_pixtext($parent, 0))[0]; $category = $category ? "$parent_name|$category" : $parent_name; } $cat eq $category and push @to_remove, $node; } delete $_->{state} foreach @to_remove; @{$ptree{$_}} = difference2($ptree{$_}, \@to_remove); } if (exists $wtree{$cat}) { delete $wtree{$cat}{$_} foreach qw(state state_stats); $w->{tree}->remove_node($wtree{$cat}); delete $wtree{$cat}; } $w->{tree}->thaw; &$update_size; }; $common->{add_nodes} = sub { my (@nodes) = @_; $w->{tree}->freeze; $add_node->($_->[0], $_->[1], $_->[2]) foreach @nodes; $w->{tree}->thaw; &$update_size; }; $common->{display_info} = sub { gtktext_insert($w->{info}, $common->{get_info}($curr)); 0 }; my $children = sub { map { ($w->{tree}->node_get_pixtext($_, 0))[0] } gtkctree_children($_[0]) }; my $toggle = sub { if (ref $curr && ! $_[0]) { $w->{tree}->toggle_expansion($curr); } else { if (ref $curr) { my @l = $common->{grep_allowed_to_toggle}($children->($curr)) or return; my @unsel = $common->{grep_unselected}(@l); my @p = @unsel ? #- not all is selected, select all if no option to potentially override (exists $common->{partialsel_unsel} && $common->{partialsel_unsel}->(\@unsel, \@l) ? difference2(\@l, \@unsel) : @unsel) : @l; $common->{toggle_nodes}($set_leaf_state, @p); &$update_size; $parent = $curr; } else { $common->{check_interactive_to_toggle}($curr) and $common->{toggle_nodes}($set_leaf_state, $curr); &$update_size; } } }; $w->{tree}->signal_connect(key_press_event => sub { my ($_w, $e) = @_; my $c = chr($e->{keyval} & 0xff); $toggle->(0) if $e->{keyval} >= 0x100 ? $c eq "\r" || $c eq "\x8d" : $c eq ' '; 1; }); $w->{tree}->signal_connect(tree_select_row => sub { Gtk->timeout_remove($idle) if $idle; if ($_[1]->row->is_leaf) { ($curr) = $w->{tree}->node_get_pixtext($_[1], 0); $parent = $_[1]->row->parent; $idle = Gtk->timeout_add(100, $common->{display_info}); } else { $curr = $_[1]; } $toggle->(1) if $_[2] == 1; }); $common->{rebuild_tree}->(); &$update_size; my $_b = before_leaving { #- ensure cleaning here. foreach (values %ptree) { delete $_->{state} foreach @$_; } foreach (values %wtree) { delete $_->{state}; delete $_->{state_stats}; } }; $w->{w}->main; } 1; #-############################################################################### #- rubbish #-############################################################################### #-sub label_align($$) { #- my $w = shift; #- local $_ = shift; #- $w->set_alignment(!/W/i, !/N/i); #- $w #-} s='add'>mdk-stage1/slang/slarith.inc
diff --git a/mdk-stage1/Makefile b/mdk-stage1/Makefile index 9a3ecae43..96eade486 100644 --- a/mdk-stage1/Makefile +++ b/mdk-stage1/Makefile @@ -47,22 +47,17 @@ INITOBJS = $(subst .c,.o,$(INITSRC)) #- frontends NEWT_FRONTEND_SRC = newt-frontend.c -NEWT_FRONTEND_LIBS = /usr/lib/libnewt.a /usr/lib/libslang.a +GLIBC_NEWT_FRONTEND_LIBS = newt/libnewt.a slang/libslang.a +DIETLIBC_NEWT_FRONTEND_LIBS = $(subst .a,-DIET.a,$(GLIBC_NEWT_FRONTEND_LIBS)) STDIO_FRONTEND_SRC = stdio-frontend.c -STDIO_FRONTEND_LIBS = +GLIBC_STDIO_FRONTEND_LIBS = +DIETLIBC_STDIO_FRONTEND_LIBS = FRONTEND_OBJS = $(subst .c,.o,$($(F)_FRONTEND_SRC)) -FRONTEND_LIBS = $($(F)_FRONTEND_LIBS) -FRONTEND_LINK = $(FRONTEND_OBJS) $(FRONTEND_LIBS) - -ifeq (DIETLIBC, $(L)) -ifeq (NEWT, $(F)) -FRONTEND_LINK = $(subst .c,.o,$(STDIO_FRONTEND_SRC)) $(STDIO_FRONTEND_LIBS) -endif -endif +FRONTEND_LINK = $(FRONTEND_OBJS) $($(L)_$(F)_FRONTEND_LIBS) ifeq (i386, $(ARCH)) INSMOD = insmod-busybox @@ -71,7 +66,7 @@ INSMOD = insmod-modutils endif GLIBC_STAGE1_OWN_LIBS = $(INSMOD)/libinsmod.a mar/libmar.a bzlib/libbzlib.a -DIETLIBC_STAGE1_OWN_LIBS = $(INSMOD)/libinsmod-DIET.a mar/libmar-DIET.a bzlib/libbzlib-DIET.a +DIETLIBC_STAGE1_OWN_LIBS = $(subst .a,-DIET.a,$(GLIBC_STAGE1_OWN_LIBS)) STAGE1_OWN_LIBS = $($(L)_STAGE1_OWN_LIBS) @@ -125,7 +120,7 @@ BINS += stage1-cdrom stage1-disk stage1-network endif -DIRS = dietlibc mar pci-resource bzlib $(INSMOD) +DIRS = dietlibc mar pci-resource bzlib $(INSMOD) slang newt ifeq (i386,$(ARCH)) DIRS += pcmcia endif diff --git a/mdk-stage1/newt-frontend.c b/mdk-stage1/newt-frontend.c index 85f56ec10..867a6d7f1 100644 --- a/mdk-stage1/newt-frontend.c +++ b/mdk-stage1/newt-frontend.c @@ -33,7 +33,7 @@ #include <sys/time.h> #include "stage1.h" #include "log.h" -#include "newt.h" +#include "newt/newt.h" #include "frontend.h" diff --git a/mdk-stage1/newt/Makefile b/mdk-stage1/newt/Makefile new file mode 100644 index 000000000..c4e6a7207 --- /dev/null +++ b/mdk-stage1/newt/Makefile @@ -0,0 +1,49 @@ + #****************************************************************************** + # + # Guillaume Cottenceau (gc@mandrakesoft.com) + # + # Copyright 2000 MandrakeSoft + # + # This software may be freely redistributed under the terms of the GNU + # public license. + # + # You should have received a copy of the GNU General Public License + # along with this program; if not, write to the Free Software + # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + # + #***************************************************************************** + +top_dir = .. + +include $(top_dir)/Makefile.common + + +all: libnewt.a libnewt-DIET.a + +clean: + rm -f *.o *.a + +FLAGS = -Wall -Werror -Os -fomit-frame-pointer -DVERSION=\"0.50.19\" -c + +INCS = -I../slang + + +OBJS = newt.o button.o form.o checkbox.o entry.o label.o listbox.o scrollbar.o textbox.o scale.o grid.o windows.o buttonbar.o checkboxtree.o +OBJS-DIET = $(subst .o,-DIET.o,$(OBJS)) + + +libnewt.a: $(OBJS) + ar -cru $@ $^ + ranlib $@ + +libnewt-DIET.a: $(OBJS-DIET) + ar -cru $@ $^ + ranlib $@ + + +$(OBJS): %.o: %.c + gcc $(FLAGS) $(GLIBC_INCLUDES) $(INCS) -c $< -o $@ + +$(OBJS-DIET): %-DIET.o: %.c + gcc $(FLAGS) $(DIETLIBC_INCLUDES) $(INCS) -c $< -o $@ + diff --git a/mdk-stage1/newt/button.c b/mdk-stage1/newt/button.c new file mode 100644 index 000000000..1ff360dc5 --- /dev/null +++ b/mdk-stage1/newt/button.c @@ -0,0 +1,190 @@ +#include <slang.h> +#include <stdlib.h> +#include <string.h> + +#include "newt.h" +#include "newt_pr.h" + +struct button { + char * text; + int compact; +}; + +static void buttonDrawIt(newtComponent co, int active, int pushed); +static void buttonDrawText(newtComponent co, int active, int pushed); + +static void buttonDraw(newtComponent c); +static void buttonDestroy(newtComponent co); +static struct eventResult buttonEvent(newtComponent c, + struct event ev); +static void buttonPlace(newtComponent co, int newLeft, int newTop); + +static struct componentOps buttonOps = { + buttonDraw, + buttonEvent, + buttonDestroy, + buttonPlace, + newtDefaultMappedHandler, +} ; + +static newtComponent createButton(int left, int row, const char * text, int compact) { + newtComponent co; + struct button * bu; + + co = malloc(sizeof(*co)); + bu = malloc(sizeof(struct button)); + co->data = bu; + + bu->text = strdup(text); + bu->compact = compact; + co->ops = &buttonOps; + + if (bu->compact) { + co->height = 1; + co->width = strlen(text) + 3; + } else { + co->height = 4; + co->width = strlen(text) + 5; + } + + co->top = row; + co->left = left; + co->takesFocus = 1; + co->isMapped = 0; + + newtGotorc(co->top, co->left); + + return co; +} + +newtComponent newtCompactButton(int left, int row, const char * text) { + return createButton(left, row, text, 1); +} + +newtComponent newtButton(int left, int row, const char * text) { + return createButton(left, row, text, 0); +} + +static void buttonDestroy(newtComponent co) { + struct button * bu = co->data; + + free(bu->text); + free(bu); + free(co); +} + +static void buttonPlace(newtComponent co, int newLeft, int newTop) { + co->top = newTop; + co->left = newLeft; + + newtGotorc(co->top, co->left); +} + +static void buttonDraw(newtComponent co) { + buttonDrawIt(co, 0, 0); +} + +static void buttonDrawIt(newtComponent co, int active, int pushed) { + struct button * bu = co->data; + + if (!co->isMapped) return; + + SLsmg_set_color(NEWT_COLORSET_BUTTON); + + if (bu->compact) { + if (active) + SLsmg_set_color(NEWT_COLORSET_COMPACTBUTTON); + else + SLsmg_set_color(NEWT_COLORSET_BUTTON); + newtGotorc(co->top+ pushed, co->left + 1 + pushed); + SLsmg_write_char('<'); + SLsmg_write_string(bu->text); + SLsmg_write_char('>'); + } else { + if (pushed) { + SLsmg_set_color(NEWT_COLORSET_BUTTON); + newtDrawBox(co->left + 1, co->top + 1, co->width - 1, 3, 0); + + SLsmg_set_color(NEWT_COLORSET_WINDOW); + newtClearBox(co->left, co->top, co->width, 1); + newtClearBox(co->left, co->top, 1, co->height); + } else { + newtDrawBox(co->left, co->top, co->width - 1, 3, 1); + } + + buttonDrawText(co, active, pushed); + } +} + +static void buttonDrawText(newtComponent co, int active, int pushed) { + struct button * bu = co->data; + + if (pushed) pushed = 1; + + if (active) + SLsmg_set_color(NEWT_COLORSET_ACTBUTTON); + else + SLsmg_set_color(NEWT_COLORSET_BUTTON); + + newtGotorc(co->top + 1 + pushed, co->left + 1 + pushed); + SLsmg_write_char(' '); + SLsmg_write_string(bu->text); + SLsmg_write_char(' '); +} + +static struct eventResult buttonEvent(newtComponent co, + struct event ev) { + struct eventResult er; + struct button * bu = co->data; + + if (ev.when == EV_NORMAL) { + switch (ev.event) { + case EV_FOCUS: + buttonDrawIt(co, 1, 0); + er.result = ER_SWALLOWED; + break; + + case EV_UNFOCUS: + buttonDrawIt(co, 0, 0); + er.result = ER_SWALLOWED; + break; + + case EV_KEYPRESS: + if (ev.u.key == ' ' || ev.u.key == '\r') { + if (!bu->compact) { + /* look pushed */ + buttonDrawIt(co, 1, 1); + newtRefresh(); + newtDelay(150000); + buttonDrawIt(co, 1, 0); + newtRefresh(); + newtDelay(150000); + } + + er.result = ER_EXITFORM; + } else + er.result = ER_IGNORED; + break; + case EV_MOUSE: + if (ev.u.mouse.type == MOUSE_BUTTON_DOWN && + co->top <= ev.u.mouse.y && + co->top + co->height - !bu->compact > ev.u.mouse.y && + co->left <= ev.u.mouse.x && + co->left + co->width - !bu->compact > ev.u.mouse.x) { + if (!bu->compact) { + buttonDrawIt(co, 1, 1); + newtRefresh(); + newtDelay(150000); + buttonDrawIt(co, 1, 0); + newtRefresh(); + newtDelay(150000); + } + er.result = ER_EXITFORM; + } + break; + } + } else + er.result = ER_IGNORED; + + return er; +} diff --git a/mdk-stage1/newt/buttonbar.c b/mdk-stage1/newt/buttonbar.c new file mode 100644 index 000000000..45473c9d2 --- /dev/null +++ b/mdk-stage1/newt/buttonbar.c @@ -0,0 +1,46 @@ +#include <stdarg.h> + +#include "newt.h" + +/* if they try and pack more then 50 buttons, screw 'em */ +newtGrid newtButtonBarv(char * button1, newtComponent * b1comp, va_list args) { + newtGrid grid; + struct buttonInfo { + char * name; + newtComponent * compPtr; + } buttons[50]; + int num; + int i; + + buttons[0].name = button1, buttons[0].compPtr = b1comp, num = 1; + while (1) { + buttons[num].name = va_arg(args, char *); + if (!buttons[num].name) break; + buttons[num].compPtr = va_arg(args, newtComponent *); + num++; + } + + grid = newtCreateGrid(num, 1); + + for (i = 0; i < num; i++) { + *buttons[i].compPtr = newtButton(-1, -1, buttons[i].name); + newtGridSetField(grid, i, 0, NEWT_GRID_COMPONENT, + *buttons[i].compPtr, + num ? 1 : 0, 0, 0, 0, 0, 0); + } + + return grid; +} + +newtGrid newtButtonBar(char * button1, newtComponent * b1comp, ...) { + va_list args; + newtGrid grid; + + va_start(args, b1comp); + + grid = newtButtonBarv(button1, b1comp, args); + + va_end(args); + + return grid; +} diff --git a/mdk-stage1/newt/checkbox.c b/mdk-stage1/newt/checkbox.c new file mode 100644 index 000000000..eee514c98 --- /dev/null +++ b/mdk-stage1/newt/checkbox.c @@ -0,0 +1,290 @@ +#include <slang.h> +#include <stdlib.h> +#include <string.h> + +#include "newt.h" +#include "newt_pr.h" + +enum type { CHECK, RADIO }; + +struct checkbox { + char * text; + char * seq; + char * result; + newtComponent prevButton, lastButton; + enum type type; + char value; + int active, inactive; + const void * data; + int flags; + int hasFocus; +}; + +static void makeActive(newtComponent co); + +static void cbDraw(newtComponent c); +static void cbDestroy(newtComponent co); +struct eventResult cbEvent(newtComponent co, struct event ev); + +static struct componentOps cbOps = { + cbDraw, + cbEvent, + cbDestroy, + newtDefaultPlaceHandler, + newtDefaultMappedHandler, +} ; + +newtComponent newtRadiobutton(int left, int top, const char * text, int isDefault, + newtComponent prevButton) { + newtComponent co; + newtComponent curr; + struct checkbox * rb; + char initialValue; + + if (isDefault) + initialValue = '*'; + else + initialValue = ' '; + + co = newtCheckbox(left, top, text, initialValue, " *", NULL); + rb = co->data; + rb->type = RADIO; + + rb->prevButton = prevButton; + + for (curr = co; curr; curr = rb->prevButton) { + rb = curr->data; + rb->lastButton = co; + } + + return co; +} + +newtComponent newtRadioGetCurrent(newtComponent setMember) { + struct checkbox * rb = setMember->data; + + setMember = rb->lastButton; + rb = setMember->data; + + while (rb && rb->value != '*') { + setMember = rb->prevButton; + if (!setMember) + return NULL; + rb = setMember->data; + } + + return setMember; +} + +char newtCheckboxGetValue(newtComponent co) { + struct checkbox * cb = co->data; + + return cb->value; +} + +void newtCheckboxSetValue(newtComponent co, char value) { + struct checkbox * cb = co->data; + + *cb->result = value; + cbDraw(co); +} + +newtComponent newtCheckbox(int left, int top, const char * text, char defValue, + const char * seq, char * result) { + newtComponent co; + struct checkbox * cb; + + if (!seq) seq = " *"; + + co = malloc(sizeof(*co)); + cb = malloc(sizeof(struct checkbox)); + co->data = cb; + cb->flags = 0; + if (result) + cb->result = result; + else + cb->result = &cb->value; + + cb->text = strdup(text); + cb->seq = strdup(seq); + cb->type = CHECK; + cb->hasFocus = 0; + cb->inactive = COLORSET_CHECKBOX; + cb->active = COLORSET_ACTCHECKBOX; + defValue ? (*cb->result = defValue) : (*cb->result = cb->seq[0]); + + co->ops = &cbOps; + + co->callback = NULL; + co->height = 1; + co->width = strlen(text) + 4; + co->top = top; + co->left = left; + co->takesFocus = 1; + + return co; +} + +void newtCheckboxSetFlags(newtComponent co, int flags, enum newtFlagsSense sense) { + struct checkbox * cb = co->data; + int row, col; + + cb->flags = newtSetFlags(cb->flags, flags, sense); + + if (!(cb->flags & NEWT_FLAG_DISABLED)) + co->takesFocus = 1; + else + co->takesFocus = 0; + + newtGetrc(&row, &col); + cbDraw(co); + newtGotorc(row, col); +} + +static void cbDraw(newtComponent c) { + struct checkbox * cb = c->data; + + if (c->top == -1 || !c->isMapped) return; + + if (cb->flags & NEWT_FLAG_DISABLED) { + cb->inactive = NEWT_COLORSET_DISENTRY; + cb->active = NEWT_COLORSET_DISENTRY; + } else { + cb->inactive = COLORSET_CHECKBOX; + cb->active = COLORSET_ACTCHECKBOX; + } + + SLsmg_set_color(cb->inactive); + + newtGotorc(c->top, c->left); + + switch (cb->type) { + case RADIO: + SLsmg_write_string("( ) "); + break; + + case CHECK: + SLsmg_write_string("[ ] "); + break; + + default: + break; + } + + SLsmg_write_string(cb->text); + + if (cb->hasFocus) + SLsmg_set_color(cb->active); + + newtGotorc(c->top, c->left + 1); + SLsmg_write_char(*cb->result); +} + +static void cbDestroy(newtComponent co) { + struct checkbox * cb = co->data; + + free(cb->text); + free(cb->seq); + free(cb); + free(co); +} + +struct eventResult cbEvent(newtComponent co, struct event ev) { + struct checkbox * cb = co->data; + struct eventResult er; + const char * cur; + + if (ev.when == EV_NORMAL) { + switch (ev.event) { + case EV_FOCUS: + cb->hasFocus = 1; + cbDraw(co); + er.result = ER_SWALLOWED; + break; + + case EV_UNFOCUS: + cb->hasFocus = 0; + cbDraw(co); + er.result = ER_SWALLOWED; + break; + + case EV_KEYPRESS: + if (ev.u.key == ' ') { + if (cb->type == RADIO) { + makeActive(co); + } else if (cb->type == CHECK) { + cur = strchr(cb->seq, *cb->result); + if (!cur) + *cb->result = *cb->seq; + else { + cur++; + if (! *cur) + *cb->result = *cb->seq; + else + *cb->result = *cur; + } + cbDraw(co); + er.result = ER_SWALLOWED; + + if (co->callback) + co->callback(co, co->callbackData); + } else { + er.result = ER_IGNORED; + } + } else if(ev.u.key == NEWT_KEY_ENTER) { + er.result = ER_IGNORED; + } else { + er.result = ER_IGNORED; + } + break; + case EV_MOUSE: + if (ev.u.mouse.type == MOUSE_BUTTON_DOWN) { + if (cb->type == RADIO) { + makeActive(co); + } else if (cb->type == CHECK) { + cur = strchr(cb->seq, *cb->result); + if (!cur) + *cb->result = *cb->seq; + else { + cur++; + if (! *cur) + *cb->result = *cb->seq; + else + *cb->result = *cur; + } + cbDraw(co); + er.result = ER_SWALLOWED; + + if (co->callback) + co->callback(co, co->callbackData); + } + } + } + } else + er.result = ER_IGNORED; + + return er; +} + +static void makeActive(newtComponent co) { + struct checkbox * cb = co->data; + struct checkbox * rb; + newtComponent curr; + + /* find the one that's turned off */ + curr = cb->lastButton; + rb = curr->data; + while (curr && rb->value == rb->seq[0]) { + curr = rb->prevButton; + if (curr) rb = curr->data; + } + if (curr) { + rb->value = rb->seq[0]; + cbDraw(curr); + } + cb->value = cb->seq[1]; + cbDraw(co); + + if (co->callback) + co->callback(co, co->callbackData); +} diff --git a/mdk-stage1/newt/checkboxtree.c b/mdk-stage1/newt/checkboxtree.c new file mode 100644 index 000000000..b56bd1e9f --- /dev/null +++ b/mdk-stage1/newt/checkboxtree.c @@ -0,0 +1,714 @@ +#include <slang.h> +#include <stdlib.h> +#include <string.h> + +#include "newt.h" +#include "newt_pr.h" + +struct items { + char * text; + const void *data; + unsigned char selected; + struct items *next; + struct items *prev; + struct items *branch; + int flags; + int depth; +}; + +struct CheckboxTree { + newtComponent sb; + int curWidth; /* size of text w/o scrollbar or border*/ + int curHeight; /* size of text w/o border */ + struct items * itemlist; + struct items ** flatList, ** currItem, ** firstItem; + int flatCount; + int flags; + int pad; + char * seq; + char * result; +}; + +static void ctDraw(newtComponent c); +static void ctDestroy(newtComponent co); +static void ctPlace(newtComponent co, int newLeft, int newTop); +struct eventResult ctEvent(newtComponent co, struct event ev); +static void ctMapped(newtComponent co, int isMapped); +static struct items * findItem(struct items * items, const void * data); +static void buildFlatList(newtComponent co); +static void doBuildFlatList(struct CheckboxTree * ct, struct items * item); +enum countWhat { COUNT_EXPOSED=0, COUNT_SELECTED=1 }; +static int countItems(struct items * item, enum countWhat justExposed); + +static struct componentOps ctOps = { + ctDraw, + ctEvent, + ctDestroy, + ctPlace, + ctMapped, +} ; + +static int countItems(struct items * item, enum countWhat what) { + int count = 0; + + while (item) { + if ((!item->branch && item->selected == what) || (what == COUNT_EXPOSED)) + count++; + if (item->branch || (what == COUNT_EXPOSED && item->selected)) + count += countItems(item->branch, what); + item = item->next; + } + + return count; +} + +static void doBuildFlatList(struct CheckboxTree * ct, struct items * item) { + while (item) { + ct->flatList[ct->flatCount++] = item; + if (item->branch && item->selected) doBuildFlatList(ct, item->branch); + item = item->next; + } +} + +static void buildFlatList(newtComponent co) { + struct CheckboxTree * ct = co->data; + + if (ct->flatList) free(ct->flatList); + ct->flatCount = countItems(ct->itemlist, COUNT_EXPOSED); + + ct->flatList = malloc(sizeof(*ct->flatList) * (ct->flatCount+1)); + ct->flatCount = 0; + doBuildFlatList(ct, ct->itemlist); + ct->flatList[ct->flatCount] = NULL; +} + +int newtCheckboxTreeAddItem(newtComponent co, + const char * text, const void * data, + int flags, int index, ...) { + va_list argList; + int numIndexes; + int * indexes; + int i; + + va_start(argList, index); + numIndexes = 0; + i = index; + while (i != NEWT_ARG_LAST) { + numIndexes++; + i = va_arg(argList, int); + } + + va_end(argList); + + indexes = alloca(sizeof(*indexes) * (numIndexes + 1)); + va_start(argList, index); + numIndexes = 0; + i = index; + va_start(argList, index); + while (i != NEWT_ARG_LAST) { + indexes[numIndexes++] = i; + i = va_arg(argList, int); + } + va_end(argList); + + indexes[numIndexes++] = NEWT_ARG_LAST; + + return newtCheckboxTreeAddArray(co, text, data, flags, indexes); +} + +static int doFindItemPath(struct items * items, void * data, int * path, + int * len) { + int where = 0; + + while (items) { + if (items->data == data) { + if (path) path[items->depth] = where; + if (len) *len = items->depth + 1; + return 1; + } + + if (items->branch && doFindItemPath(items->branch, data, path, len)) { + if (path) path[items->depth] = where; + return 1; + } + + items = items->next; + where++; + } + + return 0; +} + +int * newtCheckboxTreeFindItem(newtComponent co, void * data) { + int len; + int * path; + struct CheckboxTree * ct = co->data; + + if (!doFindItemPath(ct->itemlist, data, NULL, &len)) return NULL; + + path = malloc(sizeof(*path) * (len + 1)); + doFindItemPath(ct->itemlist, data, path, NULL); + path[len] = NEWT_ARG_LAST; + + return path; +} + +int newtCheckboxTreeAddArray(newtComponent co, + const char * text, const void * data, + int flags, int * indexes) { + struct items * curList, * newNode, * item = NULL; + struct items ** listPtr = NULL; + int i, index, numIndexes; + struct CheckboxTree * ct = co->data; + + numIndexes = 0; + while (indexes[numIndexes] != NEWT_ARG_LAST) numIndexes++; + + if (!ct->itemlist) { + if (numIndexes > 1) return -1; + + ct->itemlist = malloc(sizeof(*ct->itemlist)); + item = ct->itemlist; + item->prev = NULL; + item->next = NULL; + } else { + curList = ct->itemlist; + listPtr = &ct->itemlist; + + i = 0; + index = indexes[i]; + while (i < numIndexes) { + item = curList; + + if (index == NEWT_ARG_APPEND) { + item = NULL; + } else { + while (index && item) + item = item->next, index--; + } + + i++; + if (i < numIndexes) { + curList = item->branch; + listPtr = &item->branch; + if (!curList && (i + 1 != numIndexes)) return -1; + + index = indexes[i]; + } + } + + if (!curList) { /* create a new branch */ + item = malloc(sizeof(*curList->prev)); + item->next = item->prev = NULL; + *listPtr = item; + } else if (!item) { /* append to end */ + item = curList; + while (item->next) item = item->next; + item->next = malloc(sizeof(*curList->prev)); + item->next->prev = item; + item = item->next; + item->next = NULL; + } else { + newNode = malloc(sizeof(*newNode)); + newNode->prev = item->prev; + newNode->next = item; + + if (item->prev) item->prev->next = newNode; + item->prev = newNode; + item = newNode; + if (!item->prev) *listPtr = item; + } + } + + item->text = strdup(text); + item->data = data; + if (flags & NEWT_FLAG_SELECTED) { + item->selected = 1; + } else { + item->selected = 0; + } + item->flags = flags; + item->branch = NULL; + item->depth = numIndexes - 1; + + i = 4 + (3 * item->depth); + + if ((strlen(text) + i + ct->pad) > co->width) { + co->width = strlen(text) + i + ct->pad; + } + + return 0; +} + +static struct items * findItem(struct items * items, const void * data) { + struct items * i; + + while (items) { + if (items->data == data) return items; + if (items->branch) { + i = findItem(items->branch, data); + if (i) return i; + } + + items = items->next; + } + + return NULL; +} + +static void listSelected(struct items * items, int * num, const void ** list, int seqindex) { + while (items) { + if ((seqindex ? items->selected==seqindex : items->selected) && !items->branch) + list[(*num)++] = (void *) items->data; + if (items->branch) + listSelected(items->branch, num, list, seqindex); + items = items->next; + } +} + +const void ** newtCheckboxTreeGetSelection(newtComponent co, int *numitems) +{ + return newtCheckboxTreeGetMultiSelection(co, numitems, 0); +} + +const void ** newtCheckboxTreeGetMultiSelection(newtComponent co, int *numitems, char seqnum) +{ + struct CheckboxTree * ct; + const void **retval; + int seqindex=0; + + if(!co || !numitems) return NULL; + + ct = co->data; + + if (seqnum) { + while( ct->seq[seqindex] && ( ct->seq[seqindex] != seqnum )) seqindex++; + } else { + seqindex = 0; + } + + *numitems = countItems(ct->itemlist, (seqindex ? seqindex : COUNT_SELECTED)); + if (!*numitems) return NULL; + + retval = malloc(*numitems * sizeof(void *)); + *numitems = 0; + listSelected(ct->itemlist, numitems, retval, seqindex); + + return retval; +} + +newtComponent newtCheckboxTree(int left, int top, int height, int flags) { + return newtCheckboxTreeMulti(left, top, height, NULL, flags); +} + +newtComponent newtCheckboxTreeMulti(int left, int top, int height, char *seq, int flags) { + newtComponent co; + struct CheckboxTree * ct; + + co = malloc(sizeof(*co)); + ct = malloc(sizeof(struct CheckboxTree)); + co->callback = NULL; + co->data = ct; + co->ops = &ctOps; + co->takesFocus = 1; + co->height = height; + co->width = 0; + co->isMapped = 0; + ct->itemlist = NULL; + ct->firstItem = NULL; + ct->currItem = NULL; + ct->flatList = NULL; + if (seq) + ct->seq = strdup(seq); + else + ct->seq = strdup(" *"); + if (flags & NEWT_FLAG_SCROLL) { + ct->sb = newtVerticalScrollbar(left, top, height, + COLORSET_LISTBOX, COLORSET_ACTLISTBOX); + ct->pad = 2; + } else { + ct->sb = NULL; + ct->pad = 0; + } + + return co; +} + +static void ctMapped(newtComponent co, int isMapped) { + struct CheckboxTree * ct = co->data; + + co->isMapped = isMapped; + if (ct->sb) + ct->sb->ops->mapped(ct->sb, isMapped); +} + +static void ctPlace(newtComponent co, int newLeft, int newTop) { + struct CheckboxTree * ct = co->data; + + co->top = newTop; + co->left = newLeft; + + if (ct->sb) + ct->sb->ops->place(ct->sb, co->left + co->width - 1, co->top); +} + +int ctSetItem(newtComponent co, struct items *item, enum newtFlagsSense sense) +{ + struct CheckboxTree * ct = co->data; + struct items * currItem; + struct items * firstItem; + + if (!item) + return 1; + + switch(sense) { + case NEWT_FLAGS_RESET: + item->selected = 0; + break; + case NEWT_FLAGS_SET: + item->selected = 1; + break; + case NEWT_FLAGS_TOGGLE: + if (item->branch) + item->selected = !item->selected; + else { + item->selected++; + if (item->selected==strlen(ct->seq)) + item->selected = 0; + } + break; + } + + if (item->branch) { + currItem = *ct->currItem; + firstItem = *ct->firstItem; + + buildFlatList(co); + + ct->currItem = ct->flatList; + while (*ct->currItem != currItem) ct->currItem++; + + ct->firstItem = ct->flatList; + if (ct->flatCount > co->height) { + struct items ** last = ct->flatList + ct->flatCount - co->height; + while (*ct->firstItem != firstItem && ct->firstItem != last) + ct->firstItem++; + } + } + + return 0; +} + +static void ctSetItems(struct items *item, int selected) +{ + for (; item; item = item->next) { + if (!item->branch) + item->selected = selected; + else + ctSetItems(item->branch, selected); + } +} + +static void ctDraw(newtComponent co) { + struct CheckboxTree * ct = co->data; + struct items ** item; + int i, j; + char * spaces = NULL; + int currRow = -1; + + if (!co->isMapped) return ; + + if (!ct->firstItem) { + buildFlatList(co); + ct->firstItem = ct->currItem = ct->flatList; + } + + item = ct->firstItem; + + i = 0; + while (*item && i < co->height) { + newtGotorc(co->top + i, co->left); + if (*item == *ct->currItem) { + SLsmg_set_color(NEWT_COLORSET_ACTLISTBOX); + currRow = co->top + i; + } else + SLsmg_set_color(NEWT_COLORSET_LISTBOX); + + for (j = 0; j < (*item)->depth; j++) + SLsmg_write_string(" "); + + if ((*item)->branch) { + if ((*item)->selected) + SLsmg_write_string("<-> "); + else + SLsmg_write_string("<+> "); + } else { + char tmp[5]; + snprintf(tmp,5,"[%c] ",ct->seq[(*item)->selected]); + SLsmg_write_string(tmp); + } + + SLsmg_write_nstring((*item)->text, co->width - 4 - + (3 * (*item)->depth)); + item++; + i++; + } + + /* There could be empty lines left (i.e. if the user closes an expanded + list which is the last thing in the tree, and whose elements are + displayed at the bottom of the screen */ + if (i < co->height) { + spaces = alloca(co->width); + memset(spaces, ' ', co->width); + SLsmg_set_color(NEWT_COLORSET_LISTBOX); + } + while (i < co->height) { + newtGotorc(co->top + i, co->left); + SLsmg_write_nstring(spaces, co->width); + i++; + } + + if(ct->sb) { + newtScrollbarSet(ct->sb, ct->currItem - ct->flatList, + ct->flatCount - 1); + ct->sb->ops->draw(ct->sb); + } + + newtGotorc(currRow, co->left + 1); +} + +static void ctDestroy(newtComponent co) { + struct CheckboxTree * ct = co->data; + struct items * item, * nextitem; + + nextitem = item = ct->itemlist; + + while (item != NULL) { + nextitem = item->next; + free(item->text); + free(item); + item = nextitem; + } + + free(ct->seq); + free(ct); + free(co); +} + +struct eventResult ctEvent(newtComponent co, struct event ev) { + struct CheckboxTree * ct = co->data; + struct eventResult er; + struct items ** listEnd, ** lastItem; + int key, selnum = 1; + + er.result = ER_IGNORED; + + if(ev.when == EV_EARLY || ev.when == EV_LATE) { + return er; + } + + switch(ev.event) { + case EV_KEYPRESS: + key = ev.u.key; + if (key == (char) key && key != ' ') { + for (selnum = 0; ct->seq[selnum]; selnum++) + if (key == ct->seq[selnum]) + break; + if (!ct->seq[selnum]) + switch (key) { + case '-': selnum = 0; break; + case '+': + case '*': selnum = 1; break; + } + if (ct->seq[selnum]) + key = '*'; + } + switch(key) { + case ' ': + case NEWT_KEY_ENTER: + ctSetItem(co, *ct->currItem, NEWT_FLAGS_TOGGLE); + er.result = ER_SWALLOWED; + if (!(*ct->currItem)->branch || (*ct->currItem)->selected) + key = NEWT_KEY_DOWN; + else + key = '*'; + break; + case '*': + if ((*ct->currItem)->branch) { + ctSetItems((*ct->currItem)->branch, selnum); + if (!(*ct->currItem)->selected) + key = NEWT_KEY_DOWN; + } else { + (*ct->currItem)->selected = selnum; + key = NEWT_KEY_DOWN; + } + er.result = ER_SWALLOWED; + break; + } + switch (key) { + case '*': + ctDraw(co); + if(co->callback) co->callback(co, co->callbackData); + return er; + case NEWT_KEY_HOME: + ct->currItem = ct->flatList; + ct->firstItem = ct->flatList; + ctDraw(co); + if(co->callback) co->callback(co, co->callbackData); + er.result = ER_SWALLOWED; + return er; + case NEWT_KEY_END: + ct->currItem = ct->flatList + ct->flatCount - 1; + if (ct->flatCount <= co->height) + ct->firstItem = ct->flatList; + else + ct->firstItem = ct->flatList + ct->flatCount - co->height; + ctDraw(co); + if(co->callback) co->callback(co, co->callbackData); + er.result = ER_SWALLOWED; + return er; + case NEWT_KEY_DOWN: + if (ev.u.key != NEWT_KEY_DOWN) { + if(co->callback) co->callback(co, co->callbackData); + if (strlen(ct->seq) != 2) { + ctDraw(co); + return er; + } + } + if ((ct->currItem - ct->flatList + 1) < ct->flatCount) { + ct->currItem++; + + if (ct->currItem - ct->firstItem >= co->height) + ct->firstItem++; + + ctDraw(co); + } else if (ev.u.key != NEWT_KEY_DOWN) + ctDraw(co); + if(co->callback) co->callback(co, co->callbackData); + er.result = ER_SWALLOWED; + return er; + case NEWT_KEY_UP: + if (ct->currItem != ct->flatList) { + ct->currItem--; + + if (ct->currItem < ct->firstItem) + ct->firstItem = ct->currItem; + + ctDraw(co); + } + er.result = ER_SWALLOWED; + if(co->callback) co->callback(co, co->callbackData); + return er; + case NEWT_KEY_PGUP: + if (ct->firstItem - co->height < ct->flatList) { + ct->firstItem = ct->currItem = ct->flatList; + } else { + ct->currItem -= co->height; + ct->firstItem -= co->height; + } + + ctDraw(co); + if(co->callback) co->callback(co, co->callbackData); + er.result = ER_SWALLOWED; + return er; + case NEWT_KEY_PGDN: + listEnd = ct->flatList + ct->flatCount - 1; + lastItem = ct->firstItem + co->height - 1; + + if (lastItem + co->height > listEnd) { + ct->firstItem = listEnd - co->height + 1; + ct->currItem = listEnd; + } else { + ct->currItem += co->height; + ct->firstItem += co->height; + } + + ctDraw(co); + if(co->callback) co->callback(co, co->callbackData); + er.result = ER_SWALLOWED; + return er; + } + break; + + case EV_FOCUS: + ctDraw(co); + er.result = ER_SWALLOWED; + break; + + case EV_UNFOCUS: + ctDraw(co); + er.result = ER_SWALLOWED; + break; + default: + break; + } + + return er; +} + +const void * newtCheckboxTreeGetCurrent(newtComponent co) { + struct CheckboxTree * ct = co->data; + + if (!ct->currItem) return NULL; + return (*ct->currItem)->data; +} + +void newtCheckboxTreeSetEntry(newtComponent co, const void * data, const char * text) +{ + struct CheckboxTree * ct; + struct items * item; + int i; + + if (!co) return; + ct = co->data; + item = findItem(ct->itemlist, data); + if (!item) return; + + free(item->text); + item->text = strdup(text); + + i = 4 + (3 * item->depth); + + if ((strlen(text) + i + ct->pad) > co->width) { + co->width = strlen(text) + i + ct->pad; + } + + ctDraw(co); +} + +char newtCheckboxTreeGetEntryValue(newtComponent co, const void * data) +{ + struct CheckboxTree * ct; + struct items * item; + + if (!co) return -1; + ct = co->data; + item = findItem(ct->itemlist, data); + if (!item) return -1; + if (item->branch) + return item->selected ? NEWT_CHECKBOXTREE_EXPANDED : NEWT_CHECKBOXTREE_COLLAPSED; + else + return ct->seq[item->selected]; +} + +void newtCheckboxTreeSetEntryValue(newtComponent co, const void * data, char value) +{ + struct CheckboxTree * ct; + struct items * item; + int i; + + if (!co) return; + ct = co->data; + item = findItem(ct->itemlist, data); + if (!item || item->branch) return; + + for(i = 0; ct->seq[i]; i++) + if (value == ct->seq[i]) + break; + + if (!ct->seq[i]) return; + item->selected = i; + + ctDraw(co); +} + diff --git a/mdk-stage1/newt/entry.c b/mdk-stage1/newt/entry.c new file mode 100644 index 000000000..154edba71 --- /dev/null +++ b/mdk-stage1/newt/entry.c @@ -0,0 +1,376 @@ +#include <ctype.h> +#include <slang.h> +#include <stdlib.h> +#include <string.h> + +#include "newt.h" +#include "newt_pr.h" + +struct entry { + int flags; + char * buf; + char ** resultPtr; + int bufAlloced; + int bufUsed; /* amount of the buffer that's been used */ + int cursorPosition; /* cursor *in the string* on on screen */ + int firstChar; /* first character position being shown */ + newtEntryFilter filter; + void * filterData; +}; + +static void entryDraw(newtComponent co); +static void entryDestroy(newtComponent co); +static struct eventResult entryEvent(newtComponent co, + struct event ev); + +static struct eventResult entryHandleKey(newtComponent co, int key); + +static struct componentOps entryOps = { + entryDraw, + entryEvent, + entryDestroy, + newtDefaultPlaceHandler, + newtDefaultMappedHandler, +} ; + +void newtEntrySet(newtComponent co, const char * value, int cursorAtEnd) { + struct entry * en = co->data; + + if ((strlen(value) + 1) > (unsigned int)en->bufAlloced) { + free(en->buf); + en->bufAlloced = strlen(value) + 1; + en->buf = malloc(en->bufAlloced); + if (en->resultPtr) *en->resultPtr = en->buf; + } + memset(en->buf, 0, en->bufAlloced); /* clear the buffer */ + strcpy(en->buf, value); + en->bufUsed = strlen(value); + en->firstChar = 0; + if (cursorAtEnd) + en->cursorPosition = en->bufUsed; + else + en->cursorPosition = 0; + + entryDraw(co); +} ; + +newtComponent newtEntry(int left, int top, const char * initialValue, int width, + char ** resultPtr, int flags) { + newtComponent co; + struct entry * en; + + co = malloc(sizeof(*co)); + en = malloc(sizeof(struct entry)); + co->data = en; + + co->top = top; + co->left = left; + co->height = 1; + co->width = width; + co->isMapped = 0; + co->callback = NULL; + + co->ops = &entryOps; + + en->flags = flags; + en->cursorPosition = 0; + en->firstChar = 0; + en->bufUsed = 0; + en->bufAlloced = width + 1; + en->filter = NULL; + + if (!(en->flags & NEWT_FLAG_DISABLED)) + co->takesFocus = 1; + else + co->takesFocus = 0; + + if (initialValue && strlen(initialValue) > (unsigned int)width) { + en->bufAlloced = strlen(initialValue) + 1; + } + en->buf = malloc(en->bufAlloced); + en->resultPtr = resultPtr; + if (en->resultPtr) *en->resultPtr = en->buf; + + memset(en->buf, 0, en->bufAlloced); + if (initialValue) { + strcpy(en->buf, initialValue); + en->bufUsed = strlen(initialValue); + en->cursorPosition = en->bufUsed; + } else { + *en->buf = '\0'; + en->bufUsed = 0; + en->cursorPosition = 0; + } + + return co; +} + +static void entryDraw(newtComponent co) { + struct entry * en = co->data; + int i; + char * chptr; + int len; + + if (!co->isMapped) return; + + if (en->flags & NEWT_FLAG_DISABLED) + SLsmg_set_color(NEWT_COLORSET_DISENTRY); + else + SLsmg_set_color(NEWT_COLORSET_ENTRY); + + if (en->flags & NEWT_FLAG_HIDDEN) { + newtGotorc(co->top, co->left); + for (i = 0; i < co->width; i++) + SLsmg_write_char('_'); + newtGotorc(co->top, co->left); + + return; + } + + newtGotorc(co->top, co->left); + + if (en->cursorPosition < en->firstChar) { + /* scroll to the left */ + en->firstChar = en->cursorPosition; + } else if ((en->firstChar + co->width) <= en->cursorPosition) { + /* scroll to the right */ + en->firstChar = en->cursorPosition - co->width + 1; + } + + chptr = en->buf + en->firstChar; + + if (en->flags & NEWT_FLAG_PASSWORD) { + char *tmpptr, *p; + + tmpptr = alloca(strlen(chptr+2)); + strcpy(tmpptr, chptr); + for (p = tmpptr; *p; p++) + *p = '*'; + chptr = tmpptr; + } + + len = strlen(chptr); + + if (len <= co->width) { + i = len; + SLsmg_write_string(chptr); + while (i < co->width) { + SLsmg_write_char('_'); + i++; + } + } else { + SLsmg_write_nstring(chptr, co->width); + } + + if (en->flags & NEWT_FLAG_HIDDEN) + newtGotorc(co->top, co->left); + else + newtGotorc(co->top, co->left + (en->cursorPosition - en->firstChar)); +} + +void newtEntrySetFlags(newtComponent co, int flags, enum newtFlagsSense sense) { + struct entry * en = co->data; + int row, col; + + en->flags = newtSetFlags(en->flags, flags, sense); + + if (!(en->flags & NEWT_FLAG_DISABLED)) + co->takesFocus = 1; + else + co->takesFocus = 0; + + newtGetrc(&row, &col); + entryDraw(co); + newtGotorc(row, col); +} + +static void entryDestroy(newtComponent co) { + struct entry * en = co->data; + + free(en->buf); + free(en); + free(co); +} + +static struct eventResult entryEvent(newtComponent co, + struct event ev) { + struct entry * en = co->data; + struct eventResult er; + int ch; + + if (ev.when == EV_NORMAL) { + switch (ev.event) { + case EV_FOCUS: + newtCursorOn(); + if (en->flags & NEWT_FLAG_HIDDEN) + newtGotorc(co->top, co->left); + else + newtGotorc(co->top, co->left + + (en->cursorPosition - en->firstChar)); + er.result = ER_SWALLOWED; + break; + + case EV_UNFOCUS: + newtCursorOff(); + newtGotorc(0, 0); + er.result = ER_SWALLOWED; + if (co->callback) + co->callback(co, co->callbackData); + break; + + case EV_KEYPRESS: + ch = ev.u.key; + if (en->filter) + ch = en->filter(co, en->filterData, ch, en->cursorPosition); + if (ch) er = entryHandleKey(co, ch); + break; + + case EV_MOUSE: + if ((ev.u.mouse.type == MOUSE_BUTTON_DOWN) && + (en->flags ^ NEWT_FLAG_HIDDEN)) { + if (strlen(en->buf) >= ev.u.mouse.x - co->left) { + en->cursorPosition = ev.u.mouse.x - co->left; + newtGotorc(co->top, + co->left +(en->cursorPosition - en->firstChar)); + } else { + en->cursorPosition = strlen(en->buf); + newtGotorc(co->top, + co->left +(en->cursorPosition - en->firstChar)); + } + } + break; + } + } else + er.result = ER_IGNORED; + + return er; +} + +static struct eventResult entryHandleKey(newtComponent co, int key) { + struct entry * en = co->data; + struct eventResult er; + char * chptr, * insPoint; + + er.result = ER_SWALLOWED; + switch (key) { + case '\r': /* Return */ + if (en->flags & NEWT_FLAG_RETURNEXIT) { + er.result = ER_EXITFORM; + } else { + er.result = ER_NEXTCOMP; + } + break; + + case '\001': /* ^A */ + case NEWT_KEY_HOME: + en->cursorPosition = 0; + break; + + case '\005': /* ^E */ + case NEWT_KEY_END: + en->cursorPosition = en->bufUsed; + break; + + case '\013': /* ^K */ + en->bufUsed = en->cursorPosition; + memset(en->buf + en->bufUsed, 0, en->bufAlloced - en->bufUsed); + break; + + case '\002': /* ^B */ + case NEWT_KEY_LEFT: + if (en->cursorPosition) + en->cursorPosition--; + break; + + case '\004': + case NEWT_KEY_DELETE: + chptr = en->buf + en->cursorPosition; + if (*chptr) { + chptr++; + while (*chptr) { + *(chptr - 1) = *chptr; + chptr++; + } + *(chptr - 1) = '\0'; + en->bufUsed--; + } + break; + + case NEWT_KEY_BKSPC: + if (en->cursorPosition) { + /* if this isn't true, there's nothing to erase */ + chptr = en->buf + en->cursorPosition; + en->bufUsed--; + en->cursorPosition--; + while (*chptr) { + *(chptr - 1) = *chptr; + chptr++; + } + *(chptr - 1) = '\0'; + } + break; + + case '\006': /* ^B */ + case NEWT_KEY_RIGHT: + if (en->cursorPosition < en->bufUsed) + en->cursorPosition++; + break; + + default: + if ((key >= 0x20 && key <= 0x7e) || (key >= 0xa0 && key <= 0xff)) { + if (!(en->flags & NEWT_FLAG_SCROLL) && en->bufUsed >= co->width) { + SLtt_beep(); + break; + } + + if ((en->bufUsed + 1) == en->bufAlloced) { + en->bufAlloced += 20; + en->buf = realloc(en->buf, en->bufAlloced); + if (en->resultPtr) *en->resultPtr = en->buf; + memset(en->buf + en->bufUsed + 1, 0, 20); + } + + if (en->cursorPosition == en->bufUsed) { + en->bufUsed++; + } else { + /* insert the new character */ + + /* chptr is the last character in the string */ + chptr = (en->buf + en->bufUsed) - 1; + if ((en->bufUsed + 1) == en->bufAlloced) { + /* this string fills the buffer, so clip it */ + chptr--; + } else + en->bufUsed++; + + insPoint = en->buf + en->cursorPosition; + + while (chptr >= insPoint) { + *(chptr + 1) = *chptr; + chptr--; + } + + } + + en->buf[en->cursorPosition++] = key; + } else { + er.result = ER_IGNORED; + } + } + + entryDraw(co); + + return er; +} + +char * newtEntryGetValue(newtComponent co) { + struct entry * en = co->data; + + return en->buf; +} + +void newtEntrySetFilter(newtComponent co, newtEntryFilter filter, void * data) { + struct entry * en = co->data; + en->filter = filter; + en->filterData = data; +} diff --git a/mdk-stage1/newt/form.c b/mdk-stage1/newt/form.c new file mode 100644 index 000000000..4ad465e38 --- /dev/null +++ b/mdk-stage1/newt/form.c @@ -0,0 +1,712 @@ +#include <unistd.h> +#include <slang.h> +#include <stdarg.h> +#include <stdlib.h> +#include <sys/time.h> +#include <sys/types.h> + +#include "newt.h" +#include "newt_pr.h" + + +/**************************************************************************** + These forms handle vertical scrolling of components with a height of 1 + + Horizontal scrolling won't work, and scrolling large widgets will fail + miserably. It shouldn't be too hard to fix either of those if anyone + cares to. I only use scrolling for listboxes and text boxes though so + I didn't bother. +*****************************************************************************/ + +struct element { + int top, left; /* Actual, not virtual. These are translated */ + newtComponent co; /* into actual through vertOffset */ +}; + +struct fdInfo { + int fd; + int flags; +}; + +struct form { + int numCompsAlloced; + struct element * elements; + int numComps; + int currComp; + int fixedHeight; + int flags; + int vertOffset; + newtComponent vertBar, exitComp; + const char * help; + int numRows; + int * hotKeys; + int numHotKeys; + int background; + int beenSet; + int numFds; + struct fdInfo * fds; + int maxFd; + int timer; /* in milliseconds */ + struct timeval lastTimeout; + void * helpTag; + newtCallback helpCb; +}; + +static void gotoComponent(struct form * form, int newComp); +static struct eventResult formEvent(newtComponent co, struct event ev); +static struct eventResult sendEvent(newtComponent comp, struct event ev); +static void formPlace(newtComponent co, int left, int top); + +/* Global, ick */ +static newtCallback helpCallback; + +/* this isn't static as grid.c tests against it to find forms */ +struct componentOps formOps = { + newtDrawForm, + formEvent, + newtFormDestroy, + formPlace, + newtDefaultMappedHandler, +} ; + +static inline int componentFits(newtComponent co, int compNum) { + struct form * form = co->data; + struct element * el = form->elements + compNum; + + if ((co->top + form->vertOffset) > el->top) return 0; + if ((co->top + form->vertOffset + co->height) < + (el->top + el->co->height)) return 0; + + return 1; +} + +newtComponent newtForm(newtComponent vertBar, void * help, int flags) { + newtComponent co; + struct form * form; + + co = malloc(sizeof(*co)); + form = malloc(sizeof(*form)); + co->data = form; + co->width = 0; + co->height = 0; + co->top = -1; + co->left = -1; + co->isMapped = 0; + + co->takesFocus = 0; /* we may have 0 components */ + co->ops = &formOps; + + form->help = help; + form->flags = flags; + form->numCompsAlloced = 5; + form->numComps = 0; + form->currComp = -1; + form->vertOffset = 0; + form->fixedHeight = 0; + form->numRows = 0; + form->numFds = 0; + form->maxFd = 0; + form->fds = NULL; + form->beenSet = 0; + form->elements = malloc(sizeof(*(form->elements)) * form->numCompsAlloced); + + form->background = COLORSET_WINDOW; + form->hotKeys = malloc(sizeof(int)); + form->numHotKeys = 0; + form->timer = 0; + form->lastTimeout.tv_sec = form->lastTimeout.tv_usec = 0; + if (!(form->flags & NEWT_FLAG_NOF12)) { + newtFormAddHotKey(co, NEWT_KEY_F12); + } + + if (vertBar) + form->vertBar = vertBar; + else + form->vertBar = NULL; + + form->helpTag = help; + form->helpCb = helpCallback; + + return co; +} + +newtComponent newtFormGetCurrent(newtComponent co) { + struct form * form = co->data; + + return form->elements[form->currComp].co; +} + +void newtFormSetCurrent(newtComponent co, newtComponent subco) { + struct form * form = co->data; + int i, new; + + for (i = 0; i < form->numComps; i++) { + if (form->elements[i].co == subco) break; + } + + if (form->elements[i].co != subco) return; + new = i; + + if (co->isMapped && !componentFits(co, new)) { + gotoComponent(form, -1); + form->vertOffset = form->elements[new].top - co->top - 1; + if (form->vertOffset > (form->numRows - co->height)) + form->vertOffset = form->numRows - co->height; + } + + gotoComponent(form, new); +} + +void newtFormSetTimer(newtComponent co, int millisecs) { + struct form * form = co->data; + + form->timer = millisecs; + form->lastTimeout.tv_usec = 0; + form->lastTimeout.tv_sec = 0; +} + +void newtFormSetHeight(newtComponent co, int height) { + struct form * form = co->data; + + form->fixedHeight = 1; + co->height = height; +} + +void newtFormSetWidth(newtComponent co, int width) { + co->width = width; +} + +void newtFormAddComponent(newtComponent co, newtComponent newco) { + struct form * form = co->data; + + co->takesFocus = 1; + + if (form->numCompsAlloced == form->numComps) { + form->numCompsAlloced += 5; + form->elements = realloc(form->elements, + sizeof(*(form->elements)) * form->numCompsAlloced); + } + + /* we grab real values for these a bit later */ + form->elements[form->numComps].left = -2; + form->elements[form->numComps].top = -2; + form->elements[form->numComps].co = newco; + + if (newco->takesFocus && form->currComp == -1) + form->currComp = form->numComps; + + form->numComps++; +} + +void newtFormAddComponents(newtComponent co, ...) { + va_list ap; + newtComponent subco; + + va_start(ap, co); + + while ((subco = va_arg(ap, newtComponent))) + newtFormAddComponent(co, subco); + + va_end(ap); +} + +static void formPlace(newtComponent co, int left, int top) { + struct form * form = co->data; + int vertDelta, horizDelta; + struct element * el; + int i; + + newtFormSetSize(co); + + vertDelta = top - co->top; + horizDelta = left - co->left; + co->top = top; + co->left = left; + + for (i = 0, el = form->elements; i < form->numComps; i++, el++) { + el->co->top += vertDelta; + el->top += vertDelta; + el->co->left += horizDelta; + el->left += horizDelta; + } +} + +void newtDrawForm(newtComponent co) { + struct form * form = co->data; + struct element * el; + int i; + + newtFormSetSize(co); + + SLsmg_set_color(form->background); + newtClearBox(co->left, co->top, co->width, co->height); + for (i = 0, el = form->elements; i < form->numComps; i++, el++) { + /* the scrollbar *always* fits somewhere */ + if (el->co == form->vertBar) { + el->co->ops->mapped(el->co, 1); + el->co->ops->draw(el->co); + } else { + /* only draw it if it'll fit on the screen vertically */ + if (componentFits(co, i)) { + el->co->top = el->top - form->vertOffset; + el->co->ops->mapped(el->co, 1); + el->co->ops->draw(el->co); + } else { + el->co->ops->mapped(el->co, 0); + } + } + } + + if (form->vertBar) + newtScrollbarSet(form->vertBar, form->vertOffset, + form->numRows - co->height); +} + +static struct eventResult formEvent(newtComponent co, struct event ev) { + struct form * form = co->data; + newtComponent subco = form->elements[form->currComp].co; + int new, wrap = 0; + struct eventResult er; + int dir = 0, page = 0; + int i, num, found; + struct element * el; + + er.result = ER_IGNORED; + if (!form->numComps) return er; + + subco = form->elements[form->currComp].co; + + switch (ev.when) { + case EV_EARLY: + if (ev.event == EV_KEYPRESS) { + if (ev.u.key == NEWT_KEY_TAB) { + er.result = ER_SWALLOWED; + dir = 1; + wrap = 1; + } else if (ev.u.key == NEWT_KEY_UNTAB) { + er.result = ER_SWALLOWED; + dir = -1; + wrap = 1; + } + } + + if (form->numComps) { + i = form->currComp; + num = 0; + while (er.result == ER_IGNORED && num != form->numComps ) { + er = form->elements[i].co->ops->event(form->elements[i].co, ev); + + num++; + i++; + if (i == form->numComps) i = 0; + } + } + + break; + + case EV_NORMAL: + if (ev.event == EV_MOUSE) { + found = 0; + for (i = 0, el = form->elements; i < form->numComps; i++, el++) { + if ((el->co->top <= ev.u.mouse.y) && + (el->co->top + el->co->height > ev.u.mouse.y) && + (el->co->left <= ev.u.mouse.x) && + (el->co->left + el->co->width > ev.u.mouse.x)) { + found = 1; + if (el->co->takesFocus) { + gotoComponent(form, i); + subco = form->elements[form->currComp].co; + } + } + /* If we did not find a co to send this event to, we + should just swallow the event here. */ + } + if (!found) { + er.result = ER_SWALLOWED; + + return er; + } + } + er = subco->ops->event(subco, ev); + switch (er.result) { + case ER_NEXTCOMP: + er.result = ER_SWALLOWED; + dir = 1; + break; + + case ER_EXITFORM: + form->exitComp = subco; + break; + + default: + break; + } + break; + + case EV_LATE: + er = subco->ops->event(subco, ev); + + if (er.result == ER_IGNORED) { + switch (ev.u.key) { + case NEWT_KEY_UP: + case NEWT_KEY_LEFT: + case NEWT_KEY_BKSPC: + er.result = ER_SWALLOWED; + dir = -1; + break; + + case NEWT_KEY_DOWN: + case NEWT_KEY_RIGHT: + er.result = ER_SWALLOWED; + dir = 1; + break; + + case NEWT_KEY_PGUP: + er.result = ER_SWALLOWED; + dir = -1; + page = 1; + break; + + case NEWT_KEY_PGDN: + er.result = ER_SWALLOWED; + dir = 1; + page = 1; + break; + } + } + } + + if (dir) { + new = form->currComp; + + if (page) { + new += dir * co->height; + if (new < 0) + new = 0; + else if (new >= form->numComps) + new = (form->numComps - 1); + + while (!form->elements[new].co->takesFocus) + new = new - dir; + } else { + do { + new += dir; + + if (wrap) { + if (new < 0) + new = form->numComps - 1; + else if (new >= form->numComps) + new = 0; + } else if (new < 0 || new >= form->numComps) + return er; + } while (!form->elements[new].co->takesFocus); + } + + /* make sure this component is visible */ + if (!componentFits(co, new)) { + gotoComponent(form, -1); + + if (dir < 0) { + /* make the new component the first one */ + form->vertOffset = form->elements[new].top - co->top; + } else { + /* make the new component the last one */ + form->vertOffset = (form->elements[new].top + + form->elements[new].co->height) - + (co->top + co->height); + } + + if (form->vertOffset < 0) form->vertOffset = 0; + if (form->vertOffset > (form->numRows - co->height)) + form->vertOffset = form->numRows - co->height; + + newtDrawForm(co); + } + + gotoComponent(form, new); + er.result = ER_SWALLOWED; + } + + return er; +} + +/* this also destroys all of the components on the form */ +void newtFormDestroy(newtComponent co) { + newtComponent subco; + struct form * form = co->data; + int i; + + /* first, destroy all of the components */ + for (i = 0; i < form->numComps; i++) { + subco = form->elements[i].co; + if (subco->ops->destroy) { + subco->ops->destroy(subco); + } else { + if (subco->data) free(subco->data); + free(subco); + } + } + + if (form->hotKeys) free(form->hotKeys); + + free(form->elements); + free(form); + free(co); +} + +newtComponent newtRunForm(newtComponent co) { + struct newtExitStruct es; + + newtFormRun(co, &es); + if (es.reason == NEWT_EXIT_HOTKEY) { + if (es.u.key == NEWT_KEY_F12) { + es.reason = NEWT_EXIT_COMPONENT; + es.u.co = co; + } else { + return NULL; + } + } + + return es.u.co; +} + +void newtFormAddHotKey(newtComponent co, int key) { + struct form * form = co->data; + + form->numHotKeys++; + form->hotKeys = realloc(form->hotKeys, sizeof(int) * form->numHotKeys); + form->hotKeys[form->numHotKeys - 1] = key; +} + +void newtFormSetSize(newtComponent co) { + struct form * form = co->data; + int delta, i; + struct element * el; + + if (form->beenSet) return; + + form->beenSet = 1; + + if (!form->numComps) return; + + co->width = 0; + if (!form->fixedHeight) co->height = 0; + + co->top = form->elements[0].co->top; + co->left = form->elements[0].co->left; + for (i = 0, el = form->elements; i < form->numComps; i++, el++) { + if (el->co->ops == &formOps) + newtFormSetSize(el->co); + + el->left = el->co->left; + el->top = el->co->top; + + if (co->left > el->co->left) { + delta = co->left - el->co->left; + co->left -= delta; + co->width += delta; + } + + if (co->top > el->co->top) { + delta = co->top - el->co->top; + co->top -= delta; + if (!form->fixedHeight) + co->height += delta; + } + + if ((co->left + co->width) < (el->co->left + el->co->width)) + co->width = (el->co->left + el->co->width) - co->left; + + if (!form->fixedHeight) { + if ((co->top + co->height) < (el->co->top + el->co->height)) + co->height = (el->co->top + el->co->height) - co->top; + } + + if ((el->co->top + el->co->height - co->top) > form->numRows) { + form->numRows = el->co->top + el->co->height - co->top; + } + } +} + +void newtFormRun(newtComponent co, struct newtExitStruct * es) { + struct form * form = co->data; + struct event ev; + struct eventResult er; + int key, i, max; + int done = 0; + fd_set readSet, writeSet; + struct timeval nextTimeout, now, timeout; + + newtFormSetSize(co); + /* draw all of the components */ + newtDrawForm(co); + + if (form->currComp == -1) { + gotoComponent(form, 0); + } else + gotoComponent(form, form->currComp); + + while (!done) { + newtRefresh(); + + FD_ZERO(&readSet); + FD_ZERO(&writeSet); + FD_SET(0, &readSet); + max = form->maxFd; + + for (i = 0; i < form->numFds; i++) { + if (form->fds[i].flags & NEWT_FD_READ) + FD_SET(form->fds[i].fd, &readSet); + if (form->fds[i].flags & NEWT_FD_WRITE) + FD_SET(form->fds[i].fd, &writeSet); + } + + if (form->timer) { + /* Calculate when we next need to return with a timeout. Do + this inside the loop in case a callback resets the timer. */ + if (!form->lastTimeout.tv_sec && !form->lastTimeout.tv_usec) + gettimeofday(&form->lastTimeout, NULL); + + nextTimeout.tv_sec = form->lastTimeout.tv_sec + + (form->timer / 1000); + nextTimeout.tv_usec = form->lastTimeout.tv_usec + + (form->timer % 1000) * 1000; + + gettimeofday(&now, 0); + + if (now.tv_sec > nextTimeout.tv_sec) { + timeout.tv_sec = timeout.tv_usec = 0; + } else if (now.tv_sec == nextTimeout.tv_sec) { + timeout.tv_sec = 0; + if (now.tv_usec > nextTimeout.tv_usec) + timeout.tv_usec = 0; + else + timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec; + } else if (now.tv_sec < nextTimeout.tv_sec) { + timeout.tv_sec = nextTimeout.tv_sec - now.tv_sec; + if (now.tv_usec > nextTimeout.tv_usec) + timeout.tv_sec--, + timeout.tv_usec = nextTimeout.tv_usec + 1000000 - + now.tv_usec; + else + timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec; + } + } else { + timeout.tv_sec = timeout.tv_usec = 0; + } + + i = select(max + 1, &readSet, &writeSet, NULL, + form->timer ? &timeout : NULL); + if (i < 0) continue; /* ?? What should we do here? */ + + if (i == 0) { + done = 1; + es->reason = NEWT_EXIT_TIMER; + gettimeofday(&form->lastTimeout, NULL); + } else + { + if (FD_ISSET(0, &readSet)) { + + key = newtGetKey(); + + if (key == NEWT_KEY_RESIZE) { + /* newtResizeScreen(1); */ + continue; + } + + for (i = 0; i < form->numHotKeys; i++) { + if (form->hotKeys[i] == key) { + es->reason = NEWT_EXIT_HOTKEY; + es->u.key = key; + done = 1; + break; + } + } + + if (key == NEWT_KEY_F1 && form->helpTag && form->helpCb) + form->helpCb(co, form->helpTag); + + if (!done) { + ev.event = EV_KEYPRESS; + ev.u.key = key; + + er = sendEvent(co, ev); + + if (er.result == ER_EXITFORM) { + done = 1; + es->reason = NEWT_EXIT_COMPONENT; + es->u.co = form->exitComp; + } + } + } else { + es->reason = NEWT_EXIT_FDREADY; + done = 1; + } + } + } + newtRefresh(); +} + +static struct eventResult sendEvent(newtComponent co, struct event ev) { + struct eventResult er; + + ev.when = EV_EARLY; + er = co->ops->event(co, ev); + + if (er.result == ER_IGNORED) { + ev.when = EV_NORMAL; + er = co->ops->event(co, ev); + } + + if (er.result == ER_IGNORED) { + ev.when = EV_LATE; + er = co->ops->event(co, ev); + } + + return er; +} + +static void gotoComponent(struct form * form, int newComp) { + struct event ev; + + if (form->currComp != -1) { + ev.event = EV_UNFOCUS; + sendEvent(form->elements[form->currComp].co, ev); + } + + form->currComp = newComp; + + if (form->currComp != -1) { + ev.event = EV_FOCUS; + ev.when = EV_NORMAL; + sendEvent(form->elements[form->currComp].co, ev); + } +} + +void newtComponentAddCallback(newtComponent co, newtCallback f, void * data) { + co->callback = f; + co->callbackData = data; +} + +void newtComponentTakesFocus(newtComponent co, int val) { + co->takesFocus = val; +} + +void newtFormSetBackground(newtComponent co, int color) { + struct form * form = co->data; + + form->background = color; +} + +void newtFormWatchFd(newtComponent co, int fd, int fdFlags) { + struct form * form = co->data; + + form->fds = realloc(form->fds, (form->numFds + 1) * sizeof(*form->fds)); + form->fds[form->numFds].fd = fd; + form->fds[form->numFds++].flags = fdFlags; + if (form->maxFd < fd) form->maxFd = fd; +} + +void newtSetHelpCallback(newtCallback cb) { + helpCallback = cb; +} diff --git a/mdk-stage1/newt/grid.c b/mdk-stage1/newt/grid.c new file mode 100644 index 000000000..433011396 --- /dev/null +++ b/mdk-stage1/newt/grid.c @@ -0,0 +1,389 @@ +#include <alloca.h> +#include <stdlib.h> +#include <string.h> + +#include "newt.h" +#include "newt_pr.h" + +struct gridField { + enum newtGridElement type; + union { + newtGrid grid; + newtComponent co; + } u; + int padLeft, padTop, padRight, padBottom; + int anchor; + int flags; +}; + +struct grid_s { + int rows, cols; + int width, height; /* totals, -1 means unknown */ + struct gridField ** fields; +}; + +/* this is a bit of a hack */ +extern struct componentOps formOps[]; + +newtGrid newtCreateGrid(int cols, int rows) { + newtGrid grid; + + grid = malloc(sizeof(*grid)); + grid->rows = rows; + grid->cols = cols; + + grid->fields = malloc(sizeof(*grid->fields) * cols); + while (cols--) { + grid->fields[cols] = malloc(sizeof(**(grid->fields)) * rows); + memset(grid->fields[cols], 0, sizeof(**(grid->fields)) * rows); + } + + grid->width = grid->height = -1; + + return grid; +} + +void newtGridSetField(newtGrid grid, int col, int row, + enum newtGridElement type, void * val, int padLeft, + int padTop, int padRight, int padBottom, int anchor, + int flags) { + struct gridField * field = &grid->fields[col][row]; + + if (field->type == NEWT_GRID_SUBGRID) + newtGridFree(field->u.grid, 1); + + field->type = type; + field->u.co = (void *) val; + + field->padLeft = padLeft; + field->padRight = padRight; + field->padTop = padTop; + field->padBottom = padBottom; + field->anchor = anchor; + field->flags = flags; + + grid->width = grid->height = -1; +} + +static void distSpace(int extra, int items, int * list) { + int all, some, i; + + all = extra / items; + some = extra % items; + for (i = 0; i < items; i++) { + list[i] += all; + if (some) { + list[i]++; + some--; + } + } +} + +static void shuffleGrid(newtGrid grid, int left, int top, int set) { + struct gridField * field; + int row, col; + int i, j; + int minWidth, minHeight; + int * widths, * heights; + int thisLeft, thisTop; + int x, y, remx, remy; + + widths = alloca(sizeof(*widths) * grid->cols); + memset(widths, 0, sizeof(*widths) * grid->cols); + heights = alloca(sizeof(*heights) * grid->rows); + memset(heights, 0, sizeof(*heights) * grid->rows); + + minWidth = 0; + for (row = 0; row < grid->rows; row++) { + i = 0; + for (col = 0; col < grid->cols; col++) { + field = &grid->fields[col][row]; + if (field->type == NEWT_GRID_SUBGRID) { + /* we'll have to redo this later */ + if (field->u.grid->width == -1) + shuffleGrid(field->u.grid, left, top, 0); + j = field->u.grid->width; + } else if (field->type == NEWT_GRID_COMPONENT) { + if (field->u.co->ops == formOps) + newtFormSetSize(field->u.co); + j = field->u.co->width; + } else + j = 0; + + j += field->padLeft + field->padRight; + + if (j > widths[col]) widths[col] = j; + i += widths[col]; + } + + if (i > minWidth) minWidth = i; + } + + minHeight = 0; + for (col = 0; col < grid->cols; col++) { + i = 0; + for (row = 0; row < grid->rows; row++) { + field = &grid->fields[col][row]; + if (field->type == NEWT_GRID_SUBGRID) { + /* we'll have to redo this later */ + if (field->u.grid->height == -1) + shuffleGrid(field->u.grid, 0, 0, 0); + j = field->u.grid->height; + } else if (field->type == NEWT_GRID_COMPONENT){ + j = field->u.co->height; + } else + j = 0; + + j += field->padTop + field->padBottom; + + if (j > heights[row]) heights[row] = j; + i += heights[row]; + } + + if (i > minHeight) minHeight = i; + } + + /* this catches the -1 case */ + if (grid->width < minWidth) grid->width = minWidth; /* ack! */ + if (grid->height < minHeight) grid->height = minHeight; /* ditto! */ + + if (!set) return; + + distSpace(grid->width - minWidth, grid->cols, widths); + distSpace(grid->height - minHeight, grid->rows, heights); + + thisTop = top; + for (row = 0; row < grid->rows; row++) { + i = 0; + thisLeft = left; + for (col = 0; col < grid->cols; col++) { + field = &grid->fields[col][row]; + + if (field->type == NEWT_GRID_EMPTY) continue; + + x = thisLeft + field->padLeft; + remx = widths[col] - field->padLeft - field->padRight; + y = thisTop + field->padTop; + remy = heights[row] - field->padTop - field->padBottom; + + if (field->type == NEWT_GRID_SUBGRID) { + remx -= field->u.grid->width; + remy -= field->u.grid->height; + } else if (field->type == NEWT_GRID_COMPONENT) { + remx -= field->u.co->width; + remy -= field->u.co->height; + } + + if (!(field->flags & NEWT_GRID_FLAG_GROWX)) { + if (field->anchor & NEWT_ANCHOR_RIGHT) + x += remx; + else if (!(field->anchor & NEWT_ANCHOR_LEFT)) + x += (remx / 2); + } + + if (!(field->flags & NEWT_GRID_FLAG_GROWY)) { + if (field->anchor & NEWT_ANCHOR_BOTTOM) + y += remx; + else if (!(field->anchor & NEWT_ANCHOR_TOP)) + y += (remy / 2); + } + + if (field->type == NEWT_GRID_SUBGRID) { + if (field->flags & NEWT_GRID_FLAG_GROWX) + field->u.grid->width = widths[col] - field->padLeft + - field->padRight; + if (field->flags & NEWT_GRID_FLAG_GROWY) + field->u.grid->height = heights[col] - field->padTop + - field->padBottom; + + shuffleGrid(field->u.grid, x, y, 1); + } else if (field->type == NEWT_GRID_COMPONENT) { + field->u.co->ops->place(field->u.co, x, y); + } + + thisLeft += widths[col]; + } + + thisTop += heights[row]; + } +} + +void newtGridPlace(newtGrid grid, int left, int top) { + shuffleGrid(grid, left, top, 1); +} + +void newtGridFree(newtGrid grid, int recurse) { + int row, col; + + for (col = 0; col < grid->cols; col++) { + if (recurse) { + for (row = 0; row < grid->rows; row++) { + if (grid->fields[col][row].type == NEWT_GRID_SUBGRID) + newtGridFree(grid->fields[col][row].u.grid, 1); + } + } + + free(grid->fields[col]); + } + + free(grid->fields); + free(grid); +} + +void newtGridGetSize(newtGrid grid, int * width, int * height) { + if (grid->width == -1 || grid->height == -1) { + grid->width = grid->height = -1; + shuffleGrid(grid, 0, 0, 1); + } + + *width = grid->width; + *height = grid->height; +} + +void newtGridWrappedWindow(newtGrid grid, char * title) { + int width, height, offset = 0; + + newtGridGetSize(grid, &width, &height); + if (width < strlen(title) + 2) { + offset = ((strlen(title) + 2) - width) / 2; + width = strlen(title) + 2; + } + newtCenteredWindow(width + 2, height + 2, title); + newtGridPlace(grid, 1 + offset, 1); +} + +void newtGridWrappedWindowAt(newtGrid grid, char * title, int left, int top) { + int width, height; + + newtGridGetSize(grid, &width, &height); + newtOpenWindow(left, top, width + 2, height + 2, title); + newtGridPlace(grid, 1, 1); +} + +void newtGridAddComponentsToForm(newtGrid grid, newtComponent form, + int recurse) { + int row, col; + + for (col = 0; col < grid->cols; col++) { + for (row = 0; row < grid->rows; row++) { + if (grid->fields[col][row].type == NEWT_GRID_SUBGRID && recurse) + newtGridAddComponentsToForm(grid->fields[col][row].u.grid, + form, 1); + else if (grid->fields[col][row].type == NEWT_GRID_COMPONENT) + newtFormAddComponent(form, grid->fields[col][row].u.co); + } + } +} + +/* this handles up to 50 items */ +static newtGrid stackem(int isVert, enum newtGridElement type1, void * what1, + va_list args, int close) { + struct item { + enum newtGridElement type; + void * what; + } items[50]; + int i, num; + newtGrid grid; + + items[0].type = type1, items[0].what = what1, num = 1; + while (1) { + items[num].type = va_arg(args, enum newtGridElement); + if (items[num].type == NEWT_GRID_EMPTY) break; + + items[num].what = va_arg(args, void *); + num++; + } + + grid = newtCreateGrid(isVert ? 1 : num, isVert ? num : 1); + + for (i = 0; i < num; i++) { + newtGridSetField(grid, isVert ? 0 : i, isVert ? i : 0, + items[i].type, items[i].what, + close ? 0 : (i ? (isVert ? 0 : 1) : 0), + close ? 0 : (i ? (isVert ? 1 : 0) : 0), 0, 0, 0, 0); + } + + return grid; +} + +newtGrid newtGridHCloseStacked(enum newtGridElement type1, void * what1, ...) { + va_list args; + newtGrid grid; + + va_start(args, what1); + + grid = stackem(0, type1, what1, args, 1); + + va_start(args, what1); + + return grid; +} + +newtGrid newtGridVCloseStacked(enum newtGridElement type1, void * what1, ...) { + va_list args; + newtGrid grid; + + va_start(args, what1); + + grid = stackem(1, type1, what1, args, 1); + + va_start(args, what1); + + return grid; +} + +newtGrid newtGridVStacked(enum newtGridElement type1, void * what1, ...) { + va_list args; + newtGrid grid; + + va_start(args, what1); + + grid = stackem(1, type1, what1, args, 0); + + va_start(args, what1); + + return grid; +} + +newtGrid newtGridHStacked(enum newtGridElement type1, void * what1, ...) { + va_list args; + newtGrid grid; + + va_start(args, what1); + + grid = stackem(0, type1, what1, args, 0); + + va_start(args, what1); + + return grid; +} + +newtGrid newtGridBasicWindow(newtComponent text, newtGrid middle, + newtGrid buttons) { + newtGrid grid; + + grid = newtCreateGrid(1, 3); + newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, text, + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, middle, + 0, 1, 0, 0, 0, 0); + newtGridSetField(grid, 0, 2, NEWT_GRID_SUBGRID, buttons, + 0, 1, 0, 0, 0, NEWT_GRID_FLAG_GROWX); + + return grid; +} + +newtGrid newtGridSimpleWindow(newtComponent text, newtComponent middle, + newtGrid buttons) { + newtGrid grid; + + grid = newtCreateGrid(1, 3); + newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, text, + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(grid, 0, 1, NEWT_GRID_COMPONENT, middle, + 0, 1, 0, 0, 0, 0); + newtGridSetField(grid, 0, 2, NEWT_GRID_SUBGRID, buttons, + 0, 1, 0, 0, 0, NEWT_GRID_FLAG_GROWX); + + return grid; +} diff --git a/mdk-stage1/newt/label.c b/mdk-stage1/newt/label.c new file mode 100644 index 000000000..f1a9cebbf --- /dev/null +++ b/mdk-stage1/newt/label.c @@ -0,0 +1,81 @@ +#include <slang.h> +#include <stdlib.h> +#include <string.h> + +#include "newt.h" +#include "newt_pr.h" + +struct label { + char * text; + int length; +}; + +static void labelDraw(newtComponent co); +static void labelDestroy(newtComponent co); + +static struct componentOps labelOps = { + labelDraw, + newtDefaultEventHandler, + labelDestroy, + newtDefaultPlaceHandler, + newtDefaultMappedHandler, +} ; + +newtComponent newtLabel(int left, int top, const char * text) { + newtComponent co; + struct label * la; + + co = malloc(sizeof(*co)); + la = malloc(sizeof(struct label)); + co->data = la; + + co->ops = &labelOps; + + co->height = 1; + co->width = strlen(text); + co->top = top; + co->left = left; + co->takesFocus = 0; + + la->length = strlen(text); + la->text = strdup(text); + + return co; +} + +void newtLabelSetText(newtComponent co, const char * text) { + int newLength; + struct label * la = co->data; + + newLength = strlen(text); + if (newLength <= la->length) { + memset(la->text, ' ', la->length); + memcpy(la->text, text, newLength); + } else { + free(la->text); + la->text = strdup(text); + la->length = newLength; + co->width = newLength; + } + + labelDraw(co); +} + +static void labelDraw(newtComponent co) { + struct label * la = co->data; + + if (co->isMapped == -1) return; + + SLsmg_set_color(COLORSET_LABEL); + + newtGotorc(co->top, co->left); + SLsmg_write_string(la->text); +} + +static void labelDestroy(newtComponent co) { + struct label * la = co->data; + + free(la->text); + free(la); + free(co); +} diff --git a/mdk-stage1/newt/listbox.c b/mdk-stage1/newt/listbox.c new file mode 100644 index 000000000..ef276aeb4 --- /dev/null +++ b/mdk-stage1/newt/listbox.c @@ -0,0 +1,752 @@ +/* This goofed-up box whacked into shape by Elliot Lee <sopwith@cuc.edu> + (from the original listbox by Erik Troan <ewt@redhat.com>) + and contributed to newt for use under the LGPL license. + Copyright (C) 1996, 1997 Elliot Lee */ + +#include <slang.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "newt.h" +#include "newt_pr.h" + + +/* Linked list of items in the listbox */ +struct items { + char * text; + const void *data; + unsigned char isSelected; + struct items *next; +}; + +/* Holds all the relevant information for this listbox */ +struct listbox { + newtComponent sb; /* Scrollbar on right side of listbox */ + int curWidth; /* size of text w/o scrollbar or border*/ + int curHeight; /* size of text w/o border */ + int sbAdjust; + int bdxAdjust, bdyAdjust; + int numItems, numSelected; + int userHasSetWidth; + int currItem, startShowItem; /* startShowItem is the first item displayed + on the screen */ + int isActive; /* If we handle key events all the time, it seems + to do things even when they are supposed to be for + another button/whatever */ + struct items *boxItems; + int grow; + int flags; /* flags for this listbox, right now just + NEWT_FLAG_RETURNEXIT */ +}; + +static void listboxDraw(newtComponent co); +static void listboxDestroy(newtComponent co); +static struct eventResult listboxEvent(newtComponent co, struct event ev); +static void newtListboxRealSetCurrent(newtComponent co); +static void listboxPlace(newtComponent co, int newLeft, int newTop); +static inline void updateWidth(newtComponent co, struct listbox * li, + int maxField); +static void listboxMapped(newtComponent co, int isMapped); + +static struct componentOps listboxOps = { + listboxDraw, + listboxEvent, + listboxDestroy, + listboxPlace, + listboxMapped, +}; + +static void listboxMapped(newtComponent co, int isMapped) { + struct listbox * li = co->data; + + co->isMapped = isMapped; + if (li->sb) + li->sb->ops->mapped(li->sb, isMapped); +} + +static void listboxPlace(newtComponent co, int newLeft, int newTop) { + struct listbox * li = co->data; + + co->top = newTop; + co->left = newLeft; + + if (li->sb) + li->sb->ops->place(li->sb, co->left + co->width - li->bdxAdjust - 1, + co->top); +} + +newtComponent newtListbox(int left, int top, int height, int flags) { + newtComponent co, sb; + struct listbox * li; + + if (!(co = malloc(sizeof(*co)))) + return NULL; + + if (!(li = malloc(sizeof(struct listbox)))) { + free(co); + return NULL; + } + + li->boxItems = NULL; + li->numItems = 0; + li->currItem = 0; + li->numSelected = 0; + li->isActive = 0; + li->userHasSetWidth = 0; + li->startShowItem = 0; + li->sbAdjust = 0; + li->bdxAdjust = 0; + li->bdyAdjust = 0; + li->flags = flags & (NEWT_FLAG_RETURNEXIT | NEWT_FLAG_BORDER | + NEWT_FLAG_MULTIPLE); + + if (li->flags & NEWT_FLAG_BORDER) { + li->bdxAdjust = 2; + li->bdyAdjust = 1; + } + + co->height = height; + li->curHeight = co->height - (2 * li->bdyAdjust); + + if (height) { + li->grow = 0; + if (flags & NEWT_FLAG_SCROLL) { + sb = newtVerticalScrollbar(left, top + li->bdyAdjust, + li->curHeight, + COLORSET_LISTBOX, COLORSET_ACTLISTBOX); + li->sbAdjust = 3; + } else { + sb = NULL; + } + } else { + li->grow = 1; + sb = NULL; + } + + li->sb = sb; + co->data = li; + co->isMapped = 0; + co->left = left; + co->top = top; + co->ops = &listboxOps; + co->takesFocus = 1; + co->callback = NULL; + + updateWidth(co, li, 5); + + return co; +} + +static inline void updateWidth(newtComponent co, struct listbox * li, + int maxField) { + li->curWidth = maxField; + co->width = li->curWidth + li->sbAdjust + 2 * li->bdxAdjust; + + if (li->sb) + li->sb->left = co->left + co->width - li->bdxAdjust - 1; +} + +void newtListboxSetCurrentByKey(newtComponent co, void * key) { + struct listbox * li = co->data; + struct items * item; + int i; + + item = li->boxItems, i = 0; + while (item && item->data != key) + item = item->next, i++; + + if (item) + newtListboxSetCurrent(co, i); +} + +void newtListboxSetCurrent(newtComponent co, int num) +{ + struct listbox * li = co->data; + + if (num >= li->numItems) + li->currItem = li->numItems - 1; + else if (num < 0) + li->currItem = 0; + else + li->currItem = num; + + if (li->currItem < li->startShowItem) + li->startShowItem = li->currItem; + else if (li->currItem - li->startShowItem > li->curHeight - 1) + li->startShowItem = li->currItem - li->curHeight + 1; + if (li->startShowItem + li->curHeight > li->numItems) + li->startShowItem = li->numItems - li->curHeight; + if(li->startShowItem < 0) + li->startShowItem = 0; + + newtListboxRealSetCurrent(co); +} + +static void newtListboxRealSetCurrent(newtComponent co) +{ + struct listbox * li = co->data; + + if(li->sb) + newtScrollbarSet(li->sb, li->currItem + 1, li->numItems); + listboxDraw(co); + if(co->callback) co->callback(co, co->callbackData); +} + +void newtListboxSetWidth(newtComponent co, int width) { + struct listbox * li = co->data; + + co->width = width; + li->curWidth = co->width - li->sbAdjust - 2 * li->bdxAdjust; + li->userHasSetWidth = 1; + if (li->sb) li->sb->left = co->width + co->left - 1; + listboxDraw(co); +} + +void * newtListboxGetCurrent(newtComponent co) { + struct listbox * li = co->data; + int i; + struct items *item; + + for(i = 0, item = li->boxItems; item != NULL && i < li->currItem; + i++, item = item->next); + + if (item) + return (void *)item->data; + else + return NULL; +} + +void newtListboxSelectItem(newtComponent co, const void * key, + enum newtFlagsSense sense) +{ + struct listbox * li = co->data; + int i; + struct items * item; + + item = li->boxItems, i = 0; + while (item && item->data != key) + item = item->next, i++; + + if (!item) return; + + if (item->isSelected) + li->numSelected--; + + switch(sense) { + case NEWT_FLAGS_RESET: + item->isSelected = 0; break; + case NEWT_FLAGS_SET: + item->isSelected = 1; break; + case NEWT_FLAGS_TOGGLE: + item->isSelected = !item->isSelected; + } + + if (item->isSelected) + li->numSelected++; + + listboxDraw(co); +} + +void newtListboxClearSelection(newtComponent co) +{ + struct items *item; + struct listbox * li = co->data; + + for(item = li->boxItems; item != NULL; + item = item->next) + item->isSelected = 0; + li->numSelected = 0; + listboxDraw(co); +} + +/* Free the returned array after use, but NOT the values in the array */ +void ** newtListboxGetSelection(newtComponent co, int *numitems) +{ + struct listbox * li; + int i; + void **retval; + struct items *item; + + if(!co || !numitems) return NULL; + + li = co->data; + if(!li || !li->numSelected) return NULL; + + retval = malloc(li->numSelected * sizeof(void *)); + for(i = 0, item = li->boxItems; item != NULL; + item = item->next) + if(item->isSelected) + retval[i++] = (void *)item->data; + *numitems = li->numSelected; + return retval; +} + +void newtListboxSetEntry(newtComponent co, int num, const char * text) { + struct listbox * li = co->data; + int i; + struct items *item; + + for(i = 0, item = li->boxItems; item != NULL && i < num; + i++, item = item->next); + + if(!item) + return; + else { + free(item->text); + item->text = strdup(text); + } + if (li->userHasSetWidth == 0 && strlen(text) > li->curWidth) { + updateWidth(co, li, strlen(text)); + } + + if (num >= li->startShowItem && num <= li->startShowItem + co->height) + listboxDraw(co); +} + +void newtListboxSetData(newtComponent co, int num, void * data) { + struct listbox * li = co->data; + int i; + struct items *item; + + for(i = 0, item = li->boxItems; item != NULL && i < num; + i++, item = item->next); + + item->data = data; +} + +int newtListboxAppendEntry(newtComponent co, const char * text, + const void * data) { + struct listbox * li = co->data; + struct items *item; + + if(li->boxItems) { + for (item = li->boxItems; item->next != NULL; item = item->next); + + item = item->next = malloc(sizeof(struct items)); + } else { + item = li->boxItems = malloc(sizeof(struct items)); + } + + if (!li->userHasSetWidth && text && (strlen(text) > li->curWidth)) + updateWidth(co, li, strlen(text)); + + item->text = strdup(text); item->data = data; item->next = NULL; + item->isSelected = 0; + + if (li->grow) + co->height++, li->curHeight++; + li->numItems++; + + return 0; +} + +int newtListboxInsertEntry(newtComponent co, const char * text, + const void * data, void * key) { + struct listbox * li = co->data; + struct items *item, *t; + + if (li->boxItems) { + if (key) { + item = li->boxItems; + while (item && item->data != key) item = item->next; + + if (!item) return 1; + + t = item->next; + item = item->next = malloc(sizeof(struct items)); + item->next = t; + } else { + t = li->boxItems; + item = li->boxItems = malloc(sizeof(struct items)); + item->next = t; + } + } else if (key) { + return 1; + } else { + item = li->boxItems = malloc(sizeof(struct items)); + item->next = NULL; + } + + if (!li->userHasSetWidth && text && (strlen(text) > li->curWidth)) + updateWidth(co, li, strlen(text)); + + item->text = strdup(text?text:"(null)"); item->data = data; + item->isSelected = 0; + + if (li->sb) + li->sb->left = co->left + co->width - li->bdxAdjust - 1; + li->numItems++; + + listboxDraw(co); + + return 0; +} + +int newtListboxDeleteEntry(newtComponent co, void * key) { + struct listbox * li = co->data; + int widest = 0, t; + struct items *item, *item2 = NULL; + int num; + + if (li->boxItems == NULL || li->numItems <= 0) + return 0; + + num = 0; + + item2 = NULL, item = li->boxItems; + while (item && item->data != key) { + item2 = item; + item = item->next; + num++; + } + + if (!item) + return -1; + + if (item2) + item2->next = item->next; + else + li->boxItems = item->next; + + free(item->text); + free(item); + li->numItems--; + + if (!li->userHasSetWidth) { + widest = 0; + for (item = li->boxItems; item != NULL; item = item->next) + if ((t = strlen(item->text)) > widest) widest = t; + } + + if (li->currItem >= num) + li->currItem--; + + if (!li->userHasSetWidth) { + updateWidth(co, li, widest); + } + + listboxDraw(co); + + return 0; +} + +void newtListboxClear(newtComponent co) +{ + struct listbox * li; + struct items *anitem, *nextitem; + if(co == NULL || (li = co->data) == NULL) + return; + for(anitem = li->boxItems; anitem != NULL; anitem = nextitem) { + nextitem = anitem->next; + free(anitem->text); + free(anitem); + } + li->numItems = li->numSelected = li->currItem = li->startShowItem = 0; + li->boxItems = NULL; + if (!li->userHasSetWidth) + updateWidth(co, li, 5); +} + +/* If you don't want to get back the text, pass in NULL for the ptr-ptr. Same + goes for the data. */ +void newtListboxGetEntry(newtComponent co, int num, char **text, void **data) { + struct listbox * li = co->data; + int i; + struct items *item; + + if (!li->boxItems || num >= li->numItems) { + if(text) + *text = NULL; + if(data) + *data = NULL; + return; + } + + i = 0; + item = li->boxItems; + while (item && i < num) { + i++, item = item->next; + } + + if (item) { + if (text) + *text = item->text; + if (data) + *data = (void *)item->data; + } +} + +static void listboxDraw(newtComponent co) +{ + struct listbox * li = co->data; + struct items *item; + int i, j; + + if (!co->isMapped) return ; + + if(li->flags & NEWT_FLAG_BORDER) { + if(li->isActive) + SLsmg_set_color(NEWT_COLORSET_ACTLISTBOX); + else + SLsmg_set_color(NEWT_COLORSET_LISTBOX); + + newtDrawBox(co->left, co->top, co->width, co->height, 0); + } + + if(li->sb) + li->sb->ops->draw(li->sb); + + SLsmg_set_color(NEWT_COLORSET_LISTBOX); + + for(i = 0, item = li->boxItems; item != NULL && i < li->startShowItem; + i++, item = item->next); + + j = i; + + for (i = 0; item != NULL && i < li->curHeight; i++, item = item->next) { + if (!item->text) continue; + + newtGotorc(co->top + i + li->bdyAdjust, co->left + li->bdxAdjust); + if(j + i == li->currItem) { + if(item->isSelected) + SLsmg_set_color(NEWT_COLORSET_ACTSELLISTBOX); + else + SLsmg_set_color(NEWT_COLORSET_ACTLISTBOX); + } else if(item->isSelected) + SLsmg_set_color(NEWT_COLORSET_SELLISTBOX); + else + SLsmg_set_color(NEWT_COLORSET_LISTBOX); + + SLsmg_write_nstring(item->text, li->curWidth); + + } + newtGotorc(co->top + (li->currItem - li->startShowItem), co->left); +} + +static struct eventResult listboxEvent(newtComponent co, struct event ev) { + struct eventResult er; + struct listbox * li = co->data; + struct items *item; + int i; + + er.result = ER_IGNORED; + + if(ev.when == EV_EARLY || ev.when == EV_LATE) { + return er; + } + + switch(ev.event) { + case EV_KEYPRESS: + if (!li->isActive) break; + + switch(ev.u.key) { + case ' ': + if(!(li->flags & NEWT_FLAG_MULTIPLE)) break; + newtListboxSelectItem(co, li->boxItems[li->currItem].data, + NEWT_FLAGS_TOGGLE); + er.result = ER_SWALLOWED; + /* We don't break here, because it is cool to be able to + hold space to select a bunch of items in a list at once */ + + case NEWT_KEY_DOWN: + if(li->numItems <= 0) break; + if(li->currItem < li->numItems - 1) { + li->currItem++; + if(li->currItem > (li->startShowItem + li->curHeight - 1)) { + li->startShowItem = li->currItem - li->curHeight + 1; + if(li->startShowItem + li->curHeight > li->numItems) + li->startShowItem = li->numItems - li->curHeight; + } + if(li->sb) + newtScrollbarSet(li->sb, li->currItem + 1, li->numItems); + listboxDraw(co); + } + if(co->callback) co->callback(co, co->callbackData); + er.result = ER_SWALLOWED; + break; + + case NEWT_KEY_ENTER: + if(li->numItems <= 0) break; + if(li->flags & NEWT_FLAG_RETURNEXIT) + er.result = ER_EXITFORM; + break; + + case NEWT_KEY_UP: + if(li->numItems <= 0) break; + if(li->currItem > 0) { + li->currItem--; + if(li->currItem < li->startShowItem) + li->startShowItem = li->currItem; + if(li->sb) + newtScrollbarSet(li->sb, li->currItem + 1, li->numItems); + listboxDraw(co); + } + if(co->callback) co->callback(co, co->callbackData); + er.result = ER_SWALLOWED; + break; + + case NEWT_KEY_PGUP: + if(li->numItems <= 0) break; + li->startShowItem -= li->curHeight - 1; + if(li->startShowItem < 0) + li->startShowItem = 0; + li->currItem -= li->curHeight - 1; + if(li->currItem < 0) + li->currItem = 0; + newtListboxRealSetCurrent(co); + er.result = ER_SWALLOWED; + break; + + case NEWT_KEY_PGDN: + if(li->numItems <= 0) break; + li->startShowItem += li->curHeight; + if(li->startShowItem > (li->numItems - li->curHeight)) { + li->startShowItem = li->numItems - li->curHeight; + } + li->currItem += li->curHeight; + if(li->currItem >= li->numItems) { + li->currItem = li->numItems - 1; + } + newtListboxRealSetCurrent(co); + er.result = ER_SWALLOWED; + break; + + case NEWT_KEY_HOME: + if(li->numItems <= 0) break; + newtListboxSetCurrent(co, 0); + er.result = ER_SWALLOWED; + break; + + case NEWT_KEY_END: + if(li->numItems <= 0) break; + li->startShowItem = li->numItems - li->curHeight; + if(li->startShowItem < 0) + li->startShowItem = 0; + li->currItem = li->numItems - 1; + newtListboxRealSetCurrent(co); + er.result = ER_SWALLOWED; + break; + default: + if (li->numItems <= 0) break; + if (ev.u.key < NEWT_KEY_EXTRA_BASE && isalpha(ev.u.key)) { + for(i = 0, item = li->boxItems; item != NULL && + i < li->currItem; i++, item = item->next); + + if (item && item->text && (toupper(*item->text) == toupper(ev.u.key))) { + item = item->next; + i++; + } else { + item = li->boxItems; + i = 0; + } + while (item && item->text && + toupper(*item->text) != toupper(ev.u.key)) { + item = item->next; + i++; + } + if (item) { + li->currItem = i; + if(li->currItem < li->startShowItem || + li->currItem > li->startShowItem) + li->startShowItem = + li->currItem > li->numItems - li->curHeight ? + li->startShowItem = li->numItems - li->curHeight : + li->currItem; + if(li->sb) + newtScrollbarSet(li->sb, li->currItem + 1, li->numItems); + newtListboxRealSetCurrent(co); + er.result = ER_SWALLOWED; + } + } + } + break; + + case EV_FOCUS: + li->isActive = 1; + listboxDraw(co); + er.result = ER_SWALLOWED; + break; + + case EV_UNFOCUS: + li->isActive = 0; + listboxDraw(co); + er.result = ER_SWALLOWED; + break; + + case EV_MOUSE: + /* if this mouse click was within the listbox, make the current + item the item clicked on. */ + /* Up scroll arrow */ + if (li->sb && + ev.u.mouse.x == co->left + co->width - li->bdxAdjust - 1 && + ev.u.mouse.y == co->top + li->bdyAdjust) { + if(li->numItems <= 0) break; + if(li->currItem > 0) { + li->currItem--; + if(li->currItem < li->startShowItem) + li->startShowItem = li->currItem; + if(li->sb) + newtScrollbarSet(li->sb, li->currItem + 1, li->numItems); + listboxDraw(co); + } + if(co->callback) co->callback(co, co->callbackData); + er.result = ER_SWALLOWED; + break; + } + /* Down scroll arrow */ + if (li->sb && + ev.u.mouse.x == co->left + co->width - li->bdxAdjust - 1 && + ev.u.mouse.y == co->top + co->height - li->bdyAdjust - 1) { + if(li->numItems <= 0) break; + if(li->currItem < li->numItems - 1) { + li->currItem++; + if(li->currItem > (li->startShowItem + li->curHeight - 1)) { + li->startShowItem = li->currItem - li->curHeight + 1; + if(li->startShowItem + li->curHeight > li->numItems) + li->startShowItem = li->numItems - li->curHeight; + } + if(li->sb) + newtScrollbarSet(li->sb, li->currItem + 1, li->numItems); + listboxDraw(co); + } + if(co->callback) co->callback(co, co->callbackData); + er.result = ER_SWALLOWED; + break; + } + if ((ev.u.mouse.y >= co->top + li->bdyAdjust) && + (ev.u.mouse.y <= co->top + co->height - (li->bdyAdjust * 2)) && + (ev.u.mouse.x >= co->left + li->bdxAdjust) && + (ev.u.mouse.x <= co->left + co->width + (li->bdxAdjust * 2))) { + li->currItem = li->startShowItem + + (ev.u.mouse.y - li->bdyAdjust - co->top); + newtListboxRealSetCurrent(co); + listboxDraw(co); + if(co->callback) co->callback(co, co->callbackData); + er.result = ER_SWALLOWED; + break; + } + } + + return er; +} + +static void listboxDestroy(newtComponent co) { + struct listbox * li = co->data; + struct items * item, * nextitem; + + nextitem = item = li->boxItems; + + while (item != NULL) { + nextitem = item->next; + free(item->text); + free(item); + item = nextitem; + } + + if (li->sb) li->sb->ops->destroy(li->sb); + + free(li); + free(co); +} diff --git a/mdk-stage1/newt/newt.c b/mdk-stage1/newt/newt.c new file mode 100644 index 000000000..1cfe3ac93 --- /dev/null +++ b/mdk-stage1/newt/newt.c @@ -0,0 +1,672 @@ +#include <slang.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/signal.h> +#include <sys/time.h> +#include <sys/types.h> +#include <termios.h> +#include <unistd.h> + +#include "newt.h" +#include "newt_pr.h" + +struct Window { + int height, width, top, left; + short * buffer; + char * title; +}; + +struct keymap { + char * str; + int code; + char * tc; +}; + +static struct Window windowStack[20]; +static struct Window * currentWindow = NULL; + +static char * helplineStack[20]; +static char ** currentHelpline = NULL; + +static int cursorRow, cursorCol; +static int needResize; +static int cursorOn = 1; + +static const char * defaultHelpLine = +" <Tab>/<Alt-Tab> between elements | <Space> selects | <F12> next screen" +; + +const struct newtColors newtDefaultColorPalette = { + "cyan", "black", /* root fg, bg */ + "black", "blue", /* border fg, bg */ + "white", "blue", /* window fg, bg */ + "white", "black", /* shadow fg, bg */ + "white", "blue", /* title fg, bg */ + "black", "cyan", /* button fg, bg */ + "yellow", "cyan", /* active button fg, bg */ + "yellow", "blue", /* checkbox fg, bg */ + "blue", "brown", /* active checkbox fg, bg */ + "yellow", "blue", /* entry box fg, bg */ + "white", "blue", /* label fg, bg */ + "black", "cyan", /* listbox fg, bg */ + "yellow", "cyan", /* active listbox fg, bg */ + "white", "blue", /* textbox fg, bg */ + "cyan", "black", /* active textbox fg, bg */ + "white", "blue", /* help line */ + "yellow", "blue", /* root text */ + "blue", /* scale full */ + "red", /* scale empty */ + "blue", "cyan", /* disabled entry fg, bg */ + "white", "blue", /* compact button fg, bg */ + "yellow", "red", /* active & sel listbox */ + "black", "brown" /* selected listbox */ +}; + +static const struct keymap keymap[] = { + { "\033OA", NEWT_KEY_UP, "kh" }, + { "\033[A", NEWT_KEY_UP, "ku" }, + { "\033OB", NEWT_KEY_DOWN, "kd" }, + { "\033[B", NEWT_KEY_DOWN, "kd" }, + { "\033[C", NEWT_KEY_RIGHT, "kr" }, + { "\033OC", NEWT_KEY_RIGHT, "kr" }, + { "\033[D", NEWT_KEY_LEFT, "kl" }, + { "\033OD", NEWT_KEY_LEFT, "kl" }, + { "\033[H", NEWT_KEY_HOME, "kh" }, + { "\033[1~", NEWT_KEY_HOME, "kh" }, + { "\033Ow", NEWT_KEY_END, "kH" }, + { "\033[4~", NEWT_KEY_END, "kH" }, + + { "\033[3~", NEWT_KEY_DELETE, "kl" }, + { "\033[2~", NEWT_KEY_INSERT, NULL }, + + { "\033\t", NEWT_KEY_UNTAB, NULL }, + + { "\033[5~", NEWT_KEY_PGUP, NULL }, + { "\033[6~", NEWT_KEY_PGDN, NULL }, + { "\033V", NEWT_KEY_PGUP, "kH" }, + { "\033v", NEWT_KEY_PGUP, "kH" }, + + { "\033[[A", NEWT_KEY_F1, NULL }, + { "\033[[B", NEWT_KEY_F2, NULL }, + { "\033[[C", NEWT_KEY_F3, NULL }, + { "\033[[D", NEWT_KEY_F4, NULL }, + { "\033[[E", NEWT_KEY_F5, NULL }, + + { "\033OP", NEWT_KEY_F1, NULL }, + { "\033OQ", NEWT_KEY_F2, NULL }, + { "\033OR", NEWT_KEY_F3, NULL }, + { "\033OS", NEWT_KEY_F4, NULL }, + + { "\033[11~", NEWT_KEY_F1, NULL }, + { "\033[12~", NEWT_KEY_F2, NULL }, + { "\033[13~", NEWT_KEY_F3, NULL }, + { "\033[14~", NEWT_KEY_F4, NULL }, + { "\033[15~", NEWT_KEY_F5, NULL }, + { "\033[17~", NEWT_KEY_F6, NULL }, + { "\033[18~", NEWT_KEY_F7, NULL }, + { "\033[19~", NEWT_KEY_F8, NULL }, + { "\033[20~", NEWT_KEY_F9, NULL }, + { "\033[21~", NEWT_KEY_F10, NULL }, + { "\033[23~", NEWT_KEY_F11, NULL }, + { "\033[24~", NEWT_KEY_F12, NULL }, + + { NULL, 0, NULL }, /* LEAVE this one */ +}; +static char keyPrefix = '\033'; + +static const char * version = "Newt windowing library version " VERSION + " - (C) 1996-2000 Red Hat Software. " + "Redistributable under the term of the Library " + "GNU Public License. " + "Written by Erik Troan\n"; + +static newtSuspendCallback suspendCallback = NULL; +static void * suspendCallbackData = NULL; + +void newtSetSuspendCallback(newtSuspendCallback cb, void * data) { + suspendCallback = cb; + suspendCallbackData = data; +} + +static void handleSigwinch(int signum) { + needResize = 1; +} + +static int getkeyInterruptHook(void) { + return -1; +} + +void newtFlushInput(void) { + while (SLang_input_pending(0)) { + SLang_getkey(); + } +} + +void newtRefresh(void) { + SLsmg_refresh(); +} + +void newtSuspend(void) { + SLtt_set_cursor_visibility (1); + SLsmg_suspend_smg(); + SLang_reset_tty(); + SLtt_set_cursor_visibility (cursorOn); +} + +void newtResume(void) { + SLsmg_resume_smg (); + SLsmg_refresh(); + SLang_init_tty(0, 0, 0); +} + +void newtCls(void) { + SLsmg_set_color(NEWT_COLORSET_ROOT); + SLsmg_gotorc(0, 0); + SLsmg_erase_eos(); + + newtRefresh(); +} + +#if defined(THIS_DOESNT_WORK) +void newtResizeScreen(int redraw) { + newtPushHelpLine(""); + + SLtt_get_screen_size(); + SLang_init_tty(0, 0, 0); + + SLsmg_touch_lines (0, SLtt_Screen_Rows - 1); + + /* I don't know why I need this */ + SLsmg_refresh(); + + newtPopHelpLine(); + + if (redraw) + SLsmg_refresh(); +} +#endif + +int newtInit(void) { + char * MonoValue, * MonoEnv = "NEWT_MONO"; + + /* use the version variable just to be sure it gets included */ + strlen(version); + + SLtt_get_terminfo(); + SLtt_get_screen_size(); + + MonoValue = getenv(MonoEnv); + if ( MonoValue == NULL ) { + SLtt_Use_Ansi_Colors = 1; + } else { + SLtt_Use_Ansi_Colors = 0; + } + + SLsmg_init_smg(); + SLang_init_tty(0, 0, 0); + + newtSetColors(newtDefaultColorPalette); + newtCursorOff(); + /*initKeymap();*/ + + /*memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handleSigwinch; + sigaction(SIGWINCH, &sa, NULL);*/ + + SLsignal_intr(SIGWINCH, handleSigwinch); + SLang_getkey_intr_hook = getkeyInterruptHook; + + + + return 0; +} + +int newtFinished(void) { + SLsmg_gotorc(SLtt_Screen_Rows - 1, 0); + newtCursorOn(); + SLsmg_refresh(); + SLsmg_reset_smg(); + SLang_reset_tty(); + + return 0; +} + +void newtSetColors(struct newtColors colors) { + SLtt_set_color(NEWT_COLORSET_ROOT, "", colors.rootFg, colors.rootBg); + SLtt_set_color(NEWT_COLORSET_BORDER, "", colors.borderFg, colors.borderBg); + SLtt_set_color(NEWT_COLORSET_WINDOW, "", colors.windowFg, colors.windowBg); + SLtt_set_color(NEWT_COLORSET_SHADOW, "", colors.shadowFg, colors.shadowBg); + SLtt_set_color(NEWT_COLORSET_TITLE, "", colors.titleFg, colors.titleBg); + SLtt_set_color(NEWT_COLORSET_BUTTON, "", colors.buttonFg, colors.buttonBg); + SLtt_set_color(NEWT_COLORSET_ACTBUTTON, "", colors.actButtonFg, + colors.actButtonBg); + SLtt_set_color(NEWT_COLORSET_CHECKBOX, "", colors.checkboxFg, + colors.checkboxBg); + SLtt_set_color(NEWT_COLORSET_ACTCHECKBOX, "", colors.actCheckboxFg, + colors.actCheckboxBg); + SLtt_set_color(NEWT_COLORSET_ENTRY, "", colors.entryFg, colors.entryBg); + SLtt_set_color(NEWT_COLORSET_LABEL, "", colors.labelFg, colors.labelBg); + SLtt_set_color(NEWT_COLORSET_LISTBOX, "", colors.listboxFg, + colors.listboxBg); + SLtt_set_color(NEWT_COLORSET_ACTLISTBOX, "", colors.actListboxFg, + colors.actListboxBg); + SLtt_set_color(NEWT_COLORSET_TEXTBOX, "", colors.textboxFg, + colors.textboxBg); + SLtt_set_color(NEWT_COLORSET_ACTTEXTBOX, "", colors.actTextboxFg, + colors.actTextboxBg); + SLtt_set_color(NEWT_COLORSET_HELPLINE, "", colors.helpLineFg, + colors.helpLineBg); + SLtt_set_color(NEWT_COLORSET_ROOTTEXT, "", colors.rootTextFg, + colors.rootTextBg); + + SLtt_set_color(NEWT_COLORSET_EMPTYSCALE, "", "black", + colors.emptyScale); + SLtt_set_color(NEWT_COLORSET_FULLSCALE, "", "black", + colors.fullScale); + SLtt_set_color(NEWT_COLORSET_DISENTRY, "", colors.disabledEntryFg, + colors.disabledEntryBg); + + SLtt_set_color(NEWT_COLORSET_COMPACTBUTTON, "", colors.compactButtonFg, + colors.compactButtonBg); + + SLtt_set_color(NEWT_COLORSET_ACTSELLISTBOX, "", colors.actSelListboxFg, + colors.actSelListboxBg); + SLtt_set_color(NEWT_COLORSET_SELLISTBOX, "", colors.selListboxFg, + colors.selListboxBg); +} + +int newtGetKey(void) { + int key; + char buf[10], * chptr = buf; + const struct keymap * curr; + + do { + key = SLang_getkey(); + if (key == 0xFFFF) { + if (needResize) + return NEWT_KEY_RESIZE; + + /* ignore other signals */ + continue; + } + + if (key == NEWT_KEY_SUSPEND && suspendCallback) + suspendCallback(suspendCallbackData); + } while (key == NEWT_KEY_SUSPEND); + + switch (key) { + case 'v' | 0x80: + case 'V' | 0x80: + return NEWT_KEY_PGUP; + + case 22: + return NEWT_KEY_PGDN; + + return NEWT_KEY_BKSPC; + case 0x7f: + return NEWT_KEY_BKSPC; + + case 0x08: + return NEWT_KEY_BKSPC; + + default: + if (key != keyPrefix) return key; + } + + memset(buf, 0, sizeof(buf)); + + *chptr++ = key; + while (SLang_input_pending(5)) { + key = SLang_getkey(); + if (key == keyPrefix) { + /* he hit unknown keys too many times -- start over */ + memset(buf, 0, sizeof(buf)); + chptr = buf; + } + + *chptr++ = key; + + /* this search should use bsearch(), but when we only look through + a list of 20 (or so) keymappings, it's probably faster just to + do a inline linear search */ + + for (curr = keymap; curr->code; curr++) { + if (curr->str) { + if (!strcmp(curr->str, buf)) + return curr->code; + } + } + } + + for (curr = keymap; curr->code; curr++) { + if (curr->str) { + if (!strcmp(curr->str, buf)) + return curr->code; + } + } + + /* Looks like we were a bit overzealous in reading characters. Return + just the first character, and put everything else back in the buffer + for later */ + + chptr--; + while (chptr > buf) + SLang_ungetkey(*chptr--); + + return *chptr; +} + +void newtWaitForKey(void) { + newtRefresh(); + + SLang_getkey(); + newtClearKeyBuffer(); +} + +void newtClearKeyBuffer(void) { + while (SLang_input_pending(1)) { + SLang_getkey(); + } +} + +int newtOpenWindow(int left, int top, int width, int height, + const char * title) { + int j, row, col; + int n; + int i; + + newtFlushInput(); + + if (!currentWindow) { + currentWindow = windowStack; + } else { + currentWindow++; + } + + currentWindow->left = left; + currentWindow->top = top; + currentWindow->width = width; + currentWindow->height = height; + currentWindow->title = title ? strdup(title) : NULL; + + currentWindow->buffer = malloc(sizeof(short) * (width + 3) * (height + 3)); + + row = top - 1; + col = left - 1; + n = 0; + for (j = 0; j < height + 3; j++, row++) { + SLsmg_gotorc(row, col); + SLsmg_read_raw(currentWindow->buffer + n, + currentWindow->width + 3); + n += currentWindow->width + 3; + } + + SLsmg_set_color(NEWT_COLORSET_BORDER); + SLsmg_draw_box(top - 1, left - 1, height + 2, width + 2); + + if (currentWindow->title) { + i = strlen(currentWindow->title) + 4; + i = ((width - i) / 2) + left; + SLsmg_gotorc(top - 1, i); + SLsmg_set_char_set(1); + SLsmg_write_char(SLSMG_RTEE_CHAR); + SLsmg_set_char_set(0); + SLsmg_write_char(' '); + SLsmg_set_color(NEWT_COLORSET_TITLE); + SLsmg_write_string((char *)currentWindow->title); + SLsmg_set_color(NEWT_COLORSET_BORDER); + SLsmg_write_char(' '); + SLsmg_set_char_set(1); + SLsmg_write_char(SLSMG_LTEE_CHAR); + SLsmg_set_char_set(0); + } + + SLsmg_set_color(NEWT_COLORSET_WINDOW); + SLsmg_fill_region(top, left, height, width, ' '); + + SLsmg_set_color(NEWT_COLORSET_SHADOW); + SLsmg_fill_region(top + height + 1, left, 1, width + 2, ' '); + SLsmg_fill_region(top, left + width + 1, height + 1, 1, ' '); + + for (i = top; i < (top + height + 1); i++) { + SLsmg_gotorc(i, left + width + 1); + SLsmg_write_string(" "); + } + + return 0; +} + +int newtCenteredWindow(int width, int height, const char * title) { + int top, left; + + top = (SLtt_Screen_Rows - height) / 2; + + /* I don't know why, but this seems to look better */ + if ((SLtt_Screen_Rows % 2) && (top % 2)) top--; + + left = (SLtt_Screen_Cols - width) / 2; + + newtOpenWindow(left, top, width, height, title); + + return 0; +} + +void newtPopWindow(void) { + int j, row, col; + int n = 0; + + row = col = 0; + + row = currentWindow->top - 1; + col = currentWindow->left - 1; + for (j = 0; j < currentWindow->height + 3; j++, row++) { + SLsmg_gotorc(row, col); + SLsmg_write_raw(currentWindow->buffer + n, + currentWindow->width + 3); + n += currentWindow->width + 3; + } + + free(currentWindow->buffer); + free(currentWindow->title); + + if (currentWindow == windowStack) + currentWindow = NULL; + else + currentWindow--; + + SLsmg_set_char_set(0); + + newtRefresh(); +} + +void newtGetWindowPos(int * x, int * y) { + if (currentWindow) { + *x = currentWindow->left; + *y = currentWindow->top; + } else + *x = *y = 0; +} + +void newtGetrc(int * row, int * col) { + *row = cursorRow; + *col = cursorCol; +} + +void newtGotorc(int newRow, int newCol) { + if (currentWindow) { + newRow += currentWindow->top; + newCol += currentWindow->left; + } + + cursorRow = newRow; + cursorCol = newCol; + SLsmg_gotorc(cursorRow, cursorCol); +} + +void newtDrawBox(int left, int top, int width, int height, int shadow) { + if (currentWindow) { + top += currentWindow->top; + left += currentWindow->left; + } + + SLsmg_draw_box(top, left, height, width); + + if (shadow) { + SLsmg_set_color(NEWT_COLORSET_SHADOW); + SLsmg_fill_region(top + height, left + 1, 1, width - 1, ' '); + SLsmg_fill_region(top + 1, left + width, height, 1, ' '); + } +} + +void newtClearBox(int left, int top, int width, int height) { + if (currentWindow) { + top += currentWindow->top; + left += currentWindow->left; + } + + SLsmg_fill_region(top, left, height, width, ' '); +} + +#if 0 +/* This doesn't seem to work quite right. I don't know why not, but when + I rsh from an rxvt into a box and run this code, the machine returns + console key's (\033[B) rather then xterm ones (\033OB). */ +static void initKeymap(void) { + struct keymap * curr; + + for (curr = keymap; curr->code; curr++) { + if (!curr->str) + curr->str = SLtt_tgetstr(curr->tc); + } + + /* Newt's keymap handling is a bit broken. It assumes that any extended + keystrokes begin with ESC. If you're using a homebrek terminal you + will probably need to fix this, or just yell at me and I'll be so + ashamed of myself for doing it this way I'll fix it */ + + keyPrefix = 0x1b; /* ESC */ +} +#endif + +void newtDelay(int usecs) { + fd_set set; + struct timeval tv; + + FD_ZERO(&set); + + tv.tv_sec = usecs / 1000000; + tv.tv_usec = usecs % 1000000; + + select(0, &set, &set, &set, &tv); +} + +struct eventResult newtDefaultEventHandler(newtComponent c, + struct event ev) { + struct eventResult er; + + er.result = ER_IGNORED; + return er; +} + +void newtRedrawHelpLine(void) { + char * buf; + + SLsmg_set_color(NEWT_COLORSET_HELPLINE); + + buf = alloca(SLtt_Screen_Cols + 1); + memset(buf, ' ', SLtt_Screen_Cols); + buf[SLtt_Screen_Cols] = '\0'; + + if (currentHelpline) + memcpy(buf, *currentHelpline, strlen(*currentHelpline)); + + SLsmg_gotorc(SLtt_Screen_Rows - 1, 0); + SLsmg_write_string(buf); +} + +void newtPushHelpLine(const char * text) { + if (!text) + text = defaultHelpLine; + + if (currentHelpline) + (*(++currentHelpline)) = strdup(text); + else { + currentHelpline = helplineStack; + *currentHelpline = strdup(text); + } + + newtRedrawHelpLine(); +} + +void newtPopHelpLine(void) { + if (!currentHelpline) return; + + free(*currentHelpline); + if (currentHelpline == helplineStack) + currentHelpline = NULL; + else + currentHelpline--; + + newtRedrawHelpLine(); +} + +void newtDrawRootText(int col, int row, const char * text) { + SLsmg_set_color(NEWT_COLORSET_ROOTTEXT); + + if (col < 0) { + col = SLtt_Screen_Cols + col; + } + + if (row < 0) { + row = SLtt_Screen_Rows + row; + } + + SLsmg_gotorc(row, col); + SLsmg_write_string((char *)text); +} + +int newtSetFlags(int oldFlags, int newFlags, enum newtFlagsSense sense) { + switch (sense) { + case NEWT_FLAGS_SET: + return oldFlags | newFlags; + + case NEWT_FLAGS_RESET: + return oldFlags & (~newFlags); + + case NEWT_FLAGS_TOGGLE: + return oldFlags ^ newFlags; + + default: + return oldFlags; + } +} + +void newtBell(void) +{ + SLtt_beep(); +} + +void newtGetScreenSize(int * cols, int * rows) { + if (rows) *rows = SLtt_Screen_Rows; + if (cols) *cols = SLtt_Screen_Cols; +} + +void newtDefaultPlaceHandler(newtComponent c, int newLeft, int newTop) { + c->left = newLeft; + c->top = newTop; +} + +void newtDefaultMappedHandler(newtComponent c, int isMapped) { + c->isMapped = isMapped; +} + +void newtCursorOff(void) { + cursorOn = 0; + SLtt_set_cursor_visibility (cursorOn); +} + +void newtCursorOn(void) { + cursorOn = 1; + SLtt_set_cursor_visibility (cursorOn); +} diff --git a/mdk-stage1/newt/newt.h b/mdk-stage1/newt/newt.h new file mode 100644 index 000000000..d3fd8bedc --- /dev/null +++ b/mdk-stage1/newt/newt.h @@ -0,0 +1,362 @@ +#ifndef H_NEWT +#define H_NEWT + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdarg.h> + +#define NEWT_COLORSET_ROOT 2 +#define NEWT_COLORSET_BORDER 3 +#define NEWT_COLORSET_WINDOW 4 +#define NEWT_COLORSET_SHADOW 5 +#define NEWT_COLORSET_TITLE 6 +#define NEWT_COLORSET_BUTTON 7 +#define NEWT_COLORSET_ACTBUTTON 8 +#define NEWT_COLORSET_CHECKBOX 9 +#define NEWT_COLORSET_ACTCHECKBOX 10 +#define NEWT_COLORSET_ENTRY 11 +#define NEWT_COLORSET_LABEL 12 +#define NEWT_COLORSET_LISTBOX 13 +#define NEWT_COLORSET_ACTLISTBOX 14 +#define NEWT_COLORSET_TEXTBOX 15 +#define NEWT_COLORSET_ACTTEXTBOX 16 +#define NEWT_COLORSET_HELPLINE 17 +#define NEWT_COLORSET_ROOTTEXT 18 +#define NEWT_COLORSET_EMPTYSCALE 19 +#define NEWT_COLORSET_FULLSCALE 20 +#define NEWT_COLORSET_DISENTRY 21 +#define NEWT_COLORSET_COMPACTBUTTON 22 +#define NEWT_COLORSET_ACTSELLISTBOX 23 +#define NEWT_COLORSET_SELLISTBOX 24 + +#define NEWT_ARG_LAST -100000 +#define NEWT_ARG_APPEND -1 + +struct newtColors { + char * rootFg, * rootBg; + char * borderFg, * borderBg; + char * windowFg, * windowBg; + char * shadowFg, * shadowBg; + char * titleFg, * titleBg; + char * buttonFg, * buttonBg; + char * actButtonFg, * actButtonBg; + char * checkboxFg, * checkboxBg; + char * actCheckboxFg, * actCheckboxBg; + char * entryFg, * entryBg; + char * labelFg, * labelBg; + char * listboxFg, * listboxBg; + char * actListboxFg, * actListboxBg; + char * textboxFg, * textboxBg; + char * actTextboxFg, * actTextboxBg; + char * helpLineFg, * helpLineBg; + char * rootTextFg, * rootTextBg; + char * emptyScale, * fullScale; + char * disabledEntryFg, * disabledEntryBg; + char * compactButtonFg, * compactButtonBg; + char * actSelListboxFg, * actSelListboxBg; + char * selListboxFg, * selListboxBg; +}; + +enum newtFlagsSense { NEWT_FLAGS_SET, NEWT_FLAGS_RESET, NEWT_FLAGS_TOGGLE }; + +#define NEWT_FLAG_RETURNEXIT (1 << 0) +#define NEWT_FLAG_HIDDEN (1 << 1) +#define NEWT_FLAG_SCROLL (1 << 2) +#define NEWT_FLAG_DISABLED (1 << 3) +/* OBSOLETE #define NEWT_FLAG_NOSCROLL (1 << 4) for listboxes */ +#define NEWT_FLAG_BORDER (1 << 5) +#define NEWT_FLAG_WRAP (1 << 6) +#define NEWT_FLAG_NOF12 (1 << 7) +#define NEWT_FLAG_MULTIPLE (1 << 8) +#define NEWT_FLAG_SELECTED (1 << 9) +#define NEWT_FLAG_CHECKBOX (1 << 10) +#define NEWT_FLAG_PASSWORD (1 << 11) /* draw '*' of chars in entrybox */ +#define NEWT_FD_READ (1 << 0) +#define NEWT_FD_WRITE (1 << 1) + +#define NEWT_CHECKBOXTREE_COLLAPSED '\0' +#define NEWT_CHECKBOXTREE_EXPANDED '\1' +#define NEWT_CHECKBOXTREE_UNSELECTED ' ' +#define NEWT_CHECKBOXTREE_SELECTED '*' + +/* Backwards compatibility */ +#define NEWT_LISTBOX_RETURNEXIT NEWT_FLAG_RETURNEXIT +#define NEWT_ENTRY_SCROLL NEWT_FLAG_SCROLL +#define NEWT_ENTRY_HIDDEN NEWT_FLAG_HIDDEN +#define NEWT_ENTRY_RETURNEXIT NEWT_FLAG_RETURNEXIT +#define NEWT_ENTRY_DISABLED NEWT_FLAG_DISABLED + +#define NEWT_TEXTBOX_WRAP NEWT_FLAG_WRAP +#define NEWT_TEXTBOX_SCROLL NEWT_FLAG_SCROLL +#define NEWT_FORM_NOF12 NEWT_FLAG_NOF12 + +#define newtListboxAddEntry newtListboxAppendEntry + + +typedef struct newtComponent_struct * newtComponent; + +extern const struct newtColors newtDefaultColorPalette; + +typedef void (*newtCallback)(newtComponent, void *); +typedef void (*newtSuspendCallback)(void * data); + +int newtInit(void); +int newtFinished(void); +void newtCls(void); +void newtResizeScreen(int redraw); +void newtWaitForKey(void); +void newtClearKeyBuffer(void); +void newtDelay(int usecs); +/* top, left are *not* counting the border */ +int newtOpenWindow(int left, int top, int width, int height, + const char * title); +int newtCenteredWindow(int width, int height, const char * title); +void newtPopWindow(void); +void newtSetColors(struct newtColors colors); +void newtRefresh(void); +void newtSuspend(void); +void newtSetSuspendCallback(newtSuspendCallback cb, void * data); +void newtSetHelpCallback(newtCallback cb); +void newtResume(void); +void newtPushHelpLine(const char * text); +void newtRedrawHelpLine(void); +void newtPopHelpLine(void); +void newtDrawRootText(int col, int row, const char * text); +void newtBell(void); +void newtCursorOff(void); +void newtCursorOn(void); + +/* Components */ + +newtComponent newtCompactButton(int left, int top, const char * text); +newtComponent newtButton(int left, int top, const char * text); +newtComponent newtCheckbox(int left, int top, const char * text, char defValue, + const char * seq, char * result); +char newtCheckboxGetValue(newtComponent co); +void newtCheckboxSetValue(newtComponent co, char value); +void newtCheckboxSetFlags(newtComponent co, int flags, enum newtFlagsSense sense); + + +newtComponent newtRadiobutton(int left, int top, const char * text, int isDefault, + newtComponent prevButton); +newtComponent newtRadioGetCurrent(newtComponent setMember); +newtComponent newtListitem(int left, int top, const char * text, int isDefault, + newtComponent prevItem, const void * data, int flags); +void newtListitemSet(newtComponent co, const char * text); +void * newtListitemGetData(newtComponent co); +void newtGetScreenSize(int * cols, int * rows); + +newtComponent newtLabel(int left, int top, const char * text); +void newtLabelSetText(newtComponent co, const char * text); +newtComponent newtVerticalScrollbar(int left, int top, int height, + int normalColorset, int thumbColorset); +void newtScrollbarSet(newtComponent co, int where, int total); + +newtComponent newtListbox(int left, int top, int height, int flags); +void * newtListboxGetCurrent(newtComponent co); +void newtListboxSetCurrent(newtComponent co, int num); +void newtListboxSetCurrentByKey(newtComponent co, void * key); +void newtListboxSetEntry(newtComponent co, int num, const char * text); +void newtListboxSetWidth(newtComponent co, int width); +void newtListboxSetData(newtComponent co, int num, void * data); +int newtListboxAppendEntry(newtComponent co, const char * text, + const void * data); +/* Send the key to insert after, or NULL to insert at the top */ +int newtListboxInsertEntry(newtComponent co, const char * text, const void * data, void * key); +int newtListboxDeleteEntry(newtComponent co, void * data); +void newtListboxClear(newtComponent co); /* removes all entries from listbox */ +void newtListboxGetEntry(newtComponent co, int num, char **text, void **data); +/* Returns an array of data pointers from items, last element is NULL */ +void **newtListboxGetSelection(newtComponent co, int *numitems); +void newtListboxClearSelection(newtComponent co); +void newtListboxSelectItem(newtComponent co, const void * key, + enum newtFlagsSense sense); + +newtComponent newtCheckboxTree(int left, int top, int height, int flags); +newtComponent newtCheckboxTreeMulti(int left, int top, int height, char *seq, int flags); +const void ** newtCheckboxTreeGetSelection(newtComponent co, int *numitems); +const void * newtCheckboxTreeGetCurrent(newtComponent co); +const void ** newtCheckboxTreeGetMultiSelection(newtComponent co, int *numitems, char seqnum); +/* last item is NEWT_ARG_LAST for all of these */ +int newtCheckboxTreeAddItem(newtComponent co, + const char * text, const void * data, + int flags, int index, ...); +int newtCheckboxTreeAddArray(newtComponent co, + const char * text, const void * data, + int flags, int * indexes); +int * newtCheckboxTreeFindItem(newtComponent co, void * data); +void newtCheckboxTreeSetEntry(newtComponent co, const void * data, + const char * text); +char newtCheckboxTreeGetEntryValue(newtComponent co, const void * data); +void newtCheckboxTreeSetEntryValue(newtComponent co, const void * data, + char value); + +newtComponent newtTextboxReflowed(int left, int top, char * text, int width, + int flexDown, int flexUp, int flags); +newtComponent newtTextbox(int left, int top, int width, int height, int flags); +void newtTextboxSetText(newtComponent co, const char * text); +void newtTextboxSetHeight(newtComponent co, int height); +int newtTextboxGetNumLines(newtComponent co); +char * newtReflowText(char * text, int width, int flexDown, int flexUp, + int * actualWidth, int * actualHeight); + +struct newtExitStruct { + enum { NEWT_EXIT_HOTKEY, NEWT_EXIT_COMPONENT, NEWT_EXIT_FDREADY, + NEWT_EXIT_TIMER } reason; + union { + int key; + newtComponent co; + } u; +} ; + +newtComponent newtForm(newtComponent vertBar, void * helpTag, int flags); +void newtFormSetTimer(newtComponent form, int millisecs); +void newtFormWatchFd(newtComponent form, int fd, int fdFlags); +void newtFormSetSize(newtComponent co); +newtComponent newtFormGetCurrent(newtComponent co); +void newtFormSetBackground(newtComponent co, int color); +void newtFormSetCurrent(newtComponent co, newtComponent subco); +void newtFormAddComponent(newtComponent form, newtComponent co); +void newtFormAddComponents(newtComponent form, ...); +void newtFormSetHeight(newtComponent co, int height); +void newtFormSetWidth(newtComponent co, int width); +newtComponent newtRunForm(newtComponent form); /* obsolete */ +void newtFormRun(newtComponent co, struct newtExitStruct * es); +void newtDrawForm(newtComponent form); +void newtFormAddHotKey(newtComponent co, int key); + +typedef int (*newtEntryFilter)(newtComponent entry, void * data, int ch, + int cursor); +newtComponent newtEntry(int left, int top, const char * initialValue, int width, + char ** resultPtr, int flags); +void newtEntrySet(newtComponent co, const char * value, int cursorAtEnd); +void newtEntrySetFilter(newtComponent co, newtEntryFilter filter, void * data); +char * newtEntryGetValue(newtComponent co); +void newtEntrySetFlags(newtComponent co, int flags, enum newtFlagsSense sense); + +newtComponent newtScale(int left, int top, int width, int fullValue); +void newtScaleSet(newtComponent co, unsigned int amount); + +void newtComponentAddCallback(newtComponent co, newtCallback f, void * data); +void newtComponentTakesFocus(newtComponent co, int val); + +/* this also destroys all of the components (including other forms) on the + form */ +void newtFormDestroy(newtComponent form); + +/* Key codes */ + +#define NEWT_KEY_TAB '\t' +#define NEWT_KEY_ENTER '\r' +#define NEWT_KEY_SUSPEND '\032' /* ctrl - z*/ +#define NEWT_KEY_RETURN NEWT_KEY_ENTER + +#define NEWT_KEY_EXTRA_BASE 0x8000 +#define NEWT_KEY_UP NEWT_KEY_EXTRA_BASE + 1 +#define NEWT_KEY_DOWN NEWT_KEY_EXTRA_BASE + 2 +#define NEWT_KEY_LEFT NEWT_KEY_EXTRA_BASE + 4 +#define NEWT_KEY_RIGHT NEWT_KEY_EXTRA_BASE + 5 +#define NEWT_KEY_BKSPC NEWT_KEY_EXTRA_BASE + 6 +#define NEWT_KEY_DELETE NEWT_KEY_EXTRA_BASE + 7 +#define NEWT_KEY_HOME NEWT_KEY_EXTRA_BASE + 8 +#define NEWT_KEY_END NEWT_KEY_EXTRA_BASE + 9 +#define NEWT_KEY_UNTAB NEWT_KEY_EXTRA_BASE + 10 +#define NEWT_KEY_PGUP NEWT_KEY_EXTRA_BASE + 11 +#define NEWT_KEY_PGDN NEWT_KEY_EXTRA_BASE + 12 +#define NEWT_KEY_INSERT NEWT_KEY_EXTRA_BASE + 13 + +#define NEWT_KEY_F1 NEWT_KEY_EXTRA_BASE + 101 +#define NEWT_KEY_F2 NEWT_KEY_EXTRA_BASE + 102 +#define NEWT_KEY_F3 NEWT_KEY_EXTRA_BASE + 103 +#define NEWT_KEY_F4 NEWT_KEY_EXTRA_BASE + 104 +#define NEWT_KEY_F5 NEWT_KEY_EXTRA_BASE + 105 +#define NEWT_KEY_F6 NEWT_KEY_EXTRA_BASE + 106 +#define NEWT_KEY_F7 NEWT_KEY_EXTRA_BASE + 107 +#define NEWT_KEY_F8 NEWT_KEY_EXTRA_BASE + 108 +#define NEWT_KEY_F9 NEWT_KEY_EXTRA_BASE + 109 +#define NEWT_KEY_F10 NEWT_KEY_EXTRA_BASE + 110 +#define NEWT_KEY_F11 NEWT_KEY_EXTRA_BASE + 111 +#define NEWT_KEY_F12 NEWT_KEY_EXTRA_BASE + 112 + +/* not really a key, but newtGetKey returns it */ +#define NEWT_KEY_RESIZE NEWT_KEY_EXTRA_BASE + 113 + +#define NEWT_ANCHOR_LEFT (1 << 0) +#define NEWT_ANCHOR_RIGHT (1 << 1) +#define NEWT_ANCHOR_TOP (1 << 2) +#define NEWT_ANCHOR_BOTTOM (1 << 3) + +#define NEWT_GRID_FLAG_GROWX (1 << 0) +#define NEWT_GRID_FLAG_GROWY (1 << 1) + +typedef struct grid_s * newtGrid; +enum newtGridElement { NEWT_GRID_EMPTY = 0, + NEWT_GRID_COMPONENT, NEWT_GRID_SUBGRID }; + +newtGrid newtCreateGrid(int cols, int rows); +/* TYPE, what, TYPE, what, ..., NULL */ +newtGrid newtGridVStacked(enum newtGridElement type, void * what, ...); +newtGrid newtGridVCloseStacked(enum newtGridElement type, void * what, ...); +newtGrid newtGridHStacked(enum newtGridElement type1, void * what1, ...); +newtGrid newtGridHCloseStacked(enum newtGridElement type1, void * what1, ...); +newtGrid newtGridBasicWindow(newtComponent text, newtGrid middle, + newtGrid buttons); +newtGrid newtGridSimpleWindow(newtComponent text, newtComponent middle, + newtGrid buttons); +void newtGridSetField(newtGrid grid, int col, int row, + enum newtGridElement type, void * val, int padLeft, + int padTop, int padRight, int padBottom, int anchor, + int flags); +void newtGridPlace(newtGrid grid, int left, int top); +#define newtGridDestroy newtGridFree +void newtGridFree(newtGrid grid, int recurse); +void newtGridGetSize(newtGrid grid, int * width, int * height); +void newtGridWrappedWindow(newtGrid grid, char * title); +void newtGridWrappedWindowAt(newtGrid grid, char * title, int left, int top); +void newtGridAddComponentsToForm(newtGrid grid, newtComponent form, + int recurse); + +/* convienve */ +newtGrid newtButtonBarv(char * button1, newtComponent * b1comp, va_list args); +newtGrid newtButtonBar(char * button1, newtComponent * b1comp, ...); + +/* automatically centered and shrink wrapped */ +void newtWinMessage(char * title, char * buttonText, char * text, ...); +void newtWinMessagev(char * title, char * buttonText, char * text, + va_list argv); + +/* having separate calls for these two seems silly, but having two separate + variable length-arg lists seems like a bad idea as well */ + +/* Returns 0 if F12 was pressed, 1 for button1, 2 for button2 */ +int newtWinChoice(char * title, char * button1, char * button2, + char * text, ...); +/* Returns 0 if F12 was pressed, 1 for button1, 2 for button2, + 3 for button3 */ +int newtWinTernary(char * title, char * button1, char * button2, + char * button3, char * message, ...); + +/* Returns the button number pressed, 0 on F12 */ +int newtWinMenu(char * title, char * text, int suggestedWidth, int flexDown, + int flexUp, int maxListHeight, char ** items, int * listItem, + char * button1, ...); + +struct newtWinEntry { + char * text; + char ** value; /* may be initialized to set default */ + int flags; +}; + +/* Returns the button number pressed, 0 on F12. The final values are + dynamically allocated, and need to be freed. */ +int newtWinEntries(char * title, char * text, int suggestedWidth, int flexDown, + int flexUp, int dataWidth, + struct newtWinEntry * items, char * button1, ...); + +#ifdef __cplusplus +} /* End of extern "C" { */ +#endif + +#endif /* H_NEWT */ diff --git a/mdk-stage1/newt/newt_pr.h b/mdk-stage1/newt/newt_pr.h new file mode 100644 index 000000000..76f5e2f6f --- /dev/null +++ b/mdk-stage1/newt/newt_pr.h @@ -0,0 +1,82 @@ +#ifndef H_NEWT_PR +#define H_NEWT_PR + +#define COLORSET_ROOT NEWT_COLORSET_ROOT +#define COLORSET_BORDER NEWT_COLORSET_BORDER +#define COLORSET_WINDOW NEWT_COLORSET_WINDOW +#define COLORSET_SHADOW NEWT_COLORSET_SHADOW +#define COLORSET_TITLE NEWT_COLORSET_TITLE +#define COLORSET_BUTTON NEWT_COLORSET_BUTTON +#define COLORSET_ACTBUTTON NEWT_COLORSET_ACTBUTTON +#define COLORSET_CHECKBOX NEWT_COLORSET_CHECKBOX +#define COLORSET_ACTCHECKBOX NEWT_COLORSET_ACTCHECKBOX +#define COLORSET_ENTRY NEWT_COLORSET_ENTRY +#define COLORSET_LABEL NEWT_COLORSET_LABEL +#define COLORSET_LISTBOX NEWT_COLORSET_LISTBOX +#define COLORSET_ACTLISTBOX NEWT_COLORSET_ACTLISTBOX +#define COLORSET_TEXTBOX NEWT_COLORSET_TEXTBOX +#define COLORSET_ACTTEXTBOX NEWT_COLORSET_ACTTEXTBOX + +int newtSetFlags(int oldFlags, int newFlags, enum newtFlagsSense sense); + +void newtGotorc(int row, int col); +void newtGetrc(int * row, int * col); +void newtGetWindowPos(int * x, int * y); +void newtDrawBox(int left, int top, int width, int height, int shadow); +void newtClearBox(int left, int top, int width, int height); + +int newtGetKey(void); + +struct newtComponent_struct { + /* common data */ + int height, width; + int top, left; + int takesFocus; + int isMapped; + + struct componentOps * ops; + + newtCallback callback; + void * callbackData; + + void * data; +} ; + +enum eventResultTypes { ER_IGNORED, ER_SWALLOWED, ER_EXITFORM, ER_SETFOCUS, + ER_NEXTCOMP }; +struct eventResult { + enum eventResultTypes result; + union { + newtComponent focus; + } u; +}; + +enum eventTypes { EV_FOCUS, EV_UNFOCUS, EV_KEYPRESS, EV_MOUSE }; +enum eventSequence { EV_EARLY, EV_NORMAL, EV_LATE }; + +struct event { + enum eventTypes event; + enum eventSequence when; + union { + int key; + struct { + enum { MOUSE_MOTION, MOUSE_BUTTON_DOWN, MOUSE_BUTTON_UP } type; + int x, y; + } mouse; + } u; +} ; + +struct componentOps { + void (* draw)(newtComponent c); + struct eventResult (* event)(newtComponent c, struct event ev); + void (* destroy)(newtComponent c); + void (* place)(newtComponent c, int newLeft, int newTop); + void (* mapped)(newtComponent c, int isMapped); +} ; + +void newtDefaultPlaceHandler(newtComponent c, int newLeft, int newTop); +void newtDefaultMappedHandler(newtComponent c, int isMapped); +struct eventResult newtDefaultEventHandler(newtComponent c, + struct event ev); + +#endif /* H_NEWT_PR */ diff --git a/mdk-stage1/newt/scale.c b/mdk-stage1/newt/scale.c new file mode 100644 index 000000000..800958580 --- /dev/null +++ b/mdk-stage1/newt/scale.c @@ -0,0 +1,72 @@ +#include <slang.h> +#include <stdlib.h> +#include <string.h> + +#include "newt.h" +#include "newt_pr.h" + +struct scale { + int fullValue; + int charsSet; +}; + +static void scaleDraw(newtComponent co); + +static struct componentOps scaleOps = { + scaleDraw, + newtDefaultEventHandler, + NULL, + newtDefaultPlaceHandler, + newtDefaultMappedHandler, +} ; + +newtComponent newtScale(int left, int top, int width, int fullValue) { + newtComponent co; + struct scale * sc; + + co = malloc(sizeof(*co)); + sc = malloc(sizeof(struct scale)); + co->data = sc; + + co->ops = &scaleOps; + + co->height = 1; + co->width = width; + co->top = top; + co->left = left; + co->takesFocus = 0; + + sc->fullValue = fullValue; + sc->charsSet = 0; + + return co; +} + +void newtScaleSet(newtComponent co, unsigned int amount) { + struct scale * sc = co->data; + int newCharsSet; + + newCharsSet = (amount * co->width) / sc->fullValue; + + if (newCharsSet != sc->charsSet) { + sc->charsSet = newCharsSet; + scaleDraw(co); + } +} + +static void scaleDraw(newtComponent co) { + struct scale * sc = co->data; + int i; + + if (co->top == -1) return; + + newtGotorc(co->top, co->left); + + SLsmg_set_color(NEWT_COLORSET_FULLSCALE); + for (i = 0; i < sc->charsSet; i++) + SLsmg_write_string(" "); + + SLsmg_set_color(NEWT_COLORSET_EMPTYSCALE); + for (i = 0; i < (co->width - sc->charsSet); i++) + SLsmg_write_string(" "); +} diff --git a/mdk-stage1/newt/scrollbar.c b/mdk-stage1/newt/scrollbar.c new file mode 100644 index 000000000..cb4bc2757 --- /dev/null +++ b/mdk-stage1/newt/scrollbar.c @@ -0,0 +1,124 @@ +#include <slang.h> +#include <stdlib.h> +#include <string.h> + +#include "newt.h" +#include "newt_pr.h" + +struct scrollbar { + int curr; + int cs, csThumb; + int arrows; +} ; + +static void sbDraw(newtComponent co); +static void sbDestroy(newtComponent co); +static void sbDrawThumb(newtComponent co, int isOn); + +static struct componentOps sbOps = { + sbDraw, + newtDefaultEventHandler, + sbDestroy, + newtDefaultPlaceHandler, + newtDefaultMappedHandler, +} ; + +void newtScrollbarSet(newtComponent co, int where, int total) { + struct scrollbar * sb = co->data; + int new; + + if (sb->arrows) + new = (where * (co->height - 3)) / (total ? total : 1) + 1; + else + new = (where * (co->height - 1)) / (total ? total : 1); + if (new != sb->curr) { + sbDrawThumb(co, 0); + sb->curr = new; + sbDrawThumb(co, 1); + } +} + +newtComponent newtVerticalScrollbar(int left, int top, int height, + int normalColorset, int thumbColorset) { + newtComponent co; + struct scrollbar * sb; + + co = malloc(sizeof(*co)); + sb = malloc(sizeof(*sb)); + co->data = sb; + + if (!strcmp(getenv("TERM"), "linux") && height >= 2) { + sb->arrows = 1; + sb->curr = 1; + } else { + sb->arrows = 0; + sb->curr = 0; + } + sb->cs = normalColorset; + sb->csThumb = thumbColorset; + + co->ops = &sbOps; + co->isMapped = 0; + co->left = left; + co->top = top; + co->height = height; + co->width = 1; + co->takesFocus = 0; + + return co; +} + +static void sbDraw(newtComponent co) { + struct scrollbar * sb = co->data; + int i; + + if (!co->isMapped) return; + + SLsmg_set_color(sb->cs); + + SLsmg_set_char_set(1); + if (sb->arrows) { + newtGotorc(co->top, co->left); + SLsmg_write_char('\x2d'); + for (i = 1; i < co->height - 1; i++) { + newtGotorc(i + co->top, co->left); + SLsmg_write_char('\x61'); + } + newtGotorc(co->top + co->height - 1, co->left); + SLsmg_write_char('\x2e'); + } else { + for (i = 0; i < co->height; i++) { + newtGotorc(i + co->top, co->left); + SLsmg_write_char('\x61'); + } + } + + SLsmg_set_char_set(0); + + sbDrawThumb(co, 1); +} + +static void sbDrawThumb(newtComponent co, int isOn) { + struct scrollbar * sb = co->data; + char ch = isOn ? '#' : '\x61'; + + if (!co->isMapped) return; + + newtGotorc(sb->curr + co->top, co->left); + SLsmg_set_char_set(1); + + /*if (isOn) + SLsmg_set_color(sb->csThumb); + else*/ + SLsmg_set_color(sb->cs); + + SLsmg_write_char(ch); + SLsmg_set_char_set(0); +} + +static void sbDestroy(newtComponent co) { + struct scrollbar * sb = co->data; + + free(sb); + free(co); +} diff --git a/mdk-stage1/newt/textbox.c b/mdk-stage1/newt/textbox.c new file mode 100644 index 000000000..272c9b675 --- /dev/null +++ b/mdk-stage1/newt/textbox.c @@ -0,0 +1,409 @@ +#include <ctype.h> +#include <slang.h> +#include <stdlib.h> +#include <string.h> + +#include "newt.h" +#include "newt_pr.h" + +struct textbox { + char ** lines; + int numLines; + int linesAlloced; + int doWrap; + newtComponent sb; + int topLine; + int textWidth; +}; + +static char * expandTabs(const char * text); +static void textboxDraw(newtComponent co); +static void addLine(newtComponent co, const char * s, int len); +static void doReflow(const char * text, char ** resultPtr, int width, + int * badness, int * heightPtr); +static struct eventResult textboxEvent(newtComponent c, + struct event ev); +static void textboxDestroy(newtComponent co); +static void textboxPlace(newtComponent co, int newLeft, int newTop); +static void textboxMapped(newtComponent co, int isMapped); + +static struct componentOps textboxOps = { + textboxDraw, + textboxEvent, + textboxDestroy, + textboxPlace, + textboxMapped, +} ; + +static void textboxMapped(newtComponent co, int isMapped) { + struct textbox * tb = co->data; + + co->isMapped = isMapped; + if (tb->sb) + tb->sb->ops->mapped(tb->sb, isMapped); +} + +static void textboxPlace(newtComponent co, int newLeft, int newTop) { + struct textbox * tb = co->data; + + co->top = newTop; + co->left = newLeft; + + if (tb->sb) + tb->sb->ops->place(tb->sb, co->left + co->width - 1, co->top); +} + +void newtTextboxSetHeight(newtComponent co, int height) { + co->height = height; +} + +int newtTextboxGetNumLines(newtComponent co) { + struct textbox * tb = co->data; + + return (tb->numLines); +} + +newtComponent newtTextboxReflowed(int left, int top, char * text, int width, + int flexDown, int flexUp, int flags) { + newtComponent co; + char * reflowedText; + int actWidth, actHeight; + + reflowedText = newtReflowText(text, width, flexDown, flexUp, + &actWidth, &actHeight); + + co = newtTextbox(left, top, actWidth, actHeight, NEWT_FLAG_WRAP); + newtTextboxSetText(co, reflowedText); + free(reflowedText); + + return co; +} + +newtComponent newtTextbox(int left, int top, int width, int height, int flags) { + newtComponent co; + struct textbox * tb; + + co = malloc(sizeof(*co)); + tb = malloc(sizeof(*tb)); + co->data = tb; + + co->ops = &textboxOps; + + co->height = height; + co->top = top; + co->left = left; + co->takesFocus = 0; + co->width = width; + + tb->doWrap = flags & NEWT_FLAG_WRAP; + tb->numLines = 0; + tb->linesAlloced = 0; + tb->lines = NULL; + tb->topLine = 0; + tb->textWidth = width; + + if (flags & NEWT_FLAG_SCROLL) { + co->width += 2; + tb->sb = newtVerticalScrollbar(co->left + co->width - 1, co->top, + co->height, COLORSET_TEXTBOX, COLORSET_TEXTBOX); + } else { + tb->sb = NULL; + } + + return co; +} + +static char * expandTabs(const char * text) { + int bufAlloced = strlen(text) + 40; + char * buf, * dest; + const char * src; + int bufUsed = 0; + int linePos = 0; + int i; + + buf = malloc(bufAlloced + 1); + for (src = text, dest = buf; *src; src++) { + if ((bufUsed + 10) > bufAlloced) { + bufAlloced += strlen(text) / 2; + buf = realloc(buf, bufAlloced + 1); + dest = buf + bufUsed; + } + if (*src == '\t') { + i = 8 - (linePos & 8); + memset(dest, ' ', i); + dest += i, bufUsed += i, linePos += i; + } else { + if (*src == '\n') + linePos = 0; + else + linePos++; + + *dest++ = *src; + bufUsed++; + } + } + + *dest = '\0'; + return buf; +} + +#define iseuckanji(c) (0xa1 <= (unsigned char)(c&0xff) && (unsigned char)(c&0xff) <= 0xfe) + +static void doReflow(const char * text, char ** resultPtr, int width, + int * badness, int * heightPtr) { + char * result = NULL; + const char * chptr, * end; + int i; + int howbad = 0; + int height = 0; + int kanji = 0; + + if (resultPtr) { + /* XXX I think this will work */ + result = malloc(strlen(text) + (strlen(text) / width) + 50); + *result = '\0'; + } + + while (*text) { + kanji = 0; + end = strchr(text, '\n'); + if (!end) + end = text + strlen(text); + + while (*text && text < end) { + if (end - text < width) { + if (result) { + strncat(result, text, end - text + 1); + strcat(result, "\n"); + height++; + } + + if (end - text < (width / 2)) + howbad += ((width / 2) - (end - text)) / 2; + text = end; + if (*text) text++; + } else { + chptr = text; + kanji = 0; + for ( i = 0; i < width - 1; i++ ) { + if ( !iseuckanji(*chptr)) { + kanji = 0; + } else if ( kanji == 1 ) { + kanji = 2; + } else { + kanji = 1; + } + chptr++; + } + if (kanji == 0) { + while (chptr > text && !isspace(*chptr)) chptr--; + while (chptr > text && isspace(*chptr)) chptr--; + chptr++; + } + + if (chptr-text == 1 && !isspace(*chptr)) + chptr = text + width - 1; + + if (chptr > text) + howbad += width - (chptr - text) + 1; + if (result) { + if (kanji == 1) { + strncat(result, text, chptr - text + 1); + chptr++; + kanji = 0; + } else { + strncat(result, text, chptr - text + 1); + } + strcat(result, "\n"); + height++; + } + + if (isspace(*chptr)) + text = chptr + 1; + else + text = chptr; + while (isspace(*text)) text++; + } + } + } + +// if (result) printf("result: %s\n", result); + + if (badness) *badness = howbad; + if (resultPtr) *resultPtr = result; + if (heightPtr) *heightPtr = height; +} + +char * newtReflowText(char * text, int width, int flexDown, int flexUp, + int * actualWidth, int * actualHeight) { + int min, max; + int i; + char * result; + int minbad, minbadwidth, howbad; + char * expandedText; + + expandedText = expandTabs(text); + + if (flexDown || flexUp) { + min = width - flexDown; + max = width + flexUp; + + minbad = -1; + minbadwidth = width; + + for (i = min; i <= max; i++) { + doReflow(expandedText, NULL, i, &howbad, NULL); + + if (minbad == -1 || howbad < minbad) { + minbad = howbad; + minbadwidth = i; + } + } + + width = minbadwidth; + } + + doReflow(expandedText, &result, width, NULL, actualHeight); + free(expandedText); + if (actualWidth) *actualWidth = width; + return result; +} + +void newtTextboxSetText(newtComponent co, const char * text) { + const char * start, * end; + struct textbox * tb = co->data; + char * reflowed, * expanded; + int badness, height; + + if (tb->lines) { + free(tb->lines); + tb->linesAlloced = tb->numLines = 0; + } + + expanded = expandTabs(text); + + if (tb->doWrap) { + doReflow(expanded, &reflowed, tb->textWidth, &badness, &height); + free(expanded); + expanded = reflowed; + } + + for (start = expanded; *start; start++) + if (*start == '\n') tb->linesAlloced++; + + /* This ++ leaves room for an ending line w/o a \n */ + tb->linesAlloced++; + tb->lines = malloc(sizeof(char *) * tb->linesAlloced); + + start = expanded; + while ((end = strchr(start, '\n'))) { + addLine(co, start, end - start); + start = end + 1; + } + + if (*start) + addLine(co, start, strlen(start)); + + free(expanded); +} + +/* This assumes the buffer is allocated properly! */ +static void addLine(newtComponent co, const char * s, int len) { + struct textbox * tb = co->data; + + if (len > tb->textWidth) len = tb->textWidth; + + tb->lines[tb->numLines] = malloc(tb->textWidth + 1); + memset(tb->lines[tb->numLines], ' ', tb->textWidth); + memcpy(tb->lines[tb->numLines], s, len); + tb->lines[tb->numLines++][tb->textWidth] = '\0'; +} + +static void textboxDraw(newtComponent c) { + int i; + struct textbox * tb = c->data; + int size; + + if (tb->sb) { + size = tb->numLines - c->height; + newtScrollbarSet(tb->sb, tb->topLine, size ? size : 0); + tb->sb->ops->draw(tb->sb); + } + + SLsmg_set_color(NEWT_COLORSET_TEXTBOX); + + for (i = 0; (i + tb->topLine) < tb->numLines && i < c->height; i++) { + newtGotorc(c->top + i, c->left); + SLsmg_write_string(tb->lines[i + tb->topLine]); + } +} + +static struct eventResult textboxEvent(newtComponent co, + struct event ev) { + struct textbox * tb = co->data; + struct eventResult er; + + er.result = ER_IGNORED; + + if (ev.when == EV_EARLY && ev.event == EV_KEYPRESS && tb->sb) { + switch (ev.u.key) { + case NEWT_KEY_UP: + if (tb->topLine) tb->topLine--; + textboxDraw(co); + er.result = ER_SWALLOWED; + break; + + case NEWT_KEY_DOWN: + if (tb->topLine < (tb->numLines - co->height)) tb->topLine++; + textboxDraw(co); + er.result = ER_SWALLOWED; + break; + + case NEWT_KEY_PGDN: + tb->topLine += co->height; + if (tb->topLine > (tb->numLines - co->height)) { + tb->topLine = tb->numLines - co->height; + if (tb->topLine < 0) tb->topLine = 0; + } + textboxDraw(co); + er.result = ER_SWALLOWED; + break; + + case NEWT_KEY_PGUP: + tb->topLine -= co->height; + if (tb->topLine < 0) tb->topLine = 0; + textboxDraw(co); + er.result = ER_SWALLOWED; + break; + } + } + if (ev.when == EV_EARLY && ev.event == EV_MOUSE && tb->sb) { + /* Top scroll arrow */ + if (ev.u.mouse.x == co->width && ev.u.mouse.y == co->top) { + if (tb->topLine) tb->topLine--; + textboxDraw(co); + + er.result = ER_SWALLOWED; + } + /* Bottom scroll arrow */ + if (ev.u.mouse.x == co->width && + ev.u.mouse.y == co->top + co->height - 1) { + if (tb->topLine < (tb->numLines - co->height)) tb->topLine++; + textboxDraw(co); + + er.result = ER_SWALLOWED; + } + } + return er; +} + +static void textboxDestroy(newtComponent co) { + int i; + struct textbox * tb = co->data; + + for (i = 0; i < tb->numLines; i++) + free(tb->lines[i]); + free(tb->lines); + free(tb); + free(co); +} diff --git a/mdk-stage1/newt/windows.c b/mdk-stage1/newt/windows.c new file mode 100644 index 000000000..792d3ed76 --- /dev/null +++ b/mdk-stage1/newt/windows.c @@ -0,0 +1,275 @@ +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "errno.h" +#include "newt.h" + +static void * newtvwindow(char * title, char * button1, char * button2, + char * button3, char * message, va_list args) { + newtComponent b1, b2 = NULL, b3 = NULL, t, f, answer; + char * buf = NULL; + int size = 0; + int i = 0; + int scroll = 0; + int width, height; + char * flowedText; + newtGrid grid, buttonGrid; + + do { + size += 1000; + if (buf) free(buf); + buf = malloc(size); + i = vsnprintf(buf, size, message, args); + } while (i >= size || i == -1); + + flowedText = newtReflowText(buf, 35, 5, 5, &width, &height); + if (height > 6) { + free(flowedText); + flowedText = newtReflowText(buf, 60, 5, 5, &width, &height); + } + free(buf); + + if (height > 12) { + height = 12; + scroll = NEWT_FLAG_SCROLL; + } + t = newtTextbox(-1, -1, width, height, NEWT_TEXTBOX_WRAP | scroll); + newtTextboxSetText(t, flowedText); + free(flowedText); + + if (button3) { + buttonGrid = newtButtonBar(button1, &b1, button2, &b2, + button3, &b3, NULL); + } else if (button2) { + buttonGrid = newtButtonBar(button1, &b1, button2, &b2, NULL); + } else { + buttonGrid = newtButtonBar(button1, &b1, NULL); + } + + newtGridSetField(buttonGrid, 0, 0, NEWT_GRID_COMPONENT, b1, + 0, 0, button2 ? 1 : 0, 0, 0, 0); + + grid = newtCreateGrid(1, 2); + newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, t, 0, 0, 0, 0, 0, 0); + newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, buttonGrid, + 0, 1, 0, 0, 0, NEWT_GRID_FLAG_GROWX); + newtGridWrappedWindow(grid, title); + + f = newtForm(NULL, NULL, 0); + newtFormAddComponents(f, t, b1, NULL); + + if (button2) + newtFormAddComponent(f, b2); + if (button3) + newtFormAddComponent(f, b3); + + answer = newtRunForm(f); + newtGridFree(grid, 1); + + newtFormDestroy(f); + newtPopWindow(); + + if (answer == f) + return NULL; + else if (answer == b1) + return button1; + else if (answer == b2) + return button2; + + return button3; +} + +int newtWinChoice(char * title, char * button1, char * button2, + char * message, ...) { + va_list args; + void * rc; + + va_start(args, message); + rc = newtvwindow(title, button1, button2, NULL, message, args); + va_end(args); + + if (rc == button1) + return 1; + else if (rc == button2) + return 2; + + return 0; +} + +void newtWinMessage(char * title, char * buttonText, char * text, ...) { + va_list args; + + va_start(args, text); + newtvwindow(title, buttonText, NULL, NULL, text, args); + va_end(args); +} + +void newtWinMessagev(char * title, char * buttonText, char * text, + va_list argv) { + newtvwindow(title, buttonText, NULL, NULL, text, argv); +} + +int newtWinTernary(char * title, char * button1, char * button2, + char * button3, char * message, ...) { + va_list args; + void * rc; + + va_start(args, message); + rc = newtvwindow(title, button1, button2, button3, message, args); + va_end(args); + + if (rc == button1) + return 1; + else if (rc == button2) + return 2; + else if (rc == button3) + return 3; + + return 0; +} + +/* only supports up to 50 buttons -- shucks! */ +int newtWinMenu(char * title, char * text, int suggestedWidth, int flexDown, + int flexUp, int maxListHeight, char ** items, int * listItem, + char * button1, ...) { + newtComponent textbox, listbox, result, form; + va_list args; + newtComponent buttons[50]; + newtGrid grid, buttonBar; + int numButtons; + int i, rc; + int needScroll; + char * buttonName; + + textbox = newtTextboxReflowed(-1, -1, text, suggestedWidth, flexDown, + flexUp, 0); + + for (i = 0; items[i]; i++) ; + if (i < maxListHeight) maxListHeight = i; + needScroll = i > maxListHeight; + + listbox = newtListbox(-1, -1, maxListHeight, + (needScroll ? NEWT_FLAG_SCROLL : 0) | NEWT_FLAG_RETURNEXIT); + for (i = 0; items[i]; i++) { + newtListboxAddEntry(listbox, items[i], (void *) i); + } + + newtListboxSetCurrent(listbox, *listItem); + + buttonName = button1, numButtons = 0; + va_start(args, button1); + while (buttonName) { + buttons[numButtons] = newtButton(-1, -1, buttonName); + numButtons++; + buttonName = va_arg(args, char *); + } + + va_end(args); + + buttonBar = newtCreateGrid(numButtons, 1); + for (i = 0; i < numButtons; i++) { + newtGridSetField(buttonBar, i, 0, NEWT_GRID_COMPONENT, + buttons[i], + i ? 1 : 0, 0, 0, 0, 0, 0); + } + + grid = newtGridSimpleWindow(textbox, listbox, buttonBar); + newtGridWrappedWindow(grid, title); + + form = newtForm(NULL, 0, 0); + newtGridAddComponentsToForm(grid, form, 1); + newtGridFree(grid, 1); + + result = newtRunForm(form); + + *listItem = ((long) newtListboxGetCurrent(listbox)); + + for (rc = 0; result != buttons[rc] && rc < numButtons; rc++); + if (rc == numButtons) + rc = 0; /* F12 or return-on-exit (which are the same for us) */ + else + rc++; + + newtFormDestroy(form); + newtPopWindow(); + + return rc; +} + +/* only supports up to 50 buttons and entries -- shucks! */ +int newtWinEntries(char * title, char * text, int suggestedWidth, int flexDown, + int flexUp, int dataWidth, + struct newtWinEntry * items, char * button1, ...) { + newtComponent buttons[50], result, form, textw; + newtGrid grid, buttonBar, subgrid; + int numItems; + int rc, i; + int numButtons; + char * buttonName; + va_list args; + + textw = newtTextboxReflowed(-1, -1, text, suggestedWidth, flexDown, + flexUp, 0); + + for (numItems = 0; items[numItems].text; numItems++); + + buttonName = button1, numButtons = 0; + va_start(args, button1); + while (buttonName) { + buttons[numButtons] = newtButton(-1, -1, buttonName); + numButtons++; + buttonName = va_arg(args, char *); + } + + va_end(args); + + buttonBar = newtCreateGrid(numButtons, 1); + for (i = 0; i < numButtons; i++) { + newtGridSetField(buttonBar, i, 0, NEWT_GRID_COMPONENT, + buttons[i], + i ? 1 : 0, 0, 0, 0, 0, 0); + } + + subgrid = newtCreateGrid(2, numItems); + for (i = 0; i < numItems; i++) { + newtGridSetField(subgrid, 0, i, NEWT_GRID_COMPONENT, + newtLabel(-1, -1, items[i].text), + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(subgrid, 1, i, NEWT_GRID_COMPONENT, + newtEntry(-1, -1, items[i].value ? + *items[i].value : NULL, dataWidth, + items[i].value, items[i].flags), + 1, 0, 0, 0, 0, 0); + } + + grid = newtCreateGrid(1, 3); + form = newtForm(NULL, 0, 0); + newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, textw, + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, subgrid, + 0, 1, 0, 0, 0, 0); + newtGridSetField(grid, 0, 2, NEWT_GRID_SUBGRID, buttonBar, + 0, 1, 0, 0, 0, NEWT_GRID_FLAG_GROWX); + newtGridAddComponentsToForm(grid, form, 1); + newtGridWrappedWindow(grid, title); + newtGridFree(grid, 1); + + result = newtRunForm(form); + + for (rc = 0; rc < numItems; rc++) + *items[rc].value = strdup(*items[rc].value); + + for (rc = 0; result != buttons[rc] && rc < numButtons; rc++); + if (rc == numButtons) + rc = 0; /* F12 */ + else + rc++; + + newtFormDestroy(form); + newtPopWindow(); + + return rc; +} diff --git a/mdk-stage1/slang/Makefile b/mdk-stage1/slang/Makefile new file mode 100644 index 000000000..c78ee4668 --- /dev/null +++ b/mdk-stage1/slang/Makefile @@ -0,0 +1,48 @@ + #****************************************************************************** + # + # Guillaume Cottenceau (gc@mandrakesoft.com) + # + # Copyright 2000 MandrakeSoft + # + # This software may be freely redistributed under the terms of the GNU + # public license. + # + # You should have received a copy of the GNU General Public License + # along with this program; if not, write to the Free Software + # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + # + #***************************************************************************** + +top_dir = .. + +include $(top_dir)/Makefile.common + + +all: libslang.a libslang-DIET.a + +clean: + rm -f *.o *.a + +FLAGS = -Wall -Werror -Os -fomit-frame-pointer -Dunix -DSLANG -c + + +OBJS = sltermin.o sldisply.o slutty.o slang.o slarray.o slclass.o slcmd.o slerr.o slgetkey.o slkeymap.o slmalloc.o slmath.o slmemchr.o slmemcmp.o slmemcpy.o slmemset.o slmisc.o slparse.o slprepr.o slregexp.o slrline.o slsearch.o slsmg.o slstd.o sltoken.o sltypes.o slxstrng.o slcurses.o slscroll.o slsignal.o slkeypad.o slerrno.o slstring.o slstruct.o slcmplex.o slarrfun.o slimport.o slpath.o slarith.o slassoc.o slcompat.o slposdir.o slstdio.o slproc.o sltime.o slstrops.o slbstr.o slpack.o slintall.o slistruc.o slposio.o slnspace.o slarrmis.o slospath.o slscanf.o + +OBJS-DIET = $(subst .o,-DIET.o,$(OBJS)) + + +libslang.a: $(OBJS) + ar -cru $@ $^ + ranlib $@ + +libslang-DIET.a: $(OBJS-DIET) + ar -cru $@ $^ + ranlib $@ + + +$(OBJS): %.o: %.c + gcc $(FLAGS) $(GLIBC_INCLUDES) -c $< -o $@ + +$(OBJS-DIET): %-DIET.o: %.c + gcc $(FLAGS) $(DIETLIBC_INCLUDES) -c $< -o $@ + diff --git a/mdk-stage1/slang/_slang.h b/mdk-stage1/slang/_slang.h new file mode 100644 index 000000000..02ee13505 --- /dev/null +++ b/mdk-stage1/slang/_slang.h @@ -0,0 +1,743 @@ +#ifndef _PRIVATE_SLANG_H_ +#define _PRIVATE_SLANG_H_ +/* header file for S-Lang internal structures that users do not (should not) + need. Use slang.h for that purpose. */ +/* Copyright (c) 1992, 1999, 2001 John E. Davis + * This file is part of the S-Lang library. + * + * You may distribute under the terms of either the GNU General Public + * License or the Perl Artistic License. + */ + +/* #include "config.h" */ +#include "jdmacros.h" +#include "sllimits.h" + +#ifdef VMS +# define SLANG_SYSTEM_NAME "_VMS" +#else +# if defined (IBMPC_SYSTEM) +# define SLANG_SYSTEM_NAME "_IBMPC" +# else +# define SLANG_SYSTEM_NAME "_UNIX" +# endif +#endif /* VMS */ + +/* These quantities are main_types for byte-compiled code. They are used + * by the inner_interp routine. The _BC_ means byte-code. + */ + +#define _SLANG_BC_LVARIABLE SLANG_LVARIABLE /* 0x01 */ +#define _SLANG_BC_GVARIABLE SLANG_GVARIABLE /* 0x02 */ +#define _SLANG_BC_IVARIABLE SLANG_IVARIABLE /* 0x03 */ +#define _SLANG_BC_RVARIABLE SLANG_RVARIABLE /* 0x04 */ +#define _SLANG_BC_INTRINSIC SLANG_INTRINSIC /* 0x05 */ +#define _SLANG_BC_FUNCTION SLANG_FUNCTION /* 0x06 */ +#define _SLANG_BC_MATH_UNARY SLANG_MATH_UNARY /* 0x07 */ +#define _SLANG_BC_APP_UNARY SLANG_APP_UNARY /* 0x08 */ +#define _SLANG_BC_ICONST SLANG_ICONSTANT /* 0x09 */ +#define _SLANG_BC_DCONST SLANG_DCONSTANT /* 0x0A */ +#define _SLANG_BC_PVARIABLE SLANG_PVARIABLE /* 0x0B */ +#define _SLANG_BC_PFUNCTION SLANG_PFUNCTION /* 0x0C */ + +#define _SLANG_BC_BINARY 0x10 +#define _SLANG_BC_LITERAL 0x11 /* constant objects */ +#define _SLANG_BC_LITERAL_INT 0x12 +#define _SLANG_BC_LITERAL_STR 0x13 +#define _SLANG_BC_BLOCK 0x14 + +/* These 3 MUST be in this order too ! */ +#define _SLANG_BC_RETURN 0x15 +#define _SLANG_BC_BREAK 0x16 +#define _SLANG_BC_CONTINUE 0x17 + +#define _SLANG_BC_EXCH 0x18 +#define _SLANG_BC_LABEL 0x19 +#define _SLANG_BC_LOBJPTR 0x1A +#define _SLANG_BC_GOBJPTR 0x1B +#define _SLANG_BC_X_ERROR 0x1C +/* These must be in this order */ +#define _SLANG_BC_X_USER0 0x1D +#define _SLANG_BC_X_USER1 0x1E +#define _SLANG_BC_X_USER2 0x1F +#define _SLANG_BC_X_USER3 0x20 +#define _SLANG_BC_X_USER4 0x21 + +#define _SLANG_BC_CALL_DIRECT 0x24 +#define _SLANG_BC_CALL_DIRECT_FRAME 0x25 +#define _SLANG_BC_UNARY 0x26 +#define _SLANG_BC_UNARY_FUNC 0x27 + +#define _SLANG_BC_DEREF_ASSIGN 0x30 +#define _SLANG_BC_SET_LOCAL_LVALUE 0x31 +#define _SLANG_BC_SET_GLOBAL_LVALUE 0x32 +#define _SLANG_BC_SET_INTRIN_LVALUE 0x33 +#define _SLANG_BC_SET_STRUCT_LVALUE 0x34 +#define _SLANG_BC_FIELD 0x35 +#define _SLANG_BC_SET_ARRAY_LVALUE 0x36 + +#define _SLANG_BC_LINE_NUM 0x40 + +#define _SLANG_BC_TMP 0x50 +#define _SLANG_BC_LVARIABLE_AGET 0x60 +#define _SLANG_BC_LVARIABLE_APUT 0x61 +#define _SLANG_BC_INTEGER_PLUS 0x62 +#define _SLANG_BC_INTEGER_MINUS 0x63 +#define _SLANG_BC_ARG_LVARIABLE 0x64 +#define _SLANG_BC_EARG_LVARIABLE 0x65 + +#define _SLANG_BC_CALL_DIRECT_INTRINSIC 0x80 +#define _SLANG_BC_INTRINSIC_CALL_DIRECT 0x81 +#define _SLANG_BC_CALL_DIRECT_LSTR 0x82 +#define _SLANG_BC_CALL_DIRECT_SLFUN 0x83 +#define _SLANG_BC_CALL_DIRECT_INTRSTOP 0x84 +#define _SLANG_BC_INTRINSIC_STOP 0x85 +#define _SLANG_BC_CALL_DIRECT_EARG_LVAR 0x86 +#define _SLANG_BC_CALL_DIRECT_LINT 0x87 +#define _SLANG_BC_CALL_DIRECT_LVAR 0x88 + + +/* Byte-Code Sub Types (_BCST_) */ + +/* These are sub_types of _SLANG_BC_BLOCK */ +#define _SLANG_BCST_ERROR_BLOCK 0x01 +#define _SLANG_BCST_EXIT_BLOCK 0x02 +#define _SLANG_BCST_USER_BLOCK0 0x03 +#define _SLANG_BCST_USER_BLOCK1 0x04 +#define _SLANG_BCST_USER_BLOCK2 0x05 +#define _SLANG_BCST_USER_BLOCK3 0x06 +#define _SLANG_BCST_USER_BLOCK4 0x07 +/* The user blocks MUST be in the above order */ +#define _SLANG_BCST_LOOP 0x10 +#define _SLANG_BCST_WHILE 0x11 +#define _SLANG_BCST_FOR 0x12 +#define _SLANG_BCST_FOREVER 0x13 +#define _SLANG_BCST_CFOR 0x14 +#define _SLANG_BCST_DOWHILE 0x15 +#define _SLANG_BCST_FOREACH 0x16 + +#define _SLANG_BCST_IF 0x20 +#define _SLANG_BCST_IFNOT 0x21 +#define _SLANG_BCST_ELSE 0x22 +#define _SLANG_BCST_ANDELSE 0x23 +#define _SLANG_BCST_ORELSE 0x24 +#define _SLANG_BCST_SWITCH 0x25 +#define _SLANG_BCST_NOTELSE 0x26 + +/* assignment (_SLANG_BC_SET_*_LVALUE) subtypes. The order MUST correspond + * to the assignment token order with the ASSIGN_TOKEN as the first! + */ +#define _SLANG_BCST_ASSIGN 0x01 +#define _SLANG_BCST_PLUSEQS 0x02 +#define _SLANG_BCST_MINUSEQS 0x03 +#define _SLANG_BCST_TIMESEQS 0x04 +#define _SLANG_BCST_DIVEQS 0x05 +#define _SLANG_BCST_BOREQS 0x06 +#define _SLANG_BCST_BANDEQS 0x07 +#define _SLANG_BCST_PLUSPLUS 0x08 +#define _SLANG_BCST_POST_PLUSPLUS 0x09 +#define _SLANG_BCST_MINUSMINUS 0x0A +#define _SLANG_BCST_POST_MINUSMINUS 0x0B + +/* These use SLANG_PLUS, SLANG_MINUS, SLANG_PLUSPLUS, etc... */ + +typedef union +{ +#if SLANG_HAS_FLOAT + double double_val; + float float_val; +#endif + long long_val; + unsigned long ulong_val; + VOID_STAR ptr_val; + char *s_val; + int int_val; + unsigned int uint_val; + SLang_MMT_Type *ref; + SLang_Name_Type *n_val; + struct _SLang_Struct_Type *struct_val; + struct _SLang_Array_Type *array_val; + short short_val; + unsigned short ushort_val; + char char_val; + unsigned char uchar_val; +} +_SL_Object_Union_Type; + +typedef struct _SLang_Object_Type +{ + unsigned char data_type; /* SLANG_INT_TYPE, ... */ + _SL_Object_Union_Type v; +} +SLang_Object_Type; + +struct _SLang_MMT_Type +{ + unsigned char data_type; /* int, string, etc... */ + VOID_STAR user_data; /* address of user structure */ + unsigned int count; /* number of references */ +}; + +extern int _SLang_pop_object_of_type (unsigned char, SLang_Object_Type *, int); + +typedef struct +{ + char *name; /* slstring */ + SLang_Object_Type obj; +} +_SLstruct_Field_Type; + +typedef struct _SLang_Struct_Type +{ + _SLstruct_Field_Type *fields; + unsigned int nfields; /* number used */ + unsigned int num_refs; +} +_SLang_Struct_Type; + +extern void _SLstruct_delete_struct (_SLang_Struct_Type *); +extern int _SLang_push_struct (_SLang_Struct_Type *); +extern int _SLang_pop_struct (_SLang_Struct_Type **); +extern int _SLstruct_init (void); +/* extern int _SLstruct_get_field (char *); */ +extern int _SLstruct_define_struct (void); +extern int _SLstruct_define_typedef (void); + +extern int _SLang_pop_datatype (unsigned char *); +extern int _SLang_push_datatype (unsigned char); + +struct _SLang_Ref_Type +{ + int is_global; + union + { + SLang_Name_Type *nt; + SLang_Object_Type *local_obj; + } + v; +}; + +extern int _SLang_dereference_ref (SLang_Ref_Type *); +extern int _SLang_deref_assign (SLang_Ref_Type *); +extern int _SLang_push_ref (int, VOID_STAR); + +extern int _SL_increment_frame_pointer (void); +extern int _SL_decrement_frame_pointer (void); + +extern int SLang_pop(SLang_Object_Type *); +extern void SLang_free_object (SLang_Object_Type *); +extern int _SLanytype_typecast (unsigned char, VOID_STAR, unsigned int, + unsigned char, VOID_STAR); +extern void _SLstring_intrinsic (void); + + +/* These functions are used to create slstrings of a fixed length. Be + * very careful how they are used. In particular, if len bytes are allocated, + * then the string must be len characters long, no more and no less. + */ +extern char *_SLallocate_slstring (unsigned int); +extern char *_SLcreate_via_alloced_slstring (char *, unsigned int); +extern void _SLunallocate_slstring (char *, unsigned int); +extern int _SLpush_alloced_slstring (char *, unsigned int); + +typedef struct +{ + char **buf; + unsigned int max_num; + unsigned int num; + unsigned int delta_num; +} +_SLString_List_Type; +extern int _SLstring_list_append (_SLString_List_Type *, char *); +extern int _SLstring_list_init (_SLString_List_Type *, unsigned int, unsigned int); +extern void _SLstring_list_delete (_SLString_List_Type *); +extern int _SLstring_list_push (_SLString_List_Type *); + +/* This function assumes that s is an slstring. */ +extern char *_SLstring_dup_slstring (char *); +extern int _SLang_dup_and_push_slstring (char *); + + +extern int _SLang_init_import (void); + +/* This function checks to see if the referenced object is initialized */ +extern int _SLang_is_ref_initialized (SLang_Ref_Type *); +extern int _SLcheck_identifier_syntax (char *); +extern int _SLang_uninitialize_ref (SLang_Ref_Type *); + +extern int _SLpush_slang_obj (SLang_Object_Type *); + +extern char *_SLexpand_escaped_char(char *, char *); +extern void _SLexpand_escaped_string (char *, char *, char *); + +/* returns a pointer to an SLstring string-- use SLang_free_slstring */ +extern char *_SLstringize_object (SLang_Object_Type *); +extern int _SLdump_objects (char *, SLang_Object_Type *, unsigned int, int); + +extern SLang_Object_Type *_SLRun_Stack; +extern SLang_Object_Type *_SLStack_Pointer; + +struct _SLang_NameSpace_Type +{ + struct _SLang_NameSpace_Type *next; + char *name; /* this is the load_type name */ + char *namespace_name; /* this name is assigned by implements */ + unsigned int table_size; + SLang_Name_Type **table; +}; +extern SLang_NameSpace_Type *_SLns_allocate_namespace (char *, unsigned int); +extern SLang_NameSpace_Type *_SLns_find_namespace (char *); +extern int _SLns_set_namespace_name (SLang_NameSpace_Type *, char *); +extern SLang_Array_Type *_SLnspace_apropos (SLang_NameSpace_Type *, char *, unsigned int); +extern void _SLang_use_namespace_intrinsic (char *name); +extern char *_SLang_cur_namespace_intrinsic (void); +extern SLang_Array_Type *_SLang_apropos (char *, char *, unsigned int); +extern void _SLang_implements_intrinsic (char *); + +extern int _SLang_Trace; +extern int _SLstack_depth(void); +extern char *_SLang_Current_Function_Name; + +extern int _SLang_trace_fun(char *); +extern int _SLang_Compile_Line_Num_Info; + +extern char *_SLstring_dup_hashed_string (char *, unsigned long); +extern unsigned long _SLcompute_string_hash (char *); +extern char *_SLstring_make_hashed_string (char *, unsigned int, unsigned long *); +extern void _SLfree_hashed_string (char *, unsigned int, unsigned long); +unsigned long _SLstring_hash (unsigned char *, unsigned char *); +extern int _SLinit_slcomplex (void); + +extern int _SLang_init_slstrops (void); +extern int _SLstrops_do_sprintf_n (int); +extern int _SLang_sscanf (void); +extern double _SLang_atof (char *); +extern int _SLang_init_bstring (void); +extern int _SLang_init_sltime (void); +extern void _SLpack (void); +extern void _SLunpack (char *, SLang_BString_Type *); +extern void _SLpack_pad_format (char *); +extern unsigned int _SLpack_compute_size (char *); +extern int _SLusleep (unsigned long); + +/* frees upon error. NULL __NOT__ ok. */ +extern int _SLang_push_slstring (char *); + +extern unsigned char _SLarith_promote_type (unsigned char); +extern int _SLarith_get_precedence (unsigned char); +extern int _SLarith_typecast (unsigned char, VOID_STAR, unsigned int, + unsigned char, VOID_STAR); + +extern int SLang_push(SLang_Object_Type *); +extern int SLadd_global_variable (char *); +extern void _SLang_clear_error (void); + +extern int _SLdo_pop (void); +extern unsigned int _SLsys_getkey (void); +extern int _SLsys_input_pending (int); +#ifdef IBMPC_SYSTEM +extern unsigned int _SLpc_convert_scancode (unsigned int, unsigned int, int); +#define _SLTT_KEY_SHIFT 1 +#define _SLTT_KEY_CTRL 2 +#define _SLTT_KEY_ALT 4 +#endif + +typedef struct _SLterminfo_Type SLterminfo_Type; +extern SLterminfo_Type *_SLtt_tigetent (char *); +extern char *_SLtt_tigetstr (SLterminfo_Type *, char *); +extern int _SLtt_tigetnum (SLterminfo_Type *, char *); +extern int _SLtt_tigetflag (SLterminfo_Type *, char *); + +#if SLTT_HAS_NON_BCE_SUPPORT +extern int _SLtt_get_bce_color_offset (void); +#endif +extern void (*_SLtt_color_changed_hook)(void); + +extern unsigned char SLang_Input_Buffer [SL_MAX_INPUT_BUFFER_LEN]; + +extern int _SLregister_types (void); +extern SLang_Class_Type *_SLclass_get_class (unsigned char); +extern VOID_STAR _SLclass_get_ptr_to_value (SLang_Class_Type *, SLang_Object_Type *); +extern void _SLclass_type_mismatch_error (unsigned char, unsigned char); +extern int _SLclass_init (void); +extern int _SLclass_copy_class (unsigned char, unsigned char); + +extern unsigned char _SLclass_Class_Type [256]; + +extern int (*_SLclass_get_typecast (unsigned char, unsigned char, int)) +(unsigned char, VOID_STAR, unsigned int, + unsigned char, VOID_STAR); + +extern int (*_SLclass_get_binary_fun (int, SLang_Class_Type *, SLang_Class_Type *, SLang_Class_Type **, int)) +(int, + unsigned char, VOID_STAR, unsigned int, + unsigned char, VOID_STAR, unsigned int, + VOID_STAR); + +extern int (*_SLclass_get_unary_fun (int, SLang_Class_Type *, SLang_Class_Type **, int)) +(int, unsigned char, VOID_STAR, unsigned int, VOID_STAR); + +extern int _SLarith_register_types (void); +extern unsigned char _SLarith_Arith_Types []; +extern unsigned char _SLarith_Is_Arith_Type [256]; +extern int _SLarith_bin_op (SLang_Object_Type *, SLang_Object_Type *, int); + +extern int _SLarray_add_bin_op (unsigned char); + +extern int _SLang_call_funptr (SLang_Name_Type *); +extern void _SLset_double_format (char *); +extern SLang_Name_Type *_SLlocate_global_name (char *); +extern SLang_Name_Type *_SLlocate_name (char *); + +extern char *_SLdefines[]; + +#define SL_ERRNO_NOT_IMPLEMENTED 0x7FFF +extern int _SLerrno_errno; +extern int _SLerrno_init (void); + +extern int _SLstdio_fdopen (char *, int, char *); + +extern void _SLstruct_pop_args (int *); +extern void _SLstruct_push_args (SLang_Array_Type *); + +extern int _SLarray_aput (void); +extern int _SLarray_aget (void); +extern int _SLarray_inline_implicit_array (void); +extern int _SLarray_inline_array (void); +extern int _SLarray_wildcard_array (void); + +extern int +_SLarray_typecast (unsigned char, VOID_STAR, unsigned int, + unsigned char, VOID_STAR, int); + +extern int _SLarray_aput_transfer_elem (SLang_Array_Type *, int *, + VOID_STAR, unsigned int, int); +extern int _SLarray_aget_transfer_elem (SLang_Array_Type *, int *, + VOID_STAR, unsigned int, int); +extern void _SLarray_free_array_elements (SLang_Class_Type *, VOID_STAR, unsigned int); + +extern SLang_Foreach_Context_Type * +_SLarray_cl_foreach_open (unsigned char, unsigned int); +extern void _SLarray_cl_foreach_close (unsigned char, SLang_Foreach_Context_Type *); +extern int _SLarray_cl_foreach (unsigned char, SLang_Foreach_Context_Type *); + +extern int _SLarray_matrix_multiply (void); +extern void (*_SLang_Matrix_Multiply)(void); + +extern int _SLarray_init_slarray (void); +extern SLang_Array_Type * +SLang_create_array1 (unsigned char, int, VOID_STAR, int *, unsigned int, int); + +extern int _SLcompile_push_context (SLang_Load_Type *); +extern int _SLcompile_pop_context (void); +extern int _SLang_Auto_Declare_Globals; + +typedef struct +{ + union + { + long long_val; + char *s_val; /* Used for IDENT_TOKEN, FLOAT, etc... */ + SLang_BString_Type *b_val; + } v; + int free_sval_flag; + unsigned int num_refs; + unsigned long hash; +#if _SLANG_HAS_DEBUG_CODE + int line_number; +#endif + unsigned char type; +} +_SLang_Token_Type; + +extern void _SLcompile (_SLang_Token_Type *); +extern void (*_SLcompile_ptr)(_SLang_Token_Type *); + +/* *** TOKENS *** */ + +/* Note that that tokens corresponding to ^J, ^M, and ^Z should not be used. + * This is because a file that contains any of these characters will + * have an OS dependent interpretation, e.g., ^Z is EOF on MSDOS. + */ + +/* Special tokens */ +#define EOF_TOKEN 0x01 +#define RPN_TOKEN 0x02 +#define NL_TOKEN 0x03 +#define NOP_TOKEN 0x05 +#define FARG_TOKEN 0x06 +#define TMP_TOKEN 0x07 + +#define RESERVED1_TOKEN 0x0A /* \n */ +#define RESERVED2_TOKEN 0x0D /* \r */ + +/* Literal tokens */ +#define CHAR_TOKEN 0x10 +#define UCHAR_TOKEN 0x11 +#define SHORT_TOKEN 0x12 +#define USHORT_TOKEN 0x13 +#define INT_TOKEN 0x14 +#define UINT_TOKEN 0x15 +#define LONG_TOKEN 0x16 +#define ULONG_TOKEN 0x17 +#define IS_INTEGER_TOKEN(x) ((x >= CHAR_TOKEN) && (x <= ULONG_TOKEN)) +#define FLOAT_TOKEN 0x18 +#define DOUBLE_TOKEN 0x19 +#define RESERVED3_TOKEN 0x1A /* ^Z */ +#define COMPLEX_TOKEN 0x1B +#define STRING_TOKEN 0x1C +#define BSTRING_TOKEN 0x1D +#define _BSTRING_TOKEN 0x1E /* byte-compiled BSTRING */ +#define ESC_STRING_TOKEN 0x1F + +/* Tokens that can be LVALUES */ +#define IDENT_TOKEN 0x20 +#define ARRAY_TOKEN 0x21 +#define DOT_TOKEN 0x22 +#define IS_LVALUE_TOKEN (((t) <= DOT_TOKEN) && ((t) >= IDENT_TOKEN)) + +/* do not use these values */ +#define RESERVED4_TOKEN 0x23 /* # */ +#define RESERVED5_TOKEN 0x25 /* % */ + +/* Flags for struct fields */ +#define STATIC_TOKEN 0x26 +#define READONLY_TOKEN 0x27 +#define PRIVATE_TOKEN 0x28 +#define PUBLIC_TOKEN 0x29 + +/* Punctuation tokens */ +#define OBRACKET_TOKEN 0x2a +#define CBRACKET_TOKEN 0x2b +#define OPAREN_TOKEN 0x2c +#define CPAREN_TOKEN 0x2d +#define OBRACE_TOKEN 0x2e +#define CBRACE_TOKEN 0x2f + +#define COMMA_TOKEN 0x31 +#define SEMICOLON_TOKEN 0x32 +#define COLON_TOKEN 0x33 +#define NAMESPACE_TOKEN 0x34 + +/* Operators */ +#define POW_TOKEN 0x38 + +/* The order here must match the order in the Binop_Level table in slparse.c */ +#define FIRST_BINARY_OP 0x39 +#define ADD_TOKEN 0x39 +#define SUB_TOKEN 0x3a +#define TIMES_TOKEN 0x3b +#define DIV_TOKEN 0x3c +#define LT_TOKEN 0x3d +#define LE_TOKEN 0x3e +#define GT_TOKEN 0x3f +#define GE_TOKEN 0x40 +#define EQ_TOKEN 0x41 +#define NE_TOKEN 0x42 +#define AND_TOKEN 0x43 +#define OR_TOKEN 0x44 +#define MOD_TOKEN 0x45 +#define BAND_TOKEN 0x46 +#define SHL_TOKEN 0x47 +#define SHR_TOKEN 0x48 +#define BXOR_TOKEN 0x49 +#define BOR_TOKEN 0x4a +#define POUND_TOKEN 0x4b /* matrix multiplication */ + +#define LAST_BINARY_OP 0x4b +#define IS_BINARY_OP(t) ((t >= FIRST_BINARY_OP) && (t <= LAST_BINARY_OP)) + +/* unary tokens -- but not all of them (see grammar) */ +#define DEREF_TOKEN 0x4d +#define NOT_TOKEN 0x4e +#define BNOT_TOKEN 0x4f + +#define IS_INTERNAL_FUNC(t) ((t >= 0x50) && (t <= 0x56)) +#define POP_TOKEN 0x50 +#define CHS_TOKEN 0x51 +#define SIGN_TOKEN 0x52 +#define ABS_TOKEN 0x53 +#define SQR_TOKEN 0x54 +#define MUL2_TOKEN 0x55 +#define EXCH_TOKEN 0x56 + +/* Assignment tokens. Note: these must appear with sequential values. + * The order here must match the specific lvalue assignments below. + * These tokens are used by rpn routines in slang.c. slparse.c maps them + * onto the specific lvalue tokens while parsing infix. + * Also the assignment _SLANG_BCST_ assumes this order + */ +#define ASSIGN_TOKEN 0x57 +#define PLUSEQS_TOKEN 0x58 +#define MINUSEQS_TOKEN 0x59 +#define TIMESEQS_TOKEN 0x5A +#define DIVEQS_TOKEN 0x5B +#define BOREQS_TOKEN 0x5C +#define BANDEQS_TOKEN 0x5D +#define PLUSPLUS_TOKEN 0x5E +#define POST_PLUSPLUS_TOKEN 0x5F +#define MINUSMINUS_TOKEN 0x60 +#define POST_MINUSMINUS_TOKEN 0x61 + +/* Directives */ +#define FIRST_DIRECTIVE_TOKEN 0x62 +#define IFNOT_TOKEN 0x62 +#define IF_TOKEN 0x63 +#define ELSE_TOKEN 0x64 +#define FOREVER_TOKEN 0x65 +#define WHILE_TOKEN 0x66 +#define FOR_TOKEN 0x67 +#define _FOR_TOKEN 0x68 +#define LOOP_TOKEN 0x69 +#define SWITCH_TOKEN 0x6A +#define DOWHILE_TOKEN 0x6B +#define ANDELSE_TOKEN 0x6C +#define ORELSE_TOKEN 0x6D +#define ERRBLK_TOKEN 0x6E +#define EXITBLK_TOKEN 0x6F +/* These must be sequential */ +#define USRBLK0_TOKEN 0x70 +#define USRBLK1_TOKEN 0x71 +#define USRBLK2_TOKEN 0x72 +#define USRBLK3_TOKEN 0x73 +#define USRBLK4_TOKEN 0x74 + +#define CONT_TOKEN 0x75 +#define BREAK_TOKEN 0x76 +#define RETURN_TOKEN 0x77 + +#define CASE_TOKEN 0x78 +#define DEFINE_TOKEN 0x79 +#define DO_TOKEN 0x7a +#define VARIABLE_TOKEN 0x7b +#define GVARIABLE_TOKEN 0x7c +#define _REF_TOKEN 0x7d +#define PUSH_TOKEN 0x7e +#define STRUCT_TOKEN 0x7f +#define TYPEDEF_TOKEN 0x80 +#define NOTELSE_TOKEN 0x81 +#define DEFINE_STATIC_TOKEN 0x82 +#define FOREACH_TOKEN 0x83 +#define USING_TOKEN 0x84 +#define DEFINE_PRIVATE_TOKEN 0x85 +#define DEFINE_PUBLIC_TOKEN 0x86 + +/* Note: the order here must match the order of the generic assignment tokens. + * Also, the first token of each group must be the ?_ASSIGN_TOKEN. + * slparse.c exploits this order, as well as slang.h. + */ +#define FIRST_ASSIGN_TOKEN 0x90 +#define _STRUCT_ASSIGN_TOKEN 0x90 +#define _STRUCT_PLUSEQS_TOKEN 0x91 +#define _STRUCT_MINUSEQS_TOKEN 0x92 +#define _STRUCT_TIMESEQS_TOKEN 0x93 +#define _STRUCT_DIVEQS_TOKEN 0x94 +#define _STRUCT_BOREQS_TOKEN 0x95 +#define _STRUCT_BANDEQS_TOKEN 0x96 +#define _STRUCT_PLUSPLUS_TOKEN 0x97 +#define _STRUCT_POST_PLUSPLUS_TOKEN 0x98 +#define _STRUCT_MINUSMINUS_TOKEN 0x99 +#define _STRUCT_POST_MINUSMINUS_TOKEN 0x9A + +#define _ARRAY_ASSIGN_TOKEN 0xA0 +#define _ARRAY_PLUSEQS_TOKEN 0xA1 +#define _ARRAY_MINUSEQS_TOKEN 0xA2 +#define _ARRAY_TIMESEQS_TOKEN 0xA3 +#define _ARRAY_DIVEQS_TOKEN 0xA4 +#define _ARRAY_BOREQS_TOKEN 0xA5 +#define _ARRAY_BANDEQS_TOKEN 0xA6 +#define _ARRAY_PLUSPLUS_TOKEN 0xA7 +#define _ARRAY_POST_PLUSPLUS_TOKEN 0xA8 +#define _ARRAY_MINUSMINUS_TOKEN 0xA9 +#define _ARRAY_POST_MINUSMINUS_TOKEN 0xAA + +#define _SCALAR_ASSIGN_TOKEN 0xB0 +#define _SCALAR_PLUSEQS_TOKEN 0xB1 +#define _SCALAR_MINUSEQS_TOKEN 0xB2 +#define _SCALAR_TIMESEQS_TOKEN 0xB3 +#define _SCALAR_DIVEQS_TOKEN 0xB4 +#define _SCALAR_BOREQS_TOKEN 0xB5 +#define _SCALAR_BANDEQS_TOKEN 0xB6 +#define _SCALAR_PLUSPLUS_TOKEN 0xB7 +#define _SCALAR_POST_PLUSPLUS_TOKEN 0xB8 +#define _SCALAR_MINUSMINUS_TOKEN 0xB9 +#define _SCALAR_POST_MINUSMINUS_TOKEN 0xBA + +#define _DEREF_ASSIGN_TOKEN 0xC0 +#define _DEREF_PLUSEQS_TOKEN 0xC1 +#define _DEREF_MINUSEQS_TOKEN 0xC2 +#define _DEREF_TIMESEQS_TOKEN 0xC3 +#define _DEREF_DIVEQS_TOKEN 0xC4 +#define _DEREF_BOREQS_TOKEN 0xC5 +#define _DEREF_BANDEQS_TOKEN 0xC6 +#define _DEREF_PLUSPLUS_TOKEN 0xC7 +#define _DEREF_POST_PLUSPLUS_TOKEN 0xC8 +#define _DEREF_MINUSMINUS_TOKEN 0xC9 +#define _DEREF_POST_MINUSMINUS_TOKEN 0xCA + +#define LAST_ASSIGN_TOKEN 0xCA +#define IS_ASSIGN_TOKEN(t) (((t)>=FIRST_ASSIGN_TOKEN)&&((t)<=LAST_ASSIGN_TOKEN)) + +#define _INLINE_ARRAY_TOKEN 0xE0 +#define _INLINE_IMPLICIT_ARRAY_TOKEN 0xE1 +#define _NULL_TOKEN 0xE2 +#define _INLINE_WILDCARD_ARRAY_TOKEN 0xE3 + +#define LINE_NUM_TOKEN 0xFC +#define ARG_TOKEN 0xFD +#define EARG_TOKEN 0xFE +#define NO_OP_LITERAL 0xFF + +typedef struct +{ + /* sltoken.c */ + /* SLang_eval_object */ + SLang_Load_Type *llt; + SLPreprocess_Type *this_slpp; + /* prep_get_char() */ + char *input_line; + char cchar; + /* get_token() */ + int want_nl_token; + + /* slparse.c */ + _SLang_Token_Type ctok; + int block_depth; + int assignment_expression; + + /* slang.c : SLcompile() */ + _SLang_Token_Type save_token; + _SLang_Token_Type next_token; + void (*slcompile_ptr)(_SLang_Token_Type *); +} +_SLEval_Context; + +extern int _SLget_token (_SLang_Token_Type *); +extern void _SLparse_error (char *, _SLang_Token_Type *, int); +extern void _SLparse_start (SLang_Load_Type *); +extern int _SLget_rpn_token (_SLang_Token_Type *); +extern void _SLcompile_byte_compiled (void); + +extern int (*_SLprep_eval_hook) (char *); + +#ifdef HAVE_VSNPRINTF +#define _SLvsnprintf vsnprintf +#else +extern int _SLvsnprintf (char *, unsigned int, char *, va_list); +#endif + +#ifdef HAVE_SNPRINTF +# define _SLsnprintf snprintf +#else +extern int _SLsnprintf (char *, unsigned int, char *, ...); +#endif + +#undef _INLINE_ +#if defined(__GNUC__) && _SLANG_USE_INLINE_CODE +# define _INLINE_ __inline__ +#else +# define _INLINE_ +#endif + + +#endif /* _PRIVATE_SLANG_H_ */ diff --git a/mdk-stage1/slang/config.h b/mdk-stage1/slang/config.h new file mode 100644 index 000000000..a5ab3273c --- /dev/null +++ b/mdk-stage1/slang/config.h @@ -0,0 +1,163 @@ +/* src/sysconf.h. Generated automatically by configure. */ +/* -*- c -*- */ +/* Note: this is for unix only. */ + +#ifndef SL_CONFIG_H +#define SL_CONFIG_H + +/* define if you have stdlib.h */ +#define HAVE_STDLIB_H 1 + +/* define if you have unistd.h */ +#define HAVE_UNISTD_H 1 + +/* define if you have termios.h */ +#define HAVE_TERMIOS_H 1 + +/* define if you have memory.h */ +#define HAVE_MEMORY_H 1 + +/* define if you have malloc.h */ +#define HAVE_MALLOC_H 1 + +/* define if you have memset */ +#define HAVE_MEMSET 1 + +/* define if you have memcpy */ +#define HAVE_MEMCPY 1 + +//#define HAVE_SETLOCALE 1 +//#define HAVE_LOCALE_H 1 + +#define HAVE_VFSCANF 1 + +/* define if you have fcntl.h */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the vsnprintf, snprintf functions and they return + * EOF upon failure. + */ +#define HAVE_VSNPRINTF 1 +#define HAVE_SNPRINTF 1 + +/* define if you have sys/fcntl.h */ +#define HAVE_SYS_FCNTL_H 1 + +#define HAVE_SYS_TYPES_H 1 +#define HAVE_SYS_WAIT_H 1 +#define HAVE_SYS_TIMES_H 1 + +/* Set these to the appropriate values */ +#define SIZEOF_SHORT 2 +#define SIZEOF_INT 4 +#define SIZEOF_LONG 4 +#define SIZEOF_FLOAT 4 +#define SIZEOF_DOUBLE 8 + +/* define if you have these. */ +#define HAVE_ATEXIT 1 +#define HAVE_ON_EXIT 1 +#define HAVE_PUTENV 1 +#define HAVE_GETCWD 1 +#define HAVE_TCGETATTR 1 +#define HAVE_TCSETATTR 1 +#define HAVE_CFGETOSPEED 1 +#define HAVE_LSTAT 1 +#define HAVE_KILL 1 +#define HAVE_CHOWN 1 +#define HAVE_VSNPRINTF 1 +#define HAVE_POPEN 1 +#define HAVE_UMASK 1 +#define HAVE_READLINK 1 +#define HAVE_TIMES 1 +#define HAVE_GMTIME 1 +#define HAVE_MKFIFO 1 + +#define HAVE_GETPPID 1 +#define HAVE_GETGID 1 +#define HAVE_GETEGID 1 +#define HAVE_GETEUID 1 +/* #undef HAVE_GETUID */ + +#define HAVE_SETGID 1 +#define HAVE_SETPGID 1 +#define HAVE_SETUID 1 + +#define HAVE_ACOSH 1 +#define HAVE_ASINH 1 +#define HAVE_ATANH 1 + +#define HAVE_DIRENT_H 1 +/* #undef HAVE_SYS_NDIR_H */ +/* #undef HAVE_SYS_DIR_H */ +/* #undef HAVE_NDIR_H */ + +#define HAVE_DLFCN_H 1 + +#define HAVE_SYS_UTSNAME_H 1 +#define HAVE_UNAME 1 + +/* These two are needed on DOS-like systems. Unix does not require them. + * They are included here for consistency. + * +#define HAVE_IO_H +#define HAVE_PROCESS_H + */ + +/* #undef USE_TERMCAP */ + +/* #undef mode_t */ +/* #undef uid_t */ +/* #undef pid_t */ +/* #undef gid_t */ + +/* Do we have posix signals? */ +#define HAVE_SIGACTION 1 +#define HAVE_SIGPROCMASK 1 +#define HAVE_SIGEMPTYSET 1 +#define HAVE_SIGADDSET 1 + +#if defined(HAVE_SIGADDSET) && defined(HAVE_SIGEMPTYSET) +# if defined(HAVE_SIGACTION) && defined(HAVE_SIGPROCMASK) +# define SLANG_POSIX_SIGNALS +# endif +#endif + +/* Define if you need to in order for stat and other things to work. */ +/* #undef _POSIX_SOURCE */ + +#ifdef _AIX +# ifndef _POSIX_SOURCE +# define _POSIX_SOURCE 1 +# endif +# ifndef _ALL_SOURCE +# define _ALL_SOURCE +# endif +/* This may generate warnings but the fact is that without it, xlc will + * INCORRECTLY inline many str* functions. */ +/* # undef __STR__ */ +#endif + +/* define USE_TERMCAP if you want to use it instead of terminfo. */ +#if defined(sequent) || defined(NeXT) +# ifndef USE_TERMCAP +# define USE_TERMCAP +# endif +#endif + +#if defined(ultrix) && !defined(__GNUC__) +# ifndef NO_PROTOTYPES +# define NO_PROTOTYPES +# endif +#endif + +#ifndef unix +# define unix 1 +#endif + +#ifndef __unix__ +# define __unix__ 1 +#endif + +#define _SLANG_SOURCE_ 1 +#endif /* SL_CONFIG_H */ diff --git a/mdk-stage1/slang/jdmacros.h b/mdk-stage1/slang/jdmacros.h new file mode 100644 index 000000000..70d491b78 --- /dev/null +++ b/mdk-stage1/slang/jdmacros.h @@ -0,0 +1,53 @@ +#ifndef _JD_MACROS_H_ +#define _JD_MACROS_H_ + +#ifndef SLMEMSET +# ifdef HAVE_MEMSET +# define SLMEMSET memset +# else +# define SLMEMSET SLmemset +# endif +#endif + +#ifndef SLMEMCHR +# ifdef HAVE_MEMCHR +# define SLMEMCHR memchr +# else +# define SLMEMCHR SLmemchr +# endif +#endif + +#ifndef SLMEMCPY +# ifdef HAVE_MEMCPY +# define SLMEMCPY memcpy +# else +# define SLMEMCPY SLmemcpy +# endif +#endif + +/* Note: HAVE_MEMCMP requires an unsigned memory comparison!!! */ +#ifndef SLMEMCMP +# ifdef HAVE_MEMCMP +# define SLMEMCMP memcmp +# else +# define SLMEMCMP SLmemcmp +# endif +#endif + +#ifndef SLFREE +# define SLFREE free +#endif + +#ifndef SLMALLOC +# define SLMALLOC malloc +#endif + +#ifndef SLCALLOC +# define SLCALLOC calloc +#endif + +#ifndef SLREALLOC +# define SLREALLOC realloc +#endif + +#endif /* _JD_MACROS_H_ */ diff --git a/mdk-stage1/slang/keywhash.c b/mdk-stage1/slang/keywhash.c new file mode 100644 index 000000000..17d94d5a3 --- /dev/null +++ b/mdk-stage1/slang/keywhash.c @@ -0,0 +1,190 @@ +/* Perfect hash generated by command line: + * ./a.out 1 + */ +#define MIN_HASH_VALUE 2 +#define MAX_HASH_VALUE 118 +#define MIN_KEYWORD_LEN 2 +#define MAX_KEYWORD_LEN 11 + +static unsigned char Keyword_Hash_Table [256] = +{ + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 1, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 9, 7, 1, 8, 2, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 0, 0, 119, 0, 119, 119, 119, 7, 119, 0, 0, 119, 119, 0, + 119, 119, 0, 0, 0, 0, 119, 119, 0, 119, 119, 119, 119, 119, 119, 2, + 119, 41, 1, 1, 9, 0, 55, 8, 0, 0, 119, 0, 27, 0, 0, 0, + 7, 2, 0, 21, 0, 0, 0, 3, 2, 0, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119 +}; + +static unsigned char keyword_hash (char *s, unsigned int len) +{ + unsigned int sum; + + sum = len; + while (len) + { + len--; + sum += (unsigned int) Keyword_Hash_Table [(unsigned char)s[len]]; + } + return sum; +} + +typedef struct +{ + char *name; + unsigned int type; +} +Keyword_Table_Type; + +static Keyword_Table_Type Keyword_Table [/* 117 */] = +{ + {"or", OR_TOKEN}, + {"not", NOT_TOKEN}, + {NULL,0}, + {"xor", BXOR_TOKEN}, + {"return", RETURN_TOKEN}, + {"exch", EXCH_TOKEN}, + {NULL,0}, + {"continue", CONT_TOKEN}, + {NULL,0}, + {"do", DO_TOKEN}, + {"mod", MOD_TOKEN}, + {"ERROR_BLOCK", ERRBLK_TOKEN}, + {"USER_BLOCK2", USRBLK2_TOKEN}, + {"USER_BLOCK4", USRBLK4_TOKEN}, + {"__tmp", TMP_TOKEN}, + {"pop", POP_TOKEN}, + {NULL,0}, + {"EXIT_BLOCK", EXITBLK_TOKEN}, + {"USER_BLOCK1", USRBLK1_TOKEN}, + {"USER_BLOCK3", USRBLK3_TOKEN}, + {"USER_BLOCK0", USRBLK0_TOKEN}, + {NULL,0}, + {"shr", SHR_TOKEN}, + {"chs", CHS_TOKEN}, + {"sqr", SQR_TOKEN}, + {NULL,0}, + {"struct", STRUCT_TOKEN}, + {NULL,0}, + {NULL,0}, + {"switch", SWITCH_TOKEN}, + {"mul2", MUL2_TOKEN}, + {"sign", SIGN_TOKEN}, + {"using", USING_TOKEN}, + {"while", WHILE_TOKEN}, + {NULL,0}, + {NULL,0}, + {"loop", LOOP_TOKEN}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {"public", PUBLIC_TOKEN}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {"break", BREAK_TOKEN}, + {NULL,0}, + {"do_while", DOWHILE_TOKEN}, + {NULL,0}, + {"shl", SHL_TOKEN}, + {"else", ELSE_TOKEN}, + {"and", AND_TOKEN}, + {"orelse", ORELSE_TOKEN}, + {"private", PRIVATE_TOKEN}, + {NULL,0}, + {"if", IF_TOKEN}, + {"for", FOR_TOKEN}, + {"!if", IFNOT_TOKEN}, + {NULL,0}, + {"_for", _FOR_TOKEN}, + {"forever", FOREVER_TOKEN}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {"abs", ABS_TOKEN}, + {"case", CASE_TOKEN}, + {NULL,0}, + {"static", STATIC_TOKEN}, + {"define", DEFINE_TOKEN}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {"typedef", TYPEDEF_TOKEN}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {"foreach", FOREACH_TOKEN}, + {"andelse", ANDELSE_TOKEN}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {NULL,0}, + {"variable", VARIABLE_TOKEN}, +}; + +static Keyword_Table_Type *is_keyword (char *str, unsigned int len) +{ + unsigned int hash; + char *name; + Keyword_Table_Type *kw; + + if ((len < MIN_KEYWORD_LEN) + || (len > MAX_KEYWORD_LEN)) + return NULL; + + hash = keyword_hash (str, len); + if ((hash > MAX_HASH_VALUE) || (hash < MIN_HASH_VALUE)) + return NULL; + + kw = &Keyword_Table[hash - MIN_HASH_VALUE]; + if ((NULL != (name = kw->name)) + && (*str == *name) + && (0 == strcmp (str, name))) + return kw; + return NULL; +} diff --git a/mdk-stage1/slang/sl-feat.h b/mdk-stage1/slang/sl-feat.h new file mode 100644 index 000000000..511d72451 --- /dev/null +++ b/mdk-stage1/slang/sl-feat.h @@ -0,0 +1,60 @@ +/* Setting this to 1 enables automatic support for associative arrays. + * If this is set to 0, an application must explicitly enable associative + * array support via SLang_init_slassoc. + */ +#define SLANG_HAS_ASSOC_ARRAYS 1 + +#define SLANG_HAS_COMPLEX 1 +#define SLANG_HAS_FLOAT 1 + +/* This is the old space-speed trade off. To reduce memory usage and code + * size, set this to zero. + */ +#define _SLANG_OPTIMIZE_FOR_SPEED 2 + +#define _SLANG_USE_INLINE_CODE 1 + +/* This is experimental. It adds extra information for tracking down + * errors. + */ +#define _SLANG_HAS_DEBUG_CODE 1 + +/* Allow optimizations based upon the __tmp operator. */ +#define _SLANG_USE_TMP_OPTIMIZATION 1 + +/* Setting this to one will map 8 bit vtxxx terminals to 7 bit. Terminals + * such as the vt320 can be set up to output the two-character escape sequence + * encoded as 'ESC [' as single character. Setting this variable to 1 will + * insert code to map such characters to the 7 bit equivalent. + * This affects just input characters in the range 128-160 on non PC + * systems. + */ +#if defined(VMS) || defined(AMIGA) +# define _SLANG_MAP_VTXXX_8BIT 1 +#else +# define _SLANG_MAP_VTXXX_8BIT 0 +#endif + +/* Add support for color terminals that cannot do background color erases + * Such terminals are poorly designed and are slowly disappearing but they + * are still quite common. For example, screen is one of them! + * + * This is experimental. In particular, it is not known to work if + * KANJI suupport is enabled. + */ +#if !defined(IBMPC_SYSTEM) +# define SLTT_HAS_NON_BCE_SUPPORT 1 +#else +# define SLTT_HAS_NON_BCE_SUPPORT 0 +#endif + +/* If you want slang to assume that an xterm always has the background color + * erase feature, then set this to 1. Otherwise, it will check the terminfo + * database. This may or may not be a good idea since most good color xterms + * support bce but many terminfo systems do not support it. + */ +#define SLTT_XTERM_ALWAYS_BCE 0 + +/* Set this to 1 to enable Kanji support. See above comment. */ +#define SLANG_HAS_KANJI_SUPPORT 0 + diff --git a/mdk-stage1/slang/slang.c b/mdk-stage1/slang/slang.c new file mode 100644 index 000000000..6edc7df37 --- /dev/null +++ b/mdk-stage1/slang/slang.c @@ -0,0 +1,5547 @@ +/* -*- mode: C; mode: fold; -*- */ +/* slang.c --- guts of S-Lang interpreter */ +/* Copyright (c) 1992, 1999, 2001 John E. Davis + * This file is part of the S-Lang library. + * + * You may distribute under the terms of either the GNU General Public + * License or the Perl Artistic License. + */ + +#include "slinclud.h" + +#if SLANG_HAS_FLOAT +# include <math.h> +#endif + +#include "slang.h" +#include "_slang.h" + +#define USE_COMBINED_BYTECODES 0 + +struct _SLBlock_Type; + +typedef struct +{ + struct _SLBlock_Type *body; + unsigned int num_refs; +} +_SLBlock_Header_Type; + +typedef struct +{ + char *name; + SLang_Name_Type *next; + char name_type; + + union + { + _SLBlock_Header_Type *header; /* body of function */ + char *autoload_filename; + } + v; +#if _SLANG_HAS_DEBUG_CODE + char *file; +#endif +#define SLANG_MAX_LOCAL_VARIABLES 254 +#define AUTOLOAD_NUM_LOCALS (SLANG_MAX_LOCAL_VARIABLES + 1) + unsigned char nlocals; /* number of local variables */ + unsigned char nargs; /* number of arguments */ +} +_SLang_Function_Type; + +typedef struct +{ + char *name; + SLang_Name_Type *next; + char name_type; + + SLang_Object_Type obj; +} +SLang_Global_Var_Type; + +typedef struct +{ + char *name; + SLang_Name_Type *next; + char name_type; + + int local_var_number; +} +SLang_Local_Var_Type; + +typedef struct _SLBlock_Type +{ + unsigned char bc_main_type; + unsigned char bc_sub_type; + union + { + struct _SLBlock_Type *blk; + int i_blk; + + SLang_Name_Type *nt_blk; + SLang_App_Unary_Type *nt_unary_blk; + SLang_Intrin_Var_Type *nt_ivar_blk; + SLang_Intrin_Fun_Type *nt_ifun_blk; + SLang_Global_Var_Type *nt_gvar_blk; + SLang_IConstant_Type *iconst_blk; + SLang_DConstant_Type *dconst_blk; + _SLang_Function_Type *nt_fun_blk; + + VOID_STAR ptr_blk; + char *s_blk; + SLang_BString_Type *bs_blk; + +#if SLANG_HAS_FLOAT + double *double_blk; /*literal double is a pointer */ +#endif + float float_blk; + long l_blk; + struct _SLang_Struct_Type *struct_blk; + int (*call_function)(void); + } + b; +} +SLBlock_Type; + +/* Debugging and tracing variables */ + +void (*SLang_Enter_Function)(char *) = NULL; +void (*SLang_Exit_Function)(char *) = NULL; +/* If non null, these call C functions before and after a slang function. */ + +int _SLang_Trace = 0; +/* If _SLang_Trace = -1, do not trace intrinsics */ +static int Trace_Mode = 0; + +static char *Trace_Function; /* function to be traced */ +int SLang_Traceback = 0; +/* non zero means do traceback. If less than 0, do not show local variables */ + +/* These variables handle _NARGS processing by the parser */ +int SLang_Num_Function_Args; +static int *Num_Args_Stack; +static unsigned int Recursion_Depth; +static SLang_Object_Type *Frame_Pointer; +static int Next_Function_Num_Args; +static unsigned int Frame_Pointer_Depth; +static unsigned int *Frame_Pointer_Stack; + +static int Lang_Break_Condition = 0; +/* true if any one below is true. This keeps us from testing 3 variables. + * I know this can be perfomed with a bitmapped variable, but... + */ +static int Lang_Break = 0; +static int Lang_Return = 0; +/* static int Lang_Continue = 0; */ + +SLang_Object_Type *_SLRun_Stack; +SLang_Object_Type *_SLStack_Pointer; +static SLang_Object_Type *_SLStack_Pointer_Max; + +/* Might want to increase this. */ +static SLang_Object_Type Local_Variable_Stack[SLANG_MAX_LOCAL_STACK]; +static SLang_Object_Type *Local_Variable_Frame = Local_Variable_Stack; + +static void free_function_header (_SLBlock_Header_Type *); + +void (*SLang_Dump_Routine)(char *); + +static void call_dump_routine (char *fmt, ...) +{ + char buf[1024]; + va_list ap; + + va_start (ap, fmt); + if (SLang_Dump_Routine != NULL) + { + (void) _SLvsnprintf (buf, sizeof (buf), fmt, ap); + (*SLang_Dump_Routine) (buf); + } + else + { + vfprintf (stderr, fmt, ap); + fflush (stderr); + } + va_end (ap); +} + +static void do_traceback (char *, unsigned int, char *); +static int init_interpreter (void); + +/*{{{ push/pop/etc stack manipulation functions */ + +/* This routine is assumed to work even in the presence of a SLang_Error. */ +_INLINE_ +int SLang_pop (SLang_Object_Type *x) +{ + register SLang_Object_Type *y; + + y = _SLStack_Pointer; + if (y == _SLRun_Stack) + { + if (SLang_Error == 0) SLang_Error = SL_STACK_UNDERFLOW; + x->data_type = 0; + return -1; + } + y--; + *x = *y; + + _SLStack_Pointer = y; + return 0; +} + +static int pop_ctrl_integer (int *i) +{ + int type; + SLang_Class_Type *cl; +#if _SLANG_OPTIMIZE_FOR_SPEED + register SLang_Object_Type *y; + + /* Most of the time, either an integer or a char will be on the stack. + * Optimize these cases. + */ + y = _SLStack_Pointer; + if (y == _SLRun_Stack) + { + if (SLang_Error == 0) SLang_Error = SL_STACK_UNDERFLOW; + return -1; + } + y--; + + type = y->data_type; + if (type == SLANG_INT_TYPE) + { + _SLStack_Pointer = y; + *i = y->v.int_val; + return 0; + } + if (type == SLANG_CHAR_TYPE) + { + _SLStack_Pointer = y; + *i = y->v.char_val; + return 0; + } +#else + if (-1 == (type = SLang_peek_at_stack ())) + return -1; +#endif + + cl = _SLclass_get_class ((unsigned char) type); + if (cl->cl_to_bool == NULL) + { + SLang_verror (SL_TYPE_MISMATCH, + "%s cannot be used in a boolean context", + cl->cl_name); + return -1; + } + return cl->cl_to_bool ((unsigned char) type, i); +} + +_INLINE_ +int SLang_peek_at_stack (void) +{ + if (_SLStack_Pointer == _SLRun_Stack) + { + if (SLang_Error == 0) + SLang_Error = SL_STACK_UNDERFLOW; + return -1; + } + + return (_SLStack_Pointer - 1)->data_type; +} + +int SLang_peek_at_stack1 (void) +{ + int type; + + type = SLang_peek_at_stack (); + if (type == SLANG_ARRAY_TYPE) + type = (_SLStack_Pointer - 1)->v.array_val->data_type; + + return type; +} + +_INLINE_ +void SLang_free_object (SLang_Object_Type *obj) +{ + unsigned char data_type; + SLang_Class_Type *cl; + + if (obj == NULL) return; + data_type = obj->data_type; +#if _SLANG_OPTIMIZE_FOR_SPEED + if (SLANG_CLASS_TYPE_SCALAR == _SLclass_Class_Type [data_type]) + return; + if (data_type == SLANG_STRING_TYPE) + { + SLang_free_slstring (obj->v.s_val); + return; + } +#endif + cl = _SLclass_get_class (data_type); +#if !_SLANG_OPTIMIZE_FOR_SPEED + if (cl->cl_class_type != SLANG_CLASS_TYPE_SCALAR) +#endif + (*cl->cl_destroy) (data_type, (VOID_STAR) &obj->v); +} + +_INLINE_ +int SLang_push (SLang_Object_Type *x) +{ + register SLang_Object_Type *y; + y = _SLStack_Pointer; + + /* if there is a SLang_Error, probably not much harm will be done + if it is ignored here */ + /* if (SLang_Error) return; */ + + /* flag it now */ + if (y >= _SLStack_Pointer_Max) + { + if (!SLang_Error) SLang_Error = SL_STACK_OVERFLOW; + return -1; + } + + *y = *x; + _SLStack_Pointer = y + 1; + return 0; +} + +/* _INLINE_ */ +int SLclass_push_ptr_obj (unsigned char type, VOID_STAR pval) +{ + register SLang_Object_Type *y; + y = _SLStack_Pointer; + + if (y >= _SLStack_Pointer_Max) + { + if (!SLang_Error) SLang_Error = SL_STACK_OVERFLOW; + return -1; + } + + y->data_type = type; + y->v.ptr_val = pval; + + _SLStack_Pointer = y + 1; + return 0; +} + +_INLINE_ +int SLclass_push_int_obj (unsigned char type, int x) +{ + register SLang_Object_Type *y; + y = _SLStack_Pointer; + + if (y >= _SLStack_Pointer_Max) + { + if (!SLang_Error) SLang_Error = SL_STACK_OVERFLOW; + return -1; + } + + y->data_type = type; + y->v.int_val = x; + + _SLStack_Pointer = y + 1; + return 0; +} + +_INLINE_ +int _SLang_pop_object_of_type (unsigned char type, SLang_Object_Type *obj, + int allow_arrays) +{ + register SLang_Object_Type *y; + + y = _SLStack_Pointer; + if (y == _SLRun_Stack) + return SLang_pop (obj); + y--; + if (y->data_type != type) + { +#if _SLANG_OPTIMIZE_FOR_SPEED + /* This is an implicit typecast. We do not want to typecast + * floats to ints implicitly. + */ + if (_SLarith_Is_Arith_Type [type] + && _SLarith_Is_Arith_Type [y->data_type] + && (_SLarith_Is_Arith_Type [type] >= _SLarith_Is_Arith_Type[y->data_type])) + { + /* This should not fail */ + (void) _SLarith_typecast (y->data_type, (VOID_STAR)&y->v, 1, + type, (VOID_STAR)&obj->v); + obj->data_type = type; + _SLStack_Pointer = y; + return 0; + } +#endif + + if ((allow_arrays == 0) + || (y->data_type != SLANG_ARRAY_TYPE) + || (y->v.array_val->data_type != type)) + if (-1 == SLclass_typecast (type, 1, 0)) + return -1; + } + *obj = *y; + _SLStack_Pointer = y; + return 0; +} + +/* This function reverses the top n items on the stack and returns a + * an offset from the start of the stack to the last item. + */ +int SLreverse_stack (int n) +{ + SLang_Object_Type *otop, *obot, tmp; + + otop = _SLStack_Pointer; + if ((n > otop - _SLRun_Stack) || (n < 0)) + { + SLang_Error = SL_STACK_UNDERFLOW; + return -1; + } + obot = otop - n; + otop--; + while (otop > obot) + { + tmp = *obot; + *obot = *otop; + *otop = tmp; + otop--; + obot++; + } + return (int) ((_SLStack_Pointer - n) - _SLRun_Stack); +} + +_INLINE_ +int SLroll_stack (int np) +{ + int n, i; + SLang_Object_Type *otop, *obot, tmp; + + if ((n = abs(np)) <= 1) return 0; /* identity */ + + obot = otop = _SLStack_Pointer; + i = n; + while (i != 0) + { + if (obot <= _SLRun_Stack) + { + SLang_Error = SL_STACK_UNDERFLOW; + return -1; + } + obot--; + i--; + } + otop--; + + if (np > 0) + { + /* Put top on bottom and roll rest up. */ + tmp = *otop; + while (otop > obot) + { + *otop = *(otop - 1); + otop--; + } + *otop = tmp; + } + else + { + /* Put bottom on top and roll rest down. */ + tmp = *obot; + while (obot < otop) + { + *obot = *(obot + 1); + obot++; + } + *obot = tmp; + } + return 0; +} + +int _SLstack_depth (void) +{ + return (int) (_SLStack_Pointer - _SLRun_Stack); +} + +int SLdup_n (int n) +{ + SLang_Object_Type *bot, *top; + + if (n <= 0) + return 0; + + top = _SLStack_Pointer; + if (top < _SLRun_Stack + n) + { + if (SLang_Error == 0) + SLang_Error = SL_STACK_UNDERFLOW; + return -1; + } + if (top + n > _SLStack_Pointer_Max) + { + if (SLang_Error == 0) + SLang_Error = SL_STACK_OVERFLOW; + return -1; + } + bot = top - n; + + while (bot < top) + { + SLang_Class_Type *cl; + unsigned char data_type = bot->data_type; + +#if _SLANG_OPTIMIZE_FOR_SPEED + if (SLANG_CLASS_TYPE_SCALAR == _SLclass_Class_Type [data_type]) + { + *_SLStack_Pointer++ = *bot++; + continue; + } +#endif + cl = _SLclass_get_class (data_type); + if (-1 == (*cl->cl_push) (data_type, (VOID_STAR) &bot->v)) + return -1; + bot++; + } + return 0; +} + +/*}}}*/ + +/*{{{ inner interpreter and support functions */ + +_INLINE_ +int _SL_increment_frame_pointer (void) +{ + if (Recursion_Depth >= SLANG_MAX_RECURSIVE_DEPTH) + { + SLang_verror (SL_STACK_OVERFLOW, "Num Args Stack Overflow"); + return -1; + } + Num_Args_Stack [Recursion_Depth] = SLang_Num_Function_Args; + + SLang_Num_Function_Args = Next_Function_Num_Args; + Next_Function_Num_Args = 0; + Recursion_Depth++; + return 0; +} + +_INLINE_ +int _SL_decrement_frame_pointer (void) +{ + if (Recursion_Depth == 0) + { + SLang_verror (SL_STACK_UNDERFLOW, "Num Args Stack Underflow"); + return -1; + } + + Recursion_Depth--; + if (Recursion_Depth < SLANG_MAX_RECURSIVE_DEPTH) + SLang_Num_Function_Args = Num_Args_Stack [Recursion_Depth]; + + return 0; +} + +_INLINE_ +int SLang_start_arg_list (void) +{ + if (Frame_Pointer_Depth < SLANG_MAX_RECURSIVE_DEPTH) + { + Frame_Pointer_Stack [Frame_Pointer_Depth] = (unsigned int) (Frame_Pointer - _SLRun_Stack); + Frame_Pointer = _SLStack_Pointer; + Frame_Pointer_Depth++; + Next_Function_Num_Args = 0; + return 0; + } + + SLang_verror (SL_STACK_OVERFLOW, "Frame Stack Overflow"); + return -1; +} + +_INLINE_ +int SLang_end_arg_list (void) +{ + if (Frame_Pointer_Depth == 0) + { + SLang_verror (SL_STACK_UNDERFLOW, "Frame Stack Underflow"); + return -1; + } + Frame_Pointer_Depth--; + if (Frame_Pointer_Depth < SLANG_MAX_RECURSIVE_DEPTH) + { + Next_Function_Num_Args = (int) (_SLStack_Pointer - Frame_Pointer); + Frame_Pointer = _SLRun_Stack + Frame_Pointer_Stack [Frame_Pointer_Depth]; + } + return 0; +} + +_INLINE_ +static int do_bc_call_direct_frame (int (*f)(void)) +{ + if ((0 == SLang_end_arg_list ()) + && (0 == _SL_increment_frame_pointer ())) + { + (void) (*f) (); + _SL_decrement_frame_pointer (); + } + if (SLang_Error) + return -1; + return 0; +} + +static int do_name_type_error (SLang_Name_Type *nt) +{ + char buf[256]; + if (nt != NULL) + { + (void) _SLsnprintf (buf, sizeof (buf), "(Error occurred processing %s)", nt->name); + do_traceback (buf, 0, NULL); + } + return -1; +} + +/* local and global variable assignments */ + +static int do_binary_ab (int op, SLang_Object_Type *obja, SLang_Object_Type *objb) +{ + SLang_Class_Type *a_cl, *b_cl, *c_cl; + unsigned char b_data_type, a_data_type, c_data_type; + int (*binary_fun) (int, + unsigned char, VOID_STAR, unsigned int, + unsigned char, VOID_STAR, unsigned int, + VOID_STAR); + VOID_STAR pa; + VOID_STAR pb; + VOID_STAR pc; + int ret; + + b_data_type = objb->data_type; + a_data_type = obja->data_type; + +#if _SLANG_OPTIMIZE_FOR_SPEED + if (_SLarith_Is_Arith_Type[a_data_type] + && _SLarith_Is_Arith_Type[b_data_type]) + { + int status; + status = _SLarith_bin_op (obja, objb, op); + if (status != 1) + return status; + /* drop and try it the hard way */ + } +#endif + + a_cl = _SLclass_get_class (a_data_type); + if (a_data_type == b_data_type) + b_cl = a_cl; + else + b_cl = _SLclass_get_class (b_data_type); + + if (NULL == (binary_fun = _SLclass_get_binary_fun (op, a_cl, b_cl, &c_cl, 1))) + return -1; + + c_data_type = c_cl->cl_data_type; + +#if _SLANG_OPTIMIZE_FOR_SPEED + if (SLANG_CLASS_TYPE_SCALAR == _SLclass_Class_Type [a_data_type]) + pa = (VOID_STAR) &obja->v; + else +#endif + pa = _SLclass_get_ptr_to_value (a_cl, obja); + +#if _SLANG_OPTIMIZE_FOR_SPEED + if (SLANG_CLASS_TYPE_SCALAR == _SLclass_Class_Type [b_data_type]) + pb = (VOID_STAR) &objb->v; + else +#endif + pb = _SLclass_get_ptr_to_value (b_cl, objb); + + pc = c_cl->cl_transfer_buf; + + if (1 != (*binary_fun) (op, + a_data_type, pa, 1, + b_data_type, pb, 1, + pc)) + { + SLang_verror (SL_NOT_IMPLEMENTED, + "Binary operation between %s and %s failed", + a_cl->cl_name, b_cl->cl_name); + + return -1; + } + + /* apush will create a copy, so make sure we free after the push */ + ret = (*c_cl->cl_apush)(c_data_type, pc); +#if _SLANG_OPTIMIZE_FOR_SPEED + if (SLANG_CLASS_TYPE_SCALAR != _SLclass_Class_Type [c_data_type]) +#endif + (*c_cl->cl_adestroy)(c_data_type, pc); + + return ret; +} + +_INLINE_ +static void do_binary (int op) +{ + SLang_Object_Type obja, objb; + + if (SLang_pop (&objb)) return; + if (0 == SLang_pop (&obja)) + { + (void) do_binary_ab (op, &obja, &objb); +#if _SLANG_OPTIMIZE_FOR_SPEED + if (SLANG_CLASS_TYPE_SCALAR != _SLclass_Class_Type [obja.data_type]) +#endif + SLang_free_object (&obja); + } +#if _SLANG_OPTIMIZE_FOR_SPEED + if (SLANG_CLASS_TYPE_SCALAR != _SLclass_Class_Type [objb.data_type]) +#endif + SLang_free_object (&objb); +} + +static int do_unary_op (int op, SLang_Object_Type *obj, int unary_type) +{ + int (*f) (int, unsigned char, VOID_STAR, unsigned int, VOID_STAR); + VOID_STAR pa; + VOID_STAR pb; + SLang_Class_Type *a_cl, *b_cl; + unsigned char a_type, b_type; + int ret; + + a_type = obj->data_type; + a_cl = _SLclass_get_class (a_type); + + if (NULL == (f = _SLclass_get_unary_fun (op, a_cl, &b_cl, unary_type))) + return -1; + + b_type = b_cl->cl_data_type; + +#if _SLANG_OPTIMIZE_FOR_SPEED + if (SLANG_CLASS_TYPE_SCALAR == _SLclass_Class_Type [a_type]) + pa = (VOID_STAR) &obj->v; + else +#endif + pa = _SLclass_get_ptr_to_value (a_cl, obj); + + pb = b_cl->cl_transfer_buf; + + if (1 != (*f) (op, a_type, pa, 1, pb)) + { + SLang_verror (SL_NOT_IMPLEMENTED, + "Unary operation for %s failed", a_cl->cl_name); + return -1; + } + + ret = (*b_cl->cl_apush)(b_type, pb); + /* cl_apush creates a copy, so make sure we call cl_adestroy */ +#if _SLANG_OPTIMIZE_FOR_SPEED + if (SLANG_CLASS_TYPE_SCALAR != _SLclass_Class_Type [b_type]) +#endif + (*b_cl->cl_adestroy)(b_type, pb); + + return ret; +} + +_INLINE_ +static int do_unary (int op, int unary_type) +{ + SLang_Object_Type obj; + int ret; + + if (-1 == SLang_pop (&obj)) return -1; + ret = do_unary_op (op, &obj, unary_type); +#if _SLANG_OPTIMIZE_FOR_SPEED + if (SLANG_CLASS_TYPE_SCALAR != _SLclass_Class_Type [obj.data_type]) +#endif + SLang_free_object (&obj); + return ret; +} + +static int do_assignment_binary (int op, SLang_Object_Type *obja_ptr) +{ + SLang_Object_Type objb; + int ret; + + if (SLang_pop (&objb)) + return -1; + + ret = do_binary_ab (op, obja_ptr, &objb); +#if _SLANG_OPTIMIZE_FOR_SPEED + if (SLANG_CLASS_TYPE_SCALAR != _SLclass_Class_Type [objb.data_type]) +#endif + SLang_free_object (&objb); + return ret; +} + +/* The order of these is assumed to match the binary operators + * defined in slang.h + */ +static int +map_assignment_op_to_binary (unsigned char op_type, int *op, int *is_unary) +{ + *is_unary = 0; + switch (op_type) + { + case _SLANG_BCST_PLUSEQS: + case _SLANG_BCST_MINUSEQS: + case _SLANG_BCST_TIMESEQS: + case _SLANG_BCST_DIVEQS: + *op = SLANG_PLUS + (op_type - _SLANG_BCST_PLUSEQS); + break; + + case _SLANG_BCST_BOREQS: + *op = SLANG_BOR; + break; + + case _SLANG_BCST_BANDEQS: + *op = SLANG_BAND; + break; + + case _SLANG_BCST_POST_MINUSMINUS: + case _SLANG_BCST_MINUSMINUS: + *op = SLANG_MINUS; + *is_unary = 1; + break; + + case _SLANG_BCST_PLUSPLUS: + case _SLANG_BCST_POST_PLUSPLUS: + *op = SLANG_PLUS; + *is_unary = 1; + break; + + default: + SLang_verror (SL_NOT_IMPLEMENTED, "Assignment operator not implemented"); + return -1; + } + return 0; +} + +static int +perform_lvalue_operation (unsigned char op_type, SLang_Object_Type *obja_ptr) +{ + switch (op_type) + { + case _SLANG_BCST_ASSIGN: + break; + + /* The order of these is assumed to match the binary operators + * defined in slang.h + */ + case _SLANG_BCST_PLUSEQS: + case _SLANG_BCST_MINUSEQS: + case _SLANG_BCST_TIMESEQS: + case _SLANG_BCST_DIVEQS: + if (-1 == do_assignment_binary (SLANG_PLUS + (op_type - _SLANG_BCST_PLUSEQS), obja_ptr)) + return -1; + break; + + case _SLANG_BCST_BOREQS: + if (-1 == do_assignment_binary (SLANG_BOR, obja_ptr)) + return -1; + break; + + case _SLANG_BCST_BANDEQS: + if (-1 == do_assignment_binary (SLANG_BAND, obja_ptr)) + return -1; + break; + + case _SLANG_BCST_PLUSPLUS: + case _SLANG_BCST_POST_PLUSPLUS: +#if _SLANG_OPTIMIZE_FOR_SPEED + if (obja_ptr->data_type == SLANG_INT_TYPE) + return SLclass_push_int_obj (SLANG_INT_TYPE, obja_ptr->v.int_val + 1); +#endif + if (-1 == do_unary_op (SLANG_PLUSPLUS, obja_ptr, _SLANG_BC_UNARY)) + return -1; + break; + + case _SLANG_BCST_MINUSMINUS: + case _SLANG_BCST_POST_MINUSMINUS: +#if _SLANG_OPTIMIZE_FOR_SPEED + if (obja_ptr->data_type == SLANG_INT_TYPE) + return SLclass_push_int_obj (SLANG_INT_TYPE, obja_ptr->v.int_val - 1); +#endif + if (-1 == do_unary_op (SLANG_MINUSMINUS, obja_ptr, _SLANG_BC_UNARY)) + return -1; + break; + + default: + SLang_Error = SL_INTERNAL_ERROR; + return -1; + } + return 0; +} + +_INLINE_ +static int +set_lvalue_obj (unsigned char op_type, SLang_Object_Type *obja_ptr) +{ + if (op_type != _SLANG_BCST_ASSIGN) + { + if (-1 == perform_lvalue_operation (op_type, obja_ptr)) + return -1; + } +#if _SLANG_OPTIMIZE_FOR_SPEED + if (SLANG_CLASS_TYPE_SCALAR != _SLclass_Class_Type [obja_ptr->data_type]) +#endif + SLang_free_object (obja_ptr); + + return SLang_pop(obja_ptr); +} + +static int +set_struct_lvalue (SLBlock_Type *bc_blk) +{ + int type; + SLang_Class_Type *cl; + char *name; + int op; + + if (-1 == (type = SLang_peek_at_stack ())) + return -1; + + cl = _SLclass_get_class (type); + if ((cl->cl_sput == NULL) + || (cl->cl_sget == NULL)) + { + SLang_verror (SL_NOT_IMPLEMENTED, + "%s does not support structure access", + cl->cl_name); + SLdo_pop_n (2); /* object plus what was to be assigned */ + return -1; + } + name = bc_blk->b.s_blk; + op = bc_blk->bc_sub_type; + + if (op != _SLANG_BCST_ASSIGN) + { + /* We have something like (A.x += b) or (A.x++). In either case, + * we need A.x. + */ + SLang_Object_Type obj_A; + SLang_Object_Type obj; + + if (-1 == SLang_pop (&obj_A)) + return -1; + + if ((-1 == _SLpush_slang_obj (&obj_A)) + || (-1 == cl->cl_sget ((unsigned char) type, name)) + || (-1 == SLang_pop (&obj))) + { + SLang_free_object (&obj_A); + return -1; + } + /* Now the value of A.x is in obj. */ + if (-1 == perform_lvalue_operation (op, &obj)) + { + SLang_free_object (&obj); + SLang_free_object (&obj_A); + return -1; + } + SLang_free_object (&obj); + /* The result of the operation is now on the stack. + * Perform assignment */ + if (-1 == SLang_push (&obj_A)) + { + SLang_free_object (&obj_A); + return -1; + } + } + + return (*cl->cl_sput) ((unsigned char) type, name); +} + +static int make_unit_object (SLang_Object_Type *a, SLang_Object_Type *u) +{ + unsigned char type; + + type = a->data_type; + if (type == SLANG_ARRAY_TYPE) + type = a->v.array_val->data_type; + + u->data_type = type; + switch (type) + { + case SLANG_UCHAR_TYPE: + case SLANG_CHAR_TYPE: + u->v.char_val = 1; + break; + + case SLANG_SHORT_TYPE: + case SLANG_USHORT_TYPE: + u->v.short_val = 1; + break; + + case SLANG_LONG_TYPE: + case SLANG_ULONG_TYPE: + u->v.long_val = 1; + break; + +#if SLANG_HAS_FLOAT + case SLANG_FLOAT_TYPE: + u->v.float_val = 1; + break; + + case SLANG_COMPLEX_TYPE: + u->data_type = SLANG_DOUBLE_TYPE; + case SLANG_DOUBLE_TYPE: + u->v.double_val = 1; + break; +#endif + default: + u->data_type = SLANG_INT_TYPE; + u->v.int_val = 1; + } + return 0; +} + + +/* We want to convert 'A[i] op X' to 'A[i] = A[i] op X'. The code that + * has been generated is: X __args i A __aput-op + * where __aput-op represents this function. We need to generate: + * __args i A __eargs __aget X op __args i A __eargs __aput + * Here, __eargs implies a call to do_bc_call_direct_frame with either + * the aput or aget function. In addition, __args represents a call to + * SLang_start_arg_list. Of course, i represents a set of indices. + * + * Note: If op is an unary operation (e.g., ++ or --), then X will not + * b present an will have to be taken to be 1. + * + * Implementation note: For efficiency, calls to setup the frame, start + * arg list will be omitted and SLang_Num_Function_Args will be set. + * This is ugly but the alternative is much less efficient rendering these + * assignment operators useless. So, the plan is to roll the stack to get X, + * then duplicate the next N values, call __aget followed by op X, finally + * calling __aput. Hence, the sequence is: + * + * start: X i .. j A + * dupN: X i .. j A i .. j A + * __aget: X i .. j A Y + * roll: i .. j A Y X + * op: i .. j A Z + * roll: Z i .. j A + * __aput: + */ +static int +set_array_lvalue (int op) +{ + SLang_Object_Type x, y; + int num_args, is_unary; + + if (-1 == map_assignment_op_to_binary (op, &op, &is_unary)) + return -1; + + /* Grab the indices and the array. Do not start a new frame. */ + if (-1 == SLang_end_arg_list ()) + return -1; + num_args = Next_Function_Num_Args; + Next_Function_Num_Args = 0; + + if (-1 == SLdup_n (num_args)) + return -1; + + SLang_Num_Function_Args = num_args; + if (-1 == _SLarray_aget ()) + return -1; + + if (-1 == SLang_pop (&y)) + return -1; + + if (is_unary == 0) + { + if ((-1 == SLroll_stack (-(num_args + 1))) + || (-1 == SLang_pop (&x))) + { + SLang_free_object (&y); + return -1; + } + } + else if (-1 == make_unit_object (&y, &x)) + { + SLang_free_object (&y); + return -1; + } + + if (-1 == do_binary_ab (op, &y, &x)) + { + SLang_free_object (&y); + SLang_free_object (&x); + return -1; + } +#if _SLANG_OPTIMIZE_FOR_SPEED + if (SLANG_CLASS_TYPE_SCALAR != _SLclass_Class_Type [y.data_type]) +#endif + SLang_free_object (&y); + +#if _SLANG_OPTIMIZE_FOR_SPEED + if (SLANG_CLASS_TYPE_SCALAR != _SLclass_Class_Type [x.data_type]) +#endif + SLang_free_object (&x); + + if (-1 == SLroll_stack (num_args + 1)) + return -1; + + SLang_Num_Function_Args = num_args; + return _SLarray_aput (); +} + + +static int +set_intrin_lvalue (SLBlock_Type *bc_blk) +{ + unsigned char op_type; + SLang_Object_Type obja; + SLang_Class_Type *cl; + SLang_Intrin_Var_Type *ivar; + VOID_STAR intrinsic_addr; + unsigned char intrinsic_type; + + ivar = bc_blk->b.nt_ivar_blk; + + intrinsic_type = ivar->type; + intrinsic_addr = ivar->addr; + + op_type = bc_blk->bc_sub_type; + + cl = _SLclass_get_class (intrinsic_type); + + if (op_type != _SLANG_BCST_ASSIGN) + { + /* We want to get the current value into obja. This is the + * easiest way. + */ + if ((-1 == (*cl->cl_push) (intrinsic_type, intrinsic_addr)) + || (-1 == SLang_pop (&obja))) + return -1; + + (void) perform_lvalue_operation (op_type, &obja); + SLang_free_object (&obja); + + if (SLang_Error) + return -1; + } + + return (*cl->cl_pop) (intrinsic_type, intrinsic_addr); +} + +int _SLang_deref_assign (SLang_Ref_Type *ref) +{ + SLang_Object_Type *objp; + SLang_Name_Type *nt; + SLBlock_Type blk; + + if (ref->is_global == 0) + { + objp = ref->v.local_obj; + if (objp > Local_Variable_Frame) + { + SLang_verror (SL_UNDEFINED_NAME, "Local variable reference is out of scope"); + return -1; + } + return set_lvalue_obj (_SLANG_BCST_ASSIGN, objp); + } + + nt = ref->v.nt; + switch (nt->name_type) + { + case SLANG_GVARIABLE: + case SLANG_PVARIABLE: + if (-1 == set_lvalue_obj (_SLANG_BCST_ASSIGN, + &((SLang_Global_Var_Type *)nt)->obj)) + { + do_name_type_error (nt); + return -1; + } + break; + + case SLANG_IVARIABLE: + blk.b.nt_blk = nt; + blk.bc_sub_type = _SLANG_BCST_ASSIGN; + if (-1 == set_intrin_lvalue (&blk)) + { + do_name_type_error (nt); + return -1; + } + break; + + case SLANG_LVARIABLE: + SLang_Error = SL_INTERNAL_ERROR; + /* set_intrin_lvalue (&blk); */ + return -1; + + case SLANG_RVARIABLE: + default: + SLang_verror (SL_READONLY_ERROR, "deref assignment to %s not allowed", nt->name); + return -1; + } + + return 0; +} + +static void set_deref_lvalue (SLBlock_Type *bc_blk) +{ + SLang_Object_Type *objp; + SLang_Ref_Type *ref; + + switch (bc_blk->bc_sub_type) + { + case SLANG_LVARIABLE: + objp = (Local_Variable_Frame - bc_blk->b.i_blk); + break; + case SLANG_GVARIABLE: + case SLANG_PVARIABLE: + objp = &bc_blk->b.nt_gvar_blk->obj; + break; + default: + SLang_Error = SL_INTERNAL_ERROR; + return; + } + + if (-1 == _SLpush_slang_obj (objp)) + return; + + if (-1 == SLang_pop_ref (&ref)) + return; + (void) _SLang_deref_assign (ref); + SLang_free_ref (ref); +} + +static int push_struct_field (char *name) +{ + int type; + SLang_Class_Type *cl; + + if (-1 == (type = SLang_peek_at_stack ())) + return -1; + + cl = _SLclass_get_class ((unsigned char) type); + if (cl->cl_sget == NULL) + { + SLang_verror (SL_NOT_IMPLEMENTED, + "%s does not permit structure access", + cl->cl_name); + SLdo_pop_n (2); + return -1; + } + + return (*cl->cl_sget) ((unsigned char) type, name); +} + +static void trace_dump (char *format, char *name, SLang_Object_Type *objs, int n, int dir) +{ + unsigned int len; + char prefix [52]; + + len = Trace_Mode - 1; + if (len + 2 >= sizeof (prefix)) + len = sizeof (prefix) - 2; + + SLMEMSET (prefix, ' ', len); + prefix[len] = 0; + + call_dump_routine (prefix); + call_dump_routine (format, name, n); + + if (n > 0) + { + prefix[len] = ' '; + len++; + prefix[len] = 0; + + _SLdump_objects (prefix, objs, n, dir); + } +} + +/* Pop a data item from the stack and return a pointer to it. + * Strings are not freed from stack so use another routine to do it. + */ +static VOID_STAR pop_pointer (SLang_Object_Type *obj, unsigned char type) +{ +#ifndef _SLANG_OPTIMIZE_FOR_SPEED + SLang_Class_Type *cl; +#endif + + SLang_Array_Type *at; + + /* Arrays are special. Allow scalars to automatically convert to arrays. + */ + if (type == SLANG_ARRAY_TYPE) + { + if (-1 == SLang_pop_array (&at, 1)) + return NULL; + obj->data_type = SLANG_ARRAY_TYPE; + return obj->v.ptr_val = (VOID_STAR) at; + } + + if (type == 0) + { + /* This happens when an intrinsic is declared without any information + * regarding parameter types. + */ + if (-1 == SLang_pop (obj)) + return NULL; + type = obj->data_type; + } + else if (-1 == _SLang_pop_object_of_type (type, obj, 0)) + return NULL; + +#if _SLANG_OPTIMIZE_FOR_SPEED + type = _SLclass_Class_Type [type]; +#else + type = _SLclass_get_class (type)->cl_class_type; +#endif + + if (type == SLANG_CLASS_TYPE_SCALAR) + return (VOID_STAR) &obj->v; + else if (type == SLANG_CLASS_TYPE_MMT) + return SLang_object_from_mmt (obj->v.ref); + else + return obj->v.ptr_val; +} + +/* This is ugly. Does anyone have a advice for a cleaner way of doing + * this?? + */ +typedef void (*VF0_Type)(void); +typedef void (*VF1_Type)(VOID_STAR); +typedef void (*VF2_Type)(VOID_STAR, VOID_STAR); +typedef void (*VF3_Type)(VOID_STAR, VOID_STAR, VOID_STAR); +typedef void (*VF4_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR); +typedef void (*VF5_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR); +typedef void (*VF6_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR); +typedef void (*VF7_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR); +typedef long (*LF0_Type)(void); +typedef long (*LF1_Type)(VOID_STAR); +typedef long (*LF2_Type)(VOID_STAR, VOID_STAR); +typedef long (*LF3_Type)(VOID_STAR, VOID_STAR, VOID_STAR); +typedef long (*LF4_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR); +typedef long (*LF5_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR); +typedef long (*LF6_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR); +typedef long (*LF7_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR); +#if SLANG_HAS_FLOAT +typedef double (*FF0_Type)(void); +typedef double (*FF1_Type)(VOID_STAR); +typedef double (*FF2_Type)(VOID_STAR, VOID_STAR); +typedef double (*FF3_Type)(VOID_STAR, VOID_STAR, VOID_STAR); +typedef double (*FF4_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR); +typedef double (*FF5_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR); +typedef double (*FF6_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR); +typedef double (*FF7_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR); +#endif + +static int execute_intrinsic_fun (SLang_Intrin_Fun_Type *objf) +{ +#if SLANG_HAS_FLOAT + double xf; +#endif + VOID_STAR p[SLANG_MAX_INTRIN_ARGS]; + SLang_Object_Type objs[SLANG_MAX_INTRIN_ARGS]; + long ret; + unsigned char type; + unsigned int argc; + unsigned int i; + FVOID_STAR fptr; + unsigned char *arg_types; + int stk_depth; + + fptr = objf->i_fun; + argc = objf->num_args; + type = objf->return_type; + arg_types = objf->arg_types; + + if (argc > SLANG_MAX_INTRIN_ARGS) + { + SLang_verror(SL_APPLICATION_ERROR, + "Intrinsic function %s requires too many parameters", objf->name); + return -1; + } + + if (-1 == _SL_increment_frame_pointer ()) + return -1; + + stk_depth = -1; + if (Trace_Mode && (_SLang_Trace > 0)) + { + int nargs; + + stk_depth = _SLstack_depth (); + + nargs = SLang_Num_Function_Args; + if (nargs == 0) + nargs = (int)argc; + + stk_depth -= nargs; + + if (stk_depth >= 0) + trace_dump (">>%s (%d args)\n", + objf->name, + _SLStack_Pointer - nargs, + nargs, + 1); + } + + i = argc; + while (i != 0) + { + i--; + if (NULL == (p[i] = pop_pointer (objs + i, arg_types[i]))) + { + i++; + goto free_and_return; + } + } + + ret = 0; +#if SLANG_HAS_FLOAT + xf = 0.0; +#endif + + switch (argc) + { + case 0: + if (type == SLANG_VOID_TYPE) ((VF0_Type) fptr) (); +#if SLANG_HAS_FLOAT + else if (type == SLANG_DOUBLE_TYPE) xf = ((FF0_Type) fptr)(); +#endif + else ret = ((LF0_Type) fptr)(); + break; + + case 1: + if (type == SLANG_VOID_TYPE) ((VF1_Type) fptr)(p[0]); +#if SLANG_HAS_FLOAT + else if (type == SLANG_DOUBLE_TYPE) xf = ((FF1_Type) fptr)(p[0]); +#endif + else ret = ((LF1_Type) fptr)(p[0]); + break; + + case 2: + if (type == SLANG_VOID_TYPE) ((VF2_Type) fptr)(p[0], p[1]); +#if SLANG_HAS_FLOAT + else if (type == SLANG_DOUBLE_TYPE) xf = ((FF2_Type) fptr)(p[0], p[1]); +#endif + else ret = ((LF2_Type) fptr)(p[0], p[1]); + break; + + case 3: + if (type == SLANG_VOID_TYPE) ((VF3_Type) fptr)(p[0], p[1], p[2]); +#if SLANG_HAS_FLOAT + else if (type == SLANG_DOUBLE_TYPE) xf = ((FF3_Type) fptr)(p[0], p[1], p[2]); +#endif + else ret = ((LF3_Type) fptr)(p[0], p[1], p[2]); + break; + + case 4: + if (type == SLANG_VOID_TYPE) ((VF4_Type) fptr)(p[0], p[1], p[2], p[3]); +#if SLANG_HAS_FLOAT + else if (type == SLANG_DOUBLE_TYPE) xf = ((FF4_Type) fptr)(p[0], p[1], p[2], p[3]); +#endif + else ret = ((LF4_Type) fptr)(p[0], p[1], p[2], p[3]); + break; + + case 5: + if (type == SLANG_VOID_TYPE) ((VF5_Type) fptr)(p[0], p[1], p[2], p[3], p[4]); +#if SLANG_HAS_FLOAT + else if (type == SLANG_DOUBLE_TYPE) xf = ((FF5_Type) fptr)(p[0], p[1], p[2], p[3], p[4]); +#endif + else ret = ((LF5_Type) fptr)(p[0], p[1], p[2], p[3], p[4]); + break; + + case 6: + if (type == SLANG_VOID_TYPE) ((VF6_Type) fptr)(p[0], p[1], p[2], p[3], p[4], p[5]); +#if SLANG_HAS_FLOAT + else if (type == SLANG_DOUBLE_TYPE) xf = ((FF6_Type) fptr)(p[0], p[1], p[2], p[3], p[4], p[5]); +#endif + else ret = ((LF6_Type) fptr)(p[0], p[1], p[2], p[3], p[4], p[5]); + break; + + case 7: + if (type == SLANG_VOID_TYPE) ((VF7_Type) fptr)(p[0], p[1], p[2], p[3], p[4], p[5], p[6]); +#if SLANG_HAS_FLOAT + else if (type == SLANG_DOUBLE_TYPE) xf = ((FF7_Type) fptr)(p[0], p[1], p[2], p[3], p[4], p[5], p[6]); +#endif + else ret = ((LF7_Type) fptr)(p[0], p[1], p[2], p[3], p[4], p[5], p[6]); + break; + } + + switch (type) + { + case SLANG_VOID_TYPE: + break; + +#if SLANG_HAS_FLOAT + case SLANG_DOUBLE_TYPE: + (void) SLang_push_double (xf); + break; +#endif + case SLANG_UINT_TYPE: + case SLANG_INT_TYPE: (void) SLclass_push_int_obj (type, (int) ret); + break; + + case SLANG_CHAR_TYPE: + case SLANG_UCHAR_TYPE: (void) SLclass_push_char_obj (type, (char) ret); + break; + + case SLANG_SHORT_TYPE: + case SLANG_USHORT_TYPE: (void) SLclass_push_short_obj (type, (short) ret); + break; + + case SLANG_LONG_TYPE: + case SLANG_ULONG_TYPE: (void) SLclass_push_long_obj (type, ret); + break; + + case SLANG_STRING_TYPE: + if (NULL == (char *)ret) + { + if (SLang_Error == 0) SLang_Error = SL_INTRINSIC_ERROR; + } + else (void) SLang_push_string ((char *)ret); + break; + + default: + SLang_verror (SL_NOT_IMPLEMENTED, + "Support for intrinsic functions returning %s is not provided", + SLclass_get_datatype_name (type)); + } + + if (stk_depth >= 0) + { + stk_depth = _SLstack_depth () - stk_depth; + + trace_dump ("<<%s (returning %d values)\n", + objf->name, + _SLStack_Pointer - stk_depth, + stk_depth, + 1); + } + + free_and_return: + while (i < argc) + { + SLang_free_object (objs + i); + i++; + } + + return _SL_decrement_frame_pointer (); +} + +static int inner_interp(register SLBlock_Type *); + +/* Switch_Obj_Ptr points to the NEXT available free switch object */ +static SLang_Object_Type Switch_Objects[SLANG_MAX_NESTED_SWITCH]; +static SLang_Object_Type *Switch_Obj_Ptr = Switch_Objects; +static SLang_Object_Type *Switch_Obj_Max = Switch_Objects + SLANG_MAX_NESTED_SWITCH; + +static void +lang_do_loops (unsigned char stype, SLBlock_Type *block, unsigned int num_blocks) +{ + int i, ctrl; + int first, last; + SLBlock_Type *blks[4]; + char *loop_name; + SLang_Foreach_Context_Type *foreach_context; + SLang_Class_Type *cl; + int type; + unsigned int j; + + j = 0; + for (i = 0; i < (int) num_blocks; i++) + { + if (block[i].bc_main_type != _SLANG_BC_BLOCK) + { + if (block[i].bc_main_type == _SLANG_BC_LINE_NUM) + continue; + + SLang_verror (SL_SYNTAX_ERROR, "Bytecode is not a looping block"); + return; + } + blks[j] = block[i].b.blk; + j++; + } + + num_blocks = j; + block = blks[0]; + + switch (stype) + { + case _SLANG_BCST_FOREACH: + loop_name = "foreach"; + if (num_blocks != 1) + goto wrong_num_blocks_error; + + /* We should find Next_Function_Num_Args + 1 items on the stack. + * The first Next_Function_Num_Args items represent the arguments to + * to USING. The last item (deepest in stack) is the object to loop + * over. So, roll the stack up and grab it. + */ + if ((-1 == SLroll_stack (-(Next_Function_Num_Args + 1))) + || (-1 == (type = SLang_peek_at_stack ()))) + goto return_error; + + cl = _SLclass_get_class ((unsigned char) type); + if ((cl->cl_foreach == NULL) + || (cl->cl_foreach_open == NULL) + || (cl->cl_foreach_close == NULL)) + { + SLang_verror (SL_NOT_IMPLEMENTED, "%s does not permit foreach", cl->cl_name); + SLdo_pop_n (Next_Function_Num_Args + 1); + goto return_error; + } + + if (NULL == (foreach_context = (*cl->cl_foreach_open) ((unsigned char)type, Next_Function_Num_Args))) + goto return_error; + + while (1) + { + int status; + + if (SLang_Error) + { + (*cl->cl_foreach_close) ((unsigned char) type, foreach_context); + goto return_error; + } + + status = (*cl->cl_foreach) ((unsigned char) type, foreach_context); + if (status <= 0) + { + if (status == 0) + break; + + (*cl->cl_foreach_close) ((unsigned char) type, foreach_context); + goto return_error; + } + + inner_interp (block); + if (Lang_Break) break; + Lang_Break_Condition = /* Lang_Continue = */ 0; + } + (*cl->cl_foreach_close) ((unsigned char) type, foreach_context); + break; + + case _SLANG_BCST_WHILE: + loop_name = "while"; + + if (num_blocks != 2) + goto wrong_num_blocks_error; + + type = blks[1]->bc_main_type; + while (1) + { + if (SLang_Error) + goto return_error; + + inner_interp (block); + if (Lang_Break) break; + + if (-1 == pop_ctrl_integer (&ctrl)) + goto return_error; + + if (ctrl == 0) break; + + if (type) + { + inner_interp (blks[1]); + if (Lang_Break) break; + Lang_Break_Condition = /* Lang_Continue = */ 0; + } + } + break; + + case _SLANG_BCST_DOWHILE: + loop_name = "do...while"; + + if (num_blocks != 2) + goto wrong_num_blocks_error; + + while (1) + { + if (SLang_Error) + goto return_error; + + Lang_Break_Condition = /* Lang_Continue = */ 0; + inner_interp (block); + if (Lang_Break) break; + Lang_Break_Condition = /* Lang_Continue = */ 0; + inner_interp (blks[1]); + if (-1 == pop_ctrl_integer (&ctrl)) + goto return_error; + + if (ctrl == 0) break; + } + break; + + case _SLANG_BCST_CFOR: + loop_name = "for"; + + /* we need 4 blocks: first 3 control, the last is code */ + if (num_blocks != 4) goto wrong_num_blocks_error; + + inner_interp (block); + while (1) + { + if (SLang_Error) + goto return_error; + + inner_interp(blks[1]); /* test */ + if (-1 == pop_ctrl_integer (&ctrl)) + goto return_error; + + if (ctrl == 0) break; + inner_interp(blks[3]); /* code */ + if (Lang_Break) break; + inner_interp(blks[2]); /* bump */ + Lang_Break_Condition = /* Lang_Continue = */ 0; + } + break; + + case _SLANG_BCST_FOR: + loop_name = "_for"; + + if (num_blocks != 1) + goto wrong_num_blocks_error; + + /* 3 elements: first, last, step */ + if ((-1 == SLang_pop_integer (&ctrl)) + || (-1 == SLang_pop_integer (&last)) + || (-1 == SLang_pop_integer (&first))) + goto return_error; + + i = first; + while (1) + { + /* It is ugly to have this test here but I do not know of a + * simple way to do this without using two while loops. + */ + if (ctrl >= 0) + { + if (i > last) break; + } + else if (i < last) break; + + if (SLang_Error) goto return_error; + + SLclass_push_int_obj (SLANG_INT_TYPE, i); + inner_interp (block); + if (Lang_Break) break; + Lang_Break_Condition = /* Lang_Continue = */ 0; + + i += ctrl; + } + break; + + case _SLANG_BCST_LOOP: + loop_name = "loop"; + if (num_blocks != 1) + goto wrong_num_blocks_error; + + if (-1 == SLang_pop_integer (&ctrl)) + goto return_error; + while (ctrl > 0) + { + ctrl--; + + if (SLang_Error) + goto return_error; + + inner_interp (block); + if (Lang_Break) break; + Lang_Break_Condition = /* Lang_Continue = */ 0; + } + break; + + case _SLANG_BCST_FOREVER: + loop_name = "forever"; + + if (num_blocks != 1) + goto wrong_num_blocks_error; + + while (1) + { + if (SLang_Error) + goto return_error; + + inner_interp (block); + if (Lang_Break) break; + Lang_Break_Condition = /* Lang_Continue = */ 0; + } + break; + + default: SLang_verror(SL_INTERNAL_ERROR, "Unknown loop type"); + return; + } + Lang_Break = /* Lang_Continue = */ 0; + Lang_Break_Condition = Lang_Return; + return; + + wrong_num_blocks_error: + SLang_verror (SL_SYNTAX_ERROR, "Wrong number of blocks for '%s' construct", loop_name); + + /* drop */ + return_error: + do_traceback (loop_name, 0, NULL); +} + +static void lang_do_and_orelse (unsigned char stype, SLBlock_Type *addr, SLBlock_Type *addr_max) +{ + int test = 0; + int is_or; + + is_or = (stype == _SLANG_BCST_ORELSE); + + while (addr <= addr_max) + { + if (addr->bc_main_type == _SLANG_BC_LINE_NUM) + { + addr++; + continue; + } + + inner_interp (addr->b.blk); + if (SLang_Error + || Lang_Break_Condition + || (-1 == pop_ctrl_integer (&test))) + return; + + if (is_or == (test != 0)) + break; + + /* if (((stype == _SLANG_BCST_ANDELSE) && (test == 0)) + * || ((stype == _SLANG_BCST_ORELSE) && test)) + * break; + */ + + addr++; + } + SLclass_push_int_obj (SLANG_INT_TYPE, test); +} + +static void do_else_if (SLBlock_Type *zero_block, SLBlock_Type *non_zero_block) +{ + int test; + + if (-1 == pop_ctrl_integer (&test)) + return; + + if (test == 0) + non_zero_block = zero_block; + + if (non_zero_block != NULL) + inner_interp (non_zero_block->b.blk); +} + +int _SLang_trace_fun (char *f) +{ + if (NULL == (f = SLang_create_slstring (f))) + return -1; + + SLang_free_slstring (Trace_Function); + Trace_Function = f; + _SLang_Trace = 1; + return 0; +} + +int _SLdump_objects (char *prefix, SLang_Object_Type *x, unsigned int n, int dir) +{ + char *s; + SLang_Class_Type *cl; + + while (n) + { + cl = _SLclass_get_class (x->data_type); + + if (NULL == (s = _SLstringize_object (x))) + s = "??"; + + call_dump_routine ("%s[%s]:%s\n", prefix, cl->cl_name, s); + + SLang_free_slstring (s); + + x += dir; + n--; + } + return 0; +} + +static SLBlock_Type *Exit_Block_Ptr; +static SLBlock_Type *Global_User_Block[5]; +static SLBlock_Type **User_Block_Ptr = Global_User_Block; +char *_SLang_Current_Function_Name = NULL; + +static int execute_slang_fun (_SLang_Function_Type *fun) +{ + register unsigned int i; + register SLang_Object_Type *frame, *lvf; + register unsigned int n_locals; + _SLBlock_Header_Type *header; + /* SLBlock_Type *val; */ + SLBlock_Type *exit_block_save; + SLBlock_Type **user_block_save; + SLBlock_Type *user_blocks[5]; + char *save_fname; + + exit_block_save = Exit_Block_Ptr; + user_block_save = User_Block_Ptr; + User_Block_Ptr = user_blocks; + *(user_blocks) = NULL; + *(user_blocks + 1) = NULL; + *(user_blocks + 2) = NULL; + *(user_blocks + 3) = NULL; + *(user_blocks + 4) = NULL; + + Exit_Block_Ptr = NULL; + + save_fname = _SLang_Current_Function_Name; + _SLang_Current_Function_Name = fun->name; + + _SL_increment_frame_pointer (); + + /* need loaded? */ + if (fun->nlocals == AUTOLOAD_NUM_LOCALS) + { + header = NULL; + if (-1 == SLang_load_file(fun->v.autoload_filename)) + goto the_return; + + if (fun->nlocals == AUTOLOAD_NUM_LOCALS) + { + SLang_verror (SL_UNDEFINED_NAME, "%s: Function did not autoload", + _SLang_Current_Function_Name); + goto the_return; + } + } + + n_locals = fun->nlocals; + + /* let the error propagate through since it will do no harm + and allow us to restore stack. */ + + /* set new stack frame */ + lvf = frame = Local_Variable_Frame; + i = n_locals; + if ((lvf + i) > Local_Variable_Stack + SLANG_MAX_LOCAL_STACK) + { + SLang_verror(SL_STACK_OVERFLOW, "%s: Local Variable Stack Overflow", + _SLang_Current_Function_Name); + goto the_return; + } + + /* Make sure we do not allow this header to get destroyed by something + * like: define crash () { eval ("define crash ();") } + */ + header = fun->v.header; + header->num_refs++; + + while (i--) + { + lvf++; + lvf->data_type = SLANG_UNDEFINED_TYPE; + } + Local_Variable_Frame = lvf; + + /* read values of function arguments */ + i = fun->nargs; + while (i > 0) + { + i--; + (void) SLang_pop (Local_Variable_Frame - i); + } + + if (SLang_Enter_Function != NULL) (*SLang_Enter_Function)(_SLang_Current_Function_Name); + + if (_SLang_Trace) + { + int stack_depth; + + stack_depth = _SLstack_depth (); + + if ((Trace_Function != NULL) + && (0 == strcmp (Trace_Function, _SLang_Current_Function_Name)) + && (Trace_Mode == 0)) + Trace_Mode = 1; + + if (Trace_Mode) + { + /* The local variable frame grows backwards */ + trace_dump (">>%s (%d args)\n", + _SLang_Current_Function_Name, + Local_Variable_Frame, + (int) fun->nargs, + -1); + Trace_Mode++; + } + + inner_interp (header->body); + Lang_Break_Condition = Lang_Return = Lang_Break = 0; + if (Exit_Block_Ptr != NULL) inner_interp(Exit_Block_Ptr); + + if (Trace_Mode) + { + Trace_Mode--; + stack_depth = _SLstack_depth () - stack_depth; + + trace_dump ("<<%s (returning %d values)\n", + _SLang_Current_Function_Name, + _SLStack_Pointer - stack_depth, + stack_depth, + 1); + + if (Trace_Mode == 1) + Trace_Mode = 0; + } + } + else + { + inner_interp (header->body); + Lang_Break_Condition = Lang_Return = Lang_Break = 0; + if (Exit_Block_Ptr != NULL) inner_interp(Exit_Block_Ptr); + } + + if (SLang_Exit_Function != NULL) (*SLang_Exit_Function)(_SLang_Current_Function_Name); + + if (SLang_Error) + do_traceback(fun->name, n_locals, +#if _SLANG_HAS_DEBUG_CODE + fun->file +#else + NULL +#endif + ); + + /* free local variables.... */ + lvf = Local_Variable_Frame; + while (lvf > frame) + { +#if _SLANG_OPTIMIZE_FOR_SPEED + if (SLANG_CLASS_TYPE_SCALAR != _SLclass_Class_Type [lvf->data_type]) +#endif + SLang_free_object (lvf); + lvf--; + } + Local_Variable_Frame = lvf; + + if (header->num_refs == 1) + free_function_header (header); + else + header->num_refs--; + + the_return: + + Lang_Break_Condition = Lang_Return = Lang_Break = 0; + Exit_Block_Ptr = exit_block_save; + User_Block_Ptr = user_block_save; + _SLang_Current_Function_Name = save_fname; + _SL_decrement_frame_pointer (); + + if (SLang_Error) + return -1; + + return 0; +} + +static void do_traceback (char *name, unsigned int locals, char *file) +{ + char *s; + unsigned int i; + SLang_Object_Type *objp; + unsigned short stype; + + /* FIXME: Priority=low + * I need to make this configurable!!! That is, let the + * application decide whether or not a usage error should result in a + * traceback. + */ + if (SLang_Error == SL_USAGE_ERROR) + return; + + if (SLang_Traceback == 0) + return; + + call_dump_routine ("S-Lang Traceback: %s\n", name); + if (SLang_Traceback < 0) + return; + + if (file != NULL) + call_dump_routine ("File: %s\n", file); + + if (locals == 0) + return; + + call_dump_routine (" Local Variables:\n"); + + for (i = 0; i < locals; i++) + { + SLang_Class_Type *cl; + char *class_name; + + objp = Local_Variable_Frame - i; + stype = objp->data_type; + + s = _SLstringize_object (objp); + cl = _SLclass_get_class (stype); + class_name = cl->cl_name; + + call_dump_routine ("\t$%d: Type: %s,\tValue:\t", i, class_name); + + if (s == NULL) call_dump_routine("??\n"); + else + { + char *q = ""; +#ifndef HAVE_VSNPRINTF + char buf[256]; + if (strlen (s) >= sizeof (buf)) + { + strncpy (buf, s, sizeof(buf)); + s = buf; + s[sizeof(buf) - 1] = 0; + } +#endif + if (SLANG_STRING_TYPE == stype) q = "\""; + call_dump_routine ("%s%s%s\n", q, s, q); + } + } +} + +static void do_app_unary (SLang_App_Unary_Type *nt) +{ + if (-1 == do_unary (nt->unary_op, nt->name_type)) + do_traceback (nt->name, 0, NULL); +} + +static int inner_interp_nametype (SLang_Name_Type *nt) +{ + SLBlock_Type bc_blks[2]; + + bc_blks[0].b.nt_blk = nt; + bc_blks[0].bc_main_type = nt->name_type; + bc_blks[1].bc_main_type = 0; + return inner_interp(bc_blks); +} + +int _SLang_dereference_ref (SLang_Ref_Type *ref) +{ + if (ref == NULL) + { + SLang_Error = SL_INTERNAL_ERROR; + return -1; + } + + if (ref->is_global == 0) + { + SLang_Object_Type *obj = ref->v.local_obj; + if (obj > Local_Variable_Frame) + { + SLang_verror (SL_UNDEFINED_NAME, "Local variable deref is out of scope"); + return -1; + } + return _SLpush_slang_obj (ref->v.local_obj); + } + + (void) inner_interp_nametype (ref->v.nt); + return 0; +} + +int _SLang_is_ref_initialized (SLang_Ref_Type *ref) +{ + unsigned char type; + + if (ref == NULL) + { + SLang_Error = SL_INTERNAL_ERROR; + return -1; + } + + if (ref->is_global == 0) + { + SLang_Object_Type *obj = ref->v.local_obj; + if (obj > Local_Variable_Frame) + { + SLang_verror (SL_UNDEFINED_NAME, "Local variable deref is out of scope"); + return -1; + } + type = ref->v.local_obj->data_type; + } + else + { + SLang_Name_Type *nt = ref->v.nt; + if ((nt->name_type != SLANG_GVARIABLE) + && (nt->name_type != SLANG_PVARIABLE)) + return 1; + type = ((SLang_Global_Var_Type *)nt)->obj.data_type; + } + return type != SLANG_UNDEFINED_TYPE; +} + +int _SLang_uninitialize_ref (SLang_Ref_Type *ref) +{ + SLang_Object_Type *obj; + + if (ref == NULL) + { + SLang_Error = SL_INTERNAL_ERROR; + return -1; + } + + if (ref->is_global == 0) + { + obj = ref->v.local_obj; + if (obj > Local_Variable_Frame) + { + SLang_verror (SL_UNDEFINED_NAME, "Local variable deref is out of scope"); + return -1; + } + obj = ref->v.local_obj; + } + else + { + SLang_Name_Type *nt = ref->v.nt; + if ((nt->name_type != SLANG_GVARIABLE) + && (nt->name_type != SLANG_PVARIABLE)) + return -1; + obj = &((SLang_Global_Var_Type *)nt)->obj; + } + SLang_free_object (obj); + obj->data_type = SLANG_UNDEFINED_TYPE; + obj->v.ptr_val = NULL; + return 0; +} + +void (*SLang_Interrupt)(void); +static int Last_Error; +void (*SLang_User_Clear_Error)(void); +void _SLang_clear_error (void) +{ + if (Last_Error <= 0) + { + Last_Error = 0; + return; + } + Last_Error--; + if (SLang_User_Clear_Error != NULL) (*SLang_User_Clear_Error)(); +} + +int _SLpush_slang_obj (SLang_Object_Type *obj) +{ + unsigned char subtype; + SLang_Class_Type *cl; + + if (obj == NULL) return SLang_push_null (); + + subtype = obj->data_type; + +#if _SLANG_OPTIMIZE_FOR_SPEED + if (SLANG_CLASS_TYPE_SCALAR == _SLclass_Class_Type[subtype]) + return SLang_push (obj); +#endif + + cl = _SLclass_get_class (subtype); + return (*cl->cl_push) (subtype, (VOID_STAR) &obj->v); +} + +_INLINE_ +static int push_local_variable (int i) +{ + SLang_Class_Type *cl; + SLang_Object_Type *obj; + unsigned char subtype; + + obj = Local_Variable_Frame - i; + subtype = obj->data_type; + +#if _SLANG_OPTIMIZE_FOR_SPEED + if (SLANG_CLASS_TYPE_SCALAR == _SLclass_Class_Type[subtype]) + return SLang_push (obj); + if (subtype == SLANG_STRING_TYPE) + return _SLang_dup_and_push_slstring (obj->v.s_val); +#endif + + cl = _SLclass_get_class (subtype); + return (*cl->cl_push) (subtype, (VOID_STAR) &obj->v); +} + +static int push_intrinsic_variable (SLang_Intrin_Var_Type *ivar) +{ + SLang_Class_Type *cl; + unsigned char stype; + + stype = ivar->type; + cl = _SLclass_get_class (stype); + + if (-1 == (*cl->cl_push_intrinsic) (stype, ivar->addr)) + { + do_name_type_error ((SLang_Name_Type *) ivar); + return -1; + } + return 0; +} + +static int dereference_object (void) +{ + SLang_Object_Type obj; + SLang_Class_Type *cl; + unsigned char type; + int ret; + + if (-1 == SLang_pop (&obj)) + return -1; + + type = obj.data_type; + + cl = _SLclass_get_class (type); + ret = (*cl->cl_dereference)(type, (VOID_STAR) &obj.v); + + SLang_free_object (&obj); + return ret; +} + +static int case_function (void) +{ + unsigned char type; + SLang_Object_Type obj; + SLang_Object_Type *swobjptr; + + swobjptr = Switch_Obj_Ptr - 1; + + if ((swobjptr < Switch_Objects) + || (0 == (type = swobjptr->data_type))) + { + SLang_verror (SL_SYNTAX_ERROR, "Misplaced 'case' keyword"); + return -1; + } + + if (-1 == SLang_pop (&obj)) + return -1; + + if (obj.data_type != type) + { + SLang_Class_Type *a_cl, *b_cl; + + a_cl = _SLclass_get_class (obj.data_type); + b_cl = _SLclass_get_class (type); + + if (NULL == _SLclass_get_binary_fun (SLANG_EQ, a_cl, b_cl, &a_cl, 0)) + { + (void) SLclass_push_int_obj (SLANG_INT_TYPE, 0); + SLang_free_object (&obj); + return 0; + } + } + + (void) do_binary_ab (SLANG_EQ, swobjptr, &obj); + SLang_free_object (&obj); + return 0; +} + +static void tmp_variable_function (SLBlock_Type *addr) +{ + SLang_Object_Type *obj; + + switch (addr->bc_sub_type) + { + case SLANG_GVARIABLE: + case SLANG_PVARIABLE: + obj = &addr->b.nt_gvar_blk->obj; + break; + + case SLANG_LVARIABLE: + obj = Local_Variable_Frame - addr->b.i_blk; + break; + + default: + SLang_Error = SL_INTERNAL_ERROR; + return; + } + + /* There is no need to go through higher level routines since we are + * not creating or destroying extra copies. + */ + if (-1 == SLang_push (obj)) + return; + + obj->data_type = SLANG_UNDEFINED_TYPE; + obj->v.ptr_val = NULL; +} + + +static int +do_inner_interp_error (SLBlock_Type *err_block, + SLBlock_Type *addr_start, + SLBlock_Type *addr) +{ + int save_err, slerr; + + /* Someday I can use the these variable to provide extra information + * about what went wrong. + */ + (void) addr_start; + (void) addr; + + if (err_block == NULL) + goto return_error; + + if (SLang_Error < 0) /* errors less than 0 are severe */ + goto return_error; + + save_err = Last_Error++; + slerr = SLang_Error; + SLang_Error = 0; + inner_interp (err_block->b.blk); + + if (Last_Error <= save_err) + { + /* Caught error and cleared it */ + Last_Error = save_err; + if ((Lang_Break_Condition == 0) + /* An error may have cleared the error and then caused the + * function to return. We will allow that but let's not allow + * 'break' nor 'continue' statements until later. + */ + || Lang_Return) + return 0; + + /* drop--- either a break or continue was called */ + } + + Last_Error = save_err; + SLang_Error = slerr; + + return_error: +#if _SLANG_HAS_DEBUG_CODE + while (addr >= addr_start) + { + if (addr->bc_main_type == _SLANG_BC_LINE_NUM) + { + char buf[256]; + sprintf (buf, "(Error occurred on line %lu)", addr->b.l_blk); + do_traceback (buf, 0, NULL); + break; + } + /* Special hack for 16 bit systems to prevent pointer wrapping. */ +#if defined(__16_BIT_SYSTEM__) + if (addr == addr_start) + break; +#endif + addr--; + } +#endif + return -1; +} + + +#define GATHER_STATISTICS 0 +#if GATHER_STATISTICS +static unsigned int Bytecodes[0xFFFF]; + +static void print_stats (void) +{ + unsigned int i; + unsigned long total; + FILE *fp = fopen ("stats.txt", "w"); + if (fp == NULL) + return; + + total = 0; + for (i = 0; i < 0xFFFF; i++) + total += Bytecodes[i]; + + if (total == 0) + total = 1; + + for (i = 0; i < 0xFFFF; i++) + { + if (Bytecodes[i]) + fprintf (fp, "0x%04X %9u %e\n", i, Bytecodes[i], Bytecodes[i]/(double) total); + } + fclose (fp); +} + +static void add_to_statistics (SLBlock_Type *b) +{ + unsigned short x, y; + + x = b->bc_main_type; + if (x == 0) + { + Bytecodes[0] += 1; + return; + } + b++; + y = b->bc_main_type; + + Bytecodes[(x << 8) | y] += 1; +} + +#endif + +/* inner interpreter */ +/* The return value from this function is only meaningful when it is used + * to process blocks for the switch statement. If it returns 0, the calling + * routine should pass the next block to it. Otherwise it will + * return non-zero, with or without error. + */ +static int inner_interp (SLBlock_Type *addr_start) +{ + SLBlock_Type *block, *err_block, *addr; +#if GATHER_STATISTICS + static int inited = 0; + + if (inited == 0) + { + (void) SLang_add_cleanup_function (print_stats); + inited = 1; + } +#endif + + /* for systems that have no real interrupt facility (e.g. go32 on dos) */ + if (SLang_Interrupt != NULL) (*SLang_Interrupt)(); + + block = err_block = NULL; + addr = addr_start; + +#if GATHER_STATISTICS + add_to_statistics (addr); +#endif + while (1) + { + switch (addr->bc_main_type) + { + case 0: + return 1; + case _SLANG_BC_LVARIABLE: + push_local_variable (addr->b.i_blk); + break; + case _SLANG_BC_GVARIABLE: + if (-1 == _SLpush_slang_obj (&addr->b.nt_gvar_blk->obj)) + do_name_type_error (addr->b.nt_blk); + break; + + case _SLANG_BC_IVARIABLE: + case _SLANG_BC_RVARIABLE: + push_intrinsic_variable (addr->b.nt_ivar_blk); + break; + + case _SLANG_BC_INTRINSIC: + execute_intrinsic_fun (addr->b.nt_ifun_blk); + if (SLang_Error) + do_traceback(addr->b.nt_ifun_blk->name, 0, NULL); + break; + + case _SLANG_BC_FUNCTION: + execute_slang_fun (addr->b.nt_fun_blk); + if (Lang_Break_Condition) goto handle_break_condition; + break; + + case _SLANG_BC_MATH_UNARY: + case _SLANG_BC_APP_UNARY: + /* Make sure we treat these like function calls since the + * parser took sin(x) to be a function call. + */ + if (0 == _SL_increment_frame_pointer ()) + { + do_app_unary (addr->b.nt_unary_blk); + (void) _SL_decrement_frame_pointer (); + } + break; + + case _SLANG_BC_ICONST: + SLclass_push_int_obj (SLANG_INT_TYPE, addr->b.iconst_blk->i); + break; + +#if SLANG_HAS_FLOAT + case _SLANG_BC_DCONST: + SLang_push_double (addr->b.dconst_blk->d); + break; +#endif + + case _SLANG_BC_PVARIABLE: + if (-1 == _SLpush_slang_obj (&addr->b.nt_gvar_blk->obj)) + do_name_type_error (addr->b.nt_blk); + break; + + case _SLANG_BC_PFUNCTION: + execute_slang_fun (addr->b.nt_fun_blk); + if (Lang_Break_Condition) goto handle_break_condition; + break; + + case _SLANG_BC_BINARY: + do_binary (addr->b.i_blk); + break; + + case _SLANG_BC_LITERAL: +#if !_SLANG_OPTIMIZE_FOR_SPEED + case _SLANG_BC_LITERAL_INT: + case _SLANG_BC_LITERAL_STR: +#endif + { + SLang_Class_Type *cl = _SLclass_get_class (addr->bc_sub_type); + (*cl->cl_push_literal) (addr->bc_sub_type, (VOID_STAR) &addr->b.ptr_blk); + } + break; +#if _SLANG_OPTIMIZE_FOR_SPEED + case _SLANG_BC_LITERAL_INT: + SLclass_push_int_obj (addr->bc_sub_type, (int) addr->b.l_blk); + break; + + case _SLANG_BC_LITERAL_STR: + _SLang_dup_and_push_slstring (addr->b.s_blk); + break; +#endif + case _SLANG_BC_BLOCK: + switch (addr->bc_sub_type) + { + case _SLANG_BCST_ERROR_BLOCK: + err_block = addr; + break; + + case _SLANG_BCST_EXIT_BLOCK: + Exit_Block_Ptr = addr->b.blk; + break; + + case _SLANG_BCST_USER_BLOCK0: + case _SLANG_BCST_USER_BLOCK1: + case _SLANG_BCST_USER_BLOCK2: + case _SLANG_BCST_USER_BLOCK3: + case _SLANG_BCST_USER_BLOCK4: + User_Block_Ptr[addr->bc_sub_type - _SLANG_BCST_USER_BLOCK0] = addr->b.blk; + break; + + case _SLANG_BCST_LOOP: + case _SLANG_BCST_WHILE: + case _SLANG_BCST_FOR: + case _SLANG_BCST_FOREVER: + case _SLANG_BCST_CFOR: + case _SLANG_BCST_DOWHILE: + case _SLANG_BCST_FOREACH: + if (block == NULL) block = addr; + lang_do_loops(addr->bc_sub_type, block, 1 + (unsigned int) (addr - block)); + block = NULL; + break; + + case _SLANG_BCST_IFNOT: +#if _SLANG_OPTIMIZE_FOR_SPEED + { + int i; + + if ((0 == pop_ctrl_integer (&i)) && (i == 0)) + inner_interp (addr->b.blk); + } +#else + do_else_if (addr, NULL); +#endif + break; + + case _SLANG_BCST_IF: +#if _SLANG_OPTIMIZE_FOR_SPEED + { + int i; + + if ((0 == pop_ctrl_integer (&i)) && i) + inner_interp (addr->b.blk); + } +#else + do_else_if (NULL, addr); +#endif + break; + + case _SLANG_BCST_NOTELSE: + do_else_if (block, addr); + block = NULL; + break; + + case _SLANG_BCST_ELSE: + do_else_if (addr, block); + block = NULL; + break; + + case _SLANG_BCST_SWITCH: + if (Switch_Obj_Ptr == Switch_Obj_Max) + { + SLang_doerror("switch nesting too deep"); + break; + } + (void) SLang_pop (Switch_Obj_Ptr); + Switch_Obj_Ptr++; + + if (block == NULL) block = addr; + while ((SLang_Error == 0) + && (block <= addr) + && (Lang_Break_Condition == 0) + && (0 == inner_interp (block->b.blk))) + block++; + Switch_Obj_Ptr--; + SLang_free_object (Switch_Obj_Ptr); + Switch_Obj_Ptr->data_type = 0; + block = NULL; + break; + + case _SLANG_BCST_ANDELSE: + case _SLANG_BCST_ORELSE: + if (block == NULL) block = addr; + lang_do_and_orelse (addr->bc_sub_type, block, addr); + block = NULL; + break; + + default: + if (block == NULL) block = addr; + break; + } + if (Lang_Break_Condition) goto handle_break_condition; + break; + + case _SLANG_BC_RETURN: + Lang_Break_Condition = Lang_Return = Lang_Break = 1; return 1; + case _SLANG_BC_BREAK: + Lang_Break_Condition = Lang_Break = 1; return 1; + case _SLANG_BC_CONTINUE: + Lang_Break_Condition = /* Lang_Continue = */ 1; return 1; + + case _SLANG_BC_EXCH: + (void) SLreverse_stack (2); + break; + + case _SLANG_BC_LABEL: + { + int test; + if ((0 == SLang_pop_integer (&test)) + && (test == 0)) + return 0; + } + break; + + case _SLANG_BC_LOBJPTR: + (void)_SLang_push_ref (0, (VOID_STAR)(Local_Variable_Frame - addr->b.i_blk)); + break; + + case _SLANG_BC_GOBJPTR: + (void)_SLang_push_ref (1, (VOID_STAR)addr->b.nt_blk); + break; + + case _SLANG_BC_X_ERROR: + if (err_block != NULL) + { + inner_interp(err_block->b.blk); + if (SLang_Error) err_block = NULL; + } + else SLang_verror(SL_SYNTAX_ERROR, "No ERROR_BLOCK"); + if (Lang_Break_Condition) goto handle_break_condition; + break; + + case _SLANG_BC_X_USER0: + case _SLANG_BC_X_USER1: + case _SLANG_BC_X_USER2: + case _SLANG_BC_X_USER3: + case _SLANG_BC_X_USER4: + if (User_Block_Ptr[addr->bc_main_type - _SLANG_BC_X_USER0] != NULL) + { + inner_interp(User_Block_Ptr[addr->bc_main_type - _SLANG_BC_X_USER0]); + } + else SLang_verror(SL_SYNTAX_ERROR, "No block for X_USERBLOCK"); + if (Lang_Break_Condition) goto handle_break_condition; + break; + + case _SLANG_BC_CALL_DIRECT: + (*addr->b.call_function) (); + break; + + case _SLANG_BC_CALL_DIRECT_FRAME: + do_bc_call_direct_frame (addr->b.call_function); + break; + + case _SLANG_BC_UNARY: + do_unary (addr->b.i_blk, _SLANG_BC_UNARY); + break; + + case _SLANG_BC_UNARY_FUNC: + /* Make sure we treat these like function calls since the + * parser took abs(x) to be a function call. + */ + if (0 == _SL_increment_frame_pointer ()) + { + do_unary (addr->b.i_blk, _SLANG_BC_UNARY); + (void) _SL_decrement_frame_pointer (); + } + break; + + case _SLANG_BC_DEREF_ASSIGN: + set_deref_lvalue (addr); + break; + case _SLANG_BC_SET_LOCAL_LVALUE: + set_lvalue_obj (addr->bc_sub_type, Local_Variable_Frame - addr->b.i_blk); + break; + case _SLANG_BC_SET_GLOBAL_LVALUE: + if (-1 == set_lvalue_obj (addr->bc_sub_type, &addr->b.nt_gvar_blk->obj)) + do_name_type_error (addr->b.nt_blk); + break; + case _SLANG_BC_SET_INTRIN_LVALUE: + set_intrin_lvalue (addr); + break; + case _SLANG_BC_SET_STRUCT_LVALUE: + set_struct_lvalue (addr); + break; + + case _SLANG_BC_FIELD: + (void) push_struct_field (addr->b.s_blk); + break; + + case _SLANG_BC_SET_ARRAY_LVALUE: + set_array_lvalue (addr->bc_sub_type); + break; + +#if _SLANG_HAS_DEBUG_CODE + case _SLANG_BC_LINE_NUM: + break; +#endif + + case _SLANG_BC_TMP: + tmp_variable_function (addr); + break; + +#if _SLANG_OPTIMIZE_FOR_SPEED + case _SLANG_BC_LVARIABLE_AGET: + if (0 == push_local_variable (addr->b.i_blk)) + do_bc_call_direct_frame (_SLarray_aget); + break; + + case _SLANG_BC_LVARIABLE_APUT: + if (0 == push_local_variable (addr->b.i_blk)) + do_bc_call_direct_frame (_SLarray_aput); + break; + case _SLANG_BC_INTEGER_PLUS: + if (0 == SLclass_push_int_obj (addr->bc_sub_type, (int) addr->b.l_blk)) + do_binary (SLANG_PLUS); + break; + + case _SLANG_BC_INTEGER_MINUS: + if (0 == SLclass_push_int_obj (addr->bc_sub_type, (int) addr->b.l_blk)) + do_binary (SLANG_MINUS); + break; +#endif +#if 0 + case _SLANG_BC_ARG_LVARIABLE: + (void) SLang_start_arg_list (); + push_local_variable (addr->b.i_blk); + break; +#endif + case _SLANG_BC_EARG_LVARIABLE: + push_local_variable (addr->b.i_blk); + (void) SLang_end_arg_list (); + break; + +#if USE_COMBINED_BYTECODES + case _SLANG_BC_CALL_DIRECT_INTRINSIC: + (*addr->b.call_function) (); + addr++; + execute_intrinsic_fun (addr->b.nt_ifun_blk); + if (SLang_Error) + do_traceback(addr->b.nt_ifun_blk->name, 0, NULL); + break; + + case _SLANG_BC_INTRINSIC_CALL_DIRECT: + execute_intrinsic_fun (addr->b.nt_ifun_blk); + if (SLang_Error) + { + do_traceback(addr->b.nt_ifun_blk->name, 0, NULL); + break; + } + addr++; + (*addr->b.call_function) (); + break; + + case _SLANG_BC_CALL_DIRECT_LSTR: + (*addr->b.call_function) (); + addr++; + _SLang_dup_and_push_slstring (addr->b.s_blk); + break; + + case _SLANG_BC_CALL_DIRECT_SLFUN: + (*addr->b.call_function) (); + addr++; + execute_slang_fun (addr->b.nt_fun_blk); + if (Lang_Break_Condition) goto handle_break_condition; + break; + + case _SLANG_BC_CALL_DIRECT_INTRSTOP: + (*addr->b.call_function) (); + addr++; + /* drop */ + case _SLANG_BC_INTRINSIC_STOP: + execute_intrinsic_fun (addr->b.nt_ifun_blk); + if (SLang_Error == 0) + return 1; + do_traceback(addr->b.nt_ifun_blk->name, 0, NULL); + break; + + case _SLANG_BC_CALL_DIRECT_EARG_LVAR: + (*addr->b.call_function) (); + addr++; + push_local_variable (addr->b.i_blk); + (void) SLang_end_arg_list (); + break; + + case _SLANG_BC_CALL_DIRECT_LINT: + (*addr->b.call_function) (); + addr++; + SLclass_push_int_obj (addr->bc_sub_type, (int) addr->b.l_blk); + break; + + case _SLANG_BC_CALL_DIRECT_LVAR: + (*addr->b.call_function) (); + addr++; + push_local_variable (addr->b.i_blk); + break; +#endif /* USE_COMBINED_BYTECODES */ + + default: + SLang_verror (SL_INTERNAL_ERROR, "Byte-Code 0x%X is not valid", addr->bc_main_type); + } + + /* Someday I plan to add a 'signal' intrinsic function. Then when a + * signal is caught, a variable will be set to one and that value of + * that variable will need to be monitored here, e.g., + * if (Handle_Signal) handle_signal (); + * It would be nice to check only one variable instead of Handle_Signal + * and SLang_Error. Perhaps I should phase out SLang_Error = xxx + * and used something like: SLang_set_error (code); Then, I could + * use: + * if (Handle_Condition) + * { + * Handle_Condition = 0; + * if (SLang_Error) .... + * else if (Handle_Signal) handle_signal (); + * else.... + * } + */ + if (SLang_Error) + { + if (-1 == do_inner_interp_error (err_block, addr_start, addr)) + return 1; + if (SLang_Error) + return 1; + + /* Otherwise, error cleared. Continue onto next bytecode. + * Someday I need to add something to indicate where the + * next statement begins since continuing on the next + * bytecode is not really what is desired. + */ + if (Lang_Break_Condition) goto handle_break_condition; + } + addr++; + } + + handle_break_condition: + /* Get here if Lang_Break_Condition != 0, which implies that either + * Lang_Return, Lang_Break, or Lang_Continue is non zero + */ + if (Lang_Return) + Lang_Break = 1; + + return 1; +} + +/*}}}*/ + +/* The functions below this point are used to implement the parsed token + * to byte-compiled code. + */ +/* static SLang_Name_Type **Static_Hash_Table; */ + +static SLang_Name_Type **Locals_Hash_Table; +static int Local_Variable_Number; +static unsigned int Function_Args_Number; +int _SLang_Auto_Declare_Globals = 0; +int (*SLang_Auto_Declare_Var_Hook) (char *); + +static SLang_NameSpace_Type *This_Static_NameSpace; +static SLang_NameSpace_Type *Global_NameSpace; + +#if _SLANG_HAS_DEBUG_CODE +static char *This_Compile_Filename; +#endif +static SLBlock_Type SLShort_Blocks[6]; +/* These are initialized in add_table below. I cannot init a Union!! */ + +static int Lang_Defining_Function; +static void (*Default_Variable_Mode) (_SLang_Token_Type *); +static void (*Default_Define_Function) (char *, unsigned long); + +static int push_compile_context (char *); +static int pop_compile_context (void); + +typedef struct +{ + int block_type; + SLBlock_Type *block; /* beginning of block definition */ + SLBlock_Type *block_ptr; /* current location */ + SLBlock_Type *block_max; /* end of definition */ + SLang_NameSpace_Type *static_namespace; +} +Block_Context_Type; + +static Block_Context_Type Block_Context_Stack [SLANG_MAX_BLOCK_STACK_LEN]; +static unsigned int Block_Context_Stack_Len; + +static SLBlock_Type *Compile_ByteCode_Ptr; +static SLBlock_Type *This_Compile_Block; +static SLBlock_Type *This_Compile_Block_Max; +static int This_Compile_Block_Type; +#define COMPILE_BLOCK_TYPE_FUNCTION 1 +#define COMPILE_BLOCK_TYPE_BLOCK 2 +#define COMPILE_BLOCK_TYPE_TOP_LEVEL 3 + +/* If it returns 0, DO NOT FREE p */ +static int lang_free_branch (SLBlock_Type *p) +{ + /* Note: we look at 0,2,4, since these blocks are 0 terminated */ + if ((p == SLShort_Blocks) + || (p == SLShort_Blocks + 2) + || (p == SLShort_Blocks + 4) + ) + return 0; + + while (1) + { + SLang_Class_Type *cl; + + switch (p->bc_main_type) + { + case _SLANG_BC_BLOCK: + if (lang_free_branch(p->b.blk)) + SLfree((char *)p->b.blk); + break; + + case _SLANG_BC_LITERAL: + case _SLANG_BC_LITERAL_STR: + /* No user types should be here. */ + cl = _SLclass_get_class (p->bc_sub_type); + (*cl->cl_byte_code_destroy) (p->bc_sub_type, (VOID_STAR) &p->b.ptr_blk); + break; + + case _SLANG_BC_FIELD: + case _SLANG_BC_SET_STRUCT_LVALUE: + SLang_free_slstring (p->b.s_blk); + break; + + default: + break; + + case 0: + return 1; + } + p++; + } +} + +static void free_function_header (_SLBlock_Header_Type *h) +{ + if (h->num_refs > 1) + { + h->num_refs--; + return; + } + + if (h->body != NULL) + { + if (lang_free_branch (h->body)) + SLfree ((char *) h->body); + } + + SLfree ((char *) h); +} + +static int push_block_context (int type) +{ + Block_Context_Type *c; + unsigned int num; + SLBlock_Type *b; + + if (Block_Context_Stack_Len == SLANG_MAX_BLOCK_STACK_LEN) + { + SLang_verror (SL_STACK_OVERFLOW, "Block stack overflow"); + return -1; + } + + num = 5; /* 40 bytes */ + if (NULL == (b = (SLBlock_Type *) SLcalloc (num, sizeof (SLBlock_Type)))) + return -1; + + c = Block_Context_Stack + Block_Context_Stack_Len; + c->block = This_Compile_Block; + c->block_ptr = Compile_ByteCode_Ptr; + c->block_max = This_Compile_Block_Max; + c->block_type = This_Compile_Block_Type; + c->static_namespace = This_Static_NameSpace; + + Compile_ByteCode_Ptr = This_Compile_Block = b; + This_Compile_Block_Max = b + num; + This_Compile_Block_Type = type; + + Block_Context_Stack_Len += 1; + return 0; +} + +static int pop_block_context (void) +{ + Block_Context_Type *c; + + if (Block_Context_Stack_Len == 0) + return -1; + + Block_Context_Stack_Len -= 1; + c = Block_Context_Stack + Block_Context_Stack_Len; + + This_Compile_Block = c->block; + This_Compile_Block_Max = c->block_max; + This_Compile_Block_Type = c->block_type; + Compile_ByteCode_Ptr = c->block_ptr; + This_Static_NameSpace = c->static_namespace; + + return 0; +} + +int _SLcompile_push_context (SLang_Load_Type *load_object) +{ + if (-1 == push_compile_context (load_object->name)) + return -1; + + if (NULL == (This_Static_NameSpace = _SLns_allocate_namespace (load_object->name, SLSTATIC_HASH_TABLE_SIZE))) + { + pop_compile_context (); + return -1; + } + + if (-1 == push_block_context (COMPILE_BLOCK_TYPE_TOP_LEVEL)) + { + pop_compile_context (); + return -1; + } + + return 0; +} + +int _SLcompile_pop_context (void) +{ + if (This_Compile_Block_Type == COMPILE_BLOCK_TYPE_TOP_LEVEL) + { + Compile_ByteCode_Ptr->bc_main_type = 0; + if (lang_free_branch (This_Compile_Block)) + SLfree ((char *) This_Compile_Block); + } + + (void) pop_block_context (); + (void) pop_compile_context (); + + if (This_Compile_Block == NULL) + return 0; + +#if 0 + if (This_Compile_Block_Type != COMPILE_BLOCK_TYPE_TOP_LEVEL) + { + SLang_verror (SL_INTERNAL_ERROR, "Not at top-level"); + return -1; + } +#endif + + return 0; +} + +/*{{{ Hash and Name Table Functions */ + +static SLang_Name_Type *locate_name_in_table (char *name, unsigned long hash, + SLang_Name_Type **table, unsigned int table_size) +{ + SLang_Name_Type *t; + char ch; + + t = table [(unsigned int) (hash % table_size)]; + ch = *name++; + + while (t != NULL) + { + if ((ch == t->name[0]) + && (0 == strcmp (t->name + 1, name))) + break; + + t = t->next; + } + + return t; +} + +static SLang_Name_Type *locate_namespace_encoded_name (char *name, int err_on_bad_ns) +{ + char *ns, *ns1; + SLang_NameSpace_Type *table; + SLang_Name_Type *nt; + + ns = name; + name = strchr (name, '-'); + if ((name == NULL) || (name [1] != '>')) + name = ns; + + ns1 = SLang_create_nslstring (ns, (unsigned int) (name - ns)); + if (ns1 == NULL) + return NULL; + if (ns != name) + name += 2; + ns = ns1; + + if (*ns == 0) + { + /* Use Global Namespace */ + SLang_free_slstring (ns); + return locate_name_in_table (name, _SLcompute_string_hash (name), + Global_NameSpace->table, Global_NameSpace->table_size); + } + + if (NULL == (table = _SLns_find_namespace (ns))) + { + if (err_on_bad_ns) + SLang_verror (SL_SYNTAX_ERROR, "Unable to find namespace called %s", ns); + SLang_free_slstring (ns); + return NULL; + } + SLang_free_slstring (ns); + + /* FIXME: the hash table size should be stored in the hash table itself */ + nt = locate_name_in_table (name, _SLcompute_string_hash (name), + table->table, table->table_size); + if (nt == NULL) + return NULL; + + switch (nt->name_type) + { + /* These are private and cannot be accessed through the namespace. */ + case SLANG_PVARIABLE: + case SLANG_PFUNCTION: + return NULL; + } + return nt; +} + +static SLang_Name_Type *locate_hashed_name (char *name, unsigned long hash) +{ + SLang_Name_Type *t; + + if (Lang_Defining_Function) + { + t = locate_name_in_table (name, hash, Locals_Hash_Table, SLLOCALS_HASH_TABLE_SIZE); + if (t != NULL) + return t; + } + + if ((This_Static_NameSpace != NULL) + && (NULL != (t = locate_name_in_table (name, hash, This_Static_NameSpace->table, This_Static_NameSpace->table_size)))) + return t; + + t = locate_name_in_table (name, hash, Global_NameSpace->table, Global_NameSpace->table_size); + if (NULL != t) + return t; + + return locate_namespace_encoded_name (name, 1); +} + +SLang_Name_Type *_SLlocate_name (char *name) +{ + return locate_hashed_name (name, _SLcompute_string_hash (name)); +} + |