/* MS-DOS GLOB (3C) FUNCTION * * MS-DOS GLOB FUNCTION - Copyright (c) 1990,1,2 Data Logic Limited. * * This code is subject to the following copyright restrictions: * * 1. Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in the * source form. * * $Header: /usr/users/istewart/src/shell/sh2.1/RCS/glob.c,v 2.0 1992/04/13 17:39:09 Ian_Stewartson Exp $ * * $Log: glob.c,v $ * Revision 2.0 1992/04/13 17:39:09 Ian_Stewartson * MS-Shell 2.0 Baseline release * */ #include /* MS-DOS type definitions */ #include /* File status definitions */ #include /* Standard I/O delarations */ #include /* Standard library functions */ #include /* String library functions */ #include /* String library functions */ #include /* Direction I/O functions */ #include /* Character types function */ #include /* Other functions */ #include /* Malloc functions */ #include #ifdef MSDOS #ifdef OS2 #define INCL_DOSDEVICES #include /* OS2 functions declarations */ #else #include /* DOS BIOS functions */ #include /* DOS functions */ #endif #endif static int _GP_SortCompare _PROTO ((char **, char **)); static int _GP_ExpandField _PROTO ((char *, char *, glob_t *)); static int _GP_ExpandMetaCharacters _PROTO ((char *, glob_t *)); static int _GP_AddArgument _PROTO ((char *, glob_t *)); static bool _GP_MatchPattern _PROTO ((char *, char *)); static char *_GP_MetaChars = "?*[\\"; static char *_GP_NullString = ""; #ifdef MSDOS static int _GP_GetNumberofFloppyDrives (void); #ifdef OS2 static void _dos_setdrive (unsigned int, unsigned int *); static void _dos_getdrive (unsigned int *); #endif static char *_GP_CheckForMultipleDrives _PROTO ((char *)); #endif /* Free up space */ void globfree (gp) glob_t *gp; { int i = (gp->gl_flags & GLOB_DOOFFS) ? gp->gl_offs : 0; while (i < gp->gl_pathc) free (gp->gl_pathv[i++]); free (gp->gl_pathv); } /* Main search function */ int glob (Pattern, flags, ErrorFunction, gp) char *Pattern; int flags; int (*ErrorFunction) _PROTO ((char *, int)); glob_t *gp; { int ReturnValue; char *PatternCopy; char *cp; /* If no append mode - initialise */ if (!(flags & GLOB_APPEND)) { gp->gl_pathc = 0; gp->gl_pathv = (char **)NULL; } gp->gl_flags = flags; gp->gl_ef = ErrorFunction; if ((PatternCopy = alloca (strlen (Pattern) + 1)) == (char *)NULL) return GLOB_NOSPACE; /* Expand and kill environment */ if (ReturnValue = _GP_ExpandMetaCharacters (strcpy (PatternCopy, Pattern), gp)) return ReturnValue; /* Check for no finds. If add value, strip out \ from the string */ if ((gp->gl_pathc == 0) && (flags & GLOB_NOCHECK)) { cp = strcpy (PatternCopy, Pattern); while ((cp = strpbrk (cp, "?*[")) != (char *)NULL) { if ((cp == PatternCopy) || (*(cp - 1) != '\\')) cp++; else memmove (cp - 1, cp, strlen (cp) + 1); } if (ReturnValue = _GP_AddArgument (PatternCopy, gp)) return ReturnValue; } /* Terminate string */ if ((gp->gl_pathc != 0) && (ReturnValue = _GP_AddArgument ((char *)NULL, gp))) return ReturnValue; /* Get the sort length */ ReturnValue = (gp->gl_flags & GLOB_DOOFFS) ? gp->gl_offs : 0; if ((!(flags & GLOB_NOSORT)) && (gp->gl_pathc > 1)) qsort (&gp->gl_pathv[ReturnValue], gp->gl_pathc, sizeof (char *), _GP_SortCompare); return 0; } /* Compare function for sort */ static int _GP_SortCompare (a1, a2) char **a1, **a2; { return strcmp (*a1, *a2); } /* Expand a field if it has metacharacters in it */ static int _GP_ExpandField (CurrentDirectoryPattern, AppendString, gp) char *CurrentDirectoryPattern; /* Prefix field */ char *AppendString; /* Postfix field */ glob_t *gp; { int i; int ReturnValue = 0; /* Return Value */ char *FullFileName; /* Search file name */ char *FileNameStart; char *MatchString; /* Match string */ DIR *DirHandler; struct dirent *CurrentDirectoryEntry; #ifdef MSDOS unsigned int CurrentDrive; /* Current drive */ unsigned int MaxDrives; /* Max drive */ unsigned int SelectedDrive; /* Selected drive */ unsigned int x_drive, y_drive; /* Dummies */ char *DriveCharacter; /* Multi-drive flag */ char SDriveString[2]; /* Convert file name to lower case */ #ifndef OS2 strlwr (CurrentDirectoryPattern); #else if (!IsHPFSFileSystem (CurrentDirectoryPattern)) strlwr (CurrentDirectoryPattern); #endif /* Search all drives ? */ if ((DriveCharacter = _GP_CheckForMultipleDrives (CurrentDirectoryPattern)) != (char *)NULL) { _dos_getdrive (&CurrentDrive); /* Get number of drives */ _dos_setdrive (CurrentDrive, &MaxDrives); SDriveString[1] = 0; for (SelectedDrive = 1; SelectedDrive <= MaxDrives; ++SelectedDrive) { _dos_setdrive (SelectedDrive, &x_drive); _dos_getdrive (&y_drive); _dos_setdrive (CurrentDrive, &x_drive); /* Check to see if the second diskette drive is really there */ if ((_GP_GetNumberofFloppyDrives () < 2) && (SelectedDrive == 2)) continue; /* If the drive exists and is in our list - process it */ *DriveCharacter = 0; *SDriveString = (char)(SelectedDrive + 'a' - 1); strlwr (CurrentDirectoryPattern); if ((y_drive == SelectedDrive) && _GP_MatchPattern (SDriveString, CurrentDirectoryPattern)) { if ((FullFileName = alloca (strlen (DriveCharacter) + 3)) == (char *)NULL) return GLOB_NOSPACE; *DriveCharacter = ':'; *FullFileName = *SDriveString; strcpy (FullFileName + 1, DriveCharacter); if (i = _GP_ExpandField (FullFileName, AppendString, gp)) return i; } *DriveCharacter = ':'; } return 0; } #endif /* Get the path length */ MatchString = strrchr (CurrentDirectoryPattern, '/'); #ifdef MSDOS if ((MatchString == (char *)NULL) && (*(CurrentDirectoryPattern + 1) == ':')) MatchString = CurrentDirectoryPattern + 1; #endif /* Set up file name for search */ if ((MatchString == (char *)NULL) || (*MatchString == ':')) { if ((FullFileName = alloca (NAME_MAX + 7 + strlen (AppendString))) == (char *)NULL) return GLOB_NOSPACE; if (MatchString != (char *)NULL) *(strcpy (FullFileName, "x:.")) = *CurrentDirectoryPattern; else strcpy (FullFileName, "."); FileNameStart = FullFileName + (int)((MatchString != (char *)NULL) ? 2 : 0); } /* Case of //... */ else if ((FullFileName = alloca (NAME_MAX + 4 + strlen (AppendString) + (i = (int)(MatchString - CurrentDirectoryPattern)))) == (char *)NULL) return GLOB_NOSPACE; else { strncpy (FullFileName, CurrentDirectoryPattern, i); *((FileNameStart = FullFileName + i)) = 0; strcpy (FileNameStart++, "/"); } MatchString = (MatchString == (char *)NULL) ? CurrentDirectoryPattern : MatchString + 1; /* Search for file names */ if ((DirHandler = opendir (FullFileName)) == (DIR *)NULL) { i = 0; if (((gp->gl_ef != NULL) && (*gp->gl_ef)(FullFileName, errno)) || (gp->gl_flags & GLOB_ERR)) i = GLOB_ABEND; return i; } /* Are there any matches */ while ((CurrentDirectoryEntry = readdir (DirHandler)) != (struct dirent *)NULL) { if ((*CurrentDirectoryEntry->d_name == '.') && (*MatchString != '.')) continue; /* Check for match */ if (_GP_MatchPattern (CurrentDirectoryEntry->d_name, MatchString)) { strcpy (FileNameStart, CurrentDirectoryEntry->d_name); /* If the postfix is not null, this must be a directory */ if (strlen (AppendString)) { struct stat statb; char *p; /* If not a directory - go to the next file */ if (stat (FullFileName, &statb) < 0 || !S_ISDIR (statb.st_mode & S_IFMT)) continue; /* Are there any metacharacters in the postfix? */ if ((p = strpbrk (AppendString, _GP_MetaChars)) == (char *)NULL) { /* No - build the file name and check it exists */ strcat (strcat (FileNameStart, "/"), AppendString); if ((access (FullFileName, F_OK) == 0) && (ReturnValue = _GP_AddArgument (FullFileName, gp))) break; } /* Yes - build the filename upto the start of the meta characters */ else { if ((p = strchr (p, '/')) != (char *)NULL) *(p++) = 0; else p = _GP_NullString; /* Build the new directory name and check it out */ strcat (strcat (FileNameStart, "/"), AppendString); ReturnValue = _GP_ExpandField (FullFileName, p, gp); if (p != _GP_NullString) *(--p) = '/'; /* Check for errors */ if (ReturnValue) break; } } /* Process this file. If error - terminate */ else if ((access (FullFileName, F_OK) == 0) && (ReturnValue = _GP_AddArgument (FullFileName, gp))) break; } } closedir (DirHandler); return ReturnValue; } /* Find the location of meta-characters. If no meta, add the argument and * return. If meta characters, expand directory containing meta characters. */ static int _GP_ExpandMetaCharacters (file, gp) char *file; glob_t *gp; { char *p; int ReturnValue; /* No metas - add to string */ if ((p = strpbrk (file, _GP_MetaChars)) == (char *)NULL) { if (access (file, F_OK) < 0) return 0; return _GP_AddArgument (file, gp); } /* Ok - metas, find the end of the start of the directory */ else if ((p = strchr (p, '/')) != (char *)NULL) *(p++) = 0; else p = _GP_NullString; /* Continue recusive match */ ReturnValue = _GP_ExpandField (file, p, gp); /* Restore if necessary */ if (p != _GP_NullString) *(--p) = '/'; return ReturnValue; } /* Add an argument to the stack - file is assumed to be a array big enough * for the file name + 2 */ static int _GP_AddArgument (file, gp) char *file; glob_t *gp; { int Offset; char **p1; struct stat FileStatus; Offset = gp->gl_pathc + ((gp->gl_flags & GLOB_DOOFFS) ? gp->gl_offs : 0); p1 = gp->gl_pathv; /* Malloc space if necessary */ if (gp->gl_pathc == 0) p1 = (char **)calloc (sizeof (char *), (50 + Offset)); else if ((gp->gl_pathc % 50) == 0) p1 = (char **)realloc (p1, (Offset + 50) * (sizeof (char *))); if (p1 == (char **)NULL) return GLOB_NOSPACE; /* OK got space */ gp->gl_pathv = p1; /* End of list ? */ if (file == (char *)NULL) p1[Offset] = (char *)NULL; else { if ((gp->gl_flags & GLOB_MARK) && (file[strlen (file) - 1] != '/') && (stat (file, &FileStatus) == 0) && (S_ISDIR (FileStatus.st_mode))) strcat (file, "/"); if ((p1[Offset] = strdup (file)) == (char *)NULL) return GLOB_NOSPACE; strcpy (p1[Offset], file); /* Increment counter */ ++(gp->gl_pathc); } return 0; } #ifdef MSDOS /* Check for multi_drive prefix */ static char *_GP_CheckForMultipleDrives (prefix) char *prefix; { if (strlen (prefix) < 2) return (char *)NULL; if (((*prefix == '*') || (*prefix == '?')) && (prefix[1] == ':')) return prefix + 1; if (*prefix != '[') return (char *)NULL; while (*prefix && (*prefix != ']')) { if ((*prefix == '\\') && (*(prefix + 1))) ++prefix; ++prefix; } return (*prefix && (*(prefix + 1) == ':')) ? prefix + 1 : (char *)NULL; } /* Some OS/2 functions to emulate the DOS functions */ #ifdef OS2 static void _dos_getdrive (cdp) unsigned int *cdp; { USHORT cdr; ULONG ndr; DosQCurDisk((PUSHORT)&cdr, (PULONG) &ndr); *cdp = (unsigned int)cdr; } static void _dos_setdrive (cdr, ndp) unsigned int cdr; unsigned int *ndp; { ULONG ulDrives; USHORT usDisk; int i; DosSelectDisk ((USHORT)cdr); /* Get the current disk and check that to see the number of drives */ DosQCurDisk (&usDisk, &ulDrives); /* gets current drive */ for (i = 25; (!(ulDrives & (1L << i))) && i >= 0; --i) continue; *ndp = (unsigned int)(i + 1); } #endif /* Return the number of floppy disks */ static int _GP_GetNumberofFloppyDrives () { #ifdef OS2 BYTE nflop = 1; DosDevConfig (&nflop, DEVINFO_FLOPPY, 0); return nflop; #else return ((_bios_equiplist () & 0x00c0) >> 6) + 1; #endif } #endif /* * Pattern Matching function */ static bool _GP_MatchPattern (string, pattern) char *string; /* String to match */ char *pattern; /* Pattern to match against */ { register int cur_s; /* Current string character */ register int cur_p; /* Current pattern character */ /* Match string */ while (cur_p = *(pattern++)) { cur_s = *(string++); /* Load current string character */ switch (cur_p) /* Switch on pattern character */ { case '[': /* Match class of characters */ { while(1) { if (!(cur_p = *(pattern++))) return 0; if (cur_p == ']') return FALSE; if (cur_s != cur_p) { if (*pattern == '-') { if(cur_p > cur_s) continue; if (cur_s > *(++pattern)) continue; } else continue; } break; } while (*pattern) { if (*(pattern++) == ']') break; } break; } case '?': /* Match any character */ { if (!cur_s) return FALSE; break; } case '*': /* Match any number of any character*/ { string--; do { if (_GP_MatchPattern (string, pattern)) return TRUE; } while (*(string++)); return FALSE; } case '\\': /* Next character is non-meta */ { if (!(cur_p = *(pattern++))) return FALSE; } default: /* Match against current pattern */ { if (cur_p != cur_s) return FALSE; break; } } } return (!*string) ? TRUE : FALSE; } /* * Test program */ #ifdef TEST main (int argc, char **argv) { int i; for (i = 0; i < argc; i++) printf ("Arg %d = <%s>\n", i, argv[i]); } #endif