Java/Network Protocol/Modem

Материал из Java эксперт
Перейти к: навигация, поиск

A tiny version of Ward Christensen"s MODEM program for UNIX

/*
 * Copyright (c) Ian F. Darwin, http://www.darwinsys.ru/, 1996-2002.
 * All rights reserved. Software written by Ian F. Darwin and others.
 * $Id: LICENSE,v 1.8 2004/02/09 03:33:38 ian Exp $
 *
 * 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 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 AUTHOR 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.
 * 
 * Java, the Duke mascot, and all variants of Sun"s Java "steaming coffee
 * cup" logo are trademarks of Sun Microsystems. Sun"s, and James Gosling"s,
 * pioneering role in inventing and promulgating (and standardizing) the Java 
 * language and environment is gratefully acknowledged.
 * 
 * The pioneering role of Dennis Ritchie and Bjarne Stroustrup, of AT&T, for
 * inventing predecessor languages C and C++ is also gratefully acknowledged.
 */
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
/**
 * a tiny version of Ward Christensen"s MODEM program for UNIX. 
 * Written ~ 1980 by Andrew Scott Beals. Last revised 1982.
 * A.D. 2000 - dragged from the archives for use in Java Cookbook.
 *
 * @author C version by Andrew Scott Beals, sjobrg.andy%mit-oz@mit-mc.arpa.
 * @author Java version by Ian F. Darwin, http://www.darwinsys.ru/
 * $Id: TModem.java,v 1.10 2004/04/11 23:50:40 ian Exp $
 */
public class TModem {
  protected final byte CPMEOF = 26;    /* control/z */
  protected final int MAXERRORS = 10;    /* max times to retry one block */
  protected final int SECSIZE  = 128;    /* cpm sector, transmission block */
  protected final int SENTIMOUT = 30;    /* timeout time in send */
  protected final int  SLEEP  = 30;    /* timeout time in recv */
  /* Protocol characters used */
  protected final byte  SOH  = 1;  /* Start Of Header */
  protected final byte  EOT  = 4;  /* End Of Transmission */
  protected final byte  ACK  = 6;  /* ACKnowlege */
  protected final byte  NAK  = 0x15;  /* Negative AcKnowlege */
  protected InputStream inStream;
  protected OutputStream outStream;
  protected PrintWriter errStream;
  /** Construct a TModem */
  public TModem(InputStream is, OutputStream os, PrintWriter errs) {
    inStream = is;
    outStream = os;
    errStream = errs;
  }
  /** Construct a TModem with default files (stdin and stdout). */
  public TModem() {
    inStream = System.in;
    outStream = System.out;
    errStream = new PrintWriter(System.err);
  }
  /** A main program, for direct invocation. */
  public static void main(String[] argv) throws 
    IOException, InterruptedException {
    /* argc must == 2, i.e., `java TModem -s filename" */
    if (argv.length != 2) 
      usage();
    if (argv[0].charAt(0) != "-")
      usage();
    TModem tm = new TModem();
    tm.setStandalone(true);
    boolean OK = false;
    switch (argv[0].charAt(1)){
    case "r": 
      OK = tm.receive(argv[1]); 
      break;
    case "s": 
      OK = tm.send(argv[1]); 
      break;
    default: 
      usage();
    }
    System.out.print(OK?"Done OK":"Failed");
    System.exit(0);
  }
  /* give user minimal usage message */
  protected static void usage()
  {
    System.err.println("usage: TModem -r/-s file");
    // not errStream, not die(), since this is static.
    System.exit(1);
  }
  /** If we"re in a standalone app it is OK to System.exit() */
  protected boolean standalone = false;
  public void setStandalone(boolean is) {
    standalone = is;
  }
  public boolean isStandalone() {
    return standalone;
  }
  /** A flag used to communicate with inner class IOTimer */
  protected boolean gotChar;
  /** An inner class to provide a read timeout for alarms. */
  class IOTimer extends Thread {
    String message;
    long milliseconds;
    /** Construct an IO Timer */
    IOTimer(long sec, String mesg) {
      milliseconds = 1000 * sec;
      message = mesg;
    }
    
