modsquad
2010-07-23 17:27:45 UTC
Hello everyone:
Thanks for feedback on porting ISO libraries to Logitech 3.4 Modula-2.
I decided not to bother.
However, in the interest of portability, it turns out that it is
relatively simpler to emulate PIM2 or Logitech-style I/O using ISO
Modula-2 libraries.
I would like to submit the following Beta version of a command-line
filter for testing and feedback by the Modula-2 Group.
It filters text files delimited by CR LF pairs, or single CR or LF end-
of-line marks.
It needs testing and probably modification. It is O/S dependent since
it converts the output to CR LF. It also filters non-ASCII and control
characters other than CR or LF. Even horizontal tabs are filtered.
The filtering is not the main point of the example. That can easily be
modified.
I show how to hide the peculiarity of needing to SkipLine in the ISO
libraries when reading an end-of-line mark. I am attempting to treat
end-of-line as any other character by emulating the behavior of
Logitech and other PIM2 compatible I/O libraries when end-of-line is
read.
The ojective is not efficiency but to allow the running of programs
using PIM2 compatible Modula-2 I/O on ISO-only systems. The main point
is to simplify porting of PIM2-based m2 programs to ISO-based systems,
at least for those systems using InOut or perhaps Terminal or even
FileSystem.
I would appreciate feedback and bug reports on how it runs on various
ISO-based Modula-2 compilers. In particular, any needed modifications
to get it to run on Linux-based Modula-2 or other O/S with different
end-of-line conventions. Suggestions and improvements are welcome.
The command-line filter is supposed to handle:
1. named input files (output filenames are never to be specified)
2. redirection
MODULE ISOFilter4;
(*
Copyright © 2010 Carl Glassberg
Beta Version 23-July-2010, submitted for testing purposes to
Modula-2
user community. In particular I would like someone to test this
with
any ISO-compliant Modula-2 compilers, such as GNU m2.
The end-of-line handling is probably MSDOS-biased because it
converts
end-of-lines in the input to CR LF pairs. Also CreateOutputFN
procedure is
probably not robust across operating systems nor is it likely even
bug-free.
It might be safer to just append the output file extension to the
input
filename, keeping any existing input filename extension, although
output
filenames might become inconveniently long.
I welcome feedback and any bug reports or improvements, especially
those
that make it portable to Linux or other O/S with different end-of-
line
conventions.
This program 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 2 of the License, or (at your
option) any later
version.
This program 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.
Write to
Free Software Foundation, Inc.
59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
or goto <http://www.gnu.org/licenses/gpl.html> for a copy of the
GNU General Public License.
(* ISOFilter4 cleans-up ASCII text files. *)
(* NOTE: uses ISO Modula-2 Input/Output. *)
(* end-of-line handling is O/S dependent. *)
(* Warning: This filter does not expand horizontal tabs--- a ht is
converted
to a single space. You may need to expand tabs to spaces before
running
this filter. *)
(* Filtering:
o Printable characters in the input are output unchanged.
printable char = (40C <= ch) & (ch <= 176C)
o Each control character (other than end-of-line)
and each character > 176C is converted to 1 space.
o end-of-line characters are converted as follows:
input: output:
cr lf ---> cr lf
cr ---> cr lf
lf ---> cr lf
*)
(*
--------------------------------------------------------------------------
usage: (command-line redirection from stdin/stdout)
1. isofilter4.exe < inputfile > outputfile
2. isofilter4.exe < inputfile (* output to the terminal *)
-----------------------------------------------------------------------------
usage: (no input/output filenames specified)
3. isofilter4.exe
Waits for of a line of keyboard input.
Keyboard input is echoed to the terminal.
After the user presses <Enter>, the input is filtered
and then output to the terminal on the next line.
Ctrl-C (Break) exits the program.
-----------------------------------------------------------------------------
or
usage:
4. isofilter4.exe inputfile1 { inputfile2 }
(* no output filename(s) are allowed *)
(no command-line redirection,
input filesname(s) specified as argument(s) to isofilter4)
-----------------------------------------------------------------------------
*)
IMPORT StreamFile, TextIO, IOResult, StdChans, ProgramArgs,
STextIO, SIOResult, Strings;
CONST
EOL = 36C; (* EOL is a control character not appearing in the
input *)
VAR
inf, outf : StreamFile.ChanId;
ifn, ofn : ARRAY[0..79] OF CHAR;
result : StreamFile.OpenResults;
ch : CHAR;
(* stdin/stdout: --------------------> *)
(* stdin: -----> *)
PROCEDURE done0() : BOOLEAN;
(* inspect input from stdin; return TRUE if not at end-of-file *)
BEGIN
RETURN (SIOResult.ReadResult() # SIOResult.endOfInput)
END done0;
PROCEDURE eol0() : BOOLEAN;
(* inspect input from stdin; return TRUE if at end-of-line *)
BEGIN
RETURN (SIOResult.ReadResult() = SIOResult.endOfLine)
END eol0;
PROCEDURE read_char0(VAR ch : CHAR);
(* input from stdin *)
BEGIN
STextIO.ReadChar(ch);
IF eol0() THEN ch := EOL; STextIO.SkipLine END (* if *)
END read_char0;
(* stdout: -----> *)
PROCEDURE write_char0(ch : CHAR);
(* output to stdout *)
BEGIN
IF (40C <= ch) & (ch <= 176C) THEN (* printable character *)
STextIO.WriteChar(ch)
ELSIF ch = EOL THEN
STextIO.WriteLn
ELSE (* (ch # EOL) & ((ch < 40C) OR (ch >= 177C)) *)
(* any non-printing character (except EOL) is converted to
space: *)
STextIO.WriteChar(40C)
END (* if *)
END write_char0;
(* <----- end of stdin/stdout *)
(* stderr: -------------------------> *)
PROCEDURE WriteErrMsg(str: ARRAY OF CHAR);
(* output to stderr *)
BEGIN
TextIO.WriteString(StdChans.StdErrChan(), str)
END WriteErrMsg;
PROCEDURE WriteErrLn;
(* output to stderr *)
BEGIN
TextIO.WriteLn(StdChans.StdErrChan());
END WriteErrLn;
(* <----- end of stderr *)
(* i/o from/to named file: --------> *)
(* input from named file: *)
PROCEDURE done1() : BOOLEAN;
(* inspect input from named file; return TRUE if not at end-of-file
*)
BEGIN
RETURN (IOResult.ReadResult(inf) # IOResult.endOfInput)
END done1;
PROCEDURE eol1() : BOOLEAN;
(* inspect input from named file; return TRUE if at end-of-line *)
BEGIN
RETURN (IOResult.ReadResult(inf) = IOResult.endOfLine)
END eol1;
PROCEDURE read_char1(VAR ch : CHAR);
(* input from named file *)
BEGIN
TextIO.ReadChar(inf, ch);
IF eol1() THEN ch := EOL; TextIO.SkipLine(inf) END (* if *)
END read_char1;
(* output to named file: *)
PROCEDURE write_char1(ch : CHAR);
(* output to named file *)
BEGIN
IF (40C <= ch) & (ch <= 176C) THEN (* printable character *)
TextIO.WriteChar(outf, ch)
ELSIF ch = EOL THEN
TextIO.WriteLn(outf)
ELSE (* (ch # EOL) & ((ch < 40C) OR (ch >= 177C)) *)
(* any non-printing character (except EOL) is converted to
space: *)
TextIO.WriteChar(outf, 40C)
END (* if *)
END write_char1;
(* <----- end of named file i/o *)
PROCEDURE CreateOutputFN(VAR outputfilename : ARRAY OF CHAR (*
output *);
inputfilename : ARRAY OF CHAR (* input
*);
ext : ARRAY OF CHAR (* input
*));
(*
if inputfilename has no file extension, then
outputfilename := inputfilename + "." + ext,
otherwise
the rightmost file extension and
preceding "." is deleted from inputfilename
when constructing the outputfilename.
Input (or output) filenames beginning with "." are not allowed.
Example:
inputfilename: file.txt
ext: new
outputfilename: file.new
*)
VAR n, pos : CARDINAL; found: BOOLEAN;
BEGIN
outputfilename[0] := 0C;
n := Strings.Length(inputfilename);
IF n > 0 THEN
pos := 0;
Strings.FindPrev('.', inputfilename, n-1, found, pos);
IF found THEN
IF (inputfilename[0] # ".") THEN
Strings.Extract(inputfilename, 0, pos, outputfilename);
Strings.Append('.', outputfilename);
Strings.Append(ext, outputfilename)
ELSE
WriteErrLn; WriteErrMsg('filename beginning with "." not
allowed: ');
WriteErrMsg('('); WriteErrMsg(inputfilename);
WriteErrMsg(')')
END (* if *)
ELSE (* no extension *)
Strings.Concat(inputfilename, '.', outputfilename);
Strings.Append(ext, outputfilename)
END (* if *)
END (* if *)
END CreateOutputFN;
BEGIN
IF ProgramArgs.IsArgPresent() THEN
(* command-line arguments are named input files: *)
LOOP
TextIO.ReadToken(ProgramArgs.ArgChan(), ifn);
StreamFile.Open(inf, ifn, StreamFile.read, result);
IF result # StreamFile.opened THEN
WriteErrLn; WriteErrMsg('Could not open input file: ');
WriteErrMsg(ifn);
EXIT
END (* if *);
CreateOutputFN(ofn, ifn, "new");
StreamFile.Open(outf, ofn, StreamFile.write, result);
IF result = StreamFile.fileExists THEN
WriteErrLn; WriteErrMsg('output file: ');
WriteErrMsg(ofn); WriteErrMsg(' already exists!');
IF result = StreamFile.opened THEN
StreamFile.Close(outf)
END (* if *);
ELSE (* ofn is a new output file *)
IF result # StreamFile.opened THEN
WriteErrLn; WriteErrMsg('Could not open output file: ');
WriteErrMsg(ofn);
EXIT
END (* if *);
read_char1(ch);
WHILE done1() DO
write_char1(ch);
read_char1(ch);
END (* while *);
StreamFile.Close(inf);
StreamFile.Close(outf);
END (* if *);
ProgramArgs.NextArg();
IF NOT(ProgramArgs.IsArgPresent()) THEN EXIT END (* if *);
END (* loop *)
ELSE
(* stdin/stdout with command-line redirection: *)
read_char0(ch);
WHILE done0() DO
write_char0(ch);
read_char0(ch);
END (* while *);
END (* if *);
END ISOFilter4 .
Thanks for feedback on porting ISO libraries to Logitech 3.4 Modula-2.
I decided not to bother.
However, in the interest of portability, it turns out that it is
relatively simpler to emulate PIM2 or Logitech-style I/O using ISO
Modula-2 libraries.
I would like to submit the following Beta version of a command-line
filter for testing and feedback by the Modula-2 Group.
It filters text files delimited by CR LF pairs, or single CR or LF end-
of-line marks.
It needs testing and probably modification. It is O/S dependent since
it converts the output to CR LF. It also filters non-ASCII and control
characters other than CR or LF. Even horizontal tabs are filtered.
The filtering is not the main point of the example. That can easily be
modified.
I show how to hide the peculiarity of needing to SkipLine in the ISO
libraries when reading an end-of-line mark. I am attempting to treat
end-of-line as any other character by emulating the behavior of
Logitech and other PIM2 compatible I/O libraries when end-of-line is
read.
The ojective is not efficiency but to allow the running of programs
using PIM2 compatible Modula-2 I/O on ISO-only systems. The main point
is to simplify porting of PIM2-based m2 programs to ISO-based systems,
at least for those systems using InOut or perhaps Terminal or even
FileSystem.
I would appreciate feedback and bug reports on how it runs on various
ISO-based Modula-2 compilers. In particular, any needed modifications
to get it to run on Linux-based Modula-2 or other O/S with different
end-of-line conventions. Suggestions and improvements are welcome.
The command-line filter is supposed to handle:
1. named input files (output filenames are never to be specified)
2. redirection
MODULE ISOFilter4;
(*
Copyright © 2010 Carl Glassberg
Beta Version 23-July-2010, submitted for testing purposes to
Modula-2
user community. In particular I would like someone to test this
with
any ISO-compliant Modula-2 compilers, such as GNU m2.
The end-of-line handling is probably MSDOS-biased because it
converts
end-of-lines in the input to CR LF pairs. Also CreateOutputFN
procedure is
probably not robust across operating systems nor is it likely even
bug-free.
It might be safer to just append the output file extension to the
input
filename, keeping any existing input filename extension, although
output
filenames might become inconveniently long.
I welcome feedback and any bug reports or improvements, especially
those
that make it portable to Linux or other O/S with different end-of-
line
conventions.
This program 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 2 of the License, or (at your
option) any later
version.
This program 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.
Write to
Free Software Foundation, Inc.
59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
or goto <http://www.gnu.org/licenses/gpl.html> for a copy of the
GNU General Public License.
(* ISOFilter4 cleans-up ASCII text files. *)
(* NOTE: uses ISO Modula-2 Input/Output. *)
(* end-of-line handling is O/S dependent. *)
(* Warning: This filter does not expand horizontal tabs--- a ht is
converted
to a single space. You may need to expand tabs to spaces before
running
this filter. *)
(* Filtering:
o Printable characters in the input are output unchanged.
printable char = (40C <= ch) & (ch <= 176C)
o Each control character (other than end-of-line)
and each character > 176C is converted to 1 space.
o end-of-line characters are converted as follows:
input: output:
cr lf ---> cr lf
cr ---> cr lf
lf ---> cr lf
*)
(*
--------------------------------------------------------------------------
usage: (command-line redirection from stdin/stdout)
1. isofilter4.exe < inputfile > outputfile
2. isofilter4.exe < inputfile (* output to the terminal *)
-----------------------------------------------------------------------------
usage: (no input/output filenames specified)
3. isofilter4.exe
Waits for of a line of keyboard input.
Keyboard input is echoed to the terminal.
After the user presses <Enter>, the input is filtered
and then output to the terminal on the next line.
Ctrl-C (Break) exits the program.
-----------------------------------------------------------------------------
or
usage:
4. isofilter4.exe inputfile1 { inputfile2 }
(* no output filename(s) are allowed *)
(no command-line redirection,
input filesname(s) specified as argument(s) to isofilter4)
-----------------------------------------------------------------------------
*)
IMPORT StreamFile, TextIO, IOResult, StdChans, ProgramArgs,
STextIO, SIOResult, Strings;
CONST
EOL = 36C; (* EOL is a control character not appearing in the
input *)
VAR
inf, outf : StreamFile.ChanId;
ifn, ofn : ARRAY[0..79] OF CHAR;
result : StreamFile.OpenResults;
ch : CHAR;
(* stdin/stdout: --------------------> *)
(* stdin: -----> *)
PROCEDURE done0() : BOOLEAN;
(* inspect input from stdin; return TRUE if not at end-of-file *)
BEGIN
RETURN (SIOResult.ReadResult() # SIOResult.endOfInput)
END done0;
PROCEDURE eol0() : BOOLEAN;
(* inspect input from stdin; return TRUE if at end-of-line *)
BEGIN
RETURN (SIOResult.ReadResult() = SIOResult.endOfLine)
END eol0;
PROCEDURE read_char0(VAR ch : CHAR);
(* input from stdin *)
BEGIN
STextIO.ReadChar(ch);
IF eol0() THEN ch := EOL; STextIO.SkipLine END (* if *)
END read_char0;
(* stdout: -----> *)
PROCEDURE write_char0(ch : CHAR);
(* output to stdout *)
BEGIN
IF (40C <= ch) & (ch <= 176C) THEN (* printable character *)
STextIO.WriteChar(ch)
ELSIF ch = EOL THEN
STextIO.WriteLn
ELSE (* (ch # EOL) & ((ch < 40C) OR (ch >= 177C)) *)
(* any non-printing character (except EOL) is converted to
space: *)
STextIO.WriteChar(40C)
END (* if *)
END write_char0;
(* <----- end of stdin/stdout *)
(* stderr: -------------------------> *)
PROCEDURE WriteErrMsg(str: ARRAY OF CHAR);
(* output to stderr *)
BEGIN
TextIO.WriteString(StdChans.StdErrChan(), str)
END WriteErrMsg;
PROCEDURE WriteErrLn;
(* output to stderr *)
BEGIN
TextIO.WriteLn(StdChans.StdErrChan());
END WriteErrLn;
(* <----- end of stderr *)
(* i/o from/to named file: --------> *)
(* input from named file: *)
PROCEDURE done1() : BOOLEAN;
(* inspect input from named file; return TRUE if not at end-of-file
*)
BEGIN
RETURN (IOResult.ReadResult(inf) # IOResult.endOfInput)
END done1;
PROCEDURE eol1() : BOOLEAN;
(* inspect input from named file; return TRUE if at end-of-line *)
BEGIN
RETURN (IOResult.ReadResult(inf) = IOResult.endOfLine)
END eol1;
PROCEDURE read_char1(VAR ch : CHAR);
(* input from named file *)
BEGIN
TextIO.ReadChar(inf, ch);
IF eol1() THEN ch := EOL; TextIO.SkipLine(inf) END (* if *)
END read_char1;
(* output to named file: *)
PROCEDURE write_char1(ch : CHAR);
(* output to named file *)
BEGIN
IF (40C <= ch) & (ch <= 176C) THEN (* printable character *)
TextIO.WriteChar(outf, ch)
ELSIF ch = EOL THEN
TextIO.WriteLn(outf)
ELSE (* (ch # EOL) & ((ch < 40C) OR (ch >= 177C)) *)
(* any non-printing character (except EOL) is converted to
space: *)
TextIO.WriteChar(outf, 40C)
END (* if *)
END write_char1;
(* <----- end of named file i/o *)
PROCEDURE CreateOutputFN(VAR outputfilename : ARRAY OF CHAR (*
output *);
inputfilename : ARRAY OF CHAR (* input
*);
ext : ARRAY OF CHAR (* input
*));
(*
if inputfilename has no file extension, then
outputfilename := inputfilename + "." + ext,
otherwise
the rightmost file extension and
preceding "." is deleted from inputfilename
when constructing the outputfilename.
Input (or output) filenames beginning with "." are not allowed.
Example:
inputfilename: file.txt
ext: new
outputfilename: file.new
*)
VAR n, pos : CARDINAL; found: BOOLEAN;
BEGIN
outputfilename[0] := 0C;
n := Strings.Length(inputfilename);
IF n > 0 THEN
pos := 0;
Strings.FindPrev('.', inputfilename, n-1, found, pos);
IF found THEN
IF (inputfilename[0] # ".") THEN
Strings.Extract(inputfilename, 0, pos, outputfilename);
Strings.Append('.', outputfilename);
Strings.Append(ext, outputfilename)
ELSE
WriteErrLn; WriteErrMsg('filename beginning with "." not
allowed: ');
WriteErrMsg('('); WriteErrMsg(inputfilename);
WriteErrMsg(')')
END (* if *)
ELSE (* no extension *)
Strings.Concat(inputfilename, '.', outputfilename);
Strings.Append(ext, outputfilename)
END (* if *)
END (* if *)
END CreateOutputFN;
BEGIN
IF ProgramArgs.IsArgPresent() THEN
(* command-line arguments are named input files: *)
LOOP
TextIO.ReadToken(ProgramArgs.ArgChan(), ifn);
StreamFile.Open(inf, ifn, StreamFile.read, result);
IF result # StreamFile.opened THEN
WriteErrLn; WriteErrMsg('Could not open input file: ');
WriteErrMsg(ifn);
EXIT
END (* if *);
CreateOutputFN(ofn, ifn, "new");
StreamFile.Open(outf, ofn, StreamFile.write, result);
IF result = StreamFile.fileExists THEN
WriteErrLn; WriteErrMsg('output file: ');
WriteErrMsg(ofn); WriteErrMsg(' already exists!');
IF result = StreamFile.opened THEN
StreamFile.Close(outf)
END (* if *);
ELSE (* ofn is a new output file *)
IF result # StreamFile.opened THEN
WriteErrLn; WriteErrMsg('Could not open output file: ');
WriteErrMsg(ofn);
EXIT
END (* if *);
read_char1(ch);
WHILE done1() DO
write_char1(ch);
read_char1(ch);
END (* while *);
StreamFile.Close(inf);
StreamFile.Close(outf);
END (* if *);
ProgramArgs.NextArg();
IF NOT(ProgramArgs.IsArgPresent()) THEN EXIT END (* if *);
END (* loop *)
ELSE
(* stdin/stdout with command-line redirection: *)
read_char0(ch);
WHILE done0() DO
write_char0(ch);
read_char0(ch);
END (* while *);
END (* if *);
END ISOFilter4 .