Category : Files from Magazines
Archive   : CGAZ2-4.ZIP
Filename : EXPNDWLD.C
Output of file : EXPNDWLD.C contained in archive : CGAZ2-4.ZIP
* Expand wild-card file names into an array. Versioon 1.1 2/88 *
* Bill Lee, 5132 106A St., Edmonton, Alberta, Canada T6H 2W7 *
* CompuServe 70441,2372. Copyright (C) by Bill Lee, 1987. *
* All rights reserved. Use freely for any purpose but retain *
* the copyright notice. *
Usage: int expandwild(pnOut, pvOut, nIn, vIn, attrib)
vIn: array of pointers to strings, e.g., argv. Wild-card
strings expanded to full file name and path in *pvOut,
all unexpandable strings are copied unchanged.
nIn: no. of strings in vIn; if < 0, stop when vIn[i] == NULL.
attrib: attribute byte of files to be returned, see FA_xxxxxx constants.
pnOut: points to int that states number of strings in pvOut.
pvOut: points to pointer to output array, *pvOut[*pnOut] == NULL.
Returns: 0 if successful, non-0 (see codes below) if errors.
Compilation: All memory models of three compilers are supported,
define the appropriate constant:
DESMET: DeSmet C 3.0.
MSC: Microsoft C 4.0 and later
(none): Turbo C 1.0
Define MAIN to generate a main() test driver. */
#ifdef DESMET
/* DeSmet lacks the "#if defined(...)" preprocessor test, which is more
flexible than #ifdef/#ifndef. So fake it with a macro and 0 values
for the constants we want "undefined". */
#define defined(x) x
#define MSC 0
#ifndef LARGE_CASE
#define LARGE_CASE 0
#include
#else /* LARGE_CASE */
#include
#include
#endif
#endif
#if !defined(MSC) && !defined(DESMET) /* Turbo C */
#include
#include
#include
#include
#include
typedef char far * type_dta; /* disk transfer area pointer */
#endif
#if defined(MSC)
#define LINT_ARGS
#include
#include
#include
#include
#include
#endif
#if defined(MSC) || defined(DESMET)
/* Turbo C's stpcpy() is lacking in MSC and DeSmet libraries, this
macro is an adequate replacement for non-overlapping src and dest. */
#define stpcpy(dest, src) (strcpy((dest), (src)) + strlen(src))
/* DOS interrrupt 0x21 function codes */
#define DOS_setDTA 0x1A /* set disk transfer area address */
#define DOS_getDTA 0x2F /* get disk transfer area address */
#define DOS_findfirst 0x4E /* find first file match */
#define DOS_findnext 0x4F /* find next file match */
/* Structure used by findfirst() and findnext() */
struct ffblk {
char ff_reserved[21]; /* workarea */
char ff_attrib; /* file attribute bit flags */
unsigned int ff_ftime; /* packed time of last file update */
unsigned int ff_fdate; /* packed date of last file update */
long ff_fsize; /* file size */
char ff_name[13]; /* file name.ext, no blanks */
};
#if defined(MSC)
typedef char far * type_dta; /* disk transfer area pointer */
type_dta getdta(void);
void setdta(type_dta);
int findfirst(char *, struct ffblk *, int);
int findnext(struct ffblk *);
#else /* DESMET */
#if defined(LARGE_CASE)
typedef char * type_dta; /* disk transfer area pointer */
#else
/* Using long is a compromise for dta pointer; DeSmet has no far * type
(sigh!) and I don't want to force use of large case just for this. */
typedef long type_dta; /* disk transfer area pointer */
#endif /* #if defined(LARGE_CASE) */
type_dta getdta();
void setdta(), _doint(), printf();
int findfirst(), findnext(), strlen();
char *strcpy(), *strncpy(), *malloc();
int errno; /* "simulate" DOS error number */
#endif /* #if defined(MSC) */
/* File attributes */
#define FA_RDONLY 0x01 /* Read only attribute */
#define FA_HIDDEN 0x02 /* Hidden file */
#define FA_SYSTEM 0x04 /* System file */
#define FA_LABEL 0x08 /* Volume label */
#define FA_DIREC 0x10 /* Directory */
#define FA_ARCH 0x20 /* Archive */
/* Maximum lengths of fnsplitE() input and output strings including \0. */
#define MAXPATH 80 /* input maximum */
#define MAXDRIVE 3
#define MAXDIR 66
#define MAXFILE 9
#define MAXEXT 5
#endif /* #if defined(MSC) || defined(DESMET) */
/* Bit masks for fnsplitE() return value */
#define WILDCARDS 0x01 /* '*' and/or '?' in filename or ext */
#define EXTENSION 0x02 /* extension present */
#define FILENAME 0x04 /* filename present */
#define DIRECTORY 0x08 /* directory present */
#define DRIVE 0x10 /* drive: present */
/* Bit masks for returning truncation errors in fnsplitE() */
#define PATHt 0x0100 /* input path truncated */
#define EXTENSIONt (EXTENSION << 8) /* extension truncated */
#define FILENAMEt (FILENAME << 8) /* filename truncated */
#define DIRECTORYt (DIRECTORY << 8) /* directory truncated */
#define DRIVEt (DRIVE << 8) /* drive truncated (can't happen) */
#define FNSPLITEt (PATHt | EXTENSIONt | FILENAMEt | DIRECTORYt | DRIVEt)
/* all of the above */
/* Function prototypes */
#if defined(DESMET)
int expandwild();
int fnsplitE(), fnsplitEa();
#else /* MSC or TC */
int expandwild(int *, char **[], int, char *[], int);
int fnsplitE(char *, char *, char *, char *, char *);
int fnsplitEa(char *, char *, char *, unsigned int, int);
#endif
/* Codes returned for errors detected by expandwild().
Remember to update rmsg[] in main() if these codes are changed.) */
#define EXWINOMEM 1 /* not enough memory */
#define EXWICNTCHG 2 /* count changed between passes */
#define EXWISIZCHG 3 /* array size changed between passes */
/*---------------------------------------------------------------------------*/
int expandwild(pnOut, pvOut, nIn, vIn, attrib)
int *pnOut; /* ptr to no. of strings in output array */
char **pvOut[]; /* ptr to output array */
int nIn; /* no. of strings in vIn */
char *vIn[]; /* input string array */
int attrib; /* file attribute byte */
{
char drive[MAXDRIVE]; /* fnsplitE() workarea */
char dir[MAXDIR]; /* fnsplitE() workarea */
struct ffblk fblk; /* findfirst()/findnext() workarea */
char *pvIn; /* work ptr into vIn[] */
char **vOut = NULL; /* anchor for new output array */
char *vOutEnd; /* last byte of new array */
char *pvTmp; /* temp ptr to vOut[] */
unsigned int iIn; /* index into vIn[] */
unsigned int vOutSize; /* accumulate memory size of new array */
unsigned int nOut; /* count strings in new array */
unsigned int iOut; /* string index in new array */
unsigned int fixSize; /* size of fixed part of a new string */
int fnsFlg; /* flags returned by fnsplitE() */
int err = 0; /* error code returned by expandwild() */
type_dta olddta = getdta(); /* save old data transfer area ptr */
#if defined(MSC) || defined(DESMET)
setdta((type_dta) &fblk); /* set dta for findfirst()/findnext() */
#endif
vOutSize = sizeof(pvOut[0]); /* space for NULL ptr in new array */
/* Determine no. and total size of strings required for new array. */
for (nOut = 0, iIn = 0;
(pvIn = vIn[iIn]) != NULL && (nIn < 0 || (nIn >= 0 && iIn < nIn));
iIn++) {
/* If no truncation and if a wildcard and if a file name is found,
save space for expanded filename string. */
if (!((fnsFlg = fnsplitE(pvIn, drive, dir, NULL, NULL)) & FNSPLITEt)
&& fnsFlg & WILDCARDS && !findfirst(pvIn, &fblk, attrib)) {
fixSize = sizeof(pvOut[0]) + strlen(drive) + strlen(dir) + 1;
do {
nOut++;
vOutSize += fixSize + strlen(fblk.ff_name);
} while (!findnext(&fblk));
}
else { /* save space for non-expanded string */
nOut++;
vOutSize += sizeof(pvOut[0]) + strlen(pvIn) + 1;
}
} /* for (iIn = 0; nOut = 0; ... ) */
/* Allocate memory for new array. */
if ((vOut = (char **) malloc(vOutSize)) == NULL)
err = EXWINOMEM; /* not enough memory */
else {
vOut[0] = (char *) (vOut + nOut + 1); /* ptr to 1st string */
vOutEnd = (char *) vOut + vOutSize; /* 1st byte after last */
}
/* Go through again and copy strings to new array. */
for (iOut = 0, iIn = 0; err == 0
&& (pvIn = vIn[iIn]) != NULL && (nIn < 0 || (nIn >= 0 && iIn < nIn));
iIn++) {
/* If no truncation and if wildcard and if file name is found,
save expanded string. */
if (!((fnsFlg = fnsplitE(pvIn, drive, dir, NULL, NULL)) & FNSPLITEt)
&& fnsFlg & WILDCARDS && !findfirst(pvIn, &fblk, attrib)) {
fixSize = strlen(drive) + strlen(dir) + 1;
do {
if (iOut >= nOut) /* count changed? */
err = EXWICNTCHG; /* 2nd pass count too big */
else {
pvTmp = vOut[iOut];
if (pvTmp + fixSize + strlen(fblk.ff_name) > vOutEnd)
err = EXWISIZCHG; /* overflow */
else /* copy full name, save ptr to next new string */
vOut[++iOut] = 1 + /* whew! */
stpcpy(stpcpy(stpcpy(pvTmp,drive),dir),fblk.ff_name);
}
} while (err == 0 && !findnext(&fblk));
}
else { /* copy non-wildcard to new array */
if (iOut >= nOut) /* count changed? */
err = EXWICNTCHG; /* 2nd pass count too big */
else {
pvTmp = vOut[iOut];
if (pvTmp + strlen(pvIn) + 1 > vOutEnd)
err = EXWISIZCHG; /* overflow */
else /* copy non-wildcard, save ptr to next new string*/
vOut[++iOut] = 1 + stpcpy(pvTmp, pvIn);
}
}
} /* for (iIn = 0; iOut = 0; ... ) */
if (err == 0)
/* Check for changes in count or size between 2 passes thru names. */
if (iOut != nOut) /* count changed? */
err = EXWICNTCHG; /* 2nd pass count smaller */
else if (vOut[nOut] != vOutEnd) /* size changed? */
err = EXWISIZCHG; /* 2nd pass size smaller */
if (err == 0) { /* if no error, return results to caller */
vOut[nOut] = NULL; /* terminate array as per ANSI standard */
*pnOut = nOut;
*pvOut = vOut;
}
else if (vOut != NULL)
free((char *) vOut);
setdta(olddta);
return (err);
} /* expandwild() */
/*---------------------------------------------------------------------------*/
/* get/set disk transfer address (supplied in Turbo C standard library) */
#if defined(MSC)
type_dta getdta()
{
union REGS rx;
struct SREGS rs;
rx.h.ah = DOS_getDTA;
intdosx(&rx, &rx, &rs);
return ((char far *)(((unsigned long) rs.es << 16) | rx.x.bx));
} /* getdta() */
void setdta(dta)
type_dta dta;
{
union REGS rx;
struct SREGS rs;
rx.h.ah = DOS_setDTA;
rs.ds = FP_SEG(dta);
rx.x.dx = FP_OFF(dta);
intdosx(&rx, &rx, &rs);
} /* setdta() */
#endif /* #if defined(MSC) */
#if defined(DESMET)
type_dta getdta()
{
extern unsigned int _rax, _rbx, _res;
_rax = DOS_getDTA << 8;
_doint(0x21);
return (((long) _res << 16) | _rbx);
} /* getdta() */
void setdta(dta)
type_dta dta;
{
extern unsigned int _rax, _rdx, _rds;
_rax = DOS_setDTA << 8;
#if defined(LARGE_CASE)
_rds = FP_SEG(dta);
_rdx = FP_OFF(dta);
#else
_rds = _showds(); /* ss == ds for small case, doesn't matter which */
_rdx = (unsigned) dta;
#endif
_doint(0x21);
} /* setdta() */
#endif /* #if defined(DESMET) */
/*---------------------------------------------------------------------------*/
/* Functions for DOS find-first and find-next file. Disk transfer area must
be established via setdta() before calling these functions (Turbo C
findfirst()/findnext() both call setdta() internally).
Norton's "Programmer's Guide to the IBM PC" and the IBM "DOS Technical
Reference" state that DOS_findnext function needs DS:DX to point to dta.
This is incorrect (see Duncan's "Advanced MS-DOS", p. 363). */
#if defined(MSC)
int findfirst(path, fblk, attrib)
char *path; /* drive:\directory\filename.ext string */
struct ffblk *fblk; /* work area to pass to findnext */
int attrib; /* file attribute */
{
union REGS rx;
#if defined(M_I86CM) || defined(M_I86LM)
struct SREGS rs;
#endif
rx.h.ah = DOS_findfirst;
rx.x.cx = attrib;
rx.x.dx = FP_OFF(path);
#if defined(M_I86CM) || defined(M_I86LM)
rs.ds = FP_SEG(path);
intdosx(&rx, &rx, &rs);
#else
intdos(&rx, &rx);
#endif
if (rx.x.cflag) { /* error? */
errno = rx.x.ax; /* DOS error code */
return (-1);
}
errno = 0; /* no error */
return (0);
} /* findfirst() */
int findnext(fblk)
struct ffblk *fblk; /* work area from findfirst */
{
union REGS rx;
rx.h.ah = DOS_findnext;
intdos(&rx, &rx);
if (rx.x.cflag) { /* error? */
errno = rx.x.ax; /* DOS error code */
return (-1);
}
errno = 0; /* no error */
return (0);
} /* findnext() */
#endif /* #if defined(MSC) */
#if defined(DESMET)
int findfirst(path, fblk, attrib)
char *path; /* drive:\directory\filename.ext string */
struct ffblk *fblk; /* work area to pass to findnext */
int attrib; /* file attribute */
{
extern unsigned int _rax, _rcx, _rdx, _rds;
extern char _carryf;
_rax = DOS_findfirst << 8;
_rcx = attrib;
#if defined(LARGE_CASE)
_rds = FP_SEG(path);
_rdx = FP_OFF(path);
#else
_rdx = (unsigned) path;
#endif
_doint(0x21);
if (_carryf) { /* error? */
errno = _rax; /* DOS error code */
return (-1);
}
errno = 0; /* no error */
return (0);
} /* findfirst() */
int findnext(fblk)
struct ffblk *fblk; /* work area from findfirst */
{
extern unsigned int _rax;
extern char _carryf;
_rax = DOS_findnext << 8;
_doint(0x21);
if (_carryf) { /* error? */
errno = _rax; /* DOS error code */
return (-1);
}
errno = 0; /* no error */
return (0);
} /* findnext() */
#endif /* #if defined(DESMET) */
/*---------------------------------------------------------------------------*/
/* fnsplitE():
Split full file name path string into component parts--drive, directory,
file name, and file name extension. Returns bit flags indicating which
parts are in the input and whether any input was truncated because it is
too long. This is a work-alike function of fnsplit() in Turbo C 1.0 except
the latter doesn't indicate truncation errors. */
int fnsplitE(path, drive, dir, name, ext)
char *path; /* input path */
char *drive; /* output drive */
char *dir; /* output directory */
char *name; /* output file name */
char *ext; /* output file name extension */
{
int flags = 0; /* bit flags return value */
char *pL, *pR; /* Left & Right-hand ptrs into path */
unsigned int nchar; /* no. of characters */
/* Initialize output to null strings where present in case component
absent in input. */
if (drive != NULL)
*drive = '\0';
if (dir != NULL)
*dir = '\0';
if (name != NULL)
*name = '\0';
if (ext != NULL)
*ext = '\0';
while (*path == ' ') /* skip any leading spaces in input */
++path;
/* Truncate input if too long.
(Turbo C fnsplit() uses MAXPATH, not MAXPATH - 1.) */
if ((nchar = strlen(path)) > MAXPATH - 1) {
nchar = MAXPATH - 1;
flags |= PATHt; /* indicate truncation error */
}
/* Return if path null. */
if (nchar == 0)
return (flags);
/* Initialize current Left and Right ends to the terminal \0. */
pL = pR = path + nchar;
/* Reverse scan through input path string. */
while (--pL >= path) {
/* Check for special character in input path string. */
switch (*pL) {
case '.': /* possible left-most char of extension */
/* Extension, only if we're not already past it. */
if (!(flags & (DRIVE | DIRECTORY | FILENAME | EXTENSION))) {
flags |= fnsplitEa(pL, pR, ext, MAXEXT - 1, EXTENSION);
pR = pL;
}
break;
case '*': /* wildcard if in filename or extension */
case '?':
if (!(flags & (DRIVE | DIRECTORY)))
flags |= WILDCARDS;
break;
case '\\': /* possible directory indication */
case '/':
if (!(flags & (DRIVE | DIRECTORY | FILENAME))) {
flags |= fnsplitEa(++pL, pR, name, MAXFILE - 1, FILENAME);
pR = pL--;
}
if (!(flags & (DRIVE | DIRECTORY)))
flags |= DIRECTORY;
break;
case ':': /* right-most char of 2-char drive? */
if (pL == path + 1) { /* colon must be here to be a drive */
if (!(flags & (DRIVE | DIRECTORY | FILENAME))) {
flags |= fnsplitEa(++pL, pR, name, MAXFILE - 1,
FILENAME);
pR = pL--;
}
else if (flags & DIRECTORY) {
flags |= fnsplitEa(++pL, pR, dir, MAXDIR - 1,
DIRECTORY);
pR = pL--;
}
flags |= DRIVE;
}
} /* switch (*pL) */
/* If back to start of input string, deal with input that remains. */
if (pL == path) {
if (!(flags & (DRIVE | DIRECTORY | FILENAME)))
flags |= fnsplitEa(pL, pR, name, MAXFILE - 1, FILENAME);
else if (flags & DRIVE)
flags |= fnsplitEa(pL, pR, drive, MAXDRIVE - 1, DRIVE);
else /* only dir remains */
flags |= fnsplitEa(pL, pR, dir, MAXDIR - 1, DIRECTORY);
break;
}
} /* while (--pL >= path) */
return (flags);
} /* fnsplitE() */
/* fnsplitEa() copies string to output using left/right ptrs to input. */
int fnsplitEa(pL, pR, pOut, maxOut, flag)
char *pL, *pR; /* Left & Right-hand ptrs to input */
char *pOut; /* ptr to output, can be NULL */
unsigned int maxOut; /* max non-null char to *pOut excluding \0 */
int flag; /* fnsplitE() flag bit */
{
unsigned int nchar; /* no. of characters */
if ((nchar = pR - pL) > maxOut) { /* too long for output? */
nchar = maxOut; /* yes, truncate */
flag |= flag << 8; /* indicate truncation to caller */
}
if (pOut != NULL) { /* any output area? */
strncpy(pOut, pL, nchar); /* copy to output */
*(pOut + nchar) = '\0'; /* terminal \0 */
}
return (nchar > 0 ? flag : 0); /* return flag if input not empty */
} /* fnsplitEa() */
/*---------------------------------------------------------------------------*/
#if defined(MAIN)
/* main() test driver for expandwild(). */
#if !defined(DESMET) /* MSC or TC */
unsigned int xtoi(char []);
#else /* DESMET */
unsigned int xtoi();
#endif
int main(argc, argv)
int argc;
char *argv[]; /* argv[1] is attribute byte in ASCII hex */
{
unsigned int attrib; /* attribute byte from argv[1] via xtoi() */
int i; /* argv/newargv index */
int rc; /* return code from expandwild() */
int newargc; /* count of expanded arg strings */
char **newargv; /* ptr to new array of expanded arg strings */
/* Msgs for errors detected by expandwild(). */
static char *rmsg[] = {
"successful", /* 0 */
"not enough memory for output", /* 1 EXWINOMEM */
"output count change between passes", /* 2 EXWICNTCHG */
"output size change between passes" /* 3 EXWISIZCHG */
};
#if defined(MSC)
static char compiler[] = "Microsoft C";
#if defined(M_I86SM)
static char model[] = "small model";
#elif defined(M_I86MM)
static char model[] = "medium model";
#elif defined(M_I86CM)
static char model[] = "compact model";
/* Test M_I86HM before M_I86LM because both defined in huge model. */
#elif defined(M_I86HM)
static char model[] = "huge model";
#elif defined(M_I86LM)
static char model[] = "large model";
#endif
#else
#if defined(DESMET)
static char compiler[] = "DeSmet C";
#if defined(LARGE_CASE)
static char model[] = "large case";
#else
static char model[] = "small case";
#endif
#else
static char compiler[] = "Turbo C";
#if defined(__TINY__)
static char model[] = "tiny model";
#elif defined(__SMALL__)
static char model[] = "small model";
#elif defined(__MEDIUM__)
static char model[] = "medium model";
#elif defined(__COMPACT__)
static char model[] = "compact model";
#elif defined(__LARGE__)
static char model[] = "large model";
#elif defined(__HUGE__)
static char model[] = "huge model";
#endif
#endif
#endif
fprintf(stderr, "Test expandwild(), %s %s\n", compiler, model);
/* Check range of no. of args and value of attribute byte. */
if (argc < 2 || (attrib = xtoi(argv[1])) == 0xffff
|| attrib > (FA_RDONLY|FA_HIDDEN|FA_SYSTEM|FA_LABEL|FA_DIREC|FA_ARCH)) {
printf("\nUsage: expandwi attrib [filespec [filespec] ...]\n");
printf(" where attrib is file attribute byte in hex.\n");
return (255);
}
printf("\nFile names requested with attribute byte of 0x%X\n", attrib);
printf("\nexpandwild() input strings (count = %d)\n", argc - 2);
for (i = 2; i < argc; i++)
printf(" %s\n", argv[i]);
#if defined(DESMET) /* can't use -1 with DeSmet expandwild() */
rc = expandwild(&newargc, &newargv, argc - 2, &argv[2], attrib);
#else /* MSC or Turbo C can stop on argv[argc] == NULL */
rc = expandwild(&newargc, &newargv, -1, &argv[2], attrib);
#endif
printf("\nexpandwild() return code = %d (%s)\n", rc, rmsg[rc]);
if (rc == 0) {
printf("\nexpandwild() expanded output strings (count = %d)\n",
newargc);
for (i = 0; i < newargc; i++)
printf(" %s\n", newargv[i]);
}
return (rc);
} /* main() */
/* Convert ASCII string of hex digits to int. Return 0xffff if invalid digits
or overflow detected; warning: also returned for ASCII string "FFFF". */
#if !defined(DESMET) /* MSC or Turbo C */
#include
#else /* DESMET */
int isascii(), toupper();
#endif
unsigned int xtoi(x)
char x[]; /* input ASCII hex string, can be null */
{
unsigned int xw; /* work area for current hex digit */
unsigned int ix = 0; /* return value accumulator */
for (; *x != '\0'; ++x) {
if (!isascii(xw = *x))
return (0xffff); /* invalid digit */
xw = toupper(xw);
if (xw >= '0' && xw <= '9')
xw -= '0';
else if (xw >= 'A' && xw <= 'F')
xw -= 'A' - 0xA;
else
return (0xffff); /* invalid digit */
if (ix < 0x1000) { /* overflow? */
ix <<= 4; /* make room for current digit */
ix |= xw; /* add current digit */
}
else
return (0xffff); /* overflow */
}
return (ix);
} /* xtoi() */
#endif /* #if defined(MAIN) */
/*---------------------------------------------------------------------------*/
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/