    public void run() {
        try {
        Thread.sleep(milliseconds);
      } catch (InterruptedException e) {
        // can"t happen
      }
      /** Implement the timer */
      if (!gotChar)
      errStream.println("Timed out waiting for " + message);
      try {
        die(1);
      } catch (Exception e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
      }
    }
  }
  /*
   * send a file to the remote
   */
  public boolean send(String tfile) throws IOException, InterruptedException
  {
    char checksum, index, blocknumber, errorcount;
    byte character;
    byte[] sector = new byte[SECSIZE];
    int nbytes;
    DataInputStream foo;
    foo = new DataInputStream(new FileInputStream(tfile));
    errStream.println( "file open, ready to send");
    errorcount = 0;
    blocknumber = 1;
    // The C version uses "alarm()", a UNIX-only system call,
    // to detect if the read times out. Here we do detect it
    // by using a Thread, the IOTimer class defined above.
    gotChar = false;
    new IOTimer(SENTIMOUT, "NAK to start send").start();
    do {
      character = getchar();
      gotChar = true;
      if (character != NAK && errorcount < MAXERRORS)
        ++errorcount;
    } while (character != NAK && errorcount < MAXERRORS);
    errStream.println( "transmission beginning");
    if (errorcount == MAXERRORS) {
      xerror();
    }
    while ((nbytes=inStream.read(sector))!=0) {
      if (nbytes<SECSIZE)
        sector[nbytes]=CPMEOF;
      errorcount = 0;
      while (errorcount < MAXERRORS) {
        errStream.println( "{" + blocknumber + "} ");
        putchar(SOH);  /* here is our header */
        putchar(blocknumber);  /* the block number */
        putchar(~blocknumber);  /* & its complement */
        checksum = 0;
        for (index = 0; index < SECSIZE; index++) {
          putchar(sector[index]);
          checksum += sector[index];
        }
        putchar(checksum);  /* tell our checksum */
        if (getchar() != ACK)
          ++errorcount;
        else
          break;
      }
      if (errorcount == MAXERRORS)
        xerror();
      ++blocknumber;
    }
    boolean isAck = false;
    while (!isAck) {
      putchar(EOT);
      isAck = getchar() == ACK;
    }
    errStream.println( "Transmission complete.");
    return true;
  }
  /*
   * receive a file from the remote
   */
  public boolean receive(String tfile) throws IOException, InterruptedException
  {
    char checksum, index, blocknumber, errorcount;
    byte character;
    byte[] sector = new byte[SECSIZE];
    DataOutputStream foo;
    foo = new DataOutputStream(new FileOutputStream(tfile));
    System.out.println("you have " + SLEEP + " seconds...");
    /* wait for the user or remote to get his act together */
    gotChar = false;
    new IOTimer(SLEEP, "receive from remote").start(); 
    errStream.println("Starting receive...");
    putchar(NAK);
    errorcount = 0;
    blocknumber = 1;
    rxLoop:
    do { 
      character = getchar();
      gotChar = true;
      if (character != EOT) {
        try {
          byte not_ch;
          if (character != SOH) {
            errStream.println( "Not SOH");
            if (++errorcount < MAXERRORS)
              continue rxLoop;
            else
              xerror();
          }
          character = getchar();
          not_ch = (byte)(~getchar());
          errStream.println( "[" +  character + "] ");
          if (character != not_ch) {
            errStream.println( "Blockcounts not ~");
            ++errorcount;
            continue rxLoop;
          }
          if (character != blocknumber) {
            errStream.println( "Wrong blocknumber");
            ++errorcount;
            continue rxLoop;
          }
          checksum = 0;
          for (index = 0; index < SECSIZE; index++) {
            sector[index] = getchar();
            checksum += sector[index];
          }
          if (checksum != getchar()) {
            errStream.println( "Bad checksum");
            errorcount++;
            continue rxLoop;
          }
          putchar(ACK);
          blocknumber++;
          try {
            foo.write(sector);
          } catch (IOException e) {
            errStream.println("write failed, blocknumber " + blocknumber);
          }
        } finally {
        if (errorcount != 0)
          putchar(NAK);
      }
    }
    } while (character != EOT);
    foo.close();
    putchar(ACK);  /* tell the other end we accepted his EOT   */
    putchar(ACK);
    putchar(ACK);
    errStream.println("Receive Completed.");
    return true;
  }
  protected byte getchar() throws IOException {
    return (byte)inStream.read();
  }
  protected void putchar(int c) throws IOException {
    outStream.write(c);
  }
  protected void xerror()
  {
    errStream.println("too many errors...aborting");
    try {
      die(1);
    } catch (Exception e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
  protected void die(int how) throws Exception
  {
    if (standalone)
      System.exit(how);
    else throw new Exception("Error code " + Integer.toString(how));
  }
}





JModem - simple communications program

/*
 * Copyright (c) Ian F. Darwin, http://www.darwinsys.ru/, 1996-2002.
 * All rights reserved. Software written by Ian F. Darwin and others.
 * $Id: LICENSE,v 1.8 2004/02/09 03:33:38 ian Exp $
 *
 * 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 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 AUTHOR 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.
 * 
 * Java, the Duke mascot, and all variants of Sun"s Java "steaming coffee
 * cup" logo are trademarks of Sun Microsystems. Sun"s, and James Gosling"s,
 * pioneering role in inventing and promulgating (and standardizing) the Java 
 * language and environment is gratefully acknowledged.
 * 
 * The pioneering role of Dennis Ritchie and Bjarne Stroustrup, of AT&T, for
 * inventing predecessor languages C and C++ is also gratefully acknowledged.
 */
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.event.KeyEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Enumeration;
import java.util.HashMap;
import javax.rum.rumPortIdentifier;
import javax.rum.PortInUseException;
import javax.rum.SerialPort;
import javax.rum.UnsupportedCommOperationException;
import javax.swing.ButtonGroup;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
/**
 * JModem - simple communications program.
 * WARNING this file was built with the NetBeans Developer IDE
 * and parts of it should not be modified with a text editor.
 * @author    Ian F. Darwin, http://www.darwinsys.ru/
 * @version   $Id: JModem.java,v 1.18 2004/04/11 23:50:40 ian Exp $
 */
public class JModem extends javax.swing.JFrame {
  /** The Model. */
  JMModel theModel;
  /** The TextArea */
  JTextArea theTextArea;
  /** The courier font for the text areas and fields. */
  protected Font plainFont;
  /** The valid baud rates (actually BPS rates). */
  private int[] baudot = { 9600, 19200, 38400, 57600, 115200 };
  /** The types of remote systems. */
  private String sysTypes[] = { "Unix", "DOS", "Other" };
  private int M_RECEIVE = -1, M_SEND = +1;
  private int xferDirection = M_RECEIVE;
  /** Constructor */
  public JModem() {
    theModel = new JMModel(this);
    initComponents();
    finishConstructor();
    pack();
  }
  /** This method is called from within the constructor to
   * initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is
   * always regenerated by the FormEditor.
   */
  private void initComponents () {//GEN-BEGIN:initComponents
    setTitle ("JModem");
    addWindowListener (new java.awt.event.WindowAdapter () {
        public void windowClosing (java.awt.event.WindowEvent evt) {
          exitForm (evt);
        }
      }
    );
    getContentPane ().setLayout (new java.awt.BorderLayout ());
    jMenuBar1 = new javax.swing.JMenuBar ();
      fileMenu = new javax.swing.JMenu ();
      fileMenu.setText ("File");
        saveLogFileMenuItem = new javax.swing.JMenuItem ();
        saveLogFileMenuItem.setText ("Save Log");
        saveLogFileMenuItem.addActionListener (new java.awt.event.ActionListener () {
            public void actionPerformed (java.awt.event.ActionEvent evt) {
              saveLogFileMenuItemActionPerformed (evt);
            }
          }
        );
        fileMenu.add(saveLogFileMenuItem);
        fileMenu.addSeparator();
        exitMenuItem = new javax.swing.JMenuItem ();
        exitMenuItem.setText ("Exit");
        exitMenuItem.addActionListener (new java.awt.event.ActionListener () {
            public void actionPerformed (java.awt.event.ActionEvent evt) {
              exitMenuItemActionPerformed (evt);
            }
          }
        );
        fileMenu.add(exitMenuItem);
      jMenuBar1.add(fileMenu);
      helpMenu = new javax.swing.JMenu ();
      helpMenu.setText ("Help");
        helpAboutMenuItem = new javax.swing.JMenuItem ();
        helpAboutMenuItem.setText ("Item");
        helpAboutMenuItem.addActionListener (new java.awt.event.ActionListener () {
            public void actionPerformed (java.awt.event.ActionEvent evt) {
              helpAboutMenuItemActionPerformed (evt);
            }
          }
        );
        helpMenu.add(helpAboutMenuItem);
      jMenuBar1.add(helpMenu);

    setJMenuBar(jMenuBar1);
    connectPanel = new javax.swing.JPanel ();
    connectPanel.setLayout (new java.awt.FlowLayout ());
      connectPanelLabel = new javax.swing.JLabel ();
      connectPanelLabel.setText ("Connection");
      connectPanelLabel.setForeground (java.awt.Color.red);
      connectPanel.add (connectPanelLabel);
      portsLabel = new javax.swing.JLabel ();
      portsLabel.setText ("Port:");
      connectPanel.add (portsLabel);
      portsComboBox = new javax.swing.JComboBox ();
      portsComboBox.addActionListener (new java.awt.event.ActionListener () {
          public void actionPerformed (java.awt.event.ActionEvent evt) {
            portsComboBoxActionPerformed (evt);
          }
        }
      );
      connectPanel.add (portsComboBox);
      buadLabel = new javax.swing.JLabel ();
      buadLabel.setText ("Speed");
      connectPanel.add (buadLabel);
      baudComboBox = new javax.swing.JComboBox ();
      baudComboBox.addActionListener (new java.awt.event.ActionListener () {
          public void actionPerformed (java.awt.event.ActionEvent evt) {
            baudComboBoxActionPerformed (evt);
          }
        }
      );
      connectPanel.add (baudComboBox);
      databitsPanel = new javax.swing.JPanel ();
      databitsPanel.setPreferredSize (new java.awt.Dimension(50, 50));
      databitsPanel.setMinimumSize (new java.awt.Dimension(0, 0));
      databitsPanel.setLayout (new javax.swing.BoxLayout (databitsPanel, 1));
        d7RadioButton = new javax.swing.JRadioButton ();
        d7RadioButton.setText ("7");
        databitsPanel.add (d7RadioButton);
        d8RadioButton = new javax.swing.JRadioButton ();
        d8RadioButton.setSelected (true);
        d8RadioButton.setText ("8");
        databitsPanel.add (d8RadioButton);
      connectPanel.add (databitsPanel);
      parityPanel = new javax.swing.JPanel ();
      parityPanel.setPreferredSize (new java.awt.Dimension(50, 50));
      parityPanel.setLayout (new javax.swing.BoxLayout (parityPanel, 1));
        pNoneRadioButton = new javax.swing.JRadioButton ();
        pNoneRadioButton.setSelected (true);
        pNoneRadioButton.setText ("None");
        pNoneRadioButton.addActionListener (new java.awt.event.ActionListener () {
            public void actionPerformed (java.awt.event.ActionEvent evt) {
              pNoneRadioButtonActionPerformed (evt);
            }
          }
        );
        parityPanel.add (pNoneRadioButton);
        pEvenRadioButton = new javax.swing.JRadioButton ();
        pEvenRadioButton.setText ("Even");
        pEvenRadioButton.addActionListener (new java.awt.event.ActionListener () {
            public void actionPerformed (java.awt.event.ActionEvent evt) {
              evenRadioButtonActionPerformed (evt);
            }
          }
        );
        parityPanel.add (pEvenRadioButton);
        pOddRadioButton = new javax.swing.JRadioButton ();
        pOddRadioButton.setText ("Odd");
        pOddRadioButton.addActionListener (new java.awt.event.ActionListener () {
            public void actionPerformed (java.awt.event.ActionEvent evt) {
              oddRadioButtonActionPerformed (evt);
            }
          }
        );
        parityPanel.add (pOddRadioButton);
      connectPanel.add (parityPanel);
      sysTypeLabel = new javax.swing.JLabel ();
      sysTypeLabel.setText ("Remote:");
      connectPanel.add (sysTypeLabel);
      sysTypeComboBox = new javax.swing.JComboBox ();
      sysTypeComboBox.addActionListener (new java.awt.event.ActionListener () {
          public void actionPerformed (java.awt.event.ActionEvent evt) {
            sysTypeComboBoxActionPerformed (evt);
          }
        }
      );
      connectPanel.add (sysTypeComboBox);
      connectButton = new javax.swing.JButton ();
      connectButton.setText ("Connect");
      connectButton.addActionListener (new java.awt.event.ActionListener () {
          public void actionPerformed (java.awt.event.ActionEvent evt) {
            connectButtonActionPerformed (evt);
          }
        }
      );
      connectPanel.add (connectButton);
    getContentPane().add(connectPanel, BorderLayout.NORTH);
    xferPanel = new javax.swing.JPanel ();
    xferPanel.setLayout (new java.awt.FlowLayout ());
      xferPanelLabel = new javax.swing.JLabel ();
      xferPanelLabel.setText ("File Transfer");
      xferPanelLabel.setForeground (java.awt.Color.red);
      xferPanel.add (xferPanelLabel);
      jPanel6 = new javax.swing.JPanel ();
      jPanel6.setLayout (new javax.swing.BoxLayout (jPanel6, 1));
        sendRadioButton = new javax.swing.JRadioButton ();
        sendRadioButton.setSelected (true);
        sendRadioButton.setText ("Send");
        sendRadioButton.addActionListener (new java.awt.event.ActionListener () {
            public void actionPerformed (java.awt.event.ActionEvent evt) {
              sendRadioButtonActionPerformed (evt);
            }
          }
        );
        jPanel6.add (sendRadioButton);
        recvRadioButton = new javax.swing.JRadioButton ();
        recvRadioButton.setText ("Receive");
        recvRadioButton.addActionListener (new java.awt.event.ActionListener () {
            public void actionPerformed (java.awt.event.ActionEvent evt) {
              recvRadioButtonActionPerformed (evt);
            }
          }
        );
        jPanel6.add (recvRadioButton);
      xferPanel.add (jPanel6);
      xferFilenameLabel = new javax.swing.JLabel ();
      xferFilenameLabel.setText ("Filename:");
      xferPanel.add (xferFilenameLabel);
      xferFileNameTF = new javax.swing.JTextField ();
      xferFileNameTF.setPreferredSize (new java.awt.Dimension(100, 20));
      xferPanel.add (xferFileNameTF);
      jPanel7 = new javax.swing.JPanel ();
      jPanel7.setLayout (new javax.swing.BoxLayout (jPanel7, 1));
        xferModeTextRadioButton = new javax.swing.JRadioButton ();
        xferModeTextRadioButton.setText ("Text");
        jPanel7.add (xferModeTextRadioButton);
        xferModeBinRadioButton = new javax.swing.JRadioButton ();
        xferModeBinRadioButton.setSelected (true);
        xferModeBinRadioButton.setText ("Binary");
        jPanel7.add (xferModeBinRadioButton);
      xferPanel.add (jPanel7);
      xferButton = new javax.swing.JButton ();
      xferButton.setText ("Transfer");
      xferButton.addActionListener (new java.awt.event.ActionListener () {
          public void actionPerformed (java.awt.event.ActionEvent evt) {
            xferButtonActionPerformed (evt);
          }
        }
      );
      xferPanel.add (xferButton);
    getContentPane ().add (xferPanel, BorderLayout.SOUTH);
  }//GEN-END:initComponents
  /** Save the session log to disk.
   */
  private void saveLogFileMenuItemActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveLogFileMenuItemActionPerformed
    theModel.saveLogFile();
  }//GEN-LAST:event_saveLogFileMenuItemActionPerformed
  private void helpAboutMenuItemActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_helpAboutMenuItemActionPerformed
    note("JModem 0.0 (c) 2000 Ian F. Darwin\nhttp://www.darwinsys.ru/");
  }//GEN-LAST:event_helpAboutMenuItemActionPerformed
  private void baudComboBoxActionPerformed (java.awt.event.ActionEvent evt) {
    // Add your handling code here:
  }
  private void portsComboBoxActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_portsComboBoxActionPerformed
    // Add your handling code here:
  }//GEN-LAST:event_portsComboBoxActionPerformed
  /** A TextArea subclass with funky keypress forwarding: send to
   * remote, not to local. This IS a terminal emulator, after all.
   */
  class MyTextArea extends JTextArea {
    MyTextArea(int r, int c) {
      super(r, c);
    }
    /** Handle local KeyEvents: send KeyTyped to the remote. */
    protected void processComponentKeyEvent(java.awt.event.KeyEvent evt) {
      if (evt.getID() != KeyEvent.KEY_TYPED)
        return;
      // send keystrokes to remote, for processing.
      // do nothing locally, to avoid user keystrokes appearing twice!
      if (theModel.state != JMModel.S_CONNECTED) {
        getToolkit().beep();  // or just connect()?
        return;
      }
      char ch = evt.getKeyChar();
      if (ch == "\n") {    // XX if systemtype == dos
        // sendChar("\r");
        theModel.sendChar("\n");
        return;
      }
      theModel.sendChar(ch);
    }
  }
  /** Finish the initializations. */
  private void finishConstructor() {
    // Create the textarea with a JScrollpane wrapping it.
    // Install it in Centre of the TextArea.
    theTextArea = new MyTextArea(20, 80);
    getContentPane().add(new JScrollPane(theTextArea), BorderLayout.CENTER);
    plainFont = new Font("courier", Font.PLAIN, 13);
    theTextArea.setFont(plainFont);
    xferFileNameTF.setFont(plainFont);
    theModel.populateComboBox();
    portsComboBox.setSelectedIndex(0);
    // Load up the baud rate combo box
    for (int i=0; i<baudot.length; i++) {
      baudComboBox.addItem(Integer.toString(baudot[i]));
    }
    baudComboBox.setSelectedIndex(0);
    // Load up the System Type combo box
    for (int i=0; i<sysTypes.length; i++) {
      sysTypeComboBox.addItem(sysTypes[i]);
    }
    sysTypeComboBox.setSelectedIndex(0);
    // put radio buttons into groups to enforce single-selection
    ButtonGroup b1 = new ButtonGroup();
    b1.add(d7RadioButton);
    b1.add(d8RadioButton);
    ButtonGroup b2 = new ButtonGroup();
    b2.add(pNoneRadioButton);
    b2.add(pEvenRadioButton);
    b2.add(pOddRadioButton);
    ButtonGroup b3 = new ButtonGroup();
    b3.add(sendRadioButton);
    b3.add(recvRadioButton);
    ButtonGroup b4 = new ButtonGroup();
    b4.add(xferModeTextRadioButton);
    b4.add(xferModeBinRadioButton);
    xferModeBinRadioButton.setEnabled(true);
  }
  private void recvRadioButtonActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_recvRadioButtonActionPerformed
    xferDirection = M_RECEIVE;
  }//GEN-LAST:event_recvRadioButtonActionPerformed
  private void sendRadioButtonActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sendRadioButtonActionPerformed
    xferDirection = M_SEND;
  }//GEN-LAST:event_sendRadioButtonActionPerformed
  private void exitMenuItemActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exitMenuItemActionPerformed
    System.exit(0);
  }//GEN-LAST:event_exitMenuItemActionPerformed
  private void sysTypeComboBoxActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sysTypeComboBoxActionPerformed
    // Add your handling code here:
  }//GEN-LAST:event_sysTypeComboBoxActionPerformed
  private void pNoneRadioButtonActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pNoneRadioButtonActionPerformed
    // Add your handling code here:
  }//GEN-LAST:event_pNoneRadioButtonActionPerformed
  private void oddRadioButtonActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_oddRadioButtonActionPerformed
    // Add your handling code here:
  }//GEN-LAST:event_oddRadioButtonActionPerformed
  private void evenRadioButtonActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_evenRadioButtonActionPerformed
    // Add your handling code here:
  }//GEN-LAST:event_evenRadioButtonActionPerformed

  /** This method basically toggles between Connected mode and
   * disconnected mode.
   */
  private void connectButtonActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_connectButtonActionPerformed
    if (theModel.state == JMModel.S_CONNECTED) {
      theModel.disconnect();  // calls our disconnect() if OK
    } else {
      theModel.connect();    // calls our connect() if OK
    }
  }//GEN-LAST:event_connectButtonActionPerformed
  /** Show that we have connected to the serial port. */
  void connect() {
      connectButton.setText("Disconnect");
      theTextArea.setEditable(true);
      theTextArea.requestFocus();
  }
  /** Show that we have connected to the serial port. */
  void disconnect() {
      connectButton.setText("Connect");
      theTextArea.setEditable(false);
  }
  private void xferButtonActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_xferButtonActionPerformed
    // Do the transfer, using TModem class.
    theModel.xfer();
  }//GEN-LAST:event_xferButtonActionPerformed
  /** Exit the Application */
  private void exitForm(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_exitForm
    System.exit (0);
  }//GEN-LAST:event_exitForm

