RFC 166
Data Reconfiguration Service: An implementation specification
RFC 166: DATA RECONFIGURATION SERVICE -- AN IMPLEMENTATION SPECIFICATION ๐
Okay bestie, the header first ๐ This is RFC 166 from the Network Working Group, dated 25 May 1971 (NIC 6780). The author list is a whole squad: Bob Anderson (Rand), Vint Cerf (UCLA), Eric Harslem and John Haefner (both Rand), Jim Madden (U. of Illinois), Bob Metcalfe (MIT), Arie Shoshani (SDC), Jim White (UCSB), and David Wood (Mitre) ๐ธ The header doesn't spell out a category or status, and it doesn't list anything this RFC obsoletes or updates, so there's nothing else to report there โจ
Contents ๐ง
Network Working Group Bob Anderson
<!-- placeholder -->
I. Introduction โจ
Purpose of this RFC
So the whole point of this RFC is to specify the Data Reconfiguration Service (DRS.) ๐
The DRS experiment is a software mechanism that reformats Network data streams. It can be adapted to lots of different Network application programs. We're hoping the experiment leads to a future standard service that's built on the principles in this RFC ๐ซถ
Motivation
Application programs need specific data I/O formats, and those formats are different from program to program. The vibe here is that the Network should adapt to each program's requirements, instead of changing every program to comply with a standard. This doesn't rule out standards that describe the formats of regular message contents. It just treats a standard as a desirable mode of operation, but not a necessary one ๐
There's also a format mismatch problem when users want to hook up lots of different kinds of consoles to a single service program. It's desirable to have the Network adapt to individual console configurations, rather than needing a unique software package for each console transformation ๐
One approach is for sites with serious computing power to offer a data reconfiguration service. This document is a specification of exactly that ๐ฆ
The plan is that an applications programmer defines forms that describe data reconfigurations. The service stores the forms by name. Later on, a user (maybe not a programmer at all) uses the service to do a particular transformation of a Network data stream, just by calling the form by name โญ
We tried to make a notation tailored to some specifically needed instances of data reformatting, while keeping the notation and its implementation inside a utility range. The bottom edge of that range is a notation expressive enough to make the experimental service useful. The top edge is a notation that stops short of being a general purpose programming language ๐
II. Overview of the Data Reconfiguration Service ๐ฆ
Elements of the Data Reconfiguration SERVICE
An implementation of the Data Reconfiguration Service (DRS) includes a few main pieces ๐ There are modules for connection protocols, a handler for some of the requests you can make of the service, and a compiler and/or interpreter (called the Form Machine) that acts on those requests. There's also a file storage module for saving and retrieving definitions of data reconfigurations (forms).
This section describes connection protocols and requests. The next section covers the Form Machine language in some detail. File storage isn't described in this document, because it's transparent to how you use the service, and its implementation is different at each DRS host ๐
Conceptual Network Connections
There are three conceptual Network connections to the DRS. See Fig. 1 ๐
- The control connection (CC) is between an originating user and the DRS. Forms specifying data reconfigurations are defined over this connection. The user indicates (once) which forms to apply to data passing over the two connections described below.
- The user connection (UC) is between a user process and the DRS.
- The server connection (SC) is between the DRS and the serving process.
Since the goal is to adapt the Network to user and server processes, there's a minimum of requirements imposed on the UC and SC ๐ซถ
+------------+ +------+ +---------+
| ORIGINATING| CC | DRS | SC | SERVER |
| USER |--------------| |----------| PROCESS |
+------------+ ^ +------+ ^ +---------+
| / |
| UC/ <-----\ |
| / \ |
| +-----------+ \|
TELNET ---------+ | USER | +-- Simplex or Duplex
Protocol | PROCESS | Connections
Connection +-----------+
Figure 1. DRS Network Connections
Part 2 is coming, bestie, and it gets even more fun from here ๐ฆ
CONNECTION PROTOCOLS AND MESSAGE FORMATS ๐
okay bestie, so over a control connection it's literally a direct convo between the originating user and the DRS ๐ this is where the user defines forms or assigns predefined forms to connections for reformatting โจ
the user connects to the DRS through the standard initial connection protocol (ICP) ๐ฆ and they do NOT go through a logger ๐ instead they call a particular socket that the DRS always listens on ๐ (experimental socket numbers will be published later ๐). then the DRS switches the user over to another socket pair ๐ธ
messages sent over a control connection use the types and formats specified for TELNET ๐ (the data type code should specify ASCII, which is the default). so a user at a terminal should be able to connect to a DRS through their local TELNET, like in Fig. 2 ๐ซถ
+---------+ CC +---------+
+---------| TELNET |-------| DRS |
| +---------+ +---------+
+-----------------------+
| USER |
| (TERMINAL OR PROGRAM) |
+-----------------------+
Figure 2. A TELNET Connection to DRS
when a user connects to DRS, they supply a six-character user ID (UID) as a qualifier so the uniqueness of their form names is guaranteed ๐ and they'll start out with these commands:
DEFFORM (form)ENDFORM (form)
these two commands define a form, and the text of the form is entered in order between them ๐ the form gets stored in the DRS local file system.
PURGE (form): purges the named form (qualified by the current UID) from the DRS file system ๐๏ธLISTNAMES (UID): returns the unqualified names of all forms assigned to UID ๐LISTFORM (form): returns the source text of a named form ๐DUPLEXCONNECT (user site, user receive socket, user method, server site, server receive socket, server method, user-to-server form name, server-to-user form name): makes a duplex connection between two processes using the receive sockets and the sockets one greater. method is defined below ๐ฏ the forms define the transformations on these connections โจSIMPLEXCONNECT (user site, user socket, user method, server site, server socket, server method, form): makes a simplex connection between the two sockets as specified by method ๐ซงABORT (site, receive socket): terminates the reconfiguration of data by closing both the UC and SC specified in part in the command ๐
either one, both, or neither of the two parties specified in 6 or 7 may be at the same host as the party issuing the request ๐ sites and sockets specify user and server for the connection, and method indicates the way in which the connection is established ๐
these rules apply to the commands, so pay attention bestie ๐
- Commands may be abbreviated to the minimum number of characters to identify them uniquely.
- All commands should be at the start of a line.
- Parameters are enclosed in parentheses and separated by commas.
- Imbedded blanks are ignored.
- The parameters are:
form name 1-6 characters
UID 1-6 characters
Site 1-2 characters specifying
the hexadecimal host number
Socket 1-8 characters specifying the
hexadecimal socket number
Method A single character
- Method has the following values:
C: the site/socket is already connected to the DRS as a dummy control connection (should not be the real control connection) ๐ญI: connect via the standard ICP (does not apply toSIMPLEXCONNECT) ๐ฆD: connect directly viaSTR,RTSโก
the DRS will make at least the following minimal responses to the user ๐
- A positive or negative acknowledgement after each line (CR/LF) โ
- If a form fails or terminates, the response looks like this:
TERMINATE, ASCII Host # as hex, ASCII Socket # as hex,
ASCII Return Code as decimal
which identifies at least one end of the connection ๐ฏ
EXAMPLE CONNECTION CONFIGURATIONS ๐ง
okay so there are basically two modes of DRS operation ๐
- the user wants to set up DRS UC/SC connection(s) between the programs.
- the user wants to set up the same connection(s) with their terminal sitting at the end of the UC or the SC. this one is giving "interact from my terminal" energy, and it fits when the user wants to chat with the serving process (for example, a logger) ๐ฌ
in the first case (Fig. 1, where the originating user is either a terminal or a program) the user issues the appropriate CONNECT command ๐ the UC/SC can be simplex or duplex ๐
the second case has two possible configurations, shown in Figs. 3 and 4 ๐
+-------+ +--------+ CC +-----+ +----+
| |----| |---------| | SC | |
| USER | | TELNET | UC | DRS |--------| SP |
| |----| |---------| | | |
+-------+ +--------+ +-----+ +----+
Figure 3. Use of Dummy Control Connection
+---------+
+------+ /| USER | CC +-----+
| |---/ | SIDE |--------| | SC +----+
| USER | +---------+ UC | DRS |--------| SP |
| |---\ | SERVING |--------| | +----+
+------+ \| SIDE | +-----+
+---------+
Figure 4. Use of Server TELNET
in Fig. 3 the user tells their TELNET to make two duplex connections to DRS ๐งฉ one is for control information (the CC) and the other is a dummy. when they issue the CONNECT, they reference the dummy duplex connection (UC) using the "already connected" option ๐ซถ
in Fig. 4 the user has their TELNET (user side) call the DRS ๐ when they issue the CONNECT, the DRS calls the TELNET (server side), which accepts the call on behalf of the console. this distinction is known only to the user since to the DRS, the configuration in Fig. 4 looks identical to the one in Fig. 1 ๐คซ two points should be noted:
- TELNET protocol is needed only to define forms and direct connections. It is not required for the using and serving processes.
- The using and serving processes need only a minimum of modification for Network use, i.e., an NCP interface.
III. THE FORM MACHINE ๐
INPUT/OUTPUT STREAMS AND FORMS
this section describes the syntax and semantics of forms that specify the data reconfigurations ๐ง the Form Machine gets an input stream, reformats it according to a form describing the reconfiguration, and emits the reformatted data as an output stream โจ
while reading, it'll help to picture a form being applied to the data stream, like in Fig. 5 ๐ an input stream pointer marks the position of data (in the input stream) that a part of the form is analyzing at any given time. likewise, an output stream pointer locates data being emitted in the output stream ๐
/\/\ /\/\
^ | | FORM | | ^
| | | ----------------- | | |
| | | +- ----------------- -+ | | |
| | | | CURRENT PART OF | | | |
INPUT | |<= CURRENT < ----------------- > CURRENT => | | OUTPUT
STREAM | | POINTER | FORM BEING APPLIED | POINTER | | STREAM
| | +- ----------------- -+ | |
| | ----------------- | |
| | ----------------- | |
| | ----------------- | |
\/\/ \/\/
Figure 5. Application of Form to Data Streams
FORM MACHINE BNF SYNTAX
this is the grammar for forms, and it is very much a "read it slowly" moment ๐ฅ
form ::= rule | rule form
rule ;;= label inputstream outputstream ;
label ::= INTEGER | <null>
inputstream ::= terms | <null>
terms ::= term | terms , term
outputstream ::= : terms | <null>
term ::= identifier | identifier descriptor |
descriptor | comparator
identifier ::= an alpha character followed by 0 to 3
alphanumerics
descriptor ::= (replicationexpression , datatype ,
valueexpression , lengthexpression control)
comparator ::= (value connective value control) |
(identifier *<=* control)
replicationexpression ::= # | arithmeticexpression | <null>
datatype ::= B | O | X | E | A
valueexpression ::= value | <null>
lengthexpression ::= arithmeticexpression | <null>
connective ::= .LE. | .LT. | .GE. | .GT. | .EQ. | .NE.
value ::= literal | arithmeticexpression
arithmeticexpression ::= primary | primary operator
arithmeticexpression
primary ::= identifier | L(identifier) | V(identifier) |
INTEGER
operator ::= + | - | * | /
literal ::= literaltype "string"
literaltype ::= B | O | X | E | A
string ::= from 0 to 256 characters
control ::= : options | <null>
options ::= S(where) | F(where) | U(where) |
S(where) , F(where) |
F(where) , S(where)
where ::= arithmeticexpression | R(arithmeticexpression)
ALTERNATE SPECIFICATION OF FORM MACHINE SYNTAX ๐
okay bestie, this part is the alternate way of writing the form machine syntax ๐ง it's drawn out with the little numbers stacked above and below each piece, and "infinity" means there's no upper limit โจ here's the whole thing:
infinity
form ::= {rule}
1
1 1 1
rule ::= {INTEGER} {terms} {:terms} ;
0 0 0
infinity
terms ::= term {,term}
0
1
term ::= identifier | {identifier} descriptor
0
| comparator
1
descriptor ::= ({arithmeticexpression} , datatype ,
0
1 1 1
{value} , {lengthexpression} {:options}
0 0 0
1
comparator ::= (value connective value {:options} ) |
0
1
(identifier .<=. value {:options} )
0
connective ::= .LE. | .LT. | .GE. | .GT. | .EQ. | .NE.
lengthexpression ::= # | arithmeticexpression
datatype ::= B | O | X | E | A
value ::= literal | arithmeticexpression
infinity
arithmeticexpression ::= primary {operator primary}
0
operator ::= + | - | * | /
primary ::= identifier | L(identifier) |
V(identifier) | INTEGER
256
literal ::= literaltype "{CHARACTER} "
0
literaltype ::= B | O | X | A | E
1
options ::= S(where) {,F(where)} |
0
1
F(where) {,S(where)} | U(where)
0
where ::= arithmeticexpression |
R(arithmeticexpression)
3
identifier ::= ALPHABETIC {ALPHAMERIC}
0
FORMS ๐ธ
okay so a form is an ordered set of rules ๐ it looks like this:
form ::= rule | rule form
the current rule gets applied to the current spot in the input stream ๐ if that rule's input part doesn't correctly describe what's sitting there, another rule becomes current and gets applied to the same spot. which rule comes next is either spelled out by the current term in the current rule, or it's just the next sequential rule by default. the whole flow of control is explained more under TERM AND RULE SEQUENCING, so keep that one bookmarked ๐
if the input part of a rule does correctly describe the current input stream, then some data may be emitted at the current spot in the output stream, according to the rule ๐ the input and output stream pointers then move forward over the stuff that got described and emitted, and the next rule is applied to the new current spot in the input stream. the glow-up continues โจ
the form stops when an explicit return,R(arithmeticexpression), shows up in a rule. at that point the user and server connections get closed, and the return code,(arithmeticexpression), gets sent back to the originating user. that's the big finale ๐ฌ
RULES ๐ง
a rule is a replacement, a comparison, and/or an assignment operation. here's the shape:
rule ::= label inputstream outputstream
a label is the name of a rule. it exists so the rule can be referenced somewhere else in the form, which lets you explicitly transfer control to it ๐ labels look like this:
label ::= INTEGER | <null>
the optional integer labels fall in the range 0 >= INTEGER >= 9999 ๐ข and the rules don't have to be labeled in ascending numerical order, so you do you, no cap ๐
TERMS ๐
the inputstream (what gets matched in the input) and the outputstream (what gets emitted in the output) are both made of zero or more terms. here's how they're written:
inputstream ::= terms | <null>
outputstream ::= :terms | <null>
terms ::= term | terms , term
there are four term formats, and they look like this ๐
term ::= identifier | identifier descriptor |
descriptor | comparator
Term Format 1 ๐ฆ
the first term format is just an identifier:
identifier
the identifier is a symbolic reference to a term that was already identified (that's term format 2) earlier in the form ๐ซถ it takes on the same attributes as that term, which are value, length, and type. term format 1 is normally the one you use to emit data in the output stream.
identifiers are formed by an alpha character followed by 0 to 3 alphanumeric characters, so keep 'em short and sweet ๐ธ
Term Format 2 ๐
term format 2 looks like this:
identifier descriptor
it's generally used as an input stream term, but it can be used as an output stream term too โจ
a descriptor is defined like so:
descriptor ::= (replicationexpression, datatype,
valueexpression, lengthexpression
control)
the identifier is the symbolic name of the term, in the usual programming-language sense ๐ it takes on the type, length, value, and replication attributes of the term, and it can be referenced elsewhere in the form. iconic behavior ๐
the replication expression, if you specify one, makes the unit value of the term get generated however many times the replication expression's value says. the unit value (the quantity being replicated) comes from the data type, value expression, and length expression attributes. the data type defines what kind of data is being specified, the value expression gives a nominal value that the other attributes augment, and the length expression sets the unit length of the term. (fun fact: this was modeled after the IBM SRL Form C28-6514, and it's a similar take on the defined constant pseudo instruction ๐)
the replication expression is defined like this:
replicationexpression ::= # | arithmeticexpression | <null>
arithmeticexpression ::= primary | primary operator
arithmeticexpression
operator ::= + | - | * | /
primary ::= identifier | L(identifier) | V(identifier) |
INTEGER
it's basically a repeat function that gets applied to the combined data type, value, and length expressions. it tells you how many times the nominal value gets repeated ๐
the terminal symbol # means an arbitrary replication factor ๐ฒ it must be explicitly terminated by a match or a non-match to the input stream, and that termination can come from the same term or the following one. don't leave it hanging, bestie ๐
a null replication expression has the value of one. arithmetic expressions get evaluated from left to right, with no precedence, so no fancy PEMDAS drama here ๐
the L(identifier) is a length operator. it generates a 32-bit binary integer that matches the length of the named term ๐ the V(identifier) is a value operator, and it generates a 32-bit binary integer that matches the value of the named term. (see Restrictions and Interpretations of Term Functions for more on that ๐ง) the value operator is meant to turn character strings into their numerical correspondents, which is so cute ๐ฅน
the data type is defined like so:
datatype ::= B | O | X | E | A
the data type says what kind of data the term represents ๐ฆ (it's expected that more data types, like floating point and user-defined types, will get added as needed, so stay tuned ๐)
here's the data type table, in a code block so it stays neat:
Data Type Meaning Unit Length
B Bit string 1 bit
O Bit string 3 bits
X Bit string 4 bits
E EBCDIC character 8 bits
A Network ASCII character 8 bits
the value expression is defined like this:
valueexpression ::= value | <null>
value ::= literal | arithmeticexpression
literal ::= literaltype "string"
literaltype ::= B | O | X | E | A
it's the nominal value of a term, written in the format the data type says, and it gets repeated according to the replication expression ๐
a null value expression in the input stream defaults to whatever data is already present in the input stream ๐ซง the data must still comply with the datatype attribute, though. a null value expression in the output stream generates padding according to Restrictions and Interpretations of Term Functions, so check that section too ๐ท
the length expression is defined like this:
lengthexpression ::= arithmeticexpression | <null>
it states the length of the field that holds the value expression ๐
if the length expression is less than or equal to zero, the term still succeeds, but the appropriate stream pointer does not get advanced. positive lengths, on the other hand, do advance the appropriate stream pointer, as long as the term otherwise succeeds ๐ฆ
control is defined under TERM AND RULE SEQUENCING, so yep, same bookmark ๐
Term Format 3 ๐
term format 3 looks like this:
descriptor
it's identical to term format 2, just without the identifier ๐งโโ๏ธ it's generally used in the output stream. it also shows up in the input stream, for when input data needs to be passed over but doesn't need to be kept for emission or for later reference. think of it as the "skip it, bestie" move ๐
Term Format 4 ๐
term format 4 is the comparator, and it's used for assignment and comparison โจ here's how it's written:
comparator ::= (value connective value control) |
(identifier *<=* value control)
value ::= literal | arithmeticexpression
literal ::= literaltype "string"
literaltype ::= B | O | X | E | A
string ::= from 0 to 256 characters
connective ::= .LE. | .LT. | .GE. | .GT. | .EQ. | .NE.
the assignment operator *<=* assigns the value to the identifier ๐ซถ the connectives mean what they usually mean. one thing to watch out for: the values being compared must have the same type and length attributes, or an error condition pops up and the form fails ๐ no mixing and matching, periodt ๐
โโ๏ธ
The Application of a Term
Okay so here's how a term actually gets applied, step by step ๐
- First, the data type, value expression, and length expression team up to make one unit value. Let's call it
x๐งฉ - Next, the replication expression decides how many times
xgets repeated. All thosexs get stuck together intoy, which has lengthL๐ - If it's an input stream term, then
yof lengthLgets tested against the input value starting at the current input pointer position ๐ - If the input value satisfies the constraints of
yover lengthL, then that input value of lengthLbecomes the value of the term โจ
In an output stream term, it's basically the same procedure, except the source of input is the value of the term(s) named in the value expression, and the data gets emitted in the output stream ๐
There's also a little twist. The procedure gets modified to include a one term look-ahead whenever replicated values are of indefinite length, because of the arbitrary symbol #. That's a lot of main character energy ๐ฆ
Restrictions and Interpretations of Term Functions
- Terms with indefinite lengths (because their values get repeated according to the
#symbol) must be separated by some type-specific data, like a literal. โ๏ธ (A literal isn't specifically required, though. An arbitrary number of ASCII characters could be terminated by a non-ASCII character ๐) - Truncation and padding goes like this ๐
- a) Character to character (
A <-> E) conversion is left-justified and truncated or padded on the right with blanks. - b) Character to numeric and numeric to numeric conversions are right-justified and truncated or padded on the left with zeros.
- c) Numeric to character conversions are right-justified and left-padded with blanks.
- a) Character to character (
- The following get ignored in a form definition over the control connection ๐
โโ๏ธ
- a) TELNET control characters.
- b) Blanks, except within quotes.
- c)
/* string */is treated as comments, except within quotes.
- The following defaults prevail where the term part is omitted ๐ธ
- a) The replication expression defaults to one.
- b)
#in an output stream term defaults to one. - c) The value expression of an input stream term defaults to the value found in the input stream, but the input stream must conform to the data type and length expression. The value expression of an output stream term defaults to padding only.
- e) The length expression defaults to the size of the quantity determined by the data type and value expression.
- f) Control defaults to the next sequential term if a term is successfully applied. Else, control defaults to the next sequential rule. If where evaluates to an undefined label, the form fails ๐
- Arithmetic expressions are evaluated left-to-right with no precedence. No VIP treatment for multiplication, bestie ๐งฎ
- The following limits prevail ๐ง
- a) Binary lengths are
<= 32 bits - b) Character strings are
<= 256 8-bit characters - c) Identifier names are
<= 4 characters - d) Maximum number of identifiers is
<= 256 - e) Label integers are
>= 0and<= 9999
- a) Binary lengths are
- Value and length operators produce 32-bit binary integers. The value operator is currently intended for converting A or E type decimal character strings to their binary correspondents ๐ข For example, the value of
E'12'would be0......01100. The value ofE'AB'would cause the form to fail ๐ญ
TERM AND RULE SEQUENCING
Sequencing may be explicitly controlled by including control in a term. The grammar goes like this ๐
control ::= :options | <null>
options ::= S(where) | F(where) | U(where)
S(where) , F(where) |
F(where) , S(where)
where ::= arithmeticexpression | R(arithmeticexpression)
S, F, and U denote success, fail, and unconditional transfers, respectively. Where evaluates to a rule label, so transfer can be effected from within a rule (at the end of a term) to the beginning of another rule ๐ฆ R means terminate the form and return the evaluated expression to the initiator over the control connection (if still open) ๐ฎ
If terms aren't explicitly sequenced, these defaults kick in ๐
- When a term fails, go to the next sequential rule.
- When a term succeeds, go to the next sequential term within the rule.
- At the end of a rule, go to the next sequential rule.
Look at this example to see how transfer of control and movement of the input pointer are connected ๐
1 XYZ(,B,,8:S(2),F(3)) : XYZ ;
2 . . . . . . .
3 . . . . . . .
The value of XYZ will never be emitted in the output stream, since control is transferred out of the rule upon either success or failure ๐ If the term succeeds, the 8 bits of input get assigned as the value of XYZ, and rule 2 is then applied to the same input stream data. That's because the complete left hand side of rule 1 wasn't successfully applied, so the input stream pointer isn't advanced ๐
IV. EXAMPLES โจ
REMARKS
The following examples (forms and also single rules) are simple representative uses of the Form Machine ๐ง They're written in a term-per-line format only to help with the explanation. Typically, a single rule might be written as a single line, so don't panic when you see it spread out ๐
FIELD INSERTION
To insert a field, separate the input into two terms so the inserted field fits between them. For example, to do line numbering for a 121 character/line printer with a leading carriage control character, use this form ๐จ๏ธ
(NUMB*<=*1); /*initialize line number counter to one*/
1 CC(,E,,1:F(R(99))), /*pick up control character and save
as CC*/
/*return a code of 99 upon exhaustion*/
LINE(,E,,121 : F(R(98))) /*save text as LINE*/
:CC, /*emit control character*/
(,E,NUMB,2), /*emit counter in first two columns*/
(,E,E".",1), /*emit period after line number*/
(,E,LINE,117), /*emit text, truncated in 117 byte field*/
(NUMB*<=*NUMB+1:U(1)); /*increment line counter and go to
rule one*/;;
DELETION
Data to be deleted should be isolated as separate terms on the left, so they can be omitted (by not emitting them) on the right ๐๏ธ
(,B,,8), /*isolate 8 bits to ignore*/
SAVE(,A,,10) /*extract 10 ASCII characters from
input stream*/
:(,E,SAVE,); /*emit the characters in SAVE as EBCDIC
characters whose length defaults to the
length of SAVE, i.e., 10, and advance to
the next rule*/
In the above example, if either input stream term fails, the next sequential rule gets applied ๐
VARIABLE LENGTH RECORDS
Some devices, terminals and programs generate variable length records ๐ผ The following rule picks up variable length EBCDIC records and translates them to ASCII ๐
CHAR(#,E,,1), /*pick up all (an arbitrary number of)
EBCDIC characters in the input stream*/
(,X,X"FF",2) /*followed by a hexadecimal literal,
FF (terminal signal)*/
:(,A,CHAR,), /*emit them as ASCII*/
(,X,X"25",2); /*emit an ASCII carriage return*/
STRING LENGTH COMPUTATION
It's often necessary to prefix a length field to an arbitrarily long character string ๐ง The following rule prefixes an EBCDIC string with a one-byte length field.
Q(#,E,,1), /*pick up all EBCDIC characters*/
TS(,X,X"FF",2) /*followed by a hexadecimal literal, FF*/
:(,B,L(Q)+2,8), /*emit the length of the characters
plus the length of the literal plus
the length of the count field itself,
in an 8-bit field*/
Q, /*emit the characters*/
TS, /*emit the terminal*/
TRANSPOSITION
It's often desirable to reorder fields, like in the following example ๐
Q(,E,,20), R(,E,,10) , S(,E,,15), T(,E,,5) : R, T, S, Q ;
The terms are emitted in a different order โจ
CHARACTER PACKING AND UNPACKING
In systems such as HASP, repeated sequences of characters get packed into a count followed by the character, for more efficient storage and transmission ๐ฆ The first form packs multiple characters, and the second unpacks them ๐
/*form to pack EBCDIC streams*/
/*returns 99 if OK, input exhausted*/
/*returns 98 if illegal EBCDIC*/
/*look for terminal signal FF which is not a legal EBCDIC*/
/*duplication count must be 0-254*/
1 (,X,X"FF",2 : S(R(99))) ;
/*pick up an EBCDIC char/*
CHAR(,E,,1) ;
/*get identical EBCDIC chars/*
LEN(#,E,CHAR,1)
/*emit the count and the char/*
: (,B,L(LEN)+1,8), CHAR, (:U(1));
/*end of form*/;;
/*form to unpack EBCDIC streams*/
/*look for terminal*/
1 (,X,X"FF",2 : S(R(99))) ;
/*emit character the number of times indicated*/
/*by the count, in a field the length indicated*/
/*by the counter contents*/
CNT(,B,,8), CHAR(,E,,1) : (CNT,E,CHAR,1:U(1));
/*failure of form*/
(:U(R(98))) ;;
[ This RFC was put into machine readable form for entry ] [ into the online RFC archives by Simone Demmel 03/98 ]
๐ฆ where this page comes from
- ietf: rfc166 1971-05
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms