summaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
Diffstat (limited to 'util')
-rw-r--r--util/rundiff511
-rw-r--r--util/tap/Makefile67
-rw-r--r--util/tap/tap.cc415
-rw-r--r--util/term/Makefile45
-rw-r--r--util/term/term.c312
5 files changed, 1350 insertions, 0 deletions
diff --git a/util/rundiff b/util/rundiff
new file mode 100644
index 000000000..064e7e136
--- /dev/null
+++ b/util/rundiff
@@ -0,0 +1,511 @@
+#!/usr/bin/perl
+
+# Copyright (c) 2001 Nathan L. Binkert
+# All rights reserved.
+#
+# Permission to redistribute, use, copy, and modify this software
+# without fee is hereby granted, provided that the following
+# conditions are met:
+#
+# 1. This entire notice is included in all source code copies of any
+# software which is or includes a copy or modification of this
+# software.
+# 2. The name of the author may not be used to endorse or promote
+# products derived from this software without specific prior
+# written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+use Algorithm::Diff qw(diff);
+use vars qw ($opt_C $opt_c $opt_u $opt_U);
+
+$opt_u = "";
+$opt_c = undef;
+
+$diffsize = 2000;
+# After we've read up to a certain point in each file, the number of items
+# we've read from each file will differ by $FLD (could be 0)
+my $File_Length_Difference = 0;
+my $Context_Lines = 9;
+
+$progname = $0;
+if (scalar(@ARGV) != 2) {
+ usage();
+}
+
+my ($filename1, $filename2);
+($filename1, $start1) = parse_filearg($ARGV[0]);
+($filename2, $start2) = parse_filearg($ARGV[1]);
+
+if ($filename1 eq "-" && $filename2 eq "-") {
+ die "Only one of the inputs may be standard in\n";
+}
+
+my ($file1, $file2);
+if ($filename1 eq "-") {
+ $file1 = STDIN;
+} else {
+ open(FILE1, $filename1) || die "can't open $file1: $!\n";
+ $file1 = FILE1;
+}
+
+if ($filename2 eq "-") {
+ $file2 = STDIN;
+} else {
+ open(FILE2, $filename2) || die "can't open $file2: $!\n";
+ $file2 = FILE2;
+}
+
+my $file_offset1 = ffw($file1, $start1);
+my $file_offset2 = ffw($file2, $start2);
+
+$skip_first = 0;
+my (@buf1, @buf2, @printbuf1, @printbuf2);
+
+$Compare_Ahead = 0;
+
+while (!eof($file1) && !eof($file2)) {
+ my $line1 = <$file1>; chomp $line1;
+ my $line2 = <$file2>; chomp $line2;
+ my $printline1 = $line1;
+ my $printline2 = $line2;
+
+ push @buf1, $line1;
+ push @buf2, $line2;
+ push @printbuf1, $printline1;
+ push @printbuf2, $printline2;
+
+# while ($Compare_Ahead < $Context_Lines) {
+# $line1 = @buf1[$Compare_Ahead];
+# $line2 = @buf2[$Compare_Ahead];
+# $line2 =~ s/ *--.*$//;
+# if ($line1 ne $line2) { last; }
+# ++$Compare_Ahead;
+# }
+
+ $line1 = @buf1[$Compare_Ahead];
+ $line2 = @buf2[$Compare_Ahead];
+ $line2 =~ s/ *--.*$//;
+
+ if ($line1 ne $line2) {
+ while (!eof($file1) && scalar(@buf1) < $diffsize) {
+ $line = <$file1>; chomp $line;
+ my $printline = $line;
+
+ push @printbuf1, $printline;
+ push @buf1, $line;
+ }
+
+ while (!eof($file2) && scalar(@buf2) < $diffsize) {
+ $line = <$file2>; chomp $line;
+ my $printline = $line;
+# $line =~ s/ *--.*$//;
+
+ push @printbuf2, $printline;
+ push @buf2, $line;
+ }
+
+ my $diffs = diff(\@buf1, \@buf2);
+
+ next unless @$diffs;
+
+ my @hunklist;
+ my ($hunk,$oldhunk);
+ # Loop over hunks. If a hunk overlaps with the last hunk, join them.
+ # Otherwise, print out the old one.
+ foreach my $piece (@$diffs) {
+ $hunk = new Hunk ($piece, $Context_Lines, scalar(@buf1));
+ next unless $oldhunk;
+
+ if ($hunk->does_overlap($oldhunk)) {
+ $hunk->prepend_hunk($oldhunk);
+ } else {
+ push @hunklist, $oldhunk;
+ }
+ } continue {
+ $oldhunk = $hunk;
+ }
+
+ my $change = 0;
+ while (scalar(@hunklist) && !$change) {
+ $hunk = pop @hunklist;
+ $change = $hunk->{"change"};
+ }
+ push @hunklist, $hunk;
+ $last_start1 = $hunk->{"start1"};
+ $last_start2 = $hunk->{"start2"};
+ $last_end1 = $hunk->{"end1"};
+ $last_end2 = $hunk->{"end2"};
+
+ while (scalar(@hunklist)) {
+ $hunk = shift @hunklist;
+# $hunk->output_diff(\@buf1, \@buf2);
+ $hunk->output_diff(\@printbuf1, \@printbuf2);
+ }
+
+ $last_end1 -= $Context_Lines - 1;
+ $last_end2 -= $Context_Lines - 1;
+ $file_offset1 += $last_end1;
+ $file_offset2 += $last_end2;
+ @printbuf1 = @printbuf1[$last_end1..$#printbuf1];
+ @printbuf2 = @printbuf2[$last_end2..$#printbuf2];
+ @buf1 = @buf1[$last_end1..$#buf1];
+ @buf2 = @buf2[$last_end2..$#buf2];
+ while (scalar(@buf1) > $Context_Lines &&
+ scalar(@buf2) > $Context_Lines) {
+ $foo1 = @buf1[$Context_Lines];
+ $foo2 = @buf2[$Context_Lines];
+ if (scalar($foo1) != scalar($foo2) || $foo1 ne $foo2) { last; }
+ $foo1 = shift @printbuf1;
+ $foo2 = shift @printbuf2;
+ $foo1 = shift @buf1;
+ $foo2 = shift @buf2;
+ ++$file_offset1;
+ ++$file_offset2;
+ }
+ } else {
+ ++$file_offset1;
+ ++$file_offset2;
+ $foo1 = shift @printbuf1;
+ $foo2 = shift @printbuf2;
+ $foo1 = shift @buf1;
+ $foo2 = shift @buf2;
+ }
+}
+
+close $file1;
+close $file2;
+
+sub ffw() {
+ if (scalar(@_) != 2) { die "improper usage of ffw\n"; }
+
+ my $FILE = $_[0];
+ my $start = $_[1];
+ my $count = 0;
+
+ while ($start-- > 0 && !eof($FILE)) {
+ <$FILE>;
+ $count++;
+ }
+
+ if ($start > 0) {die "File too short for ffw amount\n"; }
+ return $count;
+}
+
+sub parse_filearg() {
+ $start = 0;
+ split /:/, @_[0];
+ if (scalar(@_) > 2) { usage(); }
+
+ $file = $_[0];
+ if (scalar(@_) > 1) { $start = $_[1]; }
+
+ return ($file, $start);
+}
+
+sub usage() {
+ printf "usage: $progname <file1>[:start] <file2>[:start]\n";
+ exit 1;
+}
+
+
+# Package Hunk. A Hunk is a group of Blocks which overlap because of the
+# context surrounding each block. (So if we're not using context, every
+# hunk will contain one block.)
+{
+package Hunk;
+
+sub new {
+# Arg1 is output from &LCS::diff (which corresponds to one Block)
+# Arg2 is the number of items (lines, e.g.,) of context around each block
+#
+# This subroutine changes $File_Length_Difference
+#
+# Fields in a Hunk:
+# blocks - a list of Block objects
+# start - index in file 1 where first block of the hunk starts
+# end - index in file 1 where last block of the hunk ends
+#
+# Variables:
+# before_diff - how much longer file 2 is than file 1 due to all hunks
+# until but NOT including this one
+# after_diff - difference due to all hunks including this one
+ my ($class, $piece, $context_items, $maxlen) = @_;
+
+ my $block = new Block ($piece); # this modifies $FLD!
+
+ my $before_diff = $File_Length_Difference; # BEFORE this hunk
+ my $after_diff = $before_diff + $block->{"length_diff"};
+ $File_Length_Difference += $block->{"length_diff"};
+
+ # @remove_array and @insert_array hold the items to insert and remove
+ # Save the start & beginning of each array. If the array doesn't exist
+ # though (e.g., we're only adding items in this block), then figure
+ # out the line number based on the line number of the other file and
+ # the current difference in file lenghts
+ my @remove_array = $block->remove;
+ my @insert_array = $block->insert;
+ my ($a1, $a2, $b1, $b2, $start1, $start2, $end1, $end2, $change);
+ $a1 = @remove_array ? $remove_array[0 ]->{"item_no"} : -1;
+ $a2 = @remove_array ? $remove_array[-1]->{"item_no"} : -1;
+ $b1 = @insert_array ? $insert_array[0 ]->{"item_no"} : -1;
+ $b2 = @insert_array ? $insert_array[-1]->{"item_no"} : -1;
+
+ $start1 = $a1 == -1 ? $b1 - $before_diff : $a1;
+ $end1 = $a2 == -1 ? $b2 - $after_diff : $a2;
+ $start2 = $b1 == -1 ? $a1 + $before_diff : $b1;
+ $end2 = $b2 == -1 ? $a2 + $after_diff : $b2;
+ $change = scalar(@remove_array) && scalar(@insert_array);
+
+ # At first, a hunk will have just one Block in it
+ my $hunk = {
+ "start1" => $start1,
+ "start2" => $start2,
+ "end1" => $end1,
+ "end2" => $end2,
+ "maxlen" => $maxlen,
+ "change" => $change,
+ "blocks" => [$block],
+ };
+ bless $hunk, $class;
+
+ $hunk->flag_context($context_items);
+
+ return $hunk;
+}
+
+# Change the "start" and "end" fields to note that context should be added
+# to this hunk
+sub flag_context {
+ my ($hunk, $context_items) = @_;
+ return unless $context_items; # no context
+
+ # add context before
+ my $start1 = $hunk->{"start1"};
+ my $num_added = $context_items > $start1 ? $start1 : $context_items;
+ $hunk->{"start1"} -= $num_added;
+ $hunk->{"start2"} -= $num_added;
+
+ # context after
+ my $end1 = $hunk->{"end1"};
+ $num_added = ($end1+$context_items > $hunk->{"maxlen"}) ?
+ $hunk->{"maxlen"} - $end1 :
+ $context_items;
+ $hunk->{"end1"} += $num_added;
+ $hunk->{"end2"} += $num_added;
+}
+
+# Is there an overlap between hunk arg0 and old hunk arg1?
+# Note: if end of old hunk is one less than beginning of second, they overlap
+sub does_overlap {
+ my ($hunk, $oldhunk) = @_;
+ return "" unless $oldhunk; # first time through, $oldhunk is empty
+
+ # Do I actually need to test both?
+ return ($hunk->{"start1"} - $oldhunk->{"end1"} <= 1 ||
+ $hunk->{"start2"} - $oldhunk->{"end2"} <= 1);
+}
+
+# Prepend hunk arg1 to hunk arg0
+# Note that arg1 isn't updated! Only arg0 is.
+sub prepend_hunk {
+ my ($hunk, $oldhunk) = @_;
+
+ $hunk->{"start1"} = $oldhunk->{"start1"};
+ $hunk->{"start2"} = $oldhunk->{"start2"};
+
+ unshift (@{$hunk->{"blocks"}}, @{$oldhunk->{"blocks"}});
+}
+
+
+# DIFF OUTPUT ROUTINES. THESE ROUTINES CONTAIN DIFF FORMATTING INFO...
+sub output_diff {
+ if (defined $main::opt_u) {&output_unified_diff(@_)}
+ elsif (defined $main::opt_c) {&output_context_diff(@_)}
+ else {die "unknown diff"}
+}
+
+sub output_unified_diff {
+ my ($hunk, $fileref1, $fileref2) = @_;
+ my @blocklist;
+
+ # Calculate item number range.
+ my $range1 = $hunk->unified_range(1, $file_offset1);
+ my $range2 = $hunk->unified_range(2, $file_offset2);
+ print "@@ -$range1 +$range2 @@\n";
+
+ # Outlist starts containing the hunk of file 1.
+ # Removing an item just means putting a '-' in front of it.
+ # Inserting an item requires getting it from file2 and splicing it in.
+ # We splice in $num_added items. Remove blocks use $num_added because
+ # splicing changed the length of outlist.
+ # We remove $num_removed items. Insert blocks use $num_removed because
+ # their item numbers---corresponding to positions in file *2*--- don't take
+ # removed items into account.
+ my $low = $hunk->{"start1"};
+ my $hi = $hunk->{"end1"};
+ my ($num_added, $num_removed) = (0,0);
+ my @outlist = @$fileref1[$low..$hi];
+ map {s/^/ /} @outlist; # assume it's just context
+
+ foreach my $block (@{$hunk->{"blocks"}}) {
+ foreach my $item ($block->remove) {
+ my $op = $item->{"sign"}; # -
+ my $offset = $item->{"item_no"} - $low + $num_added;
+ $outlist[$offset] =~ s/^ /$op/;
+ $num_removed++;
+ }
+ foreach my $item ($block->insert) {
+ my $op = $item->{"sign"}; # +
+ my $i = $item->{"item_no"};
+ my $offset = $i - $hunk->{"start2"} + $num_removed;
+ splice(@outlist,$offset,0,"$op$$fileref2[$i]");
+ $num_added++;
+ }
+ }
+
+ map {s/$/\n/} @outlist; # add \n's
+ print @outlist;
+
+}
+
+sub output_context_diff {
+ my ($hunk, $fileref1, $fileref2) = @_;
+ my @blocklist;
+
+ print "***************\n";
+ # Calculate item number range.
+ my $range1 = $hunk->context_range(1, $file_offset1);
+ my $range2 = $hunk->context_range(2, $file_offset2);
+
+ # Print out file 1 part for each block in context diff format if there are
+ # any blocks that remove items
+ print "*** $range1 ****\n";
+ my $low = $hunk->{"start1"};
+ my $hi = $hunk->{"end1"};
+ if (@blocklist = grep {$_->remove} @{$hunk->{"blocks"}}) {
+ my @outlist = @$fileref1[$low..$hi];
+ map {s/^/ /} @outlist; # assume it's just context
+ foreach my $block (@blocklist) {
+ my $op = $block->op; # - or !
+ foreach my $item ($block->remove) {
+ $outlist[$item->{"item_no"} - $low] =~ s/^ /$op/;
+ }
+ }
+ map {s/$/\n/} @outlist; # add \n's
+ print @outlist;
+ }
+
+ print "--- $range2 ----\n";
+ $low = $hunk->{"start2"};
+ $hi = $hunk->{"end2"};
+ if (@blocklist = grep {$_->insert} @{$hunk->{"blocks"}}) {
+ my @outlist = @$fileref2[$low..$hi];
+ map {s/^/ /} @outlist; # assume it's just context
+ foreach my $block (@blocklist) {
+ my $op = $block->op; # + or !
+ foreach my $item ($block->insert) {
+ $outlist[$item->{"item_no"} - $low] =~ s/^ /$op/;
+ }
+ }
+ map {s/$/\n/} @outlist; # add \n's
+ print @outlist;
+ }
+}
+
+sub context_range {
+# Generate a range of item numbers to print. Only print 1 number if the range
+# has only one item in it. Otherwise, it's 'start,end'
+ my ($hunk, $flag, $offset) = @_;
+ my ($start, $end) = ($hunk->{"start$flag"},$hunk->{"end$flag"});
+
+ # index from 1, not zero
+ $start += $offset + 1;
+ $end += $offset + 1;
+ my $range = ($start < $end) ? "$start,$end" : $end;
+ return $range;
+}
+
+sub unified_range {
+# Generate a range of item numbers to print for unified diff
+# Print number where block starts, followed by number of lines in the block
+# (don't print number of lines if it's 1)
+ my ($hunk, $flag, $offset) = @_;
+ my ($start, $end) = ($hunk->{"start$flag"},$hunk->{"end$flag"});
+
+ # index from 1, not zero
+ $start += $offset + 1;
+ $end += $offset + 1;
+ my $length = $end - $start + 1;
+ my $first = $length < 2 ? $end : $start; # strange, but correct...
+ my $range = $length== 1 ? $first : "$first,$length";
+ return $range;
+}
+} # end Package Hunk
+
+# Package Block. A block is an operation removing, adding, or changing
+# a group of items. Basically, this is just a list of changes, where each
+# change adds or deletes a single item.
+# (Change could be a separate class, but it didn't seem worth it)
+{
+package Block;
+sub new {
+# Input is a chunk from &Algorithm::LCS::diff
+# Fields in a block:
+# length_diff - how much longer file 2 is than file 1 due to this block
+# Each change has:
+# sign - '+' for insert, '-' for remove
+# item_no - number of the item in the file (e.g., line number)
+# We don't bother storing the text of the item
+#
+ my ($class,$chunk) = @_;
+ my @changes = ();
+
+# This just turns each change into a hash.
+ foreach my $item (@$chunk) {
+ my ($sign, $item_no, $text) = @$item;
+ my $hashref = {"sign" => $sign, "item_no" => $item_no};
+ push @changes, $hashref;
+ }
+
+ my $block = { "changes" => \@changes };
+ bless $block, $class;
+
+ $block->{"length_diff"} = $block->insert - $block->remove;
+ return $block;
+}
+
+
+# LOW LEVEL FUNCTIONS
+sub op {
+# what kind of block is this?
+ my $block = shift;
+ my $insert = $block->insert;
+ my $remove = $block->remove;
+
+ $remove && $insert and return '!';
+ $remove and return '-';
+ $insert and return '+';
+ warn "unknown block type";
+ return '^'; # context block
+}
+
+# Returns a list of the changes in this block that remove items
+# (or the number of removals if called in scalar context)
+sub remove { return grep {$_->{"sign"} eq '-'} @{shift->{"changes"}}; }
+
+# Returns a list of the changes in this block that insert items
+sub insert { return grep {$_->{"sign"} eq '+'} @{shift->{"changes"}}; }
+
+} # end of package Block
diff --git a/util/tap/Makefile b/util/tap/Makefile
new file mode 100644
index 000000000..c7078158b
--- /dev/null
+++ b/util/tap/Makefile
@@ -0,0 +1,67 @@
+# Copyright (c) 2003 The Regents of The University of Michigan
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Authors: Nathan Binkert
+
+# $Id$
+
+CC= gcc
+CXX= g++
+
+CURDIR?= $(shell /bin/pwd)
+SRCDIR?= .
+
+BASE_SRCDIR?= $(SRCDIR)/../../base
+SIM_SRCDIR?= $(SRCDIR)/../../sim
+
+vpath % $(BASE_SRCDIR)
+vpath % $(SIM_SRCDIR)
+
+INCLDIRS= -I. -I$(BASE_SRCDIR) -I$(SIM_SRCDIR) -I- -I/usr/local/include
+CCFLAGS= -g -O0 -MMD $(INCLDIRS)
+
+default: m5tap
+
+m5tap: tap.o cprintf.o
+ $(CXX) $(LFLAGS) -o $@ $^ -lpcap -L/usr/local/lib -ldnet
+
+
+clean:
+ @rm -f m5tap *.o *.d *~ .#*
+
+.PHONY: clean
+
+# C++ Compilation
+%.o: %.cc
+ @echo '$(CXX) $(CCFLAGS) -c $(notdir $<) -o $@'
+ @$(CXX) $(CCFLAGS) -c $< -o $@
+
+# C Compilation
+%.o: %.c
+ @echo '$(CC) $(CCFLAGS) -c $(notdir $<) -o $@'
+ @$(CC) $(CCFLAGS) -c $< -o $@
+
+-include *.d
diff --git a/util/tap/tap.cc b/util/tap/tap.cc
new file mode 100644
index 000000000..0ef49dfd7
--- /dev/null
+++ b/util/tap/tap.cc
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 2003 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <pcap.h>
+}
+
+#include <dnet.h>
+
+#include <arpa/inet.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <netdb.h>
+#include <poll.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <list>
+#include <string>
+
+#include "cprintf.hh"
+
+#define panic(arg...) \
+ do { cprintf("Panic: " arg); exit(1); } while (0)
+
+char *program = "ethertap";
+void
+usage()
+{
+ cprintf(
+ "usage: \n"
+ "\t%s [-b bufsize] [-d] [-f filter] [-p port] [-v] <device> <host>\n"
+ "\t%s [-b bufsize] [-d] [-f filter] [-l] [-p port] [-v] <device>\n",
+ program, program);
+ exit(2);
+}
+
+int verbose = 0;
+#define DPRINTF(args...) do { \
+ if (verbose > 1) \
+ cprintf(args); \
+} while (0)
+
+#define DDUMP(args...) do { \
+ if (verbose > 2) \
+ dump((const u_char *)args); \
+} while (0)
+
+void
+dump(const u_char *data, int len)
+{
+ int c, i, j;
+
+ for (i = 0; i < len; i += 16) {
+ cprintf("%08x ", i);
+ c = len - i;
+ if (c > 16) c = 16;
+
+ for (j = 0; j < c; j++) {
+ cprintf("%02x ", data[i + j] & 0xff);
+ if ((j & 0xf) == 7 && j > 0)
+ cprintf(" ");
+ }
+
+ for (; j < 16; j++)
+ cprintf(" ");
+ cprintf(" ");
+
+ for (j = 0; j < c; j++) {
+ int ch = data[i + j] & 0x7f;
+ cprintf("%c", (char)(isprint(ch) ? ch : ' '));
+ }
+
+ cprintf("\n");
+
+ if (c < 16)
+ break;
+ }
+}
+
+bool quit = false;
+void
+quit_now(int sigtype)
+{
+ DPRINTF("User requested exit\n");
+ quit = true;
+}
+
+
+int
+Socket(int reuse)
+{
+ int fd = ::socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0)
+ panic("Can't create socket!");
+
+ if (reuse) {
+ int i = 1;
+ if (::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&i,
+ sizeof(i)) < 0)
+ panic("setsockopt() SO_REUSEADDR failed!");
+ }
+
+ return fd;
+}
+
+void
+Listen(int fd, int port)
+{
+ struct sockaddr_in sockaddr;
+ sockaddr.sin_family = PF_INET;
+ sockaddr.sin_addr.s_addr = INADDR_ANY;
+
+ sockaddr.sin_port = htons(port);
+ int ret = ::bind(fd, (struct sockaddr *)&sockaddr, sizeof (sockaddr));
+ if (ret == -1)
+ panic("bind() failed!");
+
+ if (::listen(fd, 1) == -1)
+ panic("listen() failed!");
+}
+
+// Open a connection. Accept will block, so if you don't want it to,
+// make sure a connection is ready before you call accept.
+int
+Accept(int fd, bool nodelay)
+{
+ struct sockaddr_in sockaddr;
+ socklen_t slen = sizeof (sockaddr);
+ int sfd = ::accept(fd, (struct sockaddr *)&sockaddr, &slen);
+ if (sfd == -1)
+ panic("accept() failed!");
+
+ if (nodelay) {
+ int i = 1;
+ ::setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (char *)&i, sizeof(i));
+ }
+ return sfd;
+}
+
+void
+Connect(int fd, const string &host, int port)
+{
+ struct sockaddr_in sockaddr;
+ if (::inet_aton(host.c_str(), &sockaddr.sin_addr) == 0) {
+ struct hostent *hp;
+ hp = ::gethostbyname(host.c_str());
+ if (!hp)
+ panic("Host %s not found\n", host);
+
+ sockaddr.sin_family = hp->h_addrtype;
+ memcpy(&sockaddr.sin_addr, hp->h_addr, hp->h_length);
+ }
+
+ sockaddr.sin_port = htons(port);
+ if (::connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != 0)
+ panic("could not connect to %s on port %d", host, port);
+
+ DPRINTF("connected to %s on port %d\n", host, port);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int port = 3500;
+ int bufsize = 2000;
+ bool listening = false;
+ char *device = NULL;
+ char *filter = "";
+ char c;
+ int daemon = false;
+ string host;
+
+ program = basename(argv[0]);
+
+ while((c = getopt(argc, argv, "b:df:lp:v")) != -1) {
+ switch (c) {
+ case 'b':
+ bufsize = atoi(optarg);
+ break;
+ case 'd':
+ daemon = true;
+ break;
+ case 'f':
+ filter = optarg;
+ break;
+ case 'l':
+ listening = true;
+ break;
+ case 'p':
+ port = atoi(optarg);
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ signal(SIGINT, quit_now);
+ signal(SIGTERM, quit_now);
+ signal(SIGHUP, quit_now);
+
+ if (daemon) {
+ verbose = 0;
+ switch(fork()) {
+ case -1:
+ panic("Fork failed\n");
+ case 0:
+ break;
+ default:
+ exit(0);
+ }
+ }
+
+ char *buffer = new char[bufsize];
+ argc -= optind;
+ argv += optind;
+
+ if (argc-- == 0)
+ usage();
+
+ device = *argv++;
+
+ if (listening) {
+ if (argc)
+ usage();
+ } else {
+ if (argc != 1)
+ usage();
+
+ host = *argv;
+ }
+
+ char errbuf[PCAP_ERRBUF_SIZE];
+ pcap_t *pcap = pcap_open_live(device, 1500, 1, -1, errbuf);
+ if (pcap == NULL)
+ panic("pcap_open_live failed: %s\n", errbuf);
+
+ bpf_program program;
+ bpf_u_int32 localnet, netmask;
+ if (!pcap_lookupnet(device, &localnet, &netmask, errbuf))
+ panic("pcap_lookupnet failed: %s\n", errbuf);
+
+ if (pcap_compile(pcap, &program, filter, 1, netmask) == -1)
+ panic("pcap_compile failed, invalid filter:\n%s\n", filter);
+
+ if (pcap_setfilter(pcap, &program) == -1)
+ panic("pcap_setfilter failed\n");
+
+ eth_t *ethernet = eth_open(device);
+
+ pollfd pfds[3];
+ pfds[0].fd = Socket(true);
+ pfds[0].events = POLLIN;
+ pfds[0].revents = 0;
+
+ if (listening)
+ Listen(pfds[0].fd, port);
+ else
+ Connect(pfds[0].fd, host, port);
+
+ pfds[1].fd = pcap_fileno(pcap);
+ pfds[1].events = POLLIN;
+ pfds[1].revents = 0;
+
+ pfds[2].fd = 0;
+ pfds[2].events = POLLIN|POLLERR;
+ pfds[2].revents = 0;
+
+ pollfd *listen_pfd = listening ? &pfds[0] : NULL;
+ pollfd *tap_pfd = &pfds[1];
+ pollfd *client_pfd = listening ? NULL : &pfds[0];
+ int npfds = 2;
+
+ int32_t buffer_offset = 0;
+ int32_t data_len = 0;
+
+ while (!quit) {
+ int ret = ::poll(pfds, npfds, INFTIM);
+ if (ret < 0)
+ continue;
+
+ if (listen_pfd && listen_pfd->revents) {
+ if (listen_pfd->revents & POLLIN) {
+ int fd = Accept(listen_pfd->fd, false);
+ if (client_pfd) {
+ DPRINTF("Connection rejected\n");
+ close(fd);
+ } else {
+ DPRINTF("Connection accepted\n");
+ client_pfd = &pfds[2];
+ client_pfd->fd = fd;
+ npfds++;
+ }
+ }
+ listen_pfd->revents = 0;
+ }
+
+ if (tap_pfd && tap_pfd->revents) {
+ if (tap_pfd->revents & POLLIN) {
+ pcap_pkthdr hdr;
+ const u_char *data = pcap_next(pcap, &hdr);
+ if (data && client_pfd) {
+ DPRINTF("Received packet from ethernet len=%d\n", hdr.len);
+ DDUMP(data, hdr.len);
+ u_int32_t len = htonl(hdr.len);
+ write(client_pfd->fd, &len, sizeof(len));
+ write(client_pfd->fd, data, hdr.len);
+ }
+ }
+
+ tap_pfd->revents = 0;
+ }
+
+ if (client_pfd && client_pfd->revents) {
+ if (client_pfd->revents & POLLIN) {
+ if (buffer_offset < data_len + sizeof(u_int32_t)) {
+ int len = read(client_pfd->fd, buffer + buffer_offset,
+ bufsize - buffer_offset);
+
+ if (len <= 0) {
+ perror("read");
+ goto error;
+ }
+
+ buffer_offset += len;
+ if (data_len == 0)
+ data_len = ntohl(*(u_int32_t *)buffer);
+
+ DPRINTF("Received data from peer: len=%d buffer_offset=%d "
+ "data_len=%d\n", len, buffer_offset, data_len);
+ }
+
+ while (data_len != 0 &&
+ buffer_offset >= data_len + sizeof(u_int32_t)) {
+ char *data = buffer + sizeof(u_int32_t);
+ eth_send(ethernet, data, data_len);
+ DPRINTF("Sent packet to ethernet len = %d\n", data_len);
+ DDUMP(data, data_len);
+
+ buffer_offset -= data_len + sizeof(u_int32_t);
+ if (buffer_offset > 0 && data_len > 0) {
+ memmove(buffer, data + data_len, buffer_offset);
+ data_len = ntohl(*(u_int32_t *)buffer);
+ } else
+ data_len = 0;
+ }
+ }
+
+ if (client_pfd->revents & POLLERR) {
+ error:
+ DPRINTF("Error on client socket\n");
+ close(client_pfd->fd);
+ client_pfd = NULL;
+
+ if (listening)
+ npfds--;
+ else
+ quit = true;
+ }
+
+ if (client_pfd)
+ client_pfd->revents = 0;
+ }
+
+ }
+
+ delete [] buffer;
+ pcap_close(pcap);
+ eth_close(ethernet);
+ if(listen_pfd)
+ close(listen_pfd->fd);
+
+ if (client_pfd)
+ close(client_pfd->fd);
+
+ return 0;
+}
diff --git a/util/term/Makefile b/util/term/Makefile
new file mode 100644
index 000000000..39e396687
--- /dev/null
+++ b/util/term/Makefile
@@ -0,0 +1,45 @@
+# Copyright (c) 2003 The Regents of The University of Michigan
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Authors: Nathan Binkert
+
+# $Id$
+
+CC= gcc
+CCFLAGS= -g -O0
+
+default: m5term
+
+m5term: term.c
+ $(CC) $(LFLAGS) -o $@ $^
+
+install: m5term
+ $(SUDO) install -o root -m 555 m5term /usr/local/bin
+
+clean:
+ @rm -f m5term *~ .#*
+
+.PHONY: clean
diff --git a/util/term/term.c b/util/term/term.c
new file mode 100644
index 000000000..d445b4d37
--- /dev/null
+++ b/util/term/term.c
@@ -0,0 +1,312 @@
+/* $Id$ */
+/* $OpenBSD: netcat.c,v 1.57 2002/12/30 18:00:18 stevesk Exp $ */
+/*
+ * Copyright (c) 2001 Eric Jackson <ericj@monkey.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/termios.h>
+#include <sys/time.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+#include <arpa/telnet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <poll.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+ssize_t atomicio(ssize_t (*)(), int, void *, size_t);
+void readwrite(int);
+int remote_connect(char *, char *, struct addrinfo);
+
+struct termios saved_ios;
+void raw_term();
+void restore_term();
+
+char progname[256];
+void usage(int);
+
+int
+main(int argc, char *argv[])
+{
+ int ch, s, ret;
+ char *host, *port, *endp;
+ struct addrinfo hints;
+ socklen_t len;
+
+ ret = 1;
+ s = 0;
+ host = NULL;
+ port = NULL;
+ endp = NULL;
+
+ strncpy(progname, argv[0], sizeof progname);
+
+ /* Cruft to make sure options are clean, and used properly. */
+ if (argc != 3 || !argv[1] || !argv[2])
+ usage(1);
+
+ host = argv[1];
+ port = argv[2];
+
+
+ if (!isatty(STDIN_FILENO))
+ errx(1, "not attached to a terminal");
+
+ raw_term();
+
+ /* Initialize addrinfo structure */
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ s = remote_connect(host, port, hints);
+ ret = 0;
+ readwrite(s);
+
+ if (s)
+ close(s);
+
+ exit(ret);
+}
+
+/*
+ * remote_connect()
+ * Return's a socket connected to a remote host. Properly bind's to a local
+ * port or source address if needed. Return's -1 on failure.
+ */
+int
+remote_connect(char *host, char *port, struct addrinfo hints)
+{
+ struct addrinfo *res, *res0;
+ int s, error;
+
+ if ((error = getaddrinfo(host, port, &hints, &res)))
+ errx(1, "getaddrinfo: %s", gai_strerror(error));
+
+ res0 = res;
+ do {
+ if ((s = socket(res0->ai_family, res0->ai_socktype,
+ res0->ai_protocol)) < 0)
+ continue;
+
+ if (connect(s, res0->ai_addr, res0->ai_addrlen) == 0)
+ break;
+
+ close(s);
+ s = -1;
+ } while ((res0 = res0->ai_next) != NULL);
+
+ freeaddrinfo(res);
+
+ return (s);
+}
+
+/*
+ * readwrite()
+ * Loop that polls on the network file descriptor and stdin.
+ */
+void
+readwrite(int nfd)
+{
+ struct pollfd pfd[2];
+ char buf[BUFSIZ];
+ int wfd = fileno(stdin), n, ret;
+ int lfd = fileno(stdout);
+ int escape = 0;
+
+ /* Setup Network FD */
+ pfd[0].fd = nfd;
+ pfd[0].events = POLLIN;
+
+ /* Setup STDIN FD */
+ pfd[1].fd = wfd;
+ pfd[1].events = POLLIN;
+
+ while (pfd[0].fd != -1) {
+ if ((n = poll(pfd, 2, -1)) < 0) {
+ close(nfd);
+ err(1, "Polling Error");
+ }
+
+ if (n == 0)
+ return;
+
+ if (pfd[0].revents & POLLIN) {
+ if ((n = read(nfd, buf, sizeof(buf))) < 0)
+ return;
+ else if (n == 0) {
+ shutdown(nfd, SHUT_RD);
+ pfd[0].fd = -1;
+ pfd[0].events = 0;
+ } else {
+ if ((ret = atomicio(write, lfd, buf, n)) != n)
+ return;
+ }
+ }
+
+ if (pfd[1].revents & POLLIN) {
+ if ((n = read(wfd, buf, sizeof(buf))) < 0)
+ return;
+ else if (n == 0) {
+ shutdown(nfd, SHUT_WR);
+ pfd[1].fd = -1;
+ pfd[1].events = 0;
+ } else {
+ if (escape) {
+ char buf2[] = "~";
+ if (*buf == '.') {
+ printf("quit!\n");
+ return;
+ }
+ escape = 0;
+ if (*buf != '~' &&
+ (ret = atomicio(write, nfd, buf2, 1)) != n)
+ return;
+ } else {
+ escape = (*buf == '~');
+ if (escape)
+ continue;
+ }
+
+ if((ret = atomicio(write, nfd, buf, n)) != n)
+ return;
+ }
+ }
+ }
+}
+
+void
+usage(int ret)
+{
+ fprintf(stderr, "usage: %s hostname port\n", progname);
+ if (ret)
+ exit(1);
+}
+
+/*
+ * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * ensure all of data on socket comes through. f==read || f==write
+ */
+ssize_t
+atomicio(ssize_t (*f) (), int fd, void *_s, size_t n)
+{
+ char *s = _s;
+ ssize_t res, pos = 0;
+
+ while (n > pos) {
+ res = (f) (fd, s + pos, n - pos);
+ switch (res) {
+ case -1:
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ case 0:
+ return (res);
+ default:
+ pos += res;
+ }
+ }
+ return (pos);
+}
+
+/*
+ * Copyright (c) 2003 Nathan L. Binkert <binkertn@umich.edu>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+void
+raw_term()
+{
+ struct termios ios;
+
+ if (tcgetattr(STDIN_FILENO, &ios) < 0)
+ errx(1, "tcgetagttr\n");
+
+ memcpy(&saved_ios, &ios, sizeof(struct termios));
+
+ ios.c_iflag &= ~(ISTRIP|ICRNL|IGNCR|ICRNL|IXOFF|IXON);
+ ios.c_oflag &= ~(OPOST);
+ ios.c_oflag &= (ONLCR);
+ ios.c_lflag &= ~(ISIG|ICANON|ECHO);
+ ios.c_cc[VMIN] = 1;
+ ios.c_cc[VTIME] = 0;
+
+ if (tcsetattr(STDIN_FILENO, TCSANOW, &ios) < 0)
+ errx(1, "tcsetattr\n");
+
+ atexit(restore_term);
+}
+
+void
+restore_term()
+{
+ tcsetattr(STDIN_FILENO, TCSANOW, &saved_ios);
+}