// Some of these must be package-level visibility for JMModel,
// until we re-define the interface to that class a little...
// Variables declaration - do not modify//GEN-BEGIN:variables
  private javax.swing.JPanel connectPanel;
  private javax.swing.JPanel xferPanel;
  private javax.swing.JLabel connectPanelLabel;
  private javax.swing.JLabel portsLabel;
  protected javax.swing.JComboBox portsComboBox;
  private javax.swing.JLabel buadLabel;
  protected javax.swing.JComboBox baudComboBox;
  private javax.swing.JPanel databitsPanel;
  private javax.swing.JPanel parityPanel;
  private javax.swing.JLabel sysTypeLabel;
  private javax.swing.JComboBox sysTypeComboBox;
  private javax.swing.JButton connectButton;
  private javax.swing.JRadioButton d7RadioButton;
  private javax.swing.JRadioButton d8RadioButton;
  private javax.swing.JRadioButton pNoneRadioButton;
  private javax.swing.JRadioButton pEvenRadioButton;
  private javax.swing.JRadioButton pOddRadioButton;
  private javax.swing.JLabel xferPanelLabel;
  private javax.swing.JPanel jPanel6;
  private javax.swing.JLabel xferFilenameLabel;
  private javax.swing.JTextField xferFileNameTF;
  private javax.swing.JPanel jPanel7;
  private javax.swing.JButton xferButton;
  private javax.swing.JRadioButton sendRadioButton;
  private javax.swing.JRadioButton recvRadioButton;
  private javax.swing.JRadioButton xferModeTextRadioButton;
  private javax.swing.JRadioButton xferModeBinRadioButton;
  private javax.swing.JMenuBar jMenuBar1;
  private javax.swing.JMenu fileMenu;
  private javax.swing.JMenu helpMenu;
  private javax.swing.JMenuItem saveLogFileMenuItem;
  private javax.swing.JMenuItem exitMenuItem;
  private javax.swing.JMenuItem helpAboutMenuItem;
// End of variables declaration//GEN-END:variables
  /** Tell if the user wants 7 or 8-bit words */
  public int getDataBits() {
    if (d7RadioButton.isSelected())
      return 7;
    if (d8RadioButton.isSelected())
      return 8;
    throw new IllegalStateException("No word size in radio button group");
  }
  /** Tell if the user wants even, odd, or no parity. */
  public int getParity() {
    if (pNoneRadioButton.isSelected()) return JMModel.PARITY_NONE;
    if (pEvenRadioButton.isSelected()) return JMModel.PARITY_EVEN;
    if (pOddRadioButton.isSelected())  return JMModel.PARITY_ODD;
    throw new IllegalStateException("No parity in radio button group");
  }
  /** Get the filename */
  public String getXferFileName() {
    return xferFileNameTF.getText();
  }
  /** "One if by send, two if receive" */
  public boolean isSend() {
    if (sendRadioButton.isSelected())
      return true;
    if (recvRadioButton.isSelected())
      return false;
    throw new IllegalStateException("No send/recv set in radio button group");
  }
  /** Convenience routine: Show a standard-form information dialog */
  void note(String message) {
    JOptionPane.showMessageDialog(this, message,
      "JModem Notice", JOptionPane.INFORMATION_MESSAGE);
    return;
  }
  /** Convenience routine: Show a standard-form error dialog */
  void err(String message) {
    JOptionPane.showMessageDialog(this, message,
      "JModem Error", JOptionPane.ERROR_MESSAGE);
    return;
  }
  /** Main: just create and show the application class. */
  public static void main(java.lang.String[] args) {
    new JModem().setVisible(true);
  }
}
/**
 * JMModel -- Communications I/O for JModem. No GUI stuff here.
 * @author   Ian F. Darwin, http://www.darwinsys.ru/
 * @version   $Id: JMModel.java,v 1.4 2004/04/11 23:50:40 ian Exp $
 */
