package org.h3270.host;
/*
* Copyright (C) 2003-2008 akquinet tech@spree
*
* This file is part of h3270.
*
* h3270 is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* h3270 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with h3270; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.h3270.render.TextRenderer;
/**
* An implementation of the Screen interface that is fed by the
* output of s3270.
*
* @author Andre Spiegel spiegel@gnu.org
* @version $Id: S3270Screen.java,v 1.22 2008/11/21 14:47:22 spiegel Exp $
*/
public class S3270Screen extends AbstractScreen {
private List bufferData = null;
private String status = null;
public S3270Screen() {
width = 0;
height = 0;
buffer = null;
isFormatted = true;
}
public S3270Screen (InputStream in) {
try {
BufferedReader input =
new BufferedReader
(new InputStreamReader(in,
"ISO-8859-1"));
List lines = new ArrayList();
String status = null;
while (true) {
String line = input.readLine();
if (line == null) break;
if (line.startsWith ("data:"))
lines.add (line);
else if (Pattern.matches ("[ULE] [UF] [UC] .*", line))
status = line;
}
update (status, lines);
} catch (IOException ex) {
throw new RuntimeException ("error: " + ex);
}
}
/**
* Pattern that matches a status line from s3270.
* Example: U F U C(hostname) I 3 32 80 22 15 0x0 -
*/
private static Pattern statusPattern =
Pattern.compile ( "^[ULE] " // Keyboard State
+ "[FU] " // Formatted / Unformatted
+ "[PU] " // Protected / Unprotected (at cursor)
+ "(?:C\\([^)]*\\)|N) " // Connected / Not Connected
+ "[ILCN] " // Emulator Mode
+ "[2-5] " // Model Number
+ "[0-9]+ " // Number of Rows
+ "[0-9]+ " // Number of Columns
+ "([0-9]+) " // Cursor Row
+ "([0-9]+) " // Cursor Column
+ "0x0 " // Window ID (always 0x0)
+ "(?:[0-9.]+|-)$" // Time for last command
);
/**
* Updates this screen with output from "readbuffer ascii".
* @param status the status line that was returned by s3270
* @param bufferData the actual screen data, as a list of strings
*/
public void update (String status, List bufferData) {
this.status = status;
if (status.charAt(2) == 'F') {
isFormatted = true;
updateBuffer (bufferData);
} else {
isFormatted = false;
updateBuffer (bufferData);
}
Matcher m = statusPattern.matcher(status);
if (m.find()) {
cursorX = Integer.parseInt (m.group(2));
cursorY = Integer.parseInt (m.group(1));
InputField f = getInputFieldAt (cursorX, cursorY);
if (f != null)
f.setFocused (true);
} else {
cursorX = 0;
cursorY = 0;
}
}
private void updateBuffer (List bufferData) {
this.bufferData = new ArrayList (bufferData);
height = bufferData.size();
width = 0;
buffer = new char[height][];
fields = new ArrayList();
fieldStartX = 0;
fieldStartY = 0;
fieldStartCode = (byte)0xe0;
for (int y=0; y<height; y++) {
char[] line = decode ((String)bufferData.get(y), y, fields);
if (line.length > width) width = line.length;
buffer[y] = line;
}
// add the final field on the page
fields.add(createField(fieldStartCode, fieldStartX, fieldStartY,
width - 1, height - 1, color, ext_highlight));
}
public List getBufferData() {
return Collections.unmodifiableList (bufferData);
}
public void dump (String filename) {
try {
PrintWriter out = new PrintWriter (new FileWriter (filename));
for (Iterator i = bufferData.iterator(); i.hasNext();) {
out.println (i.next());
}
out.println (status);
out.println ("ok");
out.close();
} catch (IOException ex) {
throw new RuntimeException ("error: " + ex);
}
}
private static final Pattern FORMATTED_CHAR_PATTERN = Pattern.compile (
"SF\\((..)=(..)(,(..)=(..)(,(..)=(..))?)?\\)|[0-9a-fA-F]{2}"
);
private int fieldStartX = 0;
private int fieldStartY = 0;
private byte fieldStartCode = (byte)0xe0;
private int color = Field.ATTR_COL_DEFAULT;
private int ext_highlight = Field.ATTR_EH_DEFAULT;
/**
* Decodes a single line from the raw screen buffer dump.
*/
private char[] decode (String line, int y, List fields) {
int fieldEndX = 0;
int fieldEndY = 0;
int i;
int aux_startcode = -1;
int aux_color;
int aux_exthighlight;
String aux_code;
if (line.startsWith("data: ")) line = line.substring(6);
StringBuffer result = new StringBuffer();
int index = 0;
// workaround! delete all extended attributes in a line!
// must have, until h3270 supports extended attributes
// TODO:
line = line.replaceAll("SA\\(..=..\\)", "");
Matcher m = FORMATTED_CHAR_PATTERN.matcher (line);
while (m.find()) {
String code = m.group();
if (code.startsWith ("SF")) {
if (!isFormatted)
throw new RuntimeException
("format information in unformatted screen");
result.append (' ');
i = 1;
aux_color = -1;
aux_exthighlight = -1;
while (i <= m.groupCount()) {
aux_code = m.group(i);
if (aux_code == null) break;
if (aux_code.equals("c0")) {
if (fieldStartX != -1) {
// if we've been in an open field, close it now
fieldEndX = index - 1;
fieldEndY = y;
if (fieldEndX == -1) {
fieldEndX = width-1;
fieldEndY--;
}
}
aux_startcode = i + 1;
} else if (aux_code.equals("41")) {
aux_exthighlight = i + 1;
} else if (aux_code.equals("42")) {
aux_color = i + 1;
}
i = i + 3;
}
if (i > 1) {
if (fieldStartX != -1) {
fields.add (createField (fieldStartCode, fieldStartX,
fieldStartY, fieldEndX, fieldEndY, color,
ext_highlight));
}
fieldStartX = index + 1;
fieldStartY = y;
fieldStartCode = (byte) Integer.parseInt(m.group(aux_startcode), 16);
if (aux_exthighlight != -1) {
ext_highlight = Integer.parseInt(m.group(aux_exthighlight), 16);
} else {
ext_highlight = Field.ATTR_EH_DEFAULT;
}
if (aux_color != -1) {
color = Integer.parseInt(m.group(aux_color), 16);
} else {
color = Field.ATTR_COL_DEFAULT;
}
}
} else {
result.append ((char)(Integer.parseInt (code, 16)));
}
index++;
}
// a field that begins in the last column
if (fieldStartX == index && fieldStartY == y) {
fieldStartX = 0;
fieldStartY++;
}
return result.toString().toCharArray();
}
private Field createField(byte startCode, int startx, int starty,
int endx, int endy, int color, int ext_highlight) {
if ((startCode & Field.ATTR_PROTECTED) == 0)
return new InputField (this, startCode, startx, starty, endx, endy,
color, ext_highlight);
else
return new Field (this, startCode, startx, starty, endx, endy,
color, ext_highlight);
}
public static void main (String[] args) throws IOException {
BufferedReader in = new BufferedReader
(new FileReader ("src/org/h3270/test/screen8.dump"));
List lines = new ArrayList();
while (true) {
String line = in.readLine();
if (line == null || !line.startsWith ("data: ")) break;
lines.add (line.substring(6));
}
S3270Screen s = new S3270Screen();
s.update ("U F U", lines);
System.out.println (new TextRenderer().render(s));
}
}