Skip to content

Commit b8edf33

Browse files
authored
Merge pull request #557 from a740g/filesystem-lib-fixes
Fix shared directory state between KILL, FILES, and _FILES$
2 parents 60b0c2c + 29f5201 commit b8edf33

File tree

1 file changed

+61
-42
lines changed

1 file changed

+61
-42
lines changed

internal/c/libqb/src/filesystem.cpp

+61-42
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44

55
#include "libqb-common.h"
66

7+
#include "error_handle.h"
78
#include "filepath.h"
89
#include "filesystem.h"
910

1011
#include "../../libqb.h"
1112

1213
#include <algorithm>
1314
#include <dirent.h>
15+
#include <memory>
1416
#include <sys/stat.h>
1517
#include <unistd.h>
1618
#ifdef QB64_WINDOWS
@@ -423,7 +425,7 @@ qbs *func__dir(qbs *qbsContext) {
423425
/// @param path The directory path
424426
/// @return True if the directory exists
425427
int32_t func__direxists(qbs *path) {
426-
if (new_error)
428+
if (is_error_pending())
427429
return QB_FALSE;
428430

429431
std::string pathName(reinterpret_cast<char *>(path->chr), path->len);
@@ -450,7 +452,7 @@ bool FS_FileExists(const char *path) {
450452
/// @param path The file path to check for
451453
/// @return True if the file exists
452454
int32_t func__fileexists(qbs *path) {
453-
if (new_error)
455+
if (is_error_pending())
454456
return QB_FALSE;
455457

456458
std::string pathName(reinterpret_cast<char *>(path->chr), path->len);
@@ -471,7 +473,7 @@ qbs *func__startdir() {
471473
/// @brief Changes the current directory
472474
/// @param str The directory path to change to
473475
void sub_chdir(qbs *str) {
474-
if (new_error)
476+
if (is_error_pending())
475477
return;
476478

477479
std::string pathName(reinterpret_cast<char *>(str->chr), str->len);
@@ -540,21 +542,34 @@ static inline bool FS_IsPatternMatching(const char *fileSpec, const char *fileNa
540542
/// @return True if * or ? are found
541543
static inline bool FS_HasPattern(const char *fileSpec) { return fileSpec != nullptr && (strchr(fileSpec, '*') || strchr(fileSpec, '?')); }
542544

545+
struct DirectoryContext {
546+
DIR *directory;
547+
char pattern[FS_PATHNAME_LENGTH_MAX];
548+
char entry[FS_PATHNAME_LENGTH_MAX];
549+
550+
DirectoryContext() : directory(nullptr) {
551+
pattern[0] = '\0';
552+
entry[0] = '\0';
553+
}
554+
555+
~DirectoryContext() {
556+
if (directory)
557+
closedir(directory);
558+
}
559+
};
560+
543561
/// @brief An MS BASIC PDS DIR$ style function
562+
/// @param ctx The directory context
544563
/// @param fileSpec This can be a path with wildcard for the final level (i.e. C:/Windows/*.* or /usr/lib/* etc.)
545564
/// @return Returns a file or directory name matching fileSpec or an empty string when there is nothing left
546-
static const char *FS_GetDirectoryEntryName(const char *fileSpec) {
547-
static DIR *pDir = nullptr;
548-
static char pattern[FS_PATHNAME_LENGTH_MAX];
549-
static char entry[FS_PATHNAME_LENGTH_MAX];
550-
551-
entry[0] = '\0'; // set to an empty string
565+
static const char *FS_GetDirectoryEntryName(DirectoryContext *ctx, const char *fileSpec) {
566+
ctx->entry[0] = '\0'; // set to an empty string
552567

553568
if (!FS_IsStringEmpty(fileSpec)) {
554569
// We got a filespec. Check if we have one already going and if so, close it
555-
if (pDir) {
556-
closedir(pDir);
557-
pDir = nullptr;
570+
if (ctx->directory) {
571+
closedir(ctx->directory);
572+
ctx->directory = nullptr;
558573
}
559574

560575
char dirName[FS_PATHNAME_LENGTH_MAX]; // we only need this for opendir()
@@ -569,62 +584,63 @@ static const char *FS_GetDirectoryEntryName(const char *fileSpec) {
569584

570585
if (p) {
571586
// Split the path and the filespec
572-
strncpy(pattern, p + 1, FS_PATHNAME_LENGTH_MAX);
573-
pattern[FS_PATHNAME_LENGTH_MAX - 1] = '\0';
587+
strncpy(ctx->pattern, p + 1, FS_PATHNAME_LENGTH_MAX);
588+
ctx->pattern[FS_PATHNAME_LENGTH_MAX - 1] = '\0';
574589
auto len = std::min<size_t>((p - fileSpec) + 1, FS_PATHNAME_LENGTH_MAX - 1);
575590
memcpy(dirName, fileSpec, len);
576591
dirName[len] = '\0';
577592
} else {
578593
// No path. Use the current path
579-
strncpy(pattern, fileSpec, FS_PATHNAME_LENGTH_MAX);
580-
pattern[FS_PATHNAME_LENGTH_MAX - 1] = '\0';
594+
strncpy(ctx->pattern, fileSpec, FS_PATHNAME_LENGTH_MAX);
595+
ctx->pattern[FS_PATHNAME_LENGTH_MAX - 1] = '\0';
581596
strcpy(dirName, "./");
582597
}
583598
} else {
584599
// No pattern. Check if this is a file and simply return the name if it exists
585600
if (FS_FileExists(fileSpec)) {
586-
strncpy(entry, filepath_get_filename(fileSpec), FS_PATHNAME_LENGTH_MAX);
587-
entry[FS_PATHNAME_LENGTH_MAX - 1] = '\0';
601+
strncpy(ctx->entry, filepath_get_filename(fileSpec), FS_PATHNAME_LENGTH_MAX);
602+
ctx->entry[FS_PATHNAME_LENGTH_MAX - 1] = '\0';
588603

589-
return entry;
604+
return ctx->entry;
590605
}
591606

592607
// Else, We'll just assume it's a directory
593608
strncpy(dirName, fileSpec, FS_PATHNAME_LENGTH_MAX);
594609
dirName[FS_PATHNAME_LENGTH_MAX - 1] = '\0';
595-
strcpy(pattern, "*");
610+
strcpy(ctx->pattern, "*");
596611
}
597612

598-
pDir = opendir(dirName);
613+
ctx->directory = opendir(dirName);
599614
}
600615

601-
if (pDir) {
616+
if (ctx->directory) {
602617
for (;;) {
603-
auto pDirent = readdir(pDir);
618+
auto pDirent = readdir(ctx->directory);
604619
if (!pDirent) {
605-
closedir(pDir);
606-
pDir = nullptr;
620+
closedir(ctx->directory);
621+
ctx->directory = nullptr;
607622

608623
break;
609624
}
610625

611-
if (FS_IsPatternMatching(pattern, pDirent->d_name)) {
612-
strncpy(entry, pDirent->d_name, FS_PATHNAME_LENGTH_MAX);
613-
entry[FS_PATHNAME_LENGTH_MAX - 1] = '\0';
626+
if (FS_IsPatternMatching(ctx->pattern, pDirent->d_name)) {
627+
strncpy(ctx->entry, pDirent->d_name, FS_PATHNAME_LENGTH_MAX);
628+
ctx->entry[FS_PATHNAME_LENGTH_MAX - 1] = '\0';
614629

615630
break;
616631
}
617632
}
618633
}
619634

620-
return entry;
635+
return ctx->entry;
621636
}
622637

623638
/// @brief This mimics MS BASIC PDS 7.1 & VBDOS 1.0 DIR$() function
624639
/// @param qbsFileSpec This can be a path with wildcard for the final level (i.e. C:/Windows/*.* or /usr/lib/* etc.)
625640
/// @param passed Flags for optional parameters
626641
/// @return Returns a qbs with the directory entry name or an empty string if there are no more entries
627642
qbs *func__files(qbs *qbsFileSpec, int32_t passed) {
643+
static DirectoryContext directoryContext;
628644
static std::string directory;
629645
std::string pathName;
630646
const char *entry;
@@ -645,7 +661,7 @@ qbs *func__files(qbs *qbsFileSpec, int32_t passed) {
645661
directory = "./";
646662
}
647663

648-
entry = FS_GetDirectoryEntryName(fileSpec.c_str());
664+
entry = FS_GetDirectoryEntryName(&directoryContext, fileSpec.c_str());
649665
} else {
650666
// Check if we've been called the first time without a filespec
651667
if (directory.empty()) {
@@ -655,7 +671,7 @@ qbs *func__files(qbs *qbsFileSpec, int32_t passed) {
655671
return qbsFinal;
656672
}
657673

658-
entry = FS_GetDirectoryEntryName(nullptr); // get the next entry
674+
entry = FS_GetDirectoryEntryName(&directoryContext, nullptr); // get the next entry
659675
}
660676

661677
filepath_join(pathName, directory, entry);
@@ -781,7 +797,7 @@ static std::string FS_GetShortName(const char *path) {
781797
void sub_files(qbs *str, int32_t passed) {
782798
static qbs *strz = nullptr;
783799

784-
if (new_error)
800+
if (is_error_pending())
785801
return;
786802

787803
if (!strz)
@@ -819,7 +835,8 @@ void sub_files(qbs *str, int32_t passed) {
819835
qbs_set(strz, qbs_new_txt_len(shortName.c_str(), shortName.size()));
820836
qbs_print(strz, 1);
821837

822-
auto entry = FS_GetDirectoryEntryName(fileSpec.c_str()); // get the first entry
838+
auto directoryContext = std::make_unique<DirectoryContext>();
839+
auto entry = FS_GetDirectoryEntryName(directoryContext.get(), fileSpec.c_str()); // get the first entry
823840
filepath_join(pathName, directory, entry);
824841

825842
if (FS_IsStringEmpty(entry)) {
@@ -854,7 +871,7 @@ void sub_files(qbs *str, int32_t passed) {
854871
makefit(strz);
855872
qbs_print(strz, 0);
856873

857-
entry = FS_GetDirectoryEntryName(nullptr); // get the next entry
874+
entry = FS_GetDirectoryEntryName(directoryContext.get(), nullptr); // get the next entry
858875
filepath_join(pathName, directory, entry);
859876
} while (!FS_IsStringEmpty(entry));
860877

@@ -872,13 +889,15 @@ void sub_files(qbs *str, int32_t passed) {
872889
/// @brief Deletes files from disk
873890
/// @param str The file(s) to delete (may contain wildcard at the final level)
874891
void sub_kill(qbs *str) {
875-
if (new_error)
892+
893+
if (is_error_pending())
876894
return;
877895

878896
std::string directory, pathName, fileSpec(reinterpret_cast<char *>(str->chr), str->len);
879-
880897
filepath_split(filepath_fix_directory(fileSpec), directory, pathName); // split the file path
881-
auto entry = FS_GetDirectoryEntryName(fileSpec.c_str()); // get the first entry
898+
899+
auto directoryContext = std::make_unique<DirectoryContext>();
900+
auto entry = FS_GetDirectoryEntryName(directoryContext.get(), fileSpec.c_str()); // get the first entry
882901

883902
// Keep looking through the entries until we file a file
884903
while (!FS_IsStringEmpty(entry)) {
@@ -887,7 +906,7 @@ void sub_kill(qbs *str) {
887906
if (FS_FileExists(pathName.c_str()))
888907
break;
889908

890-
entry = FS_GetDirectoryEntryName(nullptr); // get the next entry
909+
entry = FS_GetDirectoryEntryName(directoryContext.get(), nullptr); // get the next entry
891910
}
892911

893912
// Check if we have exhausted the entries without ever finding a file
@@ -917,15 +936,15 @@ void sub_kill(qbs *str) {
917936
}
918937
}
919938

920-
entry = FS_GetDirectoryEntryName(nullptr); // get the next entry
939+
entry = FS_GetDirectoryEntryName(directoryContext.get(), nullptr); // get the next entry
921940
filepath_join(pathName, directory, entry);
922941
} while (!FS_IsStringEmpty(entry));
923942
}
924943

925944
/// @brief Creates a new directory
926945
/// @param str The directory path name to create
927946
void sub_mkdir(qbs *str) {
928-
if (new_error)
947+
if (is_error_pending())
929948
return;
930949

931950
std::string pathName(reinterpret_cast<char *>(str->chr), str->len);
@@ -949,7 +968,7 @@ void sub_mkdir(qbs *str) {
949968
/// @param oldname The old file / directory name
950969
/// @param newname The new file / directory name
951970
void sub_name(qbs *oldname, qbs *newname) {
952-
if (new_error)
971+
if (is_error_pending())
953972
return;
954973

955974
std::string pathNameOld(reinterpret_cast<char *>(oldname->chr), oldname->len), pathNameNew(reinterpret_cast<char *>(newname->chr), newname->len);
@@ -979,7 +998,7 @@ void sub_name(qbs *oldname, qbs *newname) {
979998
/// @brief Deletes an empty directory
980999
/// @param str The path name of the directory to delete
9811000
void sub_rmdir(qbs *str) {
982-
if (new_error)
1001+
if (is_error_pending())
9831002
return;
9841003

9851004
std::string pathName(reinterpret_cast<char *>(str->chr), str->len);

0 commit comments

Comments
 (0)