class JMModel extends java.lang.Object {
  /** The View */
  JModem theGUI;
  /** The javax.ru.rumPort object in use */
  private SerialPort thePort;
  /** The input and output streams */
  private InputStream serialInput;
  private OutputStream serialOutput;
  /** The size of the static read buffer. */
  protected static final int BUFSIZE = 1024;
  /** A buffer for the read listener; preallocated once. */
  static byte[] buf = new byte[BUFSIZE];
  /** A Thread for reading from the remote. */
  protected Thread serialReadThread;
  /** A file transfer program */
  protected TModem xferProg;
  /** The state for disconnected and connected */
  static int S_DISCONNECTED = 0, S_CONNECTED = 1;
  /** The state, either disconnected or connected */
  int state = S_DISCONNECTED;
  /** The substate settings */
  static int S_INTERACT = 0, S_XFER = 1;
  /** The online state, either interactive or in xfer. Used by the
   * main reader thread to avoid reading data meant for the xfer program.
   */
  int submode = S_INTERACT;
  // Constants to hide the Comm API from our GUI.
  public final static int PARITY_NONE = SerialPort.PARITY_NONE;
  public final static int PARITY_EVEN = SerialPort.PARITY_EVEN;
  public final static int PARITY_ODD  = SerialPort.PARITY_ODD;
  private int[] baudot = { 9600, 19200, 38400, 57600, 115200 };
  private String sysTypes[] = { "Unix", "DOS", "Other" };
  protected HashMap portsIDmap = new HashMap();
  /** Constructor */
  public JMModel(JModem gui) {
    theGUI = gui;
  }
  protected String DEFAULT_LOG_FILE = "jmodemlog.txt";;
  /** Use normal java.io to save the JTextArea"s session log
   * into a file.
   */  
  public void saveLogFile() {
    String fileName = DEFAULT_LOG_FILE;
    try {
      Writer w = new FileWriter(fileName);
      theGUI.theTextArea.write(w);
      w.write("\r"); w.write("\n");  // in case last line is a prompt.
      w.close();
    } catch (IOException e) {
      theGUI.err("Error saving log file:\n" + e.toString());
      return;
    }
    theGUI.note("Session log saved to " + fileName);
  }
  /** Load the list of Serial Ports into the chooser.
   * This code is far too chummy with the innards of class JModem.
   */
  void populateComboBox() {
    // get list of ports available on this particular computer,
    // by calling static method in CommPortIdentifier.
    Enumeration pList = CommPortIdentifier.getPortIdentifiers();
    // Process the list of ports, putting serial ports into ComboBox
    while (pList.hasMoreElements()) {
      CommPortIdentifier cpi = (CommPortIdentifier)pList.nextElement();
      if (cpi.getPortType() == CommPortIdentifier.PORT_SERIAL) {
        theGUI.portsComboBox.addItem(cpi.getName());
        portsIDmap.put(cpi.getName(), cpi);
      }
    }
  }
  /** Connect to the chosen serial port, and set parameters. */
  void connect() {
    try {
      // Open the specified serial port
      CommPortIdentifier cpi = (CommPortIdentifier)portsIDmap.get(
      theGUI.portsComboBox.getSelectedItem());
      thePort = (SerialPort)cpi.open("JModem", 15*1000);
      // Set the serial port parameters.
      thePort.setSerialPortParams(
        baudot[theGUI.baudComboBox.getSelectedIndex()],    // baud
        theGUI.getDataBits() == 7 ?
        SerialPort.DATABITS_7 : SerialPort.DATABITS_8,
        SerialPort.STOPBITS_1,              // stop bits
        theGUI.getParity());              // parity
      thePort.setFlowControlMode(SerialPort.FLOWCONTROL_RTSCTS_IN &
        SerialPort.FLOWCONTROL_RTSCTS_OUT);
    } catch (PortInUseException pue) {
      theGUI.err("Port in use: close other app, or use different port.");
      return;
    } catch (UnsupportedCommOperationException uoe) {
      theGUI.err("Unsupported options error: try different settings");
      return;
    }
    // Similar to "raw" mode: return when 1 or more chars available.
    try {
      thePort.enableReceiveThreshold(1);
      if (!thePort.isReceiveThresholdEnabled()) {
        theGUI.err("Could not set receive threshold");
        disconnect();
        return;
      }
      thePort.setInputBufferSize(buf.length);
    } catch (UnsupportedCommOperationException ev) {
      theGUI.err("Unable to set receive threshold in Comm API; port unusable.");
      disconnect();
      return;
    }
    // Get the streams
    try {
      serialInput = thePort.getInputStream();
    } catch (IOException e) {
      theGUI.err("Error getting input stream:\n" + e.toString());
      return;
    }
    try {
      serialOutput = thePort.getOutputStream();
    } catch (IOException e) {
      theGUI.err("Error getting output stream:\n" + e.toString());
      return;
    }
    // Now that we"re all set, create a Thread to read data from the remote
    serialReadThread = new Thread(new Runnable() {
    int nbytes = buf.length;
      public void run() {
        do {
          try {
            // If the xfer program is running, stay out of its way.
              if (submode == S_XFER) {
                delay(1000);
              continue;
            }
            nbytes = serialInput.read(buf, 0, buf.length);
          } catch (IOException ev) {
          theGUI.err("Error reading from remote:\n" + ev.toString());
          return;
          }
          // XXX need an appendChar() method in MyTextArea
          String tmp = new String(buf, 0, nbytes);
          theGUI.theTextArea.append(tmp);
          theGUI.theTextArea.setCaretPosition(
          theGUI.theTextArea.getText().length());
        } while (serialInput != null);
      }
    });
    serialReadThread.start();
    // Finally, tell rest of program, and user, that we"re online.
    state = S_CONNECTED;
    theGUI.connect();
  }
  /** Break our connection to the serial port. */
  void disconnect() {
    // Tell java.io we are done with the input and output
    try {
      serialReadThread.stop();  // IGNORE DEPRECATION WARNINGS; the Java
      // API still lacks a reliable termination method for Threads
      // that are blocked on e.g., local disk reads.
      serialInput.close();
      serialOutput.close();
      serialOutput = null;
    } catch (IOException e) {
      theGUI.err("IO Exception closing port:\n" + e.toString());
    }
    // Tell javax.rum we are done with the port.
    thePort.removeEventListener();
    thePort.close();
    // Discard TModem object, if present.
    xferProg = null;
    // Tell rest of program we are no longer online.
    state = S_DISCONNECTED;
    theGUI.disconnect();
  }
  /** Convenience routine, due to useless InterruptedException */
  public void delay(long milliseconds) {
    try {
      Thread.sleep(milliseconds);
    } catch (InterruptedException e) {
      // can"t happen
    }
  }
  /** Send one character to the remote */
  void sendChar(char ch) {
    if (state != S_CONNECTED)
      return;
    // System.err.println("--> " + ch);
    try {
      serialOutput.write(ch);
    } catch (IOException e) {
      theGUI.err("Output error on remote:\n" + e.toString() +
        "\nClosing connection.");
      disconnect();
    }
  }
  /** Send a String of characters to the remote. */
  private void sendString(String s) {
    if (state != S_CONNECTED)
      return;
    try {
      serialOutput.write(s.getBytes());
    } catch (IOException e) {
      theGUI.err("Output error on remote:\n" + e.toString() +
        "\nClosing connection.");
      disconnect();
    }
  }
  /** Do one complete file transfer, using TModem */
  public void xfer() {
    if (state != S_CONNECTED) {
      theGUI.err("Must be connected to do file transfers");
      return;
    }
    if (xferProg == null) {
      xferProg = new TModem(serialInput, serialOutput, 
        new PrintWriter(System.out)); // xerProg discarded in disconnect()
    }
    String fileName = theGUI.getXferFileName();
    if (fileName.length() == 0) {
      theGUI.err("Filename must be given");
      return;
    }
    // Do the transfer!  If we are sending, send a "tmodem -r" to
    // the other side; if receiving, send "tmodem -s" to ask it
    // to send the file.
    try {
      if (theGUI.isSend()) {
        if (!new File(fileName).canRead()) {
            theGUI.err("Can"t read file " + fileName + ".");
          return;
        }
        // Other end must "r"eceive what we send.
        sendString("tmodem -r " + fileName + "\r\n");
        delay(500);    // let command echo back to us
        submode = S_XFER;
        xferProg.send(fileName);
      } else {
        // Other end must send for us to receive.
        sendString("tmodem -s " + fileName + "\r\n");
        delay(500);    // let command echo back to us
        submode = S_XFER;
        xferProg.receive(fileName);
      }
    } catch (InterruptedException e) {
      theGUI.err("Timeout");
      return;
    } catch (IOException e) {
      theGUI.err("IO Exception in transfer:\n" + e);
      return;
    } catch (Exception ev) {
      theGUI.err("Protocol failure:\n" + ev);
      return;
    } finally {
      submode = S_INTERACT;
    }
    theGUI.note("File Transfer completed");
  }
}


/**
 * a tiny version of Ward Christensen"s MODEM program for UNIX. 
 * Written ~ 1980 by Andrew Scott Beals. Last revised 1982.
 * A.D. 2000 - dragged from the archives for use in Java Cookbook.
 *
 * @author C version by Andrew Scott Beals, sjobrg.andy%mit-oz@mit-mc.arpa.
 * @author Java version by Ian F. Darwin, ian@darwinsys.ru
 * $Id: TModem.java,v 1.8 2000/03/02 03:40:50 ian Exp $
 */
class TModem {
    protected final byte CPMEOF = 26;       /* control/z */
    protected final int MAXERRORS = 10;     /* max times to retry one block */
    protected final int SECSIZE = 128;      /* cpm sector, transmission block */
    protected final int SENTIMOUT = 30;     /* timeout time in send */
    protected final int SLEEP   = 30;       /* timeout time in recv */
    /* Protocol characters used */
    protected final byte    SOH = 1;    /* Start Of Header */
    protected final byte    EOT = 4;    /* End Of Transmission */
    protected final byte    ACK = 6;    /* ACKnowlege */
    protected final byte    NAK = 0x15; /* Negative AcKnowlege */
    protected InputStream inStream;
    protected OutputStream outStream;
    protected PrintWriter errStream;
    /** Construct a TModem */
    public TModem(InputStream is, OutputStream os, PrintWriter errs) {
        inStream = is;
        outStream = os;
        errStream = errs;
    }
    /** Construct a TModem with default files (stdin and stdout). */
    public TModem() {
        inStream = System.in;
        outStream = System.out;
        errStream = new PrintWriter(System.err);
    }
    /** A main program, for direct invocation. */
    public static void main(String[] argv) throws 
        IOException, InterruptedException {
        /* argc must == 2, i.e., `java TModem -s filename" */
        if (argv.length != 2) 
            usage();
        if (argv[0].charAt(0) != "-")
            usage();
        TModem tm = new TModem();
        tm.setStandalone(true);
        boolean OK = false;
        switch (argv[0].charAt(1)){
        case "r": 
            OK = tm.receive(argv[1]); 
            break;
        case "s": 
            OK = tm.send(argv[1]); 
            break;
        default: 
            usage();
        }
        System.out.print(OK?"Done OK":"Failed");
        System.exit(0);
    }
    /* give user minimal usage message */
    protected static void usage()
    {
        System.err.println("usage: TModem -r/-s file");
        // not errStream, not die(), since this is static.
        System.exit(1);
    }
    /** If we"re in a standalone app it is OK to System.exit() */
    protected boolean standalone = false;
    public void setStandalone(boolean is) {
        standalone = is;
    }
    public boolean isStandalone() {
        return standalone;
    }
    /** A flag used to communicate with inner class IOTimer */
    protected boolean gotChar;
    /** An inner class to provide a read timeout for alarms. */
    class IOTimer extends Thread {
        String message;
        long milliseconds;
        /** Construct an IO Timer */
        IOTimer(long sec, String mesg) {
            milliseconds = 1000 * sec;
            message = mesg;
        }
        
