mirror of
https://github.com/open-simh/simtools.git
synced 2026-01-25 11:46:44 +00:00
551 lines
19 KiB
C
551 lines
19 KiB
C
/* Direct.c v1.2 */
|
|
|
|
/*
|
|
This is part of ODS2 written by Paul Nankervis,
|
|
email address: Paulnank@au1.ibm.com
|
|
|
|
ODS2 is distributed freely for all members of the
|
|
VMS community to use. However all derived works
|
|
must maintain comments in their source to acknowledge
|
|
the contibution of the original author.
|
|
*/
|
|
|
|
/* This module does all directory file handling - mostly
|
|
lookups of filenames in directory files... */
|
|
|
|
/* This version takes relative version from last seen directory record
|
|
- this is no good if there are multiple directory records for a file! */
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <memory.h>
|
|
#include "ssdef.h"
|
|
#include "descrip.h"
|
|
#include "fibdef.h"
|
|
#include "access.h"
|
|
#include "direct.h"
|
|
|
|
#define DEBUGx on
|
|
|
|
|
|
int directlookups = 0;
|
|
int directsearches = 0;
|
|
int directdels = 0;
|
|
int directchecks = 0;
|
|
int directmatches = 0;
|
|
|
|
|
|
/* directshow - to print directory statistics */
|
|
|
|
void directshow(void)
|
|
{
|
|
printf("DIRECTSHOW Lookups: %d Searches: %d Deletes: %d Checks: %d Matches: %d\n",
|
|
directlookups,directsearches,directdels,directchecks,directmatches);
|
|
}
|
|
|
|
|
|
/* namecheck - take a name specification and return name length without
|
|
the version number, an integer version number, and a wildcard flag */
|
|
|
|
unsigned namecheck(char *str,int len,int *retlen,int *retver,int *wildflag)
|
|
{
|
|
int wildcard = 0;
|
|
char *spcbeg = str;
|
|
register int dots = 0;
|
|
register char *spc = spcbeg;
|
|
register char *spcend = spc + len;
|
|
directchecks++;
|
|
while (spc < spcend) {
|
|
register char ch = *spc++;
|
|
if (ch == '.') {
|
|
if ((spc - spcbeg) > 40) return SS$_BADFILENAME;
|
|
spcbeg = spc;
|
|
if (dots++ > 1) break;
|
|
} else {
|
|
if (ch == ';') {
|
|
break;
|
|
} else {
|
|
if (ch == '*' || ch == '%') {
|
|
wildcard = 1;
|
|
} else {
|
|
if (ch == '[' || ch == ']' || ch == ':' ||
|
|
!isprint(ch)) return SS$_BADFILENAME;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ((spc - spcbeg) > 40) return SS$_BADFILENAME;
|
|
*retlen = spc - str - 1;
|
|
dots = 0;
|
|
if (spc < spcend) {
|
|
register char ch = *spc;
|
|
if (ch == '*') {
|
|
if (++spc < spcend) return SS$_BADFILENAME;
|
|
dots = 32768;
|
|
wildcard = 1;
|
|
} else {
|
|
register int sign = 1;
|
|
if (ch == '-') {
|
|
spc++;
|
|
sign = -1;
|
|
}
|
|
while (spc < spcend) {
|
|
ch = *spc++;
|
|
if (!isdigit(ch)) return SS$_BADFILENAME;
|
|
dots = dots * 10 + (ch - '0');
|
|
}
|
|
dots *= sign;
|
|
}
|
|
}
|
|
*retver = dots;
|
|
*wildflag = wildcard;
|
|
#ifdef DEBUG
|
|
printf("Namecheck %d %d %d\n",*retlen,*retver,*wildflag);
|
|
#endif
|
|
return SS$_NORMAL;
|
|
}
|
|
|
|
|
|
|
|
#define MAT_LT 0
|
|
#define MAT_EQ 1
|
|
#define MAT_GT 2
|
|
#define MAT_NE 3
|
|
|
|
/* namematch - compare a name specification with a directory entry
|
|
and determine if there is a match, too big, too small... */
|
|
|
|
int namematch(char *spec,int speclen,char *entry,int entrylen)
|
|
{
|
|
int percent = 0;
|
|
register char *spc = spec,*ent = entry;
|
|
register char *spcend = spc + speclen,*entend = ent + entrylen;
|
|
directmatches++;
|
|
/* See how much matches without wildcards... */
|
|
if (spc < spcend && ent < entend) {
|
|
register int count = speclen;
|
|
if (entrylen < count) count = entrylen;
|
|
do {
|
|
register char sch = *spc,ech = *ent;
|
|
if (sch != ech) if (toupper(sch) != toupper(ech))
|
|
if (sch == '%') {
|
|
percent = 1;
|
|
} else {
|
|
break;
|
|
}
|
|
spc++;
|
|
ent++;
|
|
} while (--count > 0);
|
|
}
|
|
#ifdef DEBUG
|
|
printf("Namematch %3s %d %3s %d\n",spec,speclen,entry,entrylen);
|
|
#endif
|
|
/* Mismatch - return result unless wildcard... */
|
|
if (spc >= spcend) {
|
|
if (ent >= entend) {
|
|
return MAT_EQ;
|
|
} else {
|
|
if (percent) {
|
|
return MAT_NE;
|
|
} else {
|
|
return MAT_GT; /* Entry longer than search spec */
|
|
}
|
|
}
|
|
} else {
|
|
register int offset = 0;
|
|
if (*spc != '*') {
|
|
if (percent) return MAT_NE;
|
|
if (ent < entend)
|
|
if (toupper(*ent) > toupper(*spc)) return MAT_GT;
|
|
return MAT_LT;
|
|
}
|
|
/* See if we can find a match with wildcards */
|
|
spc++;
|
|
if (spc < spcend) {
|
|
do {
|
|
if (spc >= spcend) {
|
|
if (ent >= entend) break;
|
|
spc -= offset;
|
|
ent -= offset - 1;
|
|
offset = 0;
|
|
} else {
|
|
register char sch = toupper(*spc);
|
|
if (sch == '*') {
|
|
offset = 0;
|
|
spc++;
|
|
if (spc >= spcend) break;
|
|
} else {
|
|
if (ent < entend) {
|
|
register char ech = toupper(*ent);
|
|
if (sch == ech || sch == '%') {
|
|
offset++;
|
|
spc++;
|
|
ent++;
|
|
} else {
|
|
spc -= offset;
|
|
ent -= offset - 1;
|
|
offset = 0;
|
|
}
|
|
} else {
|
|
return MAT_NE;
|
|
}
|
|
}
|
|
}
|
|
} while (1);
|
|
}
|
|
}
|
|
return MAT_EQ;
|
|
}
|
|
|
|
|
|
unsigned freesize(char *buffer)
|
|
{
|
|
struct dir$rec *dr = (struct dir$rec *) buffer;
|
|
do {
|
|
register char *nr = (char *) dr + dr->dir$size + 2;
|
|
if (nr >= buffer + 512) break;
|
|
dr = (struct dir$rec *) nr;
|
|
} while (1);
|
|
return (char *) dr - buffer - 2;
|
|
}
|
|
|
|
unsigned insrec(void)
|
|
{
|
|
printf("Insert directory record\n");
|
|
return 0;
|
|
}
|
|
|
|
unsigned insent(struct FCB *fcb,struct VIOC *vioc,unsigned curblk,
|
|
struct dir$rec *dr,struct dir$ent *de,
|
|
char *buffer,unsigned eofblk)
|
|
{
|
|
printf("Insert directory entry\n");
|
|
if (freesize(buffer) >= sizeof(struct dir$ent)) {
|
|
char *ne = (char *) de + sizeof(struct dir$ent);
|
|
memcpy(de,ne,512 - (ne - buffer));
|
|
dr->dir$size -= sizeof(struct dir$ent);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* delent - delete a directory entry */
|
|
|
|
unsigned delent(struct FCB *fcb,struct VIOC *vioc,unsigned curblk,
|
|
struct dir$rec *dr,struct dir$ent *de,
|
|
char *buffer,unsigned modmask,unsigned eofblk)
|
|
{
|
|
unsigned sts = 1;
|
|
unsigned ent;
|
|
directdels++;
|
|
ent = (dr->dir$size - sizeof(struct dir$rec)
|
|
- dr->dir$namecount + 3) / sizeof(struct dir$ent);
|
|
printf("DELENT ent = %d %d %d\n",ent,curblk,eofblk);
|
|
if (ent > 1) {
|
|
char *ne = (char *) de + sizeof(struct dir$ent);
|
|
memcpy(de,ne,512 - (ne - buffer));
|
|
dr->dir$size -= sizeof(struct dir$ent);
|
|
} else {
|
|
char *nr = (char *) dr + dr->dir$size + 2;
|
|
if (eofblk == 1 || (char *) dr > buffer ||
|
|
(nr <= buffer + 510 && (unsigned short) *nr < 512)) {
|
|
memcpy(dr,nr,512 - (nr - buffer));
|
|
} else {
|
|
printf("DELENT shrinking file size %d %d\n",curblk,eofblk);
|
|
while (curblk < eofblk) {
|
|
char *nxtbuffer;
|
|
struct VIOC *nxtvioc;
|
|
unsigned nxtmodmask;
|
|
sts = accesschunk(fcb,++curblk,&nxtvioc,&nxtbuffer,NULL,1,&nxtmodmask);
|
|
if ((sts & 1) == 0) break;
|
|
memcpy(buffer,nxtbuffer,512);
|
|
sts = deaccesschunk(vioc,modmask,1);
|
|
if ((sts & 1) == 0) break;
|
|
buffer = nxtbuffer;
|
|
vioc = nxtvioc;
|
|
modmask = nxtmodmask;
|
|
}
|
|
if (sts & 1) {
|
|
fcb->head->fh2$w_recattr.fat$l_efblk[0] = eofblk >> 16;
|
|
fcb->head->fh2$w_recattr.fat$l_efblk[1] = (eofblk & 0xffff);
|
|
eofblk--;
|
|
}
|
|
}
|
|
}
|
|
{
|
|
unsigned retsts = deaccesschunk(vioc,modmask,1);
|
|
if (sts & 1) sts = retsts;
|
|
return sts;
|
|
}
|
|
}
|
|
|
|
|
|
/* retent - return information about a directory entry */
|
|
|
|
unsigned retent(struct FCB *fcb,struct VIOC *vioc,unsigned curblk,
|
|
struct dir$rec *dr,struct dir$ent *de,struct fibdef *fib,
|
|
unsigned short *reslen,struct dsc$descriptor *resdsc,
|
|
int wildcard)
|
|
{
|
|
register int scale = 10;
|
|
register int version = de->dir$version;
|
|
register int length = dr->dir$namecount;
|
|
register char *ptr = resdsc->dsc$a_pointer;
|
|
memcpy(ptr,dr->dir$name,length);
|
|
while (version >= scale) scale *= 10;
|
|
ptr += length++;
|
|
*ptr++ = ';';
|
|
do {
|
|
scale /= 10;
|
|
*ptr++ = version / scale + '0';
|
|
version %= scale;
|
|
length++;
|
|
} while (scale > 1);
|
|
*reslen = length;
|
|
memcpy(&fib->fib$w_fid_num,&de->dir$fid,sizeof(struct fiddef));
|
|
if (fib->fib$b_fid_rvn == 0) fib->fib$b_fid_rvn = fcb->rvn;
|
|
if (wildcard || (fib->fib$w_nmctl & FIB$M_WILD)) {
|
|
fib->fib$l_wcc = curblk;
|
|
} else {
|
|
fib->fib$l_wcc = 0;
|
|
}
|
|
return deaccesschunk(vioc,0,1);
|
|
}
|
|
|
|
|
|
/* searchent - search for a directory entry */
|
|
|
|
unsigned searchent(struct FCB * fcb,
|
|
struct dsc$descriptor * fibdsc,struct dsc$descriptor * filedsc,
|
|
unsigned short *reslen,struct dsc$descriptor * resdsc,unsigned eofblk,unsigned action)
|
|
{
|
|
register unsigned sts,curblk;
|
|
struct VIOC *vioc = NULL;
|
|
unsigned modmask;
|
|
char *searchspec,*buffer;
|
|
int searchlen,version,wildcard,wcc_flag;
|
|
struct fibdef *fib = (struct fibdef *) fibdsc->dsc$a_pointer;
|
|
directlookups++;
|
|
|
|
/* 1) Generate start block (wcc gives start point)
|
|
2) Search for start
|
|
3) Scan until found or too big or end */
|
|
|
|
curblk = fib->fib$l_wcc;
|
|
if (curblk != 0) {
|
|
searchspec = resdsc->dsc$a_pointer;
|
|
sts = namecheck(searchspec,*reslen,&searchlen,&version,&wildcard);
|
|
if (action || wildcard) return SS$_BADFILENAME;
|
|
wcc_flag = 1;
|
|
} else {
|
|
searchspec = filedsc->dsc$a_pointer;
|
|
sts = namecheck(searchspec,filedsc->dsc$w_length,&searchlen,&version,&wildcard);
|
|
if ((action && wildcard) || (action > 1 && version < 0)) return SS$_BADFILENAME;
|
|
wcc_flag = 0;
|
|
}
|
|
if ((sts & 1) == 0) return sts;
|
|
|
|
|
|
/* Identify starting block...*/
|
|
|
|
if (*searchspec == '*' || *searchspec == '%') {
|
|
curblk = 1;
|
|
} else {
|
|
register unsigned loblk = 1,hiblk = eofblk;
|
|
if (curblk < 1 || curblk > eofblk) curblk = (eofblk + 1) / 2;
|
|
while (loblk < hiblk) {
|
|
register int cmp;
|
|
register unsigned newblk;
|
|
register struct dir$rec *dr;
|
|
directsearches++;
|
|
sts = accesschunk(fcb,curblk,&vioc,&buffer,NULL,action ? 1 : 0,&modmask);
|
|
if ((sts & 1) == 0) return sts;
|
|
dr = (struct dir$rec *) buffer;
|
|
if (dr->dir$size > 510) {
|
|
cmp = MAT_GT;
|
|
} else {
|
|
cmp = namematch(searchspec,searchlen,dr->dir$name,dr->dir$namecount);
|
|
if (cmp == MAT_EQ) {
|
|
if (wildcard || version < 1 || version > 32767) {
|
|
cmp = MAT_NE; /* no match - want to find start */
|
|
} else {
|
|
register struct dir$ent *de =
|
|
(struct dir$ent *) (dr->dir$name + ((dr->dir$namecount + 1) & ~1));
|
|
if (de->dir$version < version) {
|
|
cmp = MAT_GT; /* too far... */
|
|
} else {
|
|
if (de->dir$version > version) {
|
|
cmp = MAT_LT; /* further ahead... */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
printf("Direct %6.6s %d %6.6s %d (%d<%d<%d)-> %d\n",
|
|
searchspec,searchlen,dr->dir$name,dr->dir$namecount,
|
|
loblk,curblk,hiblk,cmp);
|
|
#endif
|
|
switch (cmp) {
|
|
case MAT_LT:
|
|
if (curblk == fib->fib$l_wcc) {
|
|
newblk = hiblk = loblk = curblk;
|
|
} else {
|
|
loblk = curblk;
|
|
newblk = (loblk + hiblk + 1) / 2;
|
|
}
|
|
break;
|
|
case MAT_GT:
|
|
case MAT_NE:
|
|
newblk = (loblk + curblk) / 2;
|
|
hiblk = curblk - 1;
|
|
break;
|
|
default:
|
|
newblk = hiblk = loblk = curblk;
|
|
}
|
|
if (newblk != curblk) {
|
|
sts = deaccesschunk(vioc,0,1);
|
|
if ((sts & 1) == 0) return sts;
|
|
vioc = NULL;
|
|
curblk = newblk;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Now to read sequentially to find entry... */
|
|
|
|
while ((sts & 1) && curblk <= eofblk) {
|
|
register struct dir$rec *dr;
|
|
if (vioc == NULL) {
|
|
sts = accesschunk(fcb,curblk,&vioc,&buffer,NULL,action ? 1 : 0,&modmask);
|
|
if ((sts & 1) == 0) return sts;
|
|
}
|
|
dr = (struct dir$rec *) buffer;
|
|
do {
|
|
register int cmp;
|
|
register char *nr = (char *) dr + dr->dir$size + 2;
|
|
if (nr >= buffer + 512) break;
|
|
cmp = namematch(searchspec,searchlen,dr->dir$name,dr->dir$namecount);
|
|
#ifdef DEBUF
|
|
printf("Direct %6.6s %d %6.6s %d -> %d\n",
|
|
searchspec,searchlen,dr->dir$name,dr->dir$namecount,cmp);
|
|
#endif
|
|
if (cmp == MAT_GT) {
|
|
if (wcc_flag) {
|
|
wcc_flag = 0;
|
|
searchspec = filedsc->dsc$a_pointer;
|
|
sts = namecheck(searchspec,filedsc->dsc$w_length,&searchlen,&version,&wildcard);
|
|
if ((sts & 1) == 0) break;
|
|
} else {
|
|
curblk = eofblk; /* give up */
|
|
break;
|
|
}
|
|
} else {
|
|
if (cmp == MAT_EQ) {
|
|
register int relver = 0;
|
|
register struct dir$ent *de = (struct dir$ent *) (dr->dir$name +
|
|
((dr->dir$namecount + 1) & ~1));
|
|
while ((char *) de < nr) {
|
|
if (version >= de->dir$version || (version < 1 && version >= relver)) {
|
|
cmp = MAT_GT;
|
|
if (version > 32767 || version == relver ||
|
|
version == de->dir$version) cmp = MAT_EQ;
|
|
if (wcc_flag) {
|
|
wcc_flag = 0;
|
|
searchspec = filedsc->dsc$a_pointer;
|
|
sts = namecheck(searchspec,filedsc->dsc$w_length,&searchlen,&version,&wildcard);
|
|
if ((sts & 1) == 0) break;
|
|
if (namematch(searchspec,searchlen,dr->dir$name,
|
|
dr->dir$namecount) != MAT_EQ) {
|
|
break;
|
|
}
|
|
if (cmp == MAT_EQ) {
|
|
cmp = MAT_LT;
|
|
} else {
|
|
cmp = MAT_LT;
|
|
if (version >= de->dir$version || (version < 1 && version >= relver)) {
|
|
cmp = MAT_GT;
|
|
if (version > 32767 || version == relver ||
|
|
version == de->dir$version) cmp = MAT_EQ;
|
|
}
|
|
}
|
|
}
|
|
if (cmp == MAT_EQ) {
|
|
switch (action) {
|
|
case 0:
|
|
return
|
|
retent(fcb,vioc,curblk,dr,de,fib,reslen,resdsc,wildcard);
|
|
case 1:
|
|
return
|
|
delent(fcb,vioc,curblk,dr,de,buffer,modmask,eofblk);
|
|
default:
|
|
sts = SS$_DUPFILENAME;
|
|
de = (struct dir$ent *) nr;
|
|
}
|
|
} else {
|
|
if (cmp == MAT_GT) break;
|
|
}
|
|
}
|
|
relver--;
|
|
de++;
|
|
}
|
|
if ((sts & 1) == 0) break;
|
|
if (action == 2) {
|
|
return insent(fcb,vioc,curblk,dr,de,buffer,eofblk);
|
|
}
|
|
}
|
|
dr = (struct dir$rec *) nr;
|
|
}
|
|
} while (1);
|
|
{
|
|
register unsigned dests = deaccesschunk(vioc,0,1);
|
|
vioc = NULL;
|
|
if (sts & 1) sts = dests;
|
|
}
|
|
curblk++;
|
|
}
|
|
if (action == 2) {
|
|
return insrec();
|
|
}
|
|
if (sts & 1) {
|
|
fib->fib$l_wcc = 0;
|
|
if (wcc_flag || wildcard) {
|
|
sts = SS$_NOMOREFILES;
|
|
} else {
|
|
sts = SS$_NOSUCHFILE;
|
|
}
|
|
}
|
|
return sts;
|
|
}
|
|
|
|
|
|
/* direct - this routine handles all directory manipulations:-
|
|
action 0 - find directory entry
|
|
1 - delete entry
|
|
2 - create an entry */
|
|
|
|
unsigned direct(struct VCB *vcb,struct dsc$descriptor * fibdsc,
|
|
struct dsc$descriptor *filedsc,unsigned short *reslen,
|
|
struct dsc$descriptor *resdsc,unsigned action)
|
|
{
|
|
struct FCB *fcb;
|
|
register unsigned sts,eofblk;
|
|
register struct fibdef *fib = (struct fibdef *) fibdsc->dsc$a_pointer;
|
|
sts = accessfile(vcb,(struct fiddef *) & fib->fib$w_did_num,&fcb,action);
|
|
if (sts & 1) {
|
|
if (fcb->head->fh2$l_filechar & FH2$M_DIRECTORY) {
|
|
eofblk = swapw(fcb->head->fh2$w_recattr.fat$l_efblk);
|
|
if (fcb->head->fh2$w_recattr.fat$w_ffbyte == 0) --eofblk;
|
|
sts = searchent(fcb,fibdsc,filedsc,reslen,resdsc,eofblk,action);
|
|
} else {
|
|
sts = SS$_BADIRECTORY;
|
|
}
|
|
{
|
|
register unsigned dests = deaccessfile(fcb);
|
|
if (sts & 1) sts = dests;
|
|
}
|
|
}
|
|
return sts;
|
|
}
|