Examples
Example 4. Data Set Name Edit
I have frequently had the need to determine whether or not a data set name was a valid MVS data set name; not whether or not the data set existed, but whether the name was syntactically correct. Ordinarily, I would check for a length of up to 44 characters, segments of 8 characters, and characters of the allowed type, i.e. A-Z, 0-9, etc. I decided to develop a routine that I could call with the data set name as input, and receive a pass/fail indicator as to the validity of the name. Using an LL(k) parser tool called ANTLR, I created an Extended Backus–Naur Form (EBNF) grammar for an MVS data set name. After testing the grammar, I created a C lexer for the grammar, then converted the program to HLASM with Metal C.
The call signature for the routine vdsname is shown below. There are two structs passed by address to the routine. The first struct is used to pass the dsname to be verified as well as to return other information. The second struct is used as a work area (240 bytes) for the routine and for reentrancy. The dsname to be verified is stored in inputDsname, a 44 byte character field (not null terminated). Upon return, the struct contains the following:
- Return Code / Reason Code. If 0, data set name if syntactically correct.
- Base data set name length; if a PDS member name is included, this length does not include the member name.
- Data set name length. This is the length of the entire input name, excluding blanks or nulls.
- PDS member name if supplied, along with the length of the field.
- Relative generation number if name is in GDG format, e.g. (+0).
- An array consisting of the individual segment names, not including periods. Each segment proceeded by a length field.
Type Signature
extern int vdsname(struct dsnameData *, struct parserInfo *);Testing the routine for performance, a 40 character data set name was passed to the routine. Calling the routine repeatedly 1,000,000 times took 67.54 CPU seconds, or about 68μ seconds per call, on a zEnterprise 114 3.8GHz processor.
#ifdef __MVS__
#pragma export(VDSNAME)
#pragma csect (CODE,"VDSNAME")
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#define true 1
#define false 0
#define DSN_ERROR 8
#define SEG_ERROR 200
#define LENG_ERROR 202
#define NAME_ERROR 204
#define EOF_CHAR -3
#define EOF_TYPE 1
#define ZOS_FILESIZE 44
#define zFILESIZE 64
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#define true 1
#define false 0
#undef LA
#define LA(x,y) lookAheadCharacter(x,y)
#undef match
#define match(x,y) match(x,y)
struct segment
{
int length;
char segment[8];
} ;
struct dsnameData
{
// Input
char inputDsname[zFILESIZE]; // dsname to be parsed
// Output
int returnCode;
int reasonCode;
int messageLength; // Length of Error Message or 0;
char message[128]; // Error Message
int baseDSNLength; // dsname without pds member name length
char baseDsname[44]; // input dsname w/o pds member name
int memberNameLength; // Length of pds member name or 0
char pdsMemberName[8]; // Member name if found
int relativeGenerationNo; // Relative gdg number if found,e.g. -1,0,+1,
_Bool absoluteGeneration; // true if input is gdg and absolute generati
_Bool dataSetIsPDS; // true if input is PDS/PDSE
_Bool relativeGeneration; // true if input is relative gdg
char unused[2];
int numberOfSegments; // number of dsname segments; range 1 - 22
struct segment segments[22]; // dsname segments
} ;
struct parserInfo
{
int charBufferOffset;
int charBufferSize;
int charBufferSizeLessOne;
int charMarkerOffset;
int charNumberOfEntries;
int charNumberOfMarkers;
int charNumberToConsume;
int currentInputOffset;
int currentSizeOfInputQueue;
int inputStreamPosition;
char *inputStream;
char CharBuffer[64];
char text[128];
char inputStreamEOF;
} ;
static int vdsname(struct dsnameData *, struct parserInfo *);
static void InitLexer(struct parserInfo *);
static void appendChar(char, struct parserInfo *);
static void charQueueAppend(char, struct parserInfo *);
static void consumeChar(struct parserInfo *);
static void fillCharBuffer(int, struct parserInfo *);
static void mDATASETNAME(struct dsnameData*, struct parserInfo *);
static void mMEMBER(struct dsnameData *, struct parserInfo *);
extern void mDATASETNAME(struct dsnameData *_dsnameData, struct parserInfo * parserinfo)
{
_Bool absoluteGDG;
_Bool loop1;
_Bool loop2;
char LAC;
int datasetNameLength;
int numberOfSegments;
int rc;
int segmentLength;
int segmentNumber;
segmentLength = 0;
numberOfSegments = 0;
_dsnameData->numberOfSegments = 1;
_dsnameData->baseDSNLength = 0;
rc = 0;
loop1 = true;
loop2 = true;
absoluteGDG = false;
// 1st Character of Each Segment can be 'a'-'z', '#', '@', or '$'
do
{
LAC = LA(1, parserinfo);
segmentLength = 0;
if (LAC == '#')
{
match('#', parserinfo);
}
else if (LAC == '@')
{
match('@', parserinfo);
}
else if (LAC == '$')
{
match('$', parserinfo);
}
else if (isAlphaChar(LAC))
{
matchRange('a', 'z', parserinfo);
}
else
{
_dsnameData->returnCode = DSN_ERROR;
_dsnameData->reasonCode = SEG_ERROR;
return;
}
segmentLength = 1;
_dsnameData->baseDSNLength++;
// Second through 8th Character of Segment can be 'a'-'z', '0'-'9', '-', '#', '@', or '$'
_loop6:
do
{
LAC = LA(1, parserinfo);
if (LAC == '#')
{
match('#', parserinfo);
segmentLength++;
_dsnameData->baseDSNLength++;
}
else if (LAC == '@')
{
match('@', parserinfo);
segmentLength++;
_dsnameData->baseDSNLength++;
}
else if (LAC == '$')
{
match('$', parserinfo);
segmentLength++;
_dsnameData->baseDSNLength++;
}
else if (LAC == '-')
{
match('-', parserinfo);
segmentLength++;
}
else if (isAlphaChar(LAC))
{
matchRange('a', 'z', parserinfo);
segmentLength++;
_dsnameData->baseDSNLength++;
}
else if (isNumericChar(LAC))
{
matchRange('0', '9', parserinfo);
segmentLength++;
_dsnameData->baseDSNLength++;
}
else if (LAC == '.')
{
strcpy(_dsnameData->segments[numberOfSegments].segment, parserinfo->text);
_dsnameData->segments[numberOfSegments].length = strlen(parserinfo->text);
match('.', parserinfo);
memset(parserinfo->text, 0, sizeof (parserinfo->text));
_dsnameData->numberOfSegments++;
_dsnameData->baseDSNLength++;
numberOfSegments++;
break;
}
// If a '(' is found then this is a PDS or GDG. Exit loop.
else if (LAC == '(')
{
loop1 = false;
loop2 = false;
break;
}
else
{
loop1 = false;
loop2 = false;
break;
}
// Segment length can be from 1 - 8 bytes, not including the '.'.
if (segmentLength > 8)
{
_dsnameData->returnCode = DSN_ERROR;
_dsnameData->reasonCode = SEG_ERROR;
return;
}
// Base dsname length cannot be more than 44 bytes long.
if (_dsnameData->baseDSNLength > 44)
{
_dsnameData->returnCode = DSN_ERROR;
_dsnameData->reasonCode = LENG_ERROR;
return;
}
// Maximum number of segments is 22.
if (_dsnameData->numberOfSegments > 22)
{
_dsnameData->returnCode = DSN_ERROR;
_dsnameData->reasonCode = SEG_ERROR;
return;
}
}
while (loop2);
}
while (loop1);
strcpy(_dsnameData->segments[numberOfSegments].segment, parserinfo->text);
_dsnameData->segments[numberOfSegments].length = strlen(parserinfo->text);
/*
Name format of generation files.
gdgBaseName
The name of the generation data group and the base name of the GDG.
gdgBaseName can be any valid file name.
.gxxxxvyy
An absolute generation and version number that identify a specific generation, where:
xxxx is an unsigned 4-digit decimal number from 0001 through 9999, inclusive.
and yy is an unsigned 2-digit decimal version number (00 through 99).
An absolute name is thus of the form gdgBaseName.gxxxxvyy.
Relative Generation Number
+/-nnnn
To specify a relative number, use the GDG name followed by a negative integer,
a positive integer, or a 0, from 1 to 9999, inclusive
*/
// Absolute generation numbers are 8 bytes in length and start with 'G'. The sixth character is a 'V'.
if (strlen(parserinfo->text) == 8 && (parserinfo->text[0] == 'G' || parserinfo->text[0] == 'g'))
{
if (parserinfo->text[5] == 'V' || parserinfo->text[5] == 'v')
{
// Look for an absolute generation number in the form of: G####V## ex: G0001V00.
if (isNumericChar(parserinfo->text[1]) &&
isNumericChar(parserinfo->text[2]) &&
isNumericChar(parserinfo->text[3]) &&
isNumericChar(parserinfo->text[4]) &&
isNumericChar(parserinfo->text[6]) &&
isNumericChar(parserinfo->text[7]))
{
absoluteGDG = true;
}
}
}
// "(" Indicates relative GDG or PDS member name
if (LA(1, parserinfo) == '(')
{
// Can't have anything after the absolute generation number.
if (absoluteGDG)
{
_dsnameData->returnCode = DSN_ERROR;
_dsnameData->reasonCode = NAME_ERROR;
return;
}
match('(', parserinfo);
// Check for relative generation number which can be in the form of: (+/-####)
if (LA(1, parserinfo) == '+' ||
LA(1, parserinfo) == '-' ||
isNumericChar(LA(1, parserinfo)))
{
if (_dsnameData->baseDSNLength > 35)
{
_dsnameData->returnCode = DSN_ERROR;
_dsnameData->reasonCode = NAME_ERROR;
return;
}
memset(&parserinfo->text, 0, sizeof (parserinfo->text));
mRGDG(_dsnameData, parserinfo);
if (_dsnameData->returnCode == 0)
{
_dsnameData->relativeGeneration = true;
_dsnameData->relativeGenerationNo = atoi((char *) parserinfo->text);
}
else
{
return;
}
}
// If '(' didn't start a relative generation number, then check for PDS/PDSE member name.
// Member name format is the same as a segment name but cannot include '-'
else
if (
isAlphaChar(LA(1, parserinfo)) ||
LA(1, parserinfo) == '#' ||
LA(1, parserinfo) == '$' ||
LA(1, parserinfo) == '@')
{
memset(parserinfo->text, 0, sizeof (parserinfo->text));
mMEMBER(_dsnameData, parserinfo);
strcpy(_dsnameData->pdsMemberName, parserinfo->text);
}
else
{
_dsnameData->returnCode = DSN_ERROR;
_dsnameData->reasonCode = NAME_ERROR;
return;
}
rc = match(')', parserinfo);
if (rc != 0)
{
_dsnameData->returnCode = DSN_ERROR;
_dsnameData->reasonCode = NAME_ERROR;
return;
}
LAC = LA(1, parserinfo);
if (LAC == EOF_CHAR || LAC == ' ')
{
}
else
{
_dsnameData->returnCode = DSN_ERROR;
_dsnameData->reasonCode = NAME_ERROR;
}
}
}
// IDC3523I GENERATION DATA GROUP NAMES CANNOT EXCEED 35 CHARACTERS
extern void mMEMBER(struct dsnameData *_dsnameData, struct parserInfo * parserinfo)
{
int memberLength;
int rc;
_Bool loop1;
char LAC;
rc = 0;
loop1 = true;
_dsnameData->memberNameLength = 0;
// 1st Character of Member Name
LAC = LA(1, parserinfo);
if (LAC == '#')
{
match('#', parserinfo);
}
else if (LAC == '@')
{
match('@', parserinfo);
}
else if (LAC == '$')
{
match('$', parserinfo);
}
else if (isAlphaChar(LAC))
{
matchRange('a', 'z', parserinfo);
}
else
{
_dsnameData->returnCode = DSN_ERROR;
_dsnameData->reasonCode = NAME_ERROR;
return;
}
_dsnameData->memberNameLength = 1;
// Second through 8th Character of Member Name
_loop6:
do
{
LAC = LA(1, parserinfo);
if (LAC == '#')
{
match('#', parserinfo);
_dsnameData->memberNameLength++;
}
else if (LAC == '@')
{
match('@', parserinfo);
_dsnameData->memberNameLength++;
}
else if (LAC == '$')
{
match('$', parserinfo);
_dsnameData->memberNameLength++;
}
else if (isAlphaChar(LAC))
{
matchRange('a', 'z', parserinfo);
_dsnameData->memberNameLength++;
}
else if (isNumericChar(LAC))
{
matchRange('0', '9', parserinfo);
_dsnameData->memberNameLength++;
}
else if (LAC == ')')
{
loop1 = false;
break;
}
else
{
_dsnameData->returnCode = DSN_ERROR;
_dsnameData->reasonCode = NAME_ERROR;
return;
}
// Member Name cannot be more than 8 characters in length.
if (_dsnameData->memberNameLength > 8)
{
_dsnameData->returnCode = DSN_ERROR;
_dsnameData->reasonCode = LENG_ERROR;
return;
}
}
while (loop1);
}
extern void mRGDG(struct dsnameData *_dsnameData, struct parserInfo * parserinfo)
{
int _cnt16 = 0;
int rc;
char LAC;
_Bool loop1;
loop1 = true;
rc = 0;
LAC = LA(1, parserinfo);
if (LAC == '+')
{
match('+', parserinfo);
}
else if (LAC == '-')
{
match('-', parserinfo);
}
if (isNumericChar(LA(1, parserinfo)))
{
_loop16:
do
{
LAC = LA(1, parserinfo);
if (isNumericChar(LAC))
{
matchRange('0', '9', parserinfo);
}
else if (LAC == ')')
{
loop1 = false;
break;
}
else
{
_dsnameData->returnCode = DSN_ERROR;
_dsnameData->reasonCode = NAME_ERROR;
return;
}
_cnt16++;
// Absolute Generation Cannot Exceed 4 Digits
if (_cnt16 > 4)
{
_dsnameData->returnCode = DSN_ERROR;
_dsnameData->reasonCode = LENG_ERROR;
return;
}
}
while (loop1);
}
}
/*
Input Buffer
Mark another character for deferred consumption
*/
void consumeChar(struct parserInfo * parserinfo)
{
char c;
c = LA(1, parserinfo);
appendChar(c, parserinfo);
parserinfo->charNumberToConsume += 1;
}
int matchRange(char c1, char c2, struct parserInfo * parserinfo)
{
int LAC;
int LACS[3];
LAC = tolower(LA(1, parserinfo));
if ((LAC < c1) || (LAC > c2))
{
memset(LACS, 0, sizeof (LACS));
LACS[0] = (char) LAC;
return 8;
}
consumeChar(parserinfo);
return 0;
}
extern int match(int i, struct parserInfo * parserinfo)
{
int LAC;
LAC = tolower(LA(1, parserinfo));
if (LAC != tolower(i))
{
return 8;
}
consumeChar(parserinfo);
return 0;
}
extern char lookAheadCharacter(int LA_Amount, struct parserInfo * parserinfo)
{
char nextChar;
int index = 0;
fillCharBuffer(LA_Amount, parserinfo);
index = parserinfo->charMarkerOffset + LA_Amount - 1;
nextChar = elementAtChar(index, parserinfo);
return nextChar;
}
extern void appendChar(char c, struct parserInfo * parserinfo)
{
strncat(parserinfo->text, (char *) &c, 1);
}
extern void uppercaseString(char *c)
{
int size;
int i;
size = strlen((char *) &c[0]);
for (i = 0; i < size; i++)
{
c[i] = toupper(c[i]);
}
return;
}
extern void fillCharBuffer(int amount, struct parserInfo * parserinfo)
{
char c;
syncConsumeCharBuffer(parserinfo);
/* Fill the buffer sufficiently to hold needed characters */
while (parserinfo->charNumberOfEntries < (amount + parserinfo->charMarkerOffset))
{
/* Append the next character */
c = getNextChar(parserinfo);
charQueueAppend(c, parserinfo);
}
}
extern char elementAtChar(int idx, struct parserInfo * parserinfo)
{
int CharBufferIndex = 0;
char c;
CharBufferIndex = ((parserinfo->charBufferOffset + idx) % parserinfo->charBufferSizeLessOne);
c = parserinfo->CharBuffer[CharBufferIndex];
return (c);
}
extern void charQueueAppend(char c, struct parserInfo * parserinfo)
{
int charBufferSize = 0;
int charBufferIndex = 0;
charBufferSize = sizeof (parserinfo->CharBuffer);
parserinfo->charBufferSizeLessOne = charBufferSize - 1;
charBufferIndex = (parserinfo->charBufferOffset + parserinfo->charNumberOfEntries) %
parserinfo->charBufferSizeLessOne;
parserinfo->CharBuffer[charBufferIndex] = c;
parserinfo->charNumberOfEntries += 1;
}
extern void syncConsumeCharBuffer(struct parserInfo * parserinfo)
{
while (parserinfo->charNumberToConsume > 0)
{
if (parserinfo->charNumberOfMarkers > 0)
{
/* guess mode leave leading characters and bump offset. */
parserinfo->charMarkerOffset += 1;
}
else
{
/* normal mode - remove first character */
removeFirstChar(parserinfo);
}
parserinfo->charNumberToConsume -= 1;
}
}
extern void removeFirstChar(struct parserInfo * parserinfo)
{
parserinfo->charBufferOffset = (parserinfo->charBufferOffset + 1) % parserinfo->charBufferSizeLessOne;
parserinfo->charNumberOfEntries -= 1;
}
extern char getNextChar(struct parserInfo * parserinfo)
{
char nextChar;
if (parserinfo->currentInputOffset > zFILESIZE)
{
nextChar = EOF_CHAR;
return nextChar;
}
nextChar = parserinfo->inputStream[parserinfo->currentInputOffset];
switch (nextChar)
{
case 0x00:
{
nextChar = EOF_CHAR;
parserinfo->currentInputOffset++;
break;
}
case ' ':
{
nextChar = EOF_CHAR;
parserinfo->currentInputOffset++;
break;
}
case EOF_CHAR:
{
nextChar = EOF_CHAR;
return nextChar;
break;
}
default:
{
parserinfo->currentInputOffset++;
break;
}
}
return nextChar;
}
extern void InitLexer(struct parserInfo * parserinfo)
{
parserinfo-> charBufferOffset = 0;
parserinfo->charBufferSize = 0;
parserinfo->charBufferSizeLessOne = 0;
parserinfo->charMarkerOffset = 0;
parserinfo->charNumberOfEntries = 0;
parserinfo->charNumberOfMarkers = 0;
parserinfo->charNumberToConsume = 0;
parserinfo->currentInputOffset = 0;
parserinfo->inputStreamEOF = false;
parserinfo->inputStreamPosition = 0;
memset(parserinfo->CharBuffer, 0, sizeof (parserinfo->CharBuffer));
memset(parserinfo->text, 0, sizeof (parserinfo->text));
}
// EBCDIC Version
static _Bool isAlphaChar(char c)
{
if ((c >= 'a') && (c <= 'i')) return true;
if ((c >= 'j') && (c <= 'r')) return true;
if ((c >= 's') && (c <= 'z')) return true;
if ((c >= 'A') && (c <= 'I')) return true;
if ((c >= 'J') && (c <= 'R')) return true;
if ((c >= 'S') && (c <= 'Z')) return true;
return false;
}
static _Bool isNumericChar(char c)
{
if ((c >= '0') && (c <= '9')) return true;
return false;
}
extern int vdsname(struct dsnameData *_dsnameData, struct parserInfo * parserinfo)
{
InitLexer(parserinfo);
parserinfo->inputStream = (char*) &_dsnameData->inputDsname;
_dsnameData->returnCode = 0;
_dsnameData->reasonCode = 0;
_dsnameData->baseDSNLength = 0;
_dsnameData->messageLength = 0;
_dsnameData->memberNameLength = 0;
_dsnameData->numberOfSegments = 0;
_dsnameData->relativeGenerationNo = 0;
memset(_dsnameData->message, 0, sizeof (_dsnameData->message));
memset(_dsnameData->pdsMemberName, 0, sizeof (_dsnameData->pdsMemberName));
memset(_dsnameData->baseDsname, 0, sizeof (_dsnameData->baseDsname));
memset(_dsnameData->segments, 0, sizeof (struct segment));
mDATASETNAME(_dsnameData, parserinfo);
if (_dsnameData->returnCode == 0)
{
memcpy(_dsnameData->baseDsname, _dsnameData->inputDsname,
_dsnameData->baseDSNLength);
uppercaseString((char *) _dsnameData->baseDsname);
}
if (_dsnameData->memberNameLength > 0)
{
uppercaseString((char *) _dsnameData->pdsMemberName);
}
if (_dsnameData->returnCode > 0)
{
strcpy(_dsnameData->message, "Invalid DSNAME specification.");
_dsnameData->messageLength = strlen(_dsnameData->message);
}
return (_dsnameData->returnCode);
}