        public void run() {
          try {
            Thread.sleep(milliseconds);
          } catch (InterruptedException e) {
            // can"t happen
          }
          /** Implement the timer */
          if (!gotChar)
            errStream.println("Timed out waiting for " + message);
            die(1);
        }
    }
    /*
     * send a file to the remote
     */
    public boolean send(String tfile) throws IOException, InterruptedException
    {
        char checksum, index, blocknumber, errorcount;
        byte character;
        byte[] sector = new byte[SECSIZE];
        int nbytes;
        DataInputStream foo;
        foo = new DataInputStream(new FileInputStream(tfile));
        errStream.println( "file open, ready to send");
        errorcount = 0;
        blocknumber = 1;
        // The C version uses "alarm()", a UNIX-only system call,
        // to detect if the read times out. Here we do detect it
        // by using a Thread, the IOTimer class defined above.
        gotChar = false;
        new IOTimer(SENTIMOUT, "NAK to start send").start();
        do {
            character = getchar();
            gotChar = true;
            if (character != NAK && errorcount < MAXERRORS)
                ++errorcount;
        } while (character != NAK && errorcount < MAXERRORS);
        errStream.println( "transmission beginning");
        if (errorcount == MAXERRORS) {
            xerror();
        }
        while ((nbytes=inStream.read(sector))!=0) {
            if (nbytes<SECSIZE)
                sector[nbytes]=CPMEOF;
            errorcount = 0;
            while (errorcount < MAXERRORS) {
                errStream.println( "{" + blocknumber + "} ");
                putchar(SOH);   /* here is our header */
                putchar(blocknumber);   /* the block number */
                putchar(~blocknumber);  /* & its complement */
                checksum = 0;
                for (index = 0; index < SECSIZE; index++) {
                    putchar(sector[index]);
                    checksum += sector[index];
                }
                putchar(checksum);  /* tell our checksum */
                if (getchar() != ACK)
                    ++errorcount;
                else
                    break;
            }
            if (errorcount == MAXERRORS)
                xerror();
            ++blocknumber;
        }
        boolean isAck = false;
        while (!isAck) {
            putchar(EOT);
            isAck = getchar() == ACK;
        }
        errStream.println( "Transmission complete.");
        return true;
    }
    /*
     * receive a file from the remote
     */
    public boolean receive(String tfile) throws IOException, InterruptedException
    {
        char checksum, index, blocknumber, errorcount;
        byte character;
        byte[] sector = new byte[SECSIZE];
        DataOutputStream foo;
        foo = new DataOutputStream(new FileOutputStream(tfile));
        System.out.println("you have " + SLEEP + " seconds...");
        /* wait for the user or remote to get his act together */
        gotChar = false;
        new IOTimer(SLEEP, "receive from remote").start(); 
        errStream.println("Starting receive...");
        putchar(NAK);
        errorcount = 0;
        blocknumber = 1;
        rxLoop:
        do { 
            character = getchar();
            gotChar = true;
            if (character != EOT) {
                try {
                    byte not_ch;
                    if (character != SOH) {
                        errStream.println( "Not SOH");
                        if (++errorcount < MAXERRORS)
                            continue rxLoop;
                        else
                            xerror();
                    }
                    character = getchar();
                    not_ch = (byte)(~getchar());
                    errStream.println( "[" +  character + "] ");
                    if (character != not_ch) {
                        errStream.println( "Blockcounts not ~");
                        ++errorcount;
                        continue rxLoop;
                    }
                    if (character != blocknumber) {
                        errStream.println( "Wrong blocknumber");
                        ++errorcount;
                        continue rxLoop;
                    }
                    checksum = 0;
                    for (index = 0; index < SECSIZE; index++) {
                        sector[index] = getchar();
                        checksum += sector[index];
                    }
                    if (checksum != getchar()) {
                        errStream.println( "Bad checksum");
                        errorcount++;
                        continue rxLoop;
                    }
                    putchar(ACK);
                    blocknumber++;
                    try {
                        foo.write(sector);
                    } catch (IOException e) {
                        errStream.println("write failed, blocknumber " + blocknumber);
                    }
                } finally {
                if (errorcount != 0)
                    putchar(NAK);
            }
        }
        } while (character != EOT);
        foo.close();
        putchar(ACK);   /* tell the other end we accepted his EOT   */
        putchar(ACK);
        putchar(ACK);
        errStream.println("Receive Completed.");
        return true;
    }
    protected byte getchar() throws IOException {
        return (byte)inStream.read();
    }
    protected void putchar(int c) throws IOException {
        outStream.write(c);
    }
    protected void xerror()
    {
        errStream.println("too many errors...aborting");
        die(1);
    }
    protected void die(int how)
    {
        if (standalone)
            System.exit(how);
        else 
          System.out.println(("Error code " + how));
    }
}





Print to a serial port using Java Communications

/*
 * Copyright (c) Ian F. Darwin, http://www.darwinsys.ru/, 1996-2002.
 * All rights reserved. Software written by Ian F. Darwin and others.
 * $Id: LICENSE,v 1.8 2004/02/09 03:33:38 ian Exp $
 *
 * 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 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 AUTHOR 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.
 * 
 * Java, the Duke mascot, and all variants of Sun"s Java "steaming coffee
 * cup" logo are trademarks of Sun Microsystems. Sun"s, and James Gosling"s,
 * pioneering role in inventing and promulgating (and standardizing) the Java 
 * language and environment is gratefully acknowledged.
 * 
 * The pioneering role of Dennis Ritchie and Bjarne Stroustrup, of AT&T, for
 * inventing predecessor languages C and C++ is also gratefully acknowledged.
 */

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.Enumeration;
import java.util.HashMap;
import javax.rum.rumPort;
import javax.rum.rumPortIdentifier;
import javax.rum.NoSuchPortException;
import javax.rum.ParallelPort;
import javax.rum.PortInUseException;
import javax.rum.SerialPort;
import javax.rum.UnsupportedCommOperationException;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
/**
 * Print to a serial port using Java Communications.
 *
 * @author  Ian F. Darwin, http://www.darwinsys.ru/
  */
public class ParallelPrint extends CommPortOpen {
  protected static String inputFileName;
  public static void main(String[] argv)
    throws IOException, NoSuchPortException, PortInUseException,
      UnsupportedCommOperationException {
    if (argv.length != 1) {
      System.err.println("Usage: ParallelPrint filename");
      System.exit(1);
    }
    inputFileName = argv[0];
    new ParallelPrint(null).converse();
    System.exit(0);
  }
  /* Constructor */
  public ParallelPrint(JFrame f)
    throws IOException, NoSuchPortException, PortInUseException,
      UnsupportedCommOperationException {
    
    super(f);
  }
  /** 
   * Hold the (one-way) conversation. 
   */
  protected void converse() throws IOException {
    // Make a reader for the input file.
    BufferedReader file = new BufferedReader(
      new FileReader(inputFileName));
    String line;
    while ((line = file.readLine()) != null)
      os.println(line);
    // Finally, clean up.
    file.close();
    os.close();
  }
}

class CommPortOpen {
  /** How long to wait for the open to finish up. */
  public static final int TIMEOUTSECONDS = 30;
  /** The baud rate to use. */
  public static final int BAUD = 19200;
  /** The parent JFrame, for the chooser. */
  protected JFrame parent;
  /** The input stream */
  protected BufferedReader is;
  /** The output stream */
  protected PrintStream os;
  /** The chosen Port Identifier */
  CommPortIdentifier thePortID;
  /** The chosen Port itself */
  CommPort thePort;
  public static void main(String[] argv)
    throws IOException, NoSuchPortException, PortInUseException,
      UnsupportedCommOperationException {
    new CommPortOpen(null).converse();
    System.exit(0);
  }
  /* Constructor */
  public CommPortOpen(JFrame f)
    throws IOException, NoSuchPortException, PortInUseException,
      UnsupportedCommOperationException {
    
    // Use the PortChooser from before. Pop up the JDialog.
    PortChooser chooser = new PortChooser(null);
    String portName = null;
    do {
      chooser.setVisible(true);
      
      // Dialog done. Get the port name.
      portName = chooser.getSelectedName();
      if (portName == null)
        System.out.println("No port selected. Try again.\n");
    } while (portName == null);
    // Get the CommPortIdentifier.
    thePortID = chooser.getSelectedIdentifier();
    // Now actually open the port.
    // This form of openPort takes an Application Name and a timeout.
    // 
    System.out.println("Trying to open " + thePortID.getName() + "...");
    switch (thePortID.getPortType()) {
    case CommPortIdentifier.PORT_SERIAL:
      thePort = thePortID.open("DarwinSys DataComm",
        TIMEOUTSECONDS * 1000);
      SerialPort myPort = (SerialPort) thePort;
      // set up the serial port
      myPort.setSerialPortParams(BAUD, SerialPort.DATABITS_8,
        SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
      break;
    case CommPortIdentifier.PORT_PARALLEL:
      thePort = thePortID.open("DarwinSys Printing",
        TIMEOUTSECONDS * 1000);
      ParallelPort pPort = (ParallelPort)thePort;
      // Tell API to pick "best available mode" - can fail!
      // myPort.setMode(ParallelPort.LPT_MODE_ANY);
      // Print what the mode is
      int mode = pPort.getMode();
      switch (mode) {
        case ParallelPort.LPT_MODE_ECP:
          System.out.println("Mode is: ECP");
          break;
        case ParallelPort.LPT_MODE_EPP:
          System.out.println("Mode is: EPP");
          break;
        case ParallelPort.LPT_MODE_NIBBLE:
          System.out.println("Mode is: Nibble Mode.");
          break;
        case ParallelPort.LPT_MODE_PS2:
          System.out.println("Mode is: Byte mode.");
          break;
        case ParallelPort.LPT_MODE_SPP:
          System.out.println("Mode is: Compatibility mode.");
          break;
        // ParallelPort.LPT_MODE_ANY is a "set only" mode;
        // tells the API to pick "best mode"; will report the
        // actual mode it selected.
        default:
          throw new IllegalStateException("Parallel mode " + 
            mode + " invalid.");
      }
      break;
    default:  // Neither parallel nor serial??
      throw new IllegalStateException("Unknown port type " + thePortID);
    }
    // Get the input and output streams
    // Printers can be write-only
    try {
      is = new BufferedReader(new InputStreamReader(thePort.getInputStream()));
    } catch (IOException e) {
      System.err.println("Can"t open input stream: write-only");
      is = null;
    }
    os = new PrintStream(thePort.getOutputStream(), true);
  }
  /** This method will be overridden by non-trivial subclasses
   * to hold a conversation. 
   */
  protected void converse() throws IOException {
    System.out.println("Ready to read and write port.");
    // Input/Output code not written -- must subclass.
    // Finally, clean up.
    if (is != null)
      is.close();
    os.close();
  }
}
class PortChooser extends JDialog implements ItemListener {
  /** A mapping from names to CommPortIdentifiers. */
  protected HashMap map = new HashMap();
  /** The name of the choice the user made. */
  protected String selectedPortName;
  /** The CommPortIdentifier the user chose. */
  protected CommPortIdentifier selectedPortIdentifier;
  /** The JComboBox for serial ports */
  protected JComboBox serialPortsChoice;
  /** The JComboBox for parallel ports */
  protected JComboBox parallelPortsChoice;
  /** The JComboBox for anything else */
  protected JComboBox other;
  /** The SerialPort object */
  protected SerialPort ttya;
  /** To display the chosen */
  protected JLabel choice;
  /** Padding in the GUI */
  protected final int PAD = 5;
  /** This will be called from either of the JComboBoxen when the
   * user selects any given item.
   */
  public void itemStateChanged(ItemEvent e) {
    // Get the name
    selectedPortName = (String)((JComboBox)e.getSource()).getSelectedItem();
    // Get the given CommPortIdentifier
    selectedPortIdentifier = (CommPortIdentifier)map.get(selectedPortName);
    // Display the name.
    choice.setText(selectedPortName);
  }
  /* The public "getter" to retrieve the chosen port by name. */
  public String getSelectedName() {
    return selectedPortName;
  }
  /* The public "getter" to retrieve the selection by CommPortIdentifier. */
  public CommPortIdentifier getSelectedIdentifier() {
    return selectedPortIdentifier;
  }
  /** A test program to show up this chooser. */
  public static void main(String[] ap) {
    PortChooser c = new PortChooser(null);
    c.setVisible(true);  // blocking wait
    System.out.println("You chose " + c.getSelectedName() +
      " (known by " + c.getSelectedIdentifier() + ").");
    System.exit(0);
  }
  /** Construct a PortChooser --make the GUI and populate the ComboBoxes.
   */
  public PortChooser(JFrame parent) {
    super(parent, "Port Chooser", true);
    makeGUI();
    populate();
    finishGUI();
  }
  /** Build the GUI. You can ignore this for now if you have not
   * yet worked through the GUI chapter. Your mileage may vary.
   */
  protected void makeGUI() {
    Container cp = getContentPane();
    JPanel centerPanel = new JPanel();
    cp.add(BorderLayout.CENTER, centerPanel);
    centerPanel.setLayout(new GridLayout(0,2, PAD, PAD));
    centerPanel.add(new JLabel("Serial Ports", JLabel.RIGHT));
    serialPortsChoice = new JComboBox();
    centerPanel.add(serialPortsChoice);
    serialPortsChoice.setEnabled(false);
    centerPanel.add(new JLabel("Parallel Ports", JLabel.RIGHT));
    parallelPortsChoice = new JComboBox();
    centerPanel.add(parallelPortsChoice);
    parallelPortsChoice.setEnabled(false);
    centerPanel.add(new JLabel("Unknown Ports", JLabel.RIGHT));
    other = new JComboBox();
    centerPanel.add(other);
    other.setEnabled(false);
    centerPanel.add(new JLabel("Your choice:", JLabel.RIGHT));
    centerPanel.add(choice = new JLabel());
    JButton okButton;
    cp.add(BorderLayout.SOUTH, okButton = new JButton("OK"));
    okButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        PortChooser.this.dispose();
      }
    });
  }
  /** Populate the ComboBoxes by asking the Java Communications API
   * what ports it has.  Since the initial information comes from
   * a Properties file, it may not exactly reflect your hardware.
   */
  protected void populate() {
    // get list of ports available on this particular computer,
    // by calling static method in CommPortIdentifier.
    Enumeration pList = CommPortIdentifier.getPortIdentifiers();
    // Process the list, putting serial and parallel into ComboBoxes
    while (pList.hasMoreElements()) {
      CommPortIdentifier cpi = (CommPortIdentifier)pList.nextElement();
      // System.out.println("Port " + cpi.getName());
      map.put(cpi.getName(), cpi);
      if (cpi.getPortType() == CommPortIdentifier.PORT_SERIAL) {
        serialPortsChoice.setEnabled(true);
        serialPortsChoice.addItem(cpi.getName());
      } else if (cpi.getPortType() == CommPortIdentifier.PORT_PARALLEL) {
        parallelPortsChoice.setEnabled(true);
        parallelPortsChoice.addItem(cpi.getName());
      } else {
        other.setEnabled(true);
        other.addItem(cpi.getName());
      }
    }
    serialPortsChoice.setSelectedIndex(-1);
    parallelPortsChoice.setSelectedIndex(-1);
  }
  protected void finishGUI() {
    serialPortsChoice.addItemListener(this);
    parallelPortsChoice.addItemListener(this);
    other.addItemListener(this);
    pack();
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  }
}





Subclasses CommPortOpen and adds send/expect handling for dealing with Hayes-type modems

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.Enumeration;
import java.util.HashMap;
import javax.rum.rumPort;
import javax.rum.rumPortIdentifier;
import javax.rum.NoSuchPortException;
import javax.rum.ParallelPort;
import javax.rum.PortInUseException;
import javax.rum.SerialPort;
import javax.rum.UnsupportedCommOperationException;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
/**
 * Subclasses CommPortOpen and adds send/expect handling for dealing
 * with Hayes-type modems.
 *
 * @author  Ian F. Darwin, http://www.darwinsys.ru/
 */
 class CommPortModem extends CommPortOpen {
  /** The last line read from the serial port. */
  protected String response;
  /** A flag to control debugging output. */
  protected boolean debug = true;
  public CommPortModem(JFrame f)
    throws IOException, NoSuchPortException,PortInUseException,
      UnsupportedCommOperationException {
    super(f);
  }
  /** Send a line to a PC-style modem. Send \r\n, regardless of
   * what platform we"re on, instead of using println().
   */
  protected void send(String s) throws IOException {
    if (debug) {
      System.out.print(">>> ");
      System.out.print(s);
      System.out.println();
    }
    os.print(s);
    os.print("\r\n");
    // Expect the modem to echo the command.
    if (!expect(s)) {
      System.err.println("WARNING: Modem did not echo command.");
    }
    // The modem sends an extra blank line by way of a prompt.
    // Here we read and discard it.
    String junk = is.readLine();
    if (junk.length() != 0) {
      System.err.print("Warning: unexpected response: ");
      System.err.println(junk);
    }
  }
  /** Read a line, saving it in "response". 
   * @return true if the expected String is contained in the response, false if not.
   */
  protected boolean expect(String exp) throws IOException {
    response = is.readLine();
    if (debug) {
      System.out.print("<<< ");
      System.out.print(response);
      System.out.println();
    }
    return response.indexOf(exp) >= 0;
  }
}
/**
 * Dial a phone using the Java Communications Package.
 *
 * @author  Ian F. Darwin, http://www.darwinsys.ru/
 */
public class CommPortDial extends CommPortModem {
  protected static String number = "000-0000";
  public static void main(String[] ap)
    throws IOException, NoSuchPortException,PortInUseException,
      UnsupportedCommOperationException {
    if (ap.length == 1)
      number = ap[0];
    new CommPortDial().converse();
    System.exit(0);
  }
  public CommPortDial() 
    throws IOException, NoSuchPortException, PortInUseException,
      UnsupportedCommOperationException {
    super(null);
  }
  protected void converse() throws IOException {
    String resp;    // the modem response.
    // Send the reset command
    send("ATZ");
    expect("OK");
    send("ATDT" + number);
    expect("OK");
    try {
      Thread.sleep(5000);
    } catch (InterruptedException e) {
      // nothing to do
    }
    is.close();
    os.close();
  }
}
class CommPortOpen {
  /** How long to wait for the open to finish up. */
  public static final int TIMEOUTSECONDS = 30;
  /** The baud rate to use. */
  public static final int BAUD = 19200;
  /** The parent JFrame, for the chooser. */
  protected JFrame parent;
  /** The input stream */
  protected BufferedReader is;
  /** The output stream */
  protected PrintStream os;
  /** The chosen Port Identifier */
  CommPortIdentifier thePortID;
  /** The chosen Port itself */
  CommPort thePort;
  public static void main(String[] argv)
    throws IOException, NoSuchPortException, PortInUseException,
      UnsupportedCommOperationException {
    new CommPortOpen(null).converse();
    System.exit(0);
  }
  /* Constructor */
  public CommPortOpen(JFrame f)
    throws IOException, NoSuchPortException, PortInUseException,
      UnsupportedCommOperationException {
    
    // Use the PortChooser from before. Pop up the JDialog.
    PortChooser chooser = new PortChooser(null);
    String portName = null;
    do {
      chooser.setVisible(true);
      
      // Dialog done. Get the port name.
      portName = chooser.getSelectedName();
      if (portName == null)
        System.out.println("No port selected. Try again.\n");
    } while (portName == null);
    // Get the CommPortIdentifier.
    thePortID = chooser.getSelectedIdentifier();
    // Now actually open the port.
    // This form of openPort takes an Application Name and a timeout.
    // 
    System.out.println("Trying to open " + thePortID.getName() + "...");
    switch (thePortID.getPortType()) {
    case CommPortIdentifier.PORT_SERIAL:
      thePort = thePortID.open("DarwinSys DataComm",
        TIMEOUTSECONDS * 1000);
      SerialPort myPort = (SerialPort) thePort;
      // set up the serial port
      myPort.setSerialPortParams(BAUD, SerialPort.DATABITS_8,
        SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
      break;
    case CommPortIdentifier.PORT_PARALLEL:
      thePort = thePortID.open("DarwinSys Printing",
        TIMEOUTSECONDS * 1000);
      ParallelPort pPort = (ParallelPort)thePort;
      // Tell API to pick "best available mode" - can fail!
      // myPort.setMode(ParallelPort.LPT_MODE_ANY);
      // Print what the mode is
      int mode = pPort.getMode();
      switch (mode) {
        case ParallelPort.LPT_MODE_ECP:
          System.out.println("Mode is: ECP");
          break;
        case ParallelPort.LPT_MODE_EPP:
          System.out.println("Mode is: EPP");
          break;
        case ParallelPort.LPT_MODE_NIBBLE:
          System.out.println("Mode is: Nibble Mode.");
          break;
        case ParallelPort.LPT_MODE_PS2:
          System.out.println("Mode is: Byte mode.");
          break;
        case ParallelPort.LPT_MODE_SPP:
          System.out.println("Mode is: Compatibility mode.");
          break;
        // ParallelPort.LPT_MODE_ANY is a "set only" mode;
        // tells the API to pick "best mode"; will report the
        // actual mode it selected.
        default:
          throw new IllegalStateException("Parallel mode " + 
            mode + " invalid.");
      }
      break;
    default:  // Neither parallel nor serial??
      throw new IllegalStateException("Unknown port type " + thePortID);
    }
    // Get the input and output streams
    // Printers can be write-only
    try {
      is = new BufferedReader(new InputStreamReader(thePort.getInputStream()));
    } catch (IOException e) {
      System.err.println("Can"t open input stream: write-only");
      is = null;
    }
    os = new PrintStream(thePort.getOutputStream(), true);
  }
  /** This method will be overridden by non-trivial subclasses
   * to hold a conversation. 
   */
  protected void converse() throws IOException {
    System.out.println("Ready to read and write port.");
    // Input/Output code not written -- must subclass.
    // Finally, clean up.
    if (is != null)
      is.close();
    os.close();
  }
}
class PortChooser extends JDialog implements ItemListener {
  /** A mapping from names to CommPortIdentifiers. */
  protected HashMap map = new HashMap();
  /** The name of the choice the user made. */
  protected String selectedPortName;
  /** The CommPortIdentifier the user chose. */
  protected CommPortIdentifier selectedPortIdentifier;
  /** The JComboBox for serial ports */
  protected JComboBox serialPortsChoice;
  /** The JComboBox for parallel ports */
  protected JComboBox parallelPortsChoice;
  /** The JComboBox for anything else */
  protected JComboBox other;
  /** The SerialPort object */
  protected SerialPort ttya;
  /** To display the chosen */
  protected JLabel choice;
  /** Padding in the GUI */
  protected final int PAD = 5;
  /** This will be called from either of the JComboBoxen when the
   * user selects any given item.
   */
  public void itemStateChanged(ItemEvent e) {
    // Get the name
    selectedPortName = (String)((JComboBox)e.getSource()).getSelectedItem();
    // Get the given CommPortIdentifier
    selectedPortIdentifier = (CommPortIdentifier)map.get(selectedPortName);
    // Display the name.
    choice.setText(selectedPortName);
  }
  /* The public "getter" to retrieve the chosen port by name. */
  public String getSelectedName() {
    return selectedPortName;
  }
  /* The public "getter" to retrieve the selection by CommPortIdentifier. */
  public CommPortIdentifier getSelectedIdentifier() {
    return selectedPortIdentifier;
  }
  /** A test program to show up this chooser. */
  public static void main(String[] ap) {
    PortChooser c = new PortChooser(null);
    c.setVisible(true);  // blocking wait
    System.out.println("You chose " + c.getSelectedName() +
      " (known by " + c.getSelectedIdentifier() + ").");
    System.exit(0);
  }
  /** Construct a PortChooser --make the GUI and populate the ComboBoxes.
   */
  public PortChooser(JFrame parent) {
    super(parent, "Port Chooser", true);
    makeGUI();
    populate();
    finishGUI();
  }
  /** Build the GUI. You can ignore this for now if you have not
   * yet worked through the GUI chapter. Your mileage may vary.
   */
  protected void makeGUI() {
    Container cp = getContentPane();
    JPanel centerPanel = new JPanel();
    cp.add(BorderLayout.CENTER, centerPanel);
    centerPanel.setLayout(new GridLayout(0,2, PAD, PAD));
    centerPanel.add(new JLabel("Serial Ports", JLabel.RIGHT));
    serialPortsChoice = new JComboBox();
    centerPanel.add(serialPortsChoice);
    serialPortsChoice.setEnabled(false);
    centerPanel.add(new JLabel("Parallel Ports", JLabel.RIGHT));
    parallelPortsChoice = new JComboBox();
    centerPanel.add(parallelPortsChoice);
    parallelPortsChoice.setEnabled(false);
    centerPanel.add(new JLabel("Unknown Ports", JLabel.RIGHT));
    other = new JComboBox();
    centerPanel.add(other);
    other.setEnabled(false);
    centerPanel.add(new JLabel("Your choice:", JLabel.RIGHT));
    centerPanel.add(choice = new JLabel());
    JButton okButton;
    cp.add(BorderLayout.SOUTH, okButton = new JButton("OK"));
    okButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        PortChooser.this.dispose();
      }
    });
  }
  /** Populate the ComboBoxes by asking the Java Communications API
   * what ports it has.  Since the initial information comes from
   * a Properties file, it may not exactly reflect your hardware.
   */
  protected void populate() {
    // get list of ports available on this particular computer,
    // by calling static method in CommPortIdentifier.
    Enumeration pList = CommPortIdentifier.getPortIdentifiers();
    // Process the list, putting serial and parallel into ComboBoxes
    while (pList.hasMoreElements()) {
      CommPortIdentifier cpi = (CommPortIdentifier)pList.nextElement();
      // System.out.println("Port " + cpi.getName());
      map.put(cpi.getName(), cpi);
      if (cpi.getPortType() == CommPortIdentifier.PORT_SERIAL) {
        serialPortsChoice.setEnabled(true);
        serialPortsChoice.addItem(cpi.getName());
      } else if (cpi.getPortType() == CommPortIdentifier.PORT_PARALLEL) {
        parallelPortsChoice.setEnabled(true);
        parallelPortsChoice.addItem(cpi.getName());
      } else {
        other.setEnabled(true);
        other.addItem(cpi.getName());
      }
    }
    serialPortsChoice.setSelectedIndex(-1);
    parallelPortsChoice.setSelectedIndex(-1);
  }
  protected void finishGUI() {
    serialPortsChoice.addItemListener(this);
    parallelPortsChoice.addItemListener(this);
    other.addItemListener(this);
    pack();
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  }
}