1
1
Fork 0
mirror of https://github.com/QB64-Phoenix-Edition/QB64pe.git synced 2024-07-04 04:50:22 +00:00

Merge pull request #419 from a740g/filesystem-refactor-and-update

Filesystem refactor and update
This commit is contained in:
Samuel Gomes 2024-01-03 00:39:40 +05:30 committed by GitHub
commit 7aca957619
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 1447 additions and 740 deletions

View file

@ -52,14 +52,14 @@
# endif
// common includes
# include <stdio.h>
# include <cmath>
# include <stdint.h>
# include <errno.h>
# include <fcntl.h>
# include <fstream>
# include <iostream>
# include <limits.h>
# include <stdint.h>
# include <stdio.h>
# include <string.h>
# include <time.h>
@ -77,13 +77,13 @@
# else
# include <dlfcn.h>
# include <pthread.h>
# include <stdlib.h>
# include <sys/stat.h>
# include <sys/types.h>
# include <sys/wait.h>
# include <unistd.h>
# include <dlfcn.h>
# endif
@ -93,6 +93,105 @@
# endif
# endif
# define QB_FALSE 0
# define QB_TRUE -1
# define QB_ERROR_NEXT_WITHOUT_FOR 1
# define QB_ERROR_SYNTAX_ERROR 2
# define QB_ERROR_RETURN_WITHOUT_GOSUB 3
# define QB_ERROR_OUT_OF_DATA 4
# define QB_ERROR_ILLEGAL_FUNCTION_CALL 5
# define QB_ERROR_OVERFLOW 6
# define QB_ERROR_OUT_OF_MEMORY 7
# define QB_ERROR_LABEL_NOT_DEFINED 8
# define QB_ERROR_SUBSCRIPT_OUT_OF_RANGE 9
# define QB_ERROR_DUPLICATE_DEFINITION 10
# define QB_ERROR_DIVISION_BY_ZERO 11
# define QB_ERROR_ILLEGAL_IN_DIRECT_MODE 12
# define QB_ERROR_TYPE_MISMATCH 13
# define QB_ERROR_OUT_OF_STRING_SPACE 14
# define QB_ERROR_STRING_FORMULA_TOO_COMPLEX 16
# define QB_ERROR_CANNOT_CONTINUE 17
# define QB_ERROR_FUNCTION_NOT_DEFINED 18
# define QB_ERROR_NO_RESUME 19
# define QB_ERROR_RESUME_WITHOUT_ERROR 20
# define QB_ERROR_DEVICE_TIMEOUT 24
# define QB_ERROR_DEVICE_FAULT 25
# define QB_ERROR_FOR_WITHOUT_NEXT 26
# define QB_ERROR_OUT_OF_PAPER 27
# define QB_ERROR_WHILE_WITHOUT_WEND 29
# define QB_ERROR_WEND_WITHOUT_WHILE 30
# define QB_ERROR_DUPLICATE_LABEL 33
# define QB_ERROR_SUBPROGRAM_NOT_DEFINED 35
# define QB_ERROR_ARGUMENT_COUNT_MISMATCH 37
# define QB_ERROR_ARRAY_NOT_DEFINED 38
# define QB_ERROR_VARIABLE_REQUIRED 40
# define QB_ERROR_FIELD_OVERFLOW 50
# define QB_ERROR_INTERNAL_ERROR 51
# define QB_ERROR_BAD_FILE_NAME_OR_NUMBER 52
# define QB_ERROR_FILE_NOT_FOUND 53
# define QB_ERROR_BAD_FILE_MODE 54
# define QB_ERROR_FILE_ALREADY_OPEN 55
# define QB_ERROR_FIELD_STATEMENT_ACTIVE 56
# define QB_ERROR_DEVICE_IO_ERROR 57
# define QB_ERROR_FILE_ALREADY_EXISTS 58
# define QB_ERROR_BAD_RECORD_LENGTH 59
# define QB_ERROR_DISK_FULL 61
# define QB_ERROR_INPUT_PAST_END_OF_FILE 62
# define QB_ERROR_BAD_RECORD_NUMBER 63
# define QB_ERROR_BAD_FILE_NAME 64
# define QB_ERROR_TOO_MANY_FILES 67
# define QB_ERROR_DEVICE_UNAVAILABLE 68
# define QB_ERROR_COMMUNICATION_BUFFER_OVERFLOW 69
# define QB_ERROR_PERMISSION_DENIED 70
# define QB_ERROR_DISK_NOT_READY 71
# define QB_ERROR_DISK_MEDIA_ERROR 72
# define QB_ERROR_FEATURE_UNAVAILABLE 73
# define QB_ERROR_RENAME_ACROSS_DISKS 74
# define QB_ERROR_PATH_FILE_ACCESS_ERROR 75
# define QB_ERROR_PATH_NOT_FOUND 76
# define QB_ERROR_OUT_OF_STACK_SPACE 256
# define QB_ERROR_OUT_OF_MEMORY_FATAL 257
# define QB_ERROR_INVALID_HANDLE 258
# define QB_ERROR_CANNOT_FIND_DYNAMIC_LIBRARY_FILE 259
# define QB_ERROR_FUNCTION_NOT_FOUND_IN_DYNAMIC_LIBRARY 260
# define QB_ERROR_FUNCTION_NOT_FOUND_IN_DYNAMIC_LIBRARY_261 261
# define QB_ERROR_GL_COMMAND_OUTSIDE_SUB_GL_SCOPE 270
# define QB_ERROR_END_SYSTEM_IN_SUB_GL_SCOPE 271
# define QB_ERROR_MEMORY_REGION_OUT_OF_RANGE 300
# define QB_ERROR_INVALID_SIZE 301
# define QB_ERROR_SOURCE_MEMORY_REGION_OUT_OF_RANGE 302
# define QB_ERROR_DESTINATION_MEMORY_REGION_OUT_OF_RANGE 303
# define QB_ERROR_BOTH_MEMORY_REGIONS_OUT_OF_RANGE 304
# define QB_ERROR_SOURCE_MEMORY_FREED 305
# define QB_ERROR_DESTINATION_MEMORY_FREED 306
# define QB_ERROR_MEMORY_ALREADY_FREED 307
# define QB_ERROR_MEMORY_HAS_BEEN_FREED 308
# define QB_ERROR_MEMORY_NOT_INITIALIZED 309
# define QB_ERROR_SOURCE_MEMORY_NOT_INITIALIZED 310
# define QB_ERROR_DESTINATION_MEMORY_NOT_INITIALIZED 311
# define QB_ERROR_BOTH_MEMORY_NOT_INITIALIZED 312
# define QB_ERROR_BOTH_MEMORY_FREED 313
# define QB_ERROR_ASSERT_FAILED 314
# define QB_ERROR_ASSERT_FAILED_WITH_DESCRIPTION 315
# define QB_ERROR_OUT_OF_MEMORY_FATAL_502 502
# define QB_ERROR_OUT_OF_MEMORY_FATAL_503 503
# define QB_ERROR_OUT_OF_MEMORY_FATAL_504 504
# define QB_ERROR_OUT_OF_MEMORY_FATAL_505 505
# define QB_ERROR_OUT_OF_MEMORY_FATAL_506 506
# define QB_ERROR_OUT_OF_MEMORY_FATAL_507 507
# define QB_ERROR_OUT_OF_MEMORY_FATAL_508 508
# define QB_ERROR_OUT_OF_MEMORY_FATAL_509 509
# define QB_ERROR_OUT_OF_MEMORY_FATAL_510 510
# define QB_ERROR_OUT_OF_MEMORY_FATAL_511 511
# define QB_ERROR_OUT_OF_MEMORY_FATAL_512 512
# define QB_ERROR_OUT_OF_MEMORY_FATAL_513 513
# define QB_ERROR_OUT_OF_MEMORY_FATAL_514 514
# define QB_ERROR_OUT_OF_MEMORY_FATAL_515 515
# define QB_ERROR_OUT_OF_MEMORY_FATAL_516 516
# define QB_ERROR_OUT_OF_MEMORY_FATAL_517 517
# define QB_ERROR_OUT_OF_MEMORY_FATAL_518 518
// QB64 string descriptor structure
struct qbs_field {
int32 fileno;

View file

@ -21,6 +21,8 @@
#include "compression.h"
#include "datetime.h"
#include "event.h"
#include "filepath.h"
#include "filesystem.h"
#include "font.h"
#include "game_controller.h"
#include "glut-thread.h"
@ -3946,23 +3948,6 @@ int32 exit_value = 0;
void error(int32 error_number); // for forward references
char *fixdir(qbs *filename) {
// note: changes the slashes in a filename to make it compatible with the OS
// applied to QB commands: open, bload/bsave, loadfont, loadimage, sndopen/sndplayfile
static int32 i;
for (i = 0; i < filename->len; i++) {
#ifdef QB64_WINDOWS
if (filename->chr[i] == 47)
filename->chr[i] = 92;
#else
if (filename->chr[i] == 92)
filename->chr[i] = 47;
#endif
}
return (char *)filename->chr;
}
int32 width8050switch = 1; // if set, can automatically switch to WIDTH 80,50 if LOCATE'ing beyond row 26
uint32 pal[256];
@ -20913,7 +20898,7 @@ void sub_bsave(qbs *filename, int32 offset, int32 size) {
if (size != 65536)
size &= 0xFFFF;
qbs_set(tqbs, qbs_add(filename, nullt)); // prepare null-terminated filename
fh.open(fixdir(tqbs), std::ios::binary | std::ios::out);
fh.open(filepath_fix_directory(tqbs), std::ios::binary | std::ios::out);
if (fh.is_open() == NULL) {
error(64);
return;
@ -20954,7 +20939,7 @@ void sub_bload(qbs *filename, int32 offset, int32 passed) {
offset &= 0xFFFF;
}
qbs_set(tqbs, qbs_add(filename, nullt)); // prepare null-terminated filename
fh.open(fixdir(tqbs), std::ios::binary | std::ios::in);
fh.open(filepath_fix_directory(tqbs), std::ios::binary | std::ios::in);
if (fh.is_open() == NULL) {
error(53);
return;
@ -23022,166 +23007,6 @@ shell_complete:;
} //_DONTWAIT & _HIDE
void sub_kill(qbs *str) {
// note: file not found returned for non-existant paths too
// file already open returned if access unavailable
if (new_error)
return;
static int32 i;
static qbs *strz = NULL;
if (!strz)
strz = qbs_new(0, 0);
qbs_set(strz, qbs_add(str, qbs_new_txt_len("\0", 1)));
#ifdef QB64_WINDOWS
static WIN32_FIND_DATA fd;
static HANDLE hFind;
static qbs *strpath = NULL;
if (!strpath)
strpath = qbs_new(0, 0);
static qbs *strfullz = NULL;
if (!strfullz)
strfullz = qbs_new(0, 0);
// find path
qbs_set(strpath, strz);
for (i = strpath->len; i > 0; i--) {
if ((strpath->chr[i - 1] == 47) || (strpath->chr[i - 1] == 92)) {
strpath->len = i;
break;
}
} // i
if (i == 0)
strpath->len = 0; // no path specified
static int32 count;
count = 0;
hFind = FindFirstFile(fixdir(strz), &fd);
if (hFind == INVALID_HANDLE_VALUE) {
error(53);
return;
} // file not found
do {
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
qbs_set(strfullz, qbs_add(strpath, qbs_new_txt_len(fd.cFileName, strlen(fd.cFileName) + 1)));
if (!DeleteFile((char *)strfullz->chr)) {
i = GetLastError();
if ((i == 5) || (i == 19) || (i == 33) || (i == 32)) {
FindClose(hFind);
error(55);
return;
} // file already open
FindClose(hFind);
error(53);
return; // file not found
}
count++;
} // not a directory
} while (FindNextFile(hFind, &fd));
FindClose(hFind);
if (!count) {
error(53);
return;
} // file not found
return;
#else
if (remove(fixdir(strz))) {
i = errno;
if (i == ENOENT) {
error(53);
return;
} // file not found
if (i == EACCES) {
error(75);
return;
} // path/file access error
error(64); // bad file name (assumed)
}
#endif
}
void sub_name(qbs *oldname, qbs *newname) {
if (new_error)
return;
static qbs *strz = NULL;
if (!strz)
strz = qbs_new(0, 0);
static qbs *strz2 = NULL;
if (!strz2)
strz2 = qbs_new(0, 0);
static int32 i;
qbs_set(strz, qbs_add(oldname, qbs_new_txt_len("\0", 1)));
qbs_set(strz2, qbs_add(newname, qbs_new_txt_len("\0", 1)));
if (rename(fixdir(strz), fixdir(strz2))) {
i = errno;
if (i == ENOENT) {
error(53);
return;
} // file not found
if (i == EINVAL) {
error(64);
return;
} // bad file name
if (i == EACCES) {
error(75);
return;
} // path/file access error
error(5); // Illegal function call (assumed)
}
}
void sub_chdir(qbs *str) {
if (new_error)
return;
static qbs *strz = NULL;
if (!strz)
strz = qbs_new(0, 0);
qbs_set(strz, qbs_add(str, qbs_new_txt_len("\0", 1)));
if (chdir(fixdir(strz)) == -1) {
// assume errno==ENOENT
error(76); // path not found
}
static int32 tmp_long;
static int32 got_ports = 0;
}
void sub_mkdir(qbs *str) {
if (new_error)
return;
static qbs *strz = NULL;
if (!strz)
strz = qbs_new(0, 0);
qbs_set(strz, qbs_add(str, qbs_new_txt_len("\0", 1)));
#ifdef QB64_UNIX
if (mkdir(fixdir(strz), 0770) == -1) {
#else
if (mkdir(fixdir(strz)) == -1) {
#endif
if (errno == EEXIST) {
error(75);
return;
} // path/file access error
// assume errno==ENOENT
error(76); // path not found
}
}
void sub_rmdir(qbs *str) {
if (new_error)
return;
static qbs *strz = NULL;
if (!strz)
strz = qbs_new(0, 0);
qbs_set(strz, qbs_add(str, qbs_new_txt_len("\0", 1)));
if (rmdir(fixdir(strz)) == -1) {
if (errno == ENOTEMPTY) {
error(75);
return;
} // path/file access error
// assume errno==ENOENT
error(76); // path not found
}
}
long double pow2(long double x, long double y) {
if (x < 0) {
if (y != std::floor(y)) {
@ -24962,7 +24787,7 @@ int32_t func__loadfont(qbs *file_name, int32_t size, qbs *requirements, int32_t
FONT_DEBUG_PRINT("Loading font from memory. Size = %i", bytes);
} else {
qbs_set(fileNameZ, qbs_add(file_name, qbs_new_txt_len("\0", 1))); // s1 = filename + CHR$(0)
content = FontLoadFileToMemory((char *)fileNameZ->chr, &bytes); // this we must free!!!
content = FontLoadFileToMemory(filepath_fix_directory(fileNameZ), &bytes); // this we must free!!!
FONT_DEBUG_PRINT("Loading font from file %s", fileNameZ->chr);
}
@ -26588,7 +26413,7 @@ void sub_run(qbs *f) {
strz = qbs_new(0, 0);
qbs_set(str, f);
fixdir(str);
filepath_fix_directory(str);
#ifdef QB64_WINDOWS
@ -29733,24 +29558,24 @@ int32 gfs_open(qbs *filename, int32 access, int32 restrictions, int32 how) {
if (how == 2) {
// with truncate
if (access == 1)
f->file_handle->open(fixdir(filenamez), std::ios::in | std::ios::binary | std::ios::trunc);
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::in | std::ios::binary | std::ios::trunc);
if (access == 2)
f->file_handle->open(fixdir(filenamez), std::ios::out | std::ios::binary | std::ios::trunc);
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::out | std::ios::binary | std::ios::trunc);
if (access == 3)
f->file_handle->open(fixdir(filenamez), std::ios::in | std::ios::out | std::ios::binary | std::ios::trunc);
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::in | std::ios::out | std::ios::binary | std::ios::trunc);
} else {
// without truncate
if (access == 1)
f->file_handle->open(fixdir(filenamez), std::ios::in | std::ios::binary);
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::in | std::ios::binary);
if (access == 2)
f->file_handle->open(fixdir(filenamez), std::ios::out | std::ios::binary | std::ios::app);
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::out | std::ios::binary | std::ios::app);
if (access == 3)
f->file_handle->open(fixdir(filenamez), std::ios::in | std::ios::out | std::ios::binary);
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::in | std::ios::out | std::ios::binary);
}
if (how) {
if (!f->file_handle->is_open()) { // couldn't open file, so attempt creation
f->file_handle_o = new std::ofstream();
f->file_handle_o->open(fixdir(filenamez), std::ios::out);
f->file_handle_o->open(filepath_fix_directory(filenamez), std::ios::out);
if (f->file_handle_o->is_open()) { // created new file
f->file_handle_o->close();
// retry open
@ -29758,19 +29583,19 @@ int32 gfs_open(qbs *filename, int32 access, int32 restrictions, int32 how) {
if (how == 2) {
// with truncate
if (access == 1)
f->file_handle->open(fixdir(filenamez), std::ios::in | std::ios::binary | std::ios::trunc);
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::in | std::ios::binary | std::ios::trunc);
if (access == 2)
f->file_handle->open(fixdir(filenamez), std::ios::out | std::ios::binary | std::ios::trunc);
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::out | std::ios::binary | std::ios::trunc);
if (access == 3)
f->file_handle->open(fixdir(filenamez), std::ios::in | std::ios::out | std::ios::binary | std::ios::trunc);
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::in | std::ios::out | std::ios::binary | std::ios::trunc);
} else {
// without truncate
if (access == 1)
f->file_handle->open(fixdir(filenamez), std::ios::in | std::ios::binary);
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::in | std::ios::binary);
if (access == 2)
f->file_handle->open(fixdir(filenamez), std::ios::out | std::ios::binary | std::ios::app);
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::out | std::ios::binary | std::ios::app);
if (access == 3)
f->file_handle->open(fixdir(filenamez), std::ios::in | std::ios::out | std::ios::binary);
f->file_handle->open(filepath_fix_directory(filenamez), std::ios::in | std::ios::out | std::ios::binary);
}
}
delete f->file_handle_o;
@ -29906,7 +29731,7 @@ int32 gfs_open(qbs *filename, int32 access, int32 restrictions, int32 how) {
if (how)
x3 = OPEN_ALWAYS;
undefined_retry:
f_w->file_handle = CreateFile(fixdir(filenamez), x, x2, NULL, x3, FILE_ATTRIBUTE_NORMAL, NULL);
f_w->file_handle = CreateFile(filepath_fix_directory(filenamez), x, x2, NULL, x3, FILE_ATTRIBUTE_NORMAL, NULL);
if (f_w->file_handle == INVALID_HANDLE_VALUE) {
if (how == 3) {
@ -29950,7 +29775,7 @@ undefined_retry:
if (GetFileSize_low || GetFileSize_high) {
CloseHandle(f_w->file_handle);
x3 = TRUNCATE_EXISTING;
f_w->file_handle = CreateFile(fixdir(filenamez), x, x2, NULL, x3, FILE_ATTRIBUTE_NORMAL, NULL);
f_w->file_handle = CreateFile(filepath_fix_directory(filenamez), x, x2, NULL, x3, FILE_ATTRIBUTE_NORMAL, NULL);
if (f_w->file_handle == INVALID_HANDLE_VALUE) {
gfs_free(i);
@ -31669,134 +31494,6 @@ failed:;
#endif
void sub_files(qbs *str, int32 passed) {
if (new_error)
return;
static int32 i, i2, i3;
static qbs *strz = NULL;
if (!strz)
strz = qbs_new(0, 0);
if (passed) {
qbs_set(strz, qbs_add(str, qbs_new_txt_len("\0", 1)));
} else {
qbs_set(strz, qbs_new_txt_len("\0", 1));
}
#ifdef QB64_WINDOWS
static WIN32_FIND_DATA fd;
static HANDLE hFind;
static qbs *strpath = NULL;
if (!strpath)
strpath = qbs_new(0, 0);
static qbs *strz2 = NULL;
if (!strz2)
strz2 = qbs_new(0, 0);
i = 0;
if (strz->len >= 2) {
if (strz->chr[strz->len - 2] == 92)
i = 1;
} else
i = 1;
if (i) { // add * (and new NULL term.)
strz->chr[strz->len - 1] = 42; //"*"
qbs_set(strz, qbs_add(strz, qbs_new_txt_len("\0", 1)));
}
qbs_set(strpath, strz);
for (i = strpath->len; i > 0; i--) {
if ((strpath->chr[i - 1] == 47) || (strpath->chr[i - 1] == 92)) {
strpath->len = i;
break;
}
} // i
if (i == 0)
strpath->len = 0; // no path specified
// print the current path
// note: for QBASIC compatibility reasons it does not print the directory name of the files being displayed
static uint8 curdir[4096];
static uint8 curdir2[4096];
i2 = GetCurrentDirectory(4096, (char *)curdir);
if (i2) {
i2 = GetShortPathName((char *)curdir, (char *)curdir2, 4096);
if (i2) {
qbs_set(strz2, qbs_ucase(qbs_new_txt_len((char *)curdir2, i2)));
qbs_print(strz2, 1);
} else {
error(5);
return;
}
} else {
error(5);
return;
}
hFind = FindFirstFile(fixdir(strz), &fd);
if (hFind == INVALID_HANDLE_VALUE) {
error(53);
return;
} // file not found
do {
if (!fd.cAlternateFileName[0]) { // no alternate filename exists
qbs_set(strz2, qbs_ucase(qbs_new_txt_len(fd.cFileName, strlen(fd.cFileName))));
} else {
qbs_set(strz2, qbs_ucase(qbs_new_txt_len(fd.cAlternateFileName, strlen(fd.cAlternateFileName))));
}
if (strz2->len < 12) { // padding required
qbs_set(strz2, qbs_add(strz2, func_space(12 - strz2->len)));
i2 = 0;
for (i = 0; i < 12; i++) {
if (strz2->chr[i] == 46) {
memmove(&strz2->chr[8], &strz2->chr[i], 4);
memset(&strz2->chr[i], 32, 8 - i);
break;
}
} // i
} // padding
// add " " or "<DIR> "
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
qbs_set(strz2, qbs_add(strz2, qbs_new_txt_len("<DIR> ", 6)));
} else {
qbs_set(strz2, qbs_add(strz2, func_space(6)));
}
makefit(strz2);
qbs_print(strz2, 0);
} while (FindNextFile(hFind, &fd));
FindClose(hFind);
static ULARGE_INTEGER FreeBytesAvailableToCaller;
static ULARGE_INTEGER TotalNumberOfBytes;
static ULARGE_INTEGER TotalNumberOfFreeBytes;
static int64 bytes;
static char *cp;
qbs_set(strpath, qbs_add(strpath, qbs_new_txt_len("\0", 1)));
cp = (char *)strpath->chr;
if (strpath->len == 1)
cp = NULL;
if (GetDiskFreeSpaceEx(cp, &FreeBytesAvailableToCaller, &TotalNumberOfBytes, &TotalNumberOfFreeBytes)) {
bytes = *(int64 *)(void *)&FreeBytesAvailableToCaller;
} else {
bytes = 0;
}
if (func_pos(NULL) > 1) {
strz2->len = 0;
qbs_print(strz2, 1);
} // new line if necessary
qbs_set(strz2, qbs_add(qbs_str(bytes), qbs_new_txt_len(" Bytes free", 11)));
qbs_print(strz2, 1);
#endif
}
int32 func__keyhit() {
/*
//keyhit cyclic buffer
@ -33463,65 +33160,6 @@ int32 func_strig(int32 i, int32 controller, int32 passed) {
return 0;
}
int32 func__fileexists(qbs *file) {
if (new_error)
return 0;
static qbs *strz = NULL;
if (!strz)
strz = qbs_new(0, 0);
qbs_set(strz, qbs_add(file, qbs_new_txt_len("\0", 1)));
#ifdef QB64_WINDOWS
static int32 x;
x = GetFileAttributes(fixdir(strz));
if (x == INVALID_FILE_ATTRIBUTES)
return 0;
if (x & FILE_ATTRIBUTE_DIRECTORY)
return 0;
return -1;
#elif defined(QB64_UNIX)
struct stat sb;
if (stat(fixdir(strz), &sb) == 0 && S_ISREG(sb.st_mode))
return -1;
return 0;
#else
// generic method (not currently used)
static std::ifstream fh;
fh.open(fixdir(strz), std::ios::binary | std::ios::in);
if (fh.is_open() == NULL) {
fh.clear(std::ios::goodbit);
return 0;
}
fh.clear(std::ios::goodbit);
fh.close();
return -1;
#endif
}
int32 func__direxists(qbs *file) {
if (new_error)
return 0;
static qbs *strz = NULL;
if (!strz)
strz = qbs_new(0, 0);
qbs_set(strz, qbs_add(file, qbs_new_txt_len("\0", 1)));
#ifdef QB64_WINDOWS
static int32 x;
x = GetFileAttributes(fixdir(strz));
if (x == INVALID_FILE_ATTRIBUTES)
return 0;
if (x & FILE_ATTRIBUTE_DIRECTORY)
return -1;
return 0;
#elif defined(QB64_UNIX)
struct stat sb;
if (stat(fixdir(strz), &sb) == 0 && S_ISDIR(sb.st_mode))
return -1;
return 0;
#else
return 0; // default response
#endif
}
int32 func__console() {
if (new_error)
return -1;
@ -36318,219 +35956,8 @@ int32 func__resizeheight() { return resize_event_y; }
int32 func__scaledwidth() { return environment_2d__screen_scaled_width; }
int32 func__scaledheight() { return environment_2d__screen_scaled_height; }
// Get Current Working Directory
qbs *func__cwd() {
qbs *final, *tqbs;
int length;
char *buf, *ret;
#if defined QB64_WINDOWS
length = GetCurrentDirectoryA(0, NULL);
buf = (char *)malloc(length);
if (!buf) {
error(7); //"Out of memory"
return tqbs;
}
if (GetCurrentDirectoryA(length, buf) != --length) { // Sanity check
free(buf); // It's good practice
tqbs = qbs_new(0, 1);
error(51); //"Internal error"
return tqbs;
}
#elif defined QB64_UNIX
length = 512;
while (1) {
buf = (char *)malloc(length);
if (!buf) {
tqbs = qbs_new(0, 1);
error(7);
return tqbs;
}
ret = getcwd(buf, length);
if (ret)
break;
if (errno != ERANGE) {
tqbs = qbs_new(0, 1);
error(51);
return tqbs;
}
free(buf);
length += 512;
}
length = strlen(ret);
ret = (char *)realloc(ret, length); // Chops off the null byte
if (!ret) {
tqbs = qbs_new(0, 1);
error(7);
return tqbs;
}
buf = ret;
#endif
final = qbs_new(length, 1);
memcpy(final->chr, buf, length);
free(buf);
return final;
}
qbs *startDir = NULL; // set on startup
qbs *func__startdir() {
qbs *temp = qbs_new(0, 1);
qbs_set(temp, startDir);
return temp;
}
qbs *rootDir = NULL; // the dir moved to when program begins
qbs *func__dir(qbs *context_in) {
static qbs *context = NULL;
if (!context) {
context = qbs_new(0, 0);
}
qbs_set(context, qbs_ucase(context_in));
if (qbs_equal(qbs_ucase(context), qbs_new_txt("TEXT")) || qbs_equal(qbs_ucase(context), qbs_new_txt("DOCUMENT")) ||
qbs_equal(qbs_ucase(context), qbs_new_txt("DOCUMENTS")) || qbs_equal(qbs_ucase(context), qbs_new_txt("MY DOCUMENTS"))) {
#ifdef QB64_WINDOWS
CHAR osPath[MAX_PATH];
if (SUCCEEDED(SHGetFolderPathA(NULL, 5, NULL, 0, osPath))) { // Documents
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
}
#endif
}
if (qbs_equal(qbs_ucase(context), qbs_new_txt("MUSIC")) || qbs_equal(qbs_ucase(context), qbs_new_txt("AUDIO")) ||
qbs_equal(qbs_ucase(context), qbs_new_txt("SOUND")) || qbs_equal(qbs_ucase(context), qbs_new_txt("SOUNDS")) ||
qbs_equal(qbs_ucase(context), qbs_new_txt("MY MUSIC"))) {
#ifdef QB64_WINDOWS
CHAR osPath[MAX_PATH];
if (SUCCEEDED(SHGetFolderPathA(NULL, 13, NULL, 0, osPath))) { // Music
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
}
#endif
}
if (qbs_equal(qbs_ucase(context), qbs_new_txt("PICTURE")) || qbs_equal(qbs_ucase(context), qbs_new_txt("PICTURES")) ||
qbs_equal(qbs_ucase(context), qbs_new_txt("IMAGE")) || qbs_equal(qbs_ucase(context), qbs_new_txt("IMAGES")) ||
qbs_equal(qbs_ucase(context), qbs_new_txt("MY PICTURES"))) {
#ifdef QB64_WINDOWS
CHAR osPath[MAX_PATH];
if (SUCCEEDED(SHGetFolderPathA(NULL, 39, NULL, 0, osPath))) { // Pictures
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
}
#endif
}
if (qbs_equal(qbs_ucase(context), qbs_new_txt("DCIM")) || qbs_equal(qbs_ucase(context), qbs_new_txt("CAMERA")) ||
qbs_equal(qbs_ucase(context), qbs_new_txt("CAMERA ROLL")) || qbs_equal(qbs_ucase(context), qbs_new_txt("PHOTO")) ||
qbs_equal(qbs_ucase(context), qbs_new_txt("PHOTOS"))) {
#ifdef QB64_WINDOWS
CHAR osPath[MAX_PATH];
if (SUCCEEDED(SHGetFolderPathA(NULL, 39, NULL, 0, osPath))) { // Pictures
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
}
#endif
}
if (qbs_equal(qbs_ucase(context), qbs_new_txt("MOVIE")) || qbs_equal(qbs_ucase(context), qbs_new_txt("MOVIES")) ||
qbs_equal(qbs_ucase(context), qbs_new_txt("VIDEO")) || qbs_equal(qbs_ucase(context), qbs_new_txt("VIDEOS")) ||
qbs_equal(qbs_ucase(context), qbs_new_txt("MY VIDEOS"))) {
#ifdef QB64_WINDOWS
CHAR osPath[MAX_PATH];
if (SUCCEEDED(SHGetFolderPathA(NULL, 14, NULL, 0, osPath))) { // Videos
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
}
#endif
}
if (qbs_equal(qbs_ucase(context), qbs_new_txt("DOWNLOAD")) || qbs_equal(qbs_ucase(context), qbs_new_txt("DOWNLOADS"))) {
#ifdef QB64_WINDOWS
CHAR osPath[MAX_PATH];
if (SUCCEEDED(SHGetFolderPathA(NULL, 0x0028, NULL, 0, osPath))) { // user folder
// XP & SHGetFolderPathA do not support the concept of a Downloads folder, however it can be constructed
mkdir((char *)((qbs_add(qbs_new_txt(osPath), qbs_new_txt_len("\\Downloads\0", 11)))->chr));
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\Downloads\\"));
}
#endif
}
if (qbs_equal(qbs_ucase(context), qbs_new_txt("DESKTOP"))) {
#ifdef QB64_WINDOWS
CHAR osPath[MAX_PATH];
if (SUCCEEDED(SHGetFolderPathA(NULL, 0, NULL, 0, osPath))) { // Desktop
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
}
#endif
}
if (qbs_equal(qbs_ucase(context), qbs_new_txt("APPDATA")) || qbs_equal(qbs_ucase(context), qbs_new_txt("APPLICATION DATA")) ||
qbs_equal(qbs_ucase(context), qbs_new_txt("PROGRAM DATA")) || qbs_equal(qbs_ucase(context), qbs_new_txt("DATA"))) {
#ifdef QB64_WINDOWS
CHAR osPath[MAX_PATH];
if (SUCCEEDED(SHGetFolderPathA(NULL, 0x001a, NULL, 0, osPath))) { // CSIDL_APPDATA (%APPDATA%)
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
}
#endif
}
if (qbs_equal(qbs_ucase(context), qbs_new_txt("LOCALAPPDATA")) || qbs_equal(qbs_ucase(context), qbs_new_txt("LOCAL APPLICATION DATA")) ||
qbs_equal(qbs_ucase(context), qbs_new_txt("LOCAL PROGRAM DATA")) || qbs_equal(qbs_ucase(context), qbs_new_txt("LOCAL DATA"))) {
#ifdef QB64_WINDOWS
CHAR osPath[MAX_PATH];
if (SUCCEEDED(SHGetFolderPathA(NULL, 0x001c, NULL, 0, osPath))) { // CSIDL_LOCAL_APPDATA (%LOCALAPPDATA%)
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
}
#endif
}
if (qbs_equal(qbs_ucase(context), qbs_new_txt("PROGRAMFILES")) || qbs_equal(qbs_ucase(context), qbs_new_txt("PROGRAM FILES"))) {
#ifdef QB64_WINDOWS
CHAR osPath[MAX_PATH];
if (SUCCEEDED(SHGetFolderPathA(NULL, 0x0026, NULL, 0, osPath))) { // CSIDL_PROGRAM_FILES (%PROGRAMFILES%)
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
}
#endif
}
if (qbs_equal(qbs_ucase(context), qbs_new_txt("PROGRAMFILESX86")) || qbs_equal(qbs_ucase(context), qbs_new_txt("PROGRAMFILES X86")) ||
qbs_equal(qbs_ucase(context), qbs_new_txt("PROGRAM FILES X86")) || qbs_equal(qbs_ucase(context), qbs_new_txt("PROGRAM FILES 86")) ||
qbs_equal(qbs_ucase(context), qbs_new_txt("PROGRAM FILES (X86)")) || qbs_equal(qbs_ucase(context), qbs_new_txt("PROGRAMFILES (X86)")) ||
qbs_equal(qbs_ucase(context), qbs_new_txt("PROGRAM FILES(X86)"))) {
#ifdef QB64_WINDOWS &&_WIN64
CHAR osPath[MAX_PATH];
if (SUCCEEDED(SHGetFolderPathA(NULL, 0x002a, NULL, 0, osPath))) { // CSIDL_PROGRAM_FILES (%PROGRAMFILES(X86)%)
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
}
#endif
}
if (qbs_equal(qbs_ucase(context), qbs_new_txt("TEMP")) || qbs_equal(qbs_ucase(context), qbs_new_txt("TEMP FILES"))) {
#ifdef QB64_WINDOWS
CHAR osPath[MAX_PATH + 1];
DWORD pathlen;
pathlen = GetTempPathA(261, osPath); //%TEMP%
char path[pathlen];
memcpy(path, &osPath, pathlen);
if (pathlen > 0) {
return qbs_new_txt(path);
}
#endif
}
// general fallback location
#ifdef QB64_WINDOWS
CHAR osPath[MAX_PATH];
if (SUCCEEDED(SHGetFolderPathA(NULL, 0, NULL, 0, osPath))) { // desktop
return qbs_add(qbs_new_txt(osPath), qbs_new_txt("\\"));
}
return qbs_new_txt(".\\"); // current location
#else
return qbs_new_txt("./"); // current location
#endif
}
extern void set_dynamic_info();
int main(int argc, char *argv[]) {
#if defined(QB64_LINUX) && defined(X11)
@ -36716,9 +36143,9 @@ int main(int argc, char *argv[]) {
singlespace = qbs_new_cmem(1, 0);
singlespace->chr[0] = 32;
// store _CWD$ for recall using _STARTDIR$ in startDir
startDir = qbs_new(0, 0);
qbs_set(startDir, func__cwd());
// store _CWD$ for recall using _STARTDIR$ in g_startDir
g_startDir = qbs_new(0, 0);
qbs_set(g_startDir, func__cwd());
// switch to directory of this EXE file
// http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe
@ -36753,9 +36180,6 @@ int main(int argc, char *argv[]) {
}
#endif
rootDir = qbs_new(0, 0);
qbs_set(rootDir, func__cwd());
unknown_opcode_mess = qbs_new(0, 0);
qbs_set(unknown_opcode_mess, qbs_new_txt_len("Unknown Opcode ( )\0", 20));

View file

@ -34,6 +34,20 @@ qbs *qbs_new_txt(const char *);
qbs *qbs_new_txt_len(const char *, int32_t);
qbs *qbs_add(qbs *, qbs *);
qbs *qbs_set(qbs *, qbs *);
int32 qbs_equal(qbs *str1, qbs *str2);
qbs *func_space(int32 spaces);
void makefit(qbs *text);
qbs *qbs_str(int64 value);
qbs *qbs_str(int32 value);
qbs *qbs_str(int16 value);
qbs *qbs_str(int8 value);
qbs *qbs_str(uint64 value);
qbs *qbs_str(uint32 value);
qbs *qbs_str(uint16 value);
qbs *qbs_str(uint8 value);
qbs *qbs_str(float value);
qbs *qbs_str(double value);
qbs *qbs_str(long double value);
void qbg_sub_window(float, float, float, float, int32);
extern int32 autodisplay;
// GFS forward references

View file

@ -2,6 +2,7 @@
libqb-objs-y += $(PATH_LIBQB)/src/threading.o
libqb-objs-y += $(PATH_LIBQB)/src/buffer.o
libqb-objs-y += $(PATH_LIBQB)/src/filepath.o
libqb-objs-y += $(PATH_LIBQB)/src/filesystem.o
libqb-objs-y += $(PATH_LIBQB)/src/datetime.o
libqb-objs-y += $(PATH_LIBQB)/src/rounding.o
@ -20,4 +21,12 @@ ifeq ($(OS),osx)
libqb-objs-y$(DEP_CONSOLE_ONLY) += $(PATH_LIBQB)/src/mac-key-monitor.o
endif
$(PATH_LIBQB)/src/%.o: $(PATH_LIBQB)/src/%.cpp
$(CXX) -O2 $(CXXFLAGS) -Wall $< -c -o $@
ifeq ($(OS),osx)
$(PATH_LIBQB)/src/%.o: $(PATH_LIBQB)/src/%.mm
$(CXX) -O2 $(CXXFLAGS) -Wall $< -c -o $@
endif
CLEAN_LIST += $(libqb-objs-y) $(libqb-objs-yy) $(libqb-objs-)

View file

@ -1,6 +1,10 @@
#ifndef INCLUDE_LIBQB_FILEPATH_H
#define INCLUDE_LIBQB_FILEPATH_H
#include <string>
struct qbs;
// Takes a path + filename, and returns just the filename portion
// Returns either NULL or empty string if it has none.
const char *filepath_get_filename(const char *path);
@ -12,4 +16,15 @@ const char *filepath_get_extension(const char *fliename);
// Returns true if the path is to a file that matches the provided extension
bool filepath_has_extension(const char *path, const char *extension);
// The following overloaded functions changes the path separators in path based on the OS (path is modified)
const char *filepath_fix_directory(char *path);
const char *filepath_fix_directory(qbs *path);
const char *filepath_fix_directory(std::string &path);
// Splits a file path into directory and file name
void filepath_split(const std::string &filePath, std::string &directory, std::string &fileName);
// Joins a directory and file name into a file path
void filepath_join(std::string &filePath, const std::string &directory, const std::string &fileName);
#endif

View file

@ -0,0 +1,26 @@
//-----------------------------------------------------------------------------------------------------
// QB64-PE filesystem related functions
//-----------------------------------------------------------------------------------------------------
#pragma once
#include <stdint.h>
struct qbs;
/// @brief This is a global variable that is set on startup and holds the directory that was current when the program was loaded
extern qbs *g_startDir;
qbs *func__cwd();
qbs *func__dir(qbs *qbsContext);
int32_t func__direxists(qbs *path);
int32_t func__fileexists(qbs *path);
qbs *func__startdir();
void sub_chdir(qbs *str);
qbs *func__files(qbs *qbsFileSpec, int32_t passed);
qbs *func__fullpath(qbs *qbsPathName);
void sub_files(qbs *str, int32_t passed);
void sub_kill(qbs *str);
void sub_mkdir(qbs *str);
void sub_name(qbs *oldname, qbs *newname);
void sub_rmdir(qbs *str);

View file

@ -3,10 +3,12 @@
#include "libqb-common.h"
#include <string.h>
#include <algorithm>
const char *filepath_get_filename(const char *path)
{
#include "../../libqb.h"
#include "filepath.h"
const char *filepath_get_filename(const char *path) {
const char *fileName;
if (path == NULL) {
@ -32,8 +34,7 @@ const char *filepath_get_filename(const char *path)
return fileName;
}
const char *filepath_get_extension(const char *path)
{
const char *filepath_get_extension(const char *path) {
const char *extension;
const char *lastOccurance;
@ -57,8 +58,7 @@ const char *filepath_get_extension(const char *path)
return (lastOccurance != NULL) ? lastOccurance : extension;
}
bool filepath_has_extension(const char *path, const char *extension)
{
bool filepath_has_extension(const char *path, const char *extension) {
const char *ext1;
const char *ext2;
@ -75,3 +75,86 @@ bool filepath_has_extension(const char *path, const char *extension)
return strcasecmp(ext1, ext2) == 0;
#endif
}
/// @brief Changes the slashes in a file name / path to make it compatible with the OS
/// @param path The path to fix (contents may be changed)
/// @return Returns the C-string for convenience
const char *filepath_fix_directory(char *path) {
auto len = strlen(path);
for (size_t i = 0; i < len; i++) {
#ifdef QB64_WINDOWS
if (path[i] == '/')
path[i] = '\\';
#else
if (path[i] == '\\')
path[i] = '/';
#endif
}
return path;
}
/// @brief Changes the slashes in a file name / path to make it compatible with the OS
/// @param path The path to fix (contents may be changed)
/// @return Returns the C-string for convenience
const char *filepath_fix_directory(qbs *path) {
for (size_t i = 0; i < path->len; i++) {
#ifdef QB64_WINDOWS
if (path->chr[i] == '/')
path->chr[i] = '\\';
#else
if (path->chr[i] == '\\')
path->chr[i] = '/';
#endif
}
return reinterpret_cast<char *>(path->chr);
}
/// @brief Changes the slashes in a file name / path to make it compatible with the OS
/// @param path The path to fix (contents may be changed)
/// @return Returns the C-string for convenience
const char *filepath_fix_directory(std::string &path) {
std::transform(path.begin(), path.end(), path.begin(), [](unsigned char c) {
#ifdef QB64_WINDOWS
return c == '/' ? '\\' : c;
#else
return c == '\\' ? '/' : c;
#endif
});
return path.c_str();
}
// Splits a file path into directory and file name
void filepath_split(const std::string &filePath, std::string &directory, std::string &fileName) {
// Find the last occurrence of either '/' or '\\'
size_t lastSlash = filePath.find_last_of("/\\");
if (lastSlash != std::string::npos) {
directory = filePath.substr(0, lastSlash + 1); // include the trailing separator
fileName = filePath.substr(lastSlash + 1);
} else {
// No directory separator found
directory.clear();
fileName = filePath;
}
}
// Joins a directory and file name into a file path
void filepath_join(std::string &filePath, const std::string &directory, const std::string &fileName) {
// Check if the directory has a trailing separator, and add one if not
filePath = directory;
if (!filePath.empty() && filePath.back() != '/' && filePath.back() != '\\') {
#ifdef QB64_WINDOWS
filePath += '\\';
#else
filePath += '/';
#endif
}
// Append the file name to the directory
filePath += fileName;
}

View file

@ -0,0 +1,974 @@
//-----------------------------------------------------------------------------------------------------
// QB64-PE filesystem related functions
//-----------------------------------------------------------------------------------------------------
#include "libqb-common.h"
#include "filepath.h"
#include "filesystem.h"
#include "../../libqb.h"
#include <algorithm>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#ifdef QB64_WINDOWS
# include <shlobj.h>
#else
# include <sys/statvfs.h>
#endif
#ifdef QB64_BACKSLASH_FILESYSTEM
# define FS_PATH_SEPARATOR '\\'
#else
# define FS_PATH_SEPARATOR '/'
#endif
#if (FILENAME_MAX > 4096)
# define FS_PATHNAME_LENGTH_MAX FILENAME_MAX
#else
# define FS_PATHNAME_LENGTH_MAX 4096
#endif
/// @brief This is a global variable that is set on startup and holds the directory that was current when the program was loaded
qbs *g_startDir = nullptr;
/// @brief Gets the current working directory
/// @return A qbs containing the current working directory or an empty string on error
qbs *func__cwd() {
std::string path;
qbs *qbsFinal;
path.resize(FILENAME_MAX, '\0');
for (;;) {
if (getcwd(&path[0], path.size())) {
auto size = strlen(path.c_str());
qbsFinal = qbs_new(size, 1);
memcpy(qbsFinal->chr, &path[0], size);
return qbsFinal;
} else {
if (errno == ERANGE)
path.resize(path.size() << 1, '\0'); // buffer size was not sufficient; try again with a larger buffer
else
break; // some other error occurred
}
}
qbsFinal = qbs_new(0, 1);
error(QB_ERROR_INTERNAL_ERROR);
return qbsFinal;
}
/// @brief Returns true if the specified directory exists
/// @param path The directory to check for
/// @return True if the directory exists
static inline bool FS_DirectoryExists(const char *path) {
#ifdef QB64_WINDOWS
auto x = GetFileAttributesA(path);
return x != INVALID_FILE_ATTRIBUTES && (x & FILE_ATTRIBUTE_DIRECTORY);
#else
struct stat info;
return stat(path, &info) == 0 && S_ISDIR(info.st_mode);
#endif
}
/// @brief Known directories (primarily Windows based, but we'll do our best to emulate on other platforms)
enum class FS_KnownDirectory {
HOME = 0,
DESKTOP,
DOCUMENTS,
PICTURES,
MUSIC,
VIDEOS,
DOWNLOAD,
APP_DATA,
LOCAL_APP_DATA,
PROGRAM_DATA,
SYSTEM_FONTS,
USER_FONTS,
TEMP,
PROGRAM_FILES,
PROGRAM_FILES_32,
};
#ifdef QB64_WINDOWS
/// @brief Returns the full path for a known directory
/// @param kD Is a value from FS_KnownDirectory (above)
/// @return The full path that ends with the system path separator
static std::string FS_GetKnownDirectory(FS_KnownDirectory kD) {
std::string path(FS_PATHNAME_LENGTH_MAX, '\0'); // allocate something that is sufficiently large
switch (kD) {
case FS_KnownDirectory::DESKTOP: // %USERPROFILE%\OneDrive\Desktop
SHGetFolderPathA(NULL, CSIDL_DESKTOPDIRECTORY | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
break;
case FS_KnownDirectory::DOCUMENTS: // %USERPROFILE%\OneDrive\Documents
SHGetFolderPathA(NULL, CSIDL_MYDOCUMENTS | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
break;
case FS_KnownDirectory::PICTURES: // %USERPROFILE%\OneDrive\Pictures
SHGetFolderPathA(NULL, CSIDL_MYPICTURES | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
break;
case FS_KnownDirectory::MUSIC: // %USERPROFILE%\Music
SHGetFolderPathA(NULL, CSIDL_MYMUSIC | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
break;
case FS_KnownDirectory::VIDEOS: // %USERPROFILE%\Videos
SHGetFolderPathA(NULL, CSIDL_MYVIDEO | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
break;
case FS_KnownDirectory::DOWNLOAD: // %USERPROFILE%\Downloads
if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_PROFILE | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]))) {
// XP & SHGetFolderPathA do not support the concept of a Downloads folder, however it can be constructed
path.resize(strlen(path.c_str()));
path.append("\\Downloads");
mkdir(path.c_str());
if (!FS_DirectoryExists(path.c_str()))
path.clear();
}
break;
case FS_KnownDirectory::APP_DATA: // %USERPROFILE%\AppData\Roaming
SHGetFolderPathA(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
break;
case FS_KnownDirectory::LOCAL_APP_DATA: // %USERPROFILE%\AppData\Local
SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
break;
case FS_KnownDirectory::PROGRAM_DATA: // %SystemDrive%\ProgramData
SHGetFolderPathA(NULL, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
break;
case FS_KnownDirectory::SYSTEM_FONTS: // %SystemRoot%\Fonts
SHGetFolderPathA(NULL, CSIDL_FONTS | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
break;
case FS_KnownDirectory::USER_FONTS: // %USERPROFILE%\AppData\Local\Microsoft\Windows\Fonts
if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]))) {
path.resize(strlen(path.c_str()));
path.append("\\Microsoft\\Windows\\Fonts");
if (!FS_DirectoryExists(path.c_str()))
path.clear();
}
break;
case FS_KnownDirectory::TEMP: // %USERPROFILE%\AppData\Local\Temp
GetTempPathA(path.size(), &path[0]);
break;
case FS_KnownDirectory::PROGRAM_FILES: // %SystemDrive%\Program Files
SHGetFolderPathA(NULL, CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
break;
case FS_KnownDirectory::PROGRAM_FILES_32: // %SystemDrive%\Program Files (x86)
# ifdef _WIN64
SHGetFolderPathA(NULL, CSIDL_PROGRAM_FILESX86 | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
# else
SHGetFolderPathA(NULL, CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
# endif
break;
case FS_KnownDirectory::HOME: // %USERPROFILE%
default:
SHGetFolderPathA(NULL, CSIDL_PROFILE | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0]);
}
// Check if we got anything at all
if (!strlen(path.c_str())) {
path.resize(FS_PATHNAME_LENGTH_MAX, '\0'); // just in case this was shrunk above
if (FAILED(SHGetFolderPathA(NULL, CSIDL_PROFILE | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &path[0])))
path.assign(".\\"); // fallback to the current directory
}
// Add the trailing slash
path.resize(strlen(path.c_str()));
if (path.back() != FS_PATH_SEPARATOR)
path.append(1, FS_PATH_SEPARATOR);
return path;
}
#else
/// @brief Returns the full path for a known directory
/// @param kD Is a value from FS_KnownDirectory (above)
/// @return The full path that ends with the system path separator
static std::string FS_GetKnownDirectory(FS_KnownDirectory kD) {
std::string path;
auto envVar = getenv("HOME");
switch (kD) {
case FS_KnownDirectory::DESKTOP: // %USERPROFILE%\OneDrive\Desktop
if (envVar) {
path.assign(envVar);
path.append("/Desktop");
if (!FS_DirectoryExists(path.c_str()))
path.clear();
}
break;
case FS_KnownDirectory::DOCUMENTS: // %USERPROFILE%\OneDrive\Documents
if (envVar) {
path.assign(envVar);
path.append("/Documents");
if (!FS_DirectoryExists(path.c_str()))
path.clear();
}
break;
case FS_KnownDirectory::PICTURES: // %USERPROFILE%\OneDrive\Pictures
if (envVar) {
path.assign(envVar);
path.append("/Pictures");
if (!FS_DirectoryExists(path.c_str()))
path.clear();
}
break;
case FS_KnownDirectory::MUSIC: // %USERPROFILE%\Music
if (envVar) {
path.assign(envVar);
path.append("/Music");
if (!FS_DirectoryExists(path.c_str()))
path.clear();
}
break;
case FS_KnownDirectory::VIDEOS: // %USERPROFILE%\Videos
if (envVar) {
path.assign(envVar);
# ifdef QB64_MACOSX
path.append("/Movies");
# else
path.append("/Videos");
# endif
if (!FS_DirectoryExists(path.c_str()))
path.clear();
}
break;
case FS_KnownDirectory::DOWNLOAD: // %USERPROFILE%\Downloads
if (envVar) {
path.assign(envVar);
path.append("/Downloads");
if (!FS_DirectoryExists(path.c_str()))
path.clear();
}
break;
case FS_KnownDirectory::APP_DATA: // %USERPROFILE%\AppData\Roaming
if (envVar) {
path.assign(envVar);
path.append("/.config");
if (!FS_DirectoryExists(path.c_str()))
path.clear();
}
break;
case FS_KnownDirectory::LOCAL_APP_DATA: // %USERPROFILE%\AppData\Local
case FS_KnownDirectory::PROGRAM_DATA: // %SystemDrive%\ProgramData
if (envVar) {
path.assign(envVar);
path.append("/.local/share");
if (!FS_DirectoryExists(path.c_str()))
path.clear();
}
break;
case FS_KnownDirectory::SYSTEM_FONTS: // %SystemRoot%\Fonts
# ifdef QB64_MACOSX
path.assign("/System/Library/Fonts");
if (!FS_DirectoryExists(path.c_str())) {
path.assign("/Library/Fonts");
if (!FS_DirectoryExists(path.c_str()))
path.clear();
}
# else
path.assign("/usr/share/fonts");
if (!FS_DirectoryExists(path.c_str())) {
path.assign("/usr/local/share/fonts");
if (!FS_DirectoryExists(path.c_str()))
path.clear();
}
# endif
break;
case FS_KnownDirectory::USER_FONTS: // %USERPROFILE%\AppData\Local\Microsoft\Windows\Fonts
if (envVar) {
path.assign(envVar);
# ifdef QB64_MACOSX
path.append("/Library/Fonts");
if (!FS_DirectoryExists(path.c_str()))
path.clear();
# else
path.append("/.local/share/fonts");
if (!FS_DirectoryExists(path.c_str())) {
path.assign(envVar);
path.append("/.fonts");
if (!FS_DirectoryExists(path.c_str()))
path.clear();
}
# endif
}
break;
case FS_KnownDirectory::TEMP: // %USERPROFILE%\AppData\Local\Temp
path.assign("/var/tmp");
if (!FS_DirectoryExists(path.c_str())) {
path.assign("/tmp");
if (!FS_DirectoryExists(path.c_str()))
path.clear();
}
break;
case FS_KnownDirectory::PROGRAM_FILES: // %SystemDrive%\Program Files
case FS_KnownDirectory::PROGRAM_FILES_32: // %SystemDrive%\Program Files (x86)
# ifdef QB64_MACOSX
path.assign("/Applications");
# else
path.assign("/opt");
# endif
if (!FS_DirectoryExists(path.c_str()))
path.clear();
break;
case FS_KnownDirectory::HOME: // %USERPROFILE%
default:
if (envVar)
path.assign(envVar);
}
// Check if we got anything at all
if (!strlen(path.c_str()))
path.assign(envVar ? envVar : "./");
// Add the trailing slash
path.resize(strlen(path.c_str()));
if (path.back() != FS_PATH_SEPARATOR)
path.append(1, FS_PATH_SEPARATOR);
return path;
}
#endif
/// @brief Returns common paths such as My Documents, My Pictures, My Music, Desktop
/// @param qbsContext Is the directory type
/// @return A qbs containing the directory or an empty string on error
qbs *func__dir(qbs *qbsContext) {
std::string path, context(reinterpret_cast<char *>(qbsContext->chr), qbsContext->len);
std::transform(context.begin(), context.end(), context.begin(), [](unsigned char c) { return std::toupper(c); });
// The following is largely unchanged from what we previously had
if (context.compare("TEXT") == 0 || context.compare("DOCUMENT") == 0 || context.compare("DOCUMENTS") == 0 || context.compare("MY DOCUMENTS") == 0) {
path = FS_GetKnownDirectory(FS_KnownDirectory::DOCUMENTS);
} else if (context.compare("MUSIC") == 0 || context.compare("AUDIO") == 0 || context.compare("SOUND") == 0 || context.compare("SOUNDS") == 0 ||
context.compare("MY MUSIC") == 0) {
path = FS_GetKnownDirectory(FS_KnownDirectory::MUSIC);
} else if (context.compare("PICTURE") == 0 || context.compare("PICTURES") == 0 || context.compare("IMAGE") == 0 || context.compare("IMAGES") == 0 ||
context.compare("MY PICTURES") == 0 || context.compare("DCIM") == 0 || context.compare("CAMERA") == 0 || context.compare("CAMERA ROLL") == 0 ||
context.compare("PHOTO") == 0 || context.compare("PHOTOS") == 0) {
path = FS_GetKnownDirectory(FS_KnownDirectory::PICTURES);
} else if (context.compare("MOVIE") == 0 || context.compare("MOVIES") == 0 || context.compare("VIDEO") == 0 || context.compare("VIDEOS") == 0 ||
context.compare("MY VIDEOS") == 0) {
path = FS_GetKnownDirectory(FS_KnownDirectory::VIDEOS);
} else if (context.compare("DOWNLOAD") == 0 || context.compare("DOWNLOADS") == 0) {
path = FS_GetKnownDirectory(FS_KnownDirectory::DOWNLOAD);
} else if (context.compare("DESKTOP") == 0) {
path = FS_GetKnownDirectory(FS_KnownDirectory::DESKTOP);
} else if (context.compare("APPDATA") == 0 || context.compare("APPLICATION DATA") == 0 || context.compare("PROGRAM DATA") == 0 ||
context.compare("DATA") == 0) {
path = FS_GetKnownDirectory(FS_KnownDirectory::APP_DATA);
} else if (context.compare("LOCALAPPDATA") == 0 || context.compare("LOCAL APPLICATION DATA") == 0 || context.compare("LOCAL PROGRAM DATA") == 0 ||
context.compare("LOCAL DATA") == 0) {
path = FS_GetKnownDirectory(FS_KnownDirectory::LOCAL_APP_DATA);
} else if (context.compare("PROGRAMFILES") == 0 || context.compare("PROGRAM FILES") == 0) {
path = FS_GetKnownDirectory(FS_KnownDirectory::PROGRAM_FILES);
} else if (context.compare("PROGRAMFILESX86") == 0 || context.compare("PROGRAMFILES X86") == 0 || context.compare("PROGRAM FILES X86") == 0 ||
context.compare("PROGRAM FILES 86") == 0 || context.compare("PROGRAM FILES (X86)") == 0 || context.compare("PROGRAMFILES (X86)") == 0 ||
context.compare("PROGRAM FILES(X86)") == 0 || context.compare("PROGRAMFILES(X86)") == 0) {
path = FS_GetKnownDirectory(FS_KnownDirectory::PROGRAM_FILES_32);
} else if (context.compare("TMP") == 0 || context.compare("TEMP") == 0 || context.compare("TEMP FILES") == 0) {
path = FS_GetKnownDirectory(FS_KnownDirectory::TEMP);
} else if (context.compare("HOME") == 0 || context.compare("USER") == 0 || context.compare("PROFILE") == 0 || context.compare("USERPROFILE") == 0 ||
context.compare("USER PROFILE") == 0) {
path = FS_GetKnownDirectory(FS_KnownDirectory::HOME);
} else if (context.compare("FONT") == 0 || context.compare("FONTS") == 0) {
path = FS_GetKnownDirectory(FS_KnownDirectory::SYSTEM_FONTS);
} else if (context.compare("USERFONT") == 0 || context.compare("USER FONT") == 0 || context.compare("USERFONTS") == 0 ||
context.compare("USER FONTS") == 0) {
path = FS_GetKnownDirectory(FS_KnownDirectory::USER_FONTS);
} else if (context.compare("PROGRAMDATA") == 0 || context.compare("COMMON PROGRAM DATA") == 0) {
path = FS_GetKnownDirectory(FS_KnownDirectory::PROGRAM_DATA);
} else {
path = FS_GetKnownDirectory(FS_KnownDirectory::DESKTOP); // anything else defaults to the desktop where the user can easily see stuff
}
auto size = path.size();
auto qbsFinal = qbs_new(size, 1);
memcpy(qbsFinal->chr, &path[0], size);
return qbsFinal;
}
/// @brief Returns true if a directory specified exists
/// @param path The directory path
/// @return True if the directory exists
int32_t func__direxists(qbs *path) {
if (new_error)
return QB_FALSE;
std::string pathName(reinterpret_cast<char *>(path->chr), path->len);
return FS_DirectoryExists(filepath_fix_directory(pathName)) ? QB_TRUE : QB_FALSE;
}
/// @brief Returns true if a file specified exists
/// @param path The file path to check for
/// @return True if the file exists
static inline bool FS_FileExists(const char *path) {
#ifdef QB64_WINDOWS
auto x = GetFileAttributesA(path);
return x != INVALID_FILE_ATTRIBUTES && !(x & FILE_ATTRIBUTE_DIRECTORY);
#else
struct stat info;
return stat(path, &info) == 0 && S_ISREG(info.st_mode);
#endif
}
/// @brief Returns true if a file specified exists
/// @param path The file path to check for
/// @return True if the file exists
int32_t func__fileexists(qbs *path) {
if (new_error)
return QB_FALSE;
std::string pathName(reinterpret_cast<char *>(path->chr), path->len);
return FS_FileExists(filepath_fix_directory(pathName)) ? QB_TRUE : QB_FALSE;
}
/// @brief Return the startup directory
/// @return A qbs containing the directory path
qbs *func__startdir() {
auto temp = qbs_new(0, 1);
qbs_set(temp, g_startDir);
return temp;
}
/// @brief Changes the current directory
/// @param str The directory path to change to
void sub_chdir(qbs *str) {
if (new_error)
return;
std::string pathName(reinterpret_cast<char *>(str->chr), str->len);
if (chdir(filepath_fix_directory(pathName)) == -1)
error(QB_ERROR_PATH_NOT_FOUND); // assume errno == ENOENT; path not found
}
/// @brief Checks if s is an empty string (either NULL or zero length)
/// @param s A null-terminated string or NULL
/// @return False is we have a valid string > length 0
static inline bool FS_IsStringEmpty(const char *s) { return s == nullptr || s[0] == '\0'; }
/// @brief This is a basic pattern matching function used by FS_GetDirectoryEntryName()
/// @param fileSpec The pattern to match
/// @param fileName The filename to match
/// @return True if it is a match, false otherwise
static inline bool FS_IsPatternMatching(const char *fileSpec, const char *fileName) {
auto spec = fileSpec;
auto name = fileName;
const char *any = nullptr;
while (*spec || *name) {
switch (*spec) {
case '*': // handle wildcard '*' character
any = spec;
spec++;
while (*name && *name != *spec)
name++;
break;
case '?': // handle wildcard '?' character
spec++;
if (*name)
name++;
break;
default: // compare non-wildcard characters
if (*spec != *name) {
if (any && *name)
spec = any;
else
return false;
} else {
spec++;
name++;
}
break;
}
}
return true;
}
/// @brief Returns true if fileSpec has any wildcards
/// @param fileSpec The string to check
/// @return True if * or ? are found
static inline bool FS_HasPattern(const char *fileSpec) { return fileSpec != nullptr && (strchr(fileSpec, '*') || strchr(fileSpec, '?')); }
/// @brief An MS BASIC PDS DIR$ style function
/// @param fileSpec This can be a path with wildcard for the final level (i.e. C:/Windows/*.* or /usr/lib/* etc.)
/// @return Returns a file or directory name matching fileSpec or an empty string when there is nothing left
static const char *FS_GetDirectoryEntryName(const char *fileSpec) {
static DIR *pDir = nullptr;
static char pattern[FS_PATHNAME_LENGTH_MAX];
static char entry[FS_PATHNAME_LENGTH_MAX];
entry[0] = '\0'; // set to an empty string
if (!FS_IsStringEmpty(fileSpec)) {
// We got a filespec. Check if we have one already going and if so, close it
if (pDir) {
closedir(pDir);
pDir = nullptr;
}
char dirName[FS_PATHNAME_LENGTH_MAX]; // we only need this for opendir()
if (FS_HasPattern(fileSpec)) {
// We have a pattern. Check if we have a path in it
auto p = strrchr(fileSpec, '/'); // try *nix style separator
#ifdef QB64_WINDOWS
if (!p)
p = strrchr(fileSpec, '\\'); // try windows style separator
#endif
if (p) {
// Split the path and the filespec
strncpy(pattern, p + 1, FS_PATHNAME_LENGTH_MAX);
pattern[FS_PATHNAME_LENGTH_MAX - 1] = '\0';
auto len = std::min<size_t>((p - fileSpec) + 1, FS_PATHNAME_LENGTH_MAX - 1);
memcpy(dirName, fileSpec, len);
dirName[len] = '\0';
} else {
// No path. Use the current path
strncpy(pattern, fileSpec, FS_PATHNAME_LENGTH_MAX);
pattern[FS_PATHNAME_LENGTH_MAX - 1] = '\0';
strcpy(dirName, "./");
}
} else {
// No pattern. Check if this is a file and simply return the name if it exists
if (FS_FileExists(fileSpec)) {
strncpy(entry, filepath_get_filename(fileSpec), FS_PATHNAME_LENGTH_MAX);
entry[FS_PATHNAME_LENGTH_MAX - 1] = '\0';
return entry;
}
// Else, We'll just assume it's a directory
strncpy(dirName, fileSpec, FS_PATHNAME_LENGTH_MAX);
dirName[FS_PATHNAME_LENGTH_MAX - 1] = '\0';
strcpy(pattern, "*");
}
pDir = opendir(dirName);
}
if (pDir) {
for (;;) {
auto pDirent = readdir(pDir);
if (!pDirent) {
closedir(pDir);
pDir = nullptr;
break;
}
if (FS_IsPatternMatching(pattern, pDirent->d_name)) {
strncpy(entry, pDirent->d_name, FS_PATHNAME_LENGTH_MAX);
entry[FS_PATHNAME_LENGTH_MAX - 1] = '\0';
break;
}
}
}
return entry;
}
/// @brief This mimics MS BASIC PDS 7.1 & VBDOS 1.0 DIR$() function
/// @param qbsFileSpec This can be a path with wildcard for the final level (i.e. C:/Windows/*.* or /usr/lib/* etc.)
/// @param passed Flags for optional parameters
/// @return Retuns a qbs with the directory entry name or an empty string if there are no more entries
qbs *func__files(qbs *qbsFileSpec, int32_t passed) {
static std::string directory;
std::string pathName;
const char *entry;
qbs *qbsFinal;
// Check if fresh arguments were passed and we need to begin a new session
if (passed) {
std::string fileSpec(reinterpret_cast<char *>(qbsFileSpec->chr), qbsFileSpec->len);
if (FS_DirectoryExists(filepath_fix_directory(fileSpec)))
directory = fileSpec;
else
filepath_split(fileSpec, directory, pathName); // split the file path
entry = FS_GetDirectoryEntryName(fileSpec.c_str());
if (FS_IsStringEmpty(entry)) {
// This is per MS BASIC PDS 7.1 and VBDOS 1.0 behavior
qbsFinal = qbs_new(0, 1);
error(QB_ERROR_FILE_NOT_FOUND);
return qbsFinal;
}
} else {
entry = FS_GetDirectoryEntryName(nullptr);
}
filepath_join(pathName, directory, entry);
auto size = strlen(entry);
if (size && FS_DirectoryExists(pathName.c_str())) {
// Add a trailing slash if it is a directory
qbsFinal = qbs_new(size + 1, 1);
memcpy(qbsFinal->chr, entry, size);
qbsFinal->chr[size] = FS_PATH_SEPARATOR;
} else {
qbsFinal = qbs_new(size, 1);
memcpy(qbsFinal->chr, entry, size);
}
return qbsFinal;
}
/// @brief Returns the free volume space for a given directory
/// @param path A directory that resides on the volume we want the free space for
/// @return The free space in bytes
static uint64_t FS_GetFreeDiskSpace(const char *path) {
#ifdef QB64_WINDOWS
ULARGE_INTEGER freeBytesAvailable;
ULARGE_INTEGER totalNumberOfBytes;
ULARGE_INTEGER totalNumberOfFreeBytes;
if (GetDiskFreeSpaceExA(path, &freeBytesAvailable, &totalNumberOfBytes, &totalNumberOfFreeBytes))
return totalNumberOfFreeBytes.QuadPart;
#else
struct statvfs stat;
if (statvfs(path, &stat) == 0)
return static_cast<uint64_t>(stat.f_bsize) * stat.f_bfree;
#endif
return 0; // zero if something failed
}
/// @brief Gets the fully qualified name (FQN)
/// @param path The path name to get the FQN for
/// @return The FQN
static std::string FS_GetFQN(const char *path) {
std::string FQN = path; // fallback
#ifdef QB64_WINDOWS
DWORD size = GetFullPathNameA(path, 0, nullptr, nullptr); // get the required buffer size
if (size) {
FQN.resize(size);
if (GetFullPathNameA(path, size, &FQN[0], nullptr))
FQN.resize(size - 1); // resize to exclude the null terminator
}
#else
char *result = realpath(path, nullptr);
if (result) {
FQN = result;
free(result); // cleanup memory allocated by realpath
}
#endif
if (FS_DirectoryExists(FQN.c_str()) && FQN.back() != FS_PATH_SEPARATOR)
FQN.append(1, FS_PATH_SEPARATOR);
return FQN;
}
/// @brief Gets the fully qualified name (FQN)
/// @param qbsPathName The path name to get the FQN for
/// @return The FQN
qbs *func__fullpath(qbs *qbsPathName) {
qbs *temp;
if (!qbsPathName->len) {
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
temp = qbs_new(0, 1);
return temp;
}
std::string pathName(reinterpret_cast<char *>(qbsPathName->chr), qbsPathName->len);
filepath_fix_directory(pathName);
if (!FS_DirectoryExists(pathName.c_str()) && !FS_FileExists(pathName.c_str())) {
// Path not found
error(QB_ERROR_PATH_NOT_FOUND);
temp = qbs_new(0, 1);
return temp;
}
pathName = FS_GetFQN(pathName.c_str());
temp = qbs_new(pathName.size(), 1);
memcpy(temp->chr, &pathName[0], pathName.size());
return temp;
}
/// @brief Gets the short name for a file / directory (if possible)
/// @param path The file / directory to get the short name for
/// @return The short name
static std::string FS_GetShortName(const char *path) {
#ifdef QB64_WINDOWS
DWORD size = GetShortPathNameA(path, nullptr, 0); // get the required buffer size
if (size) {
std::string shortPath;
shortPath.resize(size);
if (GetShortPathNameA(path, &shortPath[0], size)) {
shortPath.resize(size - 1); // resize to exclude the null terminator
return shortPath;
}
}
#endif
return path; // return the path as-is for *nix or if GetShortPathNameA failed
}
/// @brief Prints a list of files in the current directory using a file specification
/// @param str Is a string containing a path (it can include wildcards)
/// @param passed Optional parameters
void sub_files(qbs *str, int32_t passed) {
static qbs *strz = nullptr;
if (new_error)
return;
if (!strz)
strz = qbs_new(0, 0);
std::string fileSpec, directory, pathName;
if (passed && str->len) {
fileSpec.assign(reinterpret_cast<char *>(str->chr), str->len);
if (FS_DirectoryExists(filepath_fix_directory(fileSpec))) {
directory = FS_GetFQN(fileSpec.c_str());
} else {
std::string d;
filepath_split(fileSpec, d, pathName);
directory = FS_GetFQN(d.c_str());
}
} else {
fileSpec = "./";
directory = FS_GetFQN(fileSpec.c_str());
}
if (!directory.size()) {
// Invalid filespec
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
std::string shortName = FS_GetShortName(directory.c_str());
// Print the path
qbs_set(strz, qbs_new_txt_len(shortName.c_str(), shortName.size()));
qbs_print(strz, 1);
auto entry = FS_GetDirectoryEntryName(fileSpec.c_str()); // get the first entry
filepath_join(pathName, directory, entry);
if (FS_IsStringEmpty(entry)) {
// File not found
error(QB_ERROR_FILE_NOT_FOUND);
return;
}
// Print directory entries
do {
fileSpec = FS_GetShortName(pathName.c_str()); // we do not need fileSpec anymore
filepath_split(fileSpec, directory, shortName);
qbs_set(strz, qbs_new_txt_len(shortName.c_str(), shortName.size()));
if (strz->len < 12) {
// Padding required
qbs_set(strz, qbs_add(strz, func_space(12 - strz->len)));
for (auto i = 0; i < 12; i++) {
if (strz->chr[i] == 46) {
memmove(&strz->chr[8], &strz->chr[i], 4);
memset(&strz->chr[i], 32, 8 - i);
break;
}
}
}
if (FS_DirectoryExists(pathName.c_str()))
qbs_set(strz, qbs_add(strz, qbs_new_txt_len("<DIR> ", 6)));
else
qbs_set(strz, qbs_add(strz, func_space(6)));
makefit(strz);
qbs_print(strz, 0);
entry = FS_GetDirectoryEntryName(nullptr); // get the next entry
filepath_join(pathName, directory, entry);
} while (!FS_IsStringEmpty(entry));
if (func_pos(0) > 1) {
// Move to a new line if necessary
strz->len = 0;
qbs_print(strz, 1);
}
// Print the free volume space
qbs_set(strz, qbs_add(qbs_str(FS_GetFreeDiskSpace(directory.c_str())), qbs_new_txt_len(" Bytes free", 11)));
qbs_print(strz, 1);
}
/// @brief Deletes files from disk
/// @param str The file(s) to delete (may contain wildcard at the final level)
void sub_kill(qbs *str) {
if (new_error)
return;
std::string directory, pathName, fileSpec(reinterpret_cast<char *>(str->chr), str->len);
filepath_split(filepath_fix_directory(fileSpec), directory, pathName); // split the file path
auto entry = FS_GetDirectoryEntryName(fileSpec.c_str()); // get the first entry
// Keep looking through the entries until we file a file
while (!FS_IsStringEmpty(entry)) {
filepath_join(pathName, directory, entry);
if (FS_FileExists(pathName.c_str()))
break;
entry = FS_GetDirectoryEntryName(nullptr); // get the next entry
}
// Check if we have exhausted the entries without ever finding a file
if (FS_IsStringEmpty(entry)) {
// This behavior is per QBasic 1.1
error(QB_ERROR_FILE_NOT_FOUND);
return;
}
// Process all matches
do {
// We'll delete only if it is a file
if (FS_FileExists(pathName.c_str())) {
if (remove(pathName.c_str())) {
auto i = errno;
if (i == ENOENT) {
error(QB_ERROR_FILE_NOT_FOUND);
return;
} // file not found
if (i == EACCES) {
error(QB_ERROR_PATH_FILE_ACCESS_ERROR);
return;
} // path / file access error
error(QB_ERROR_BAD_FILE_NAME); // bad file name (assumed)
}
}
entry = FS_GetDirectoryEntryName(nullptr); // get the next entry
filepath_join(pathName, directory, entry);
} while (!FS_IsStringEmpty(entry));
}
/// @brief Creates a new directory
/// @param str The directory path name to create
void sub_mkdir(qbs *str) {
if (new_error)
return;
std::string pathName(reinterpret_cast<char *>(str->chr), str->len);
#ifdef QB64_WINDOWS
if (mkdir(filepath_fix_directory(pathName)) == -1)
#else
if (mkdir(filepath_fix_directory(pathName), S_IRWXU | S_IRWXG) == -1)
#endif
{
if (errno == EEXIST) {
error(QB_ERROR_PATH_FILE_ACCESS_ERROR);
return;
} // path / file access error
error(QB_ERROR_PATH_NOT_FOUND); // assume errno == ENOENT; path not found
}
}
/// @brief Renames a file or directory
/// @param oldname The old file / directory name
/// @param newname The new file / directory name
void sub_name(qbs *oldname, qbs *newname) {
if (new_error)
return;
std::string pathNameOld(reinterpret_cast<char *>(oldname->chr), oldname->len), pathNameNew(reinterpret_cast<char *>(newname->chr), newname->len);
if (rename(filepath_fix_directory(pathNameOld), filepath_fix_directory(pathNameNew))) {
auto i = errno;
if (i == ENOENT) {
error(QB_ERROR_FILE_NOT_FOUND);
return;
} // file not found
if (i == EINVAL) {
error(QB_ERROR_BAD_FILE_NAME);
return;
} // bad file name
if (i == EACCES) {
error(QB_ERROR_PATH_FILE_ACCESS_ERROR);
return;
} // path / file access error
error(QB_ERROR_ILLEGAL_FUNCTION_CALL); // illegal function call (assumed)
}
}
/// @brief Deletes an empty directory
/// @param str The path name of the directory to delete
void sub_rmdir(qbs *str) {
if (new_error)
return;
std::string pathName(reinterpret_cast<char *>(str->chr), str->len);
if (rmdir(filepath_fix_directory(pathName)) == -1) {
if (errno == ENOTEMPTY) {
error(QB_ERROR_PATH_FILE_ACCESS_ERROR);
return;
} // path/file access error; not an empty directory
error(QB_ERROR_PATH_NOT_FOUND); // assume errno == ENOENT; path not found
}
}

View file

@ -17,6 +17,7 @@
#define STB_VORBIS_HEADER_ONLY
#include "datetime.h"
#include "extras/stb_vorbis.c"
#include "filepath.h"
#include "miniaudio.h"
#include "mutex.h"
#include <algorithm>
@ -28,9 +29,6 @@
#define INVALID_MEM_LOCK 1073741821
// This should be defined elsewhere (in libqb?). Since it is not, we are doing it here
#define MEM_TYPE_SOUND 5
// In QuickBASIC false means 0 and true means -1 (sad, but true XD)
#define QB_FALSE MA_FALSE
#define QB_TRUE -MA_TRUE
// This is returned to the caller if handle allocation fails with a -1
// CreateHandle() does not return 0 because it is a valid internal handle
// Handle 0 is 'handled' as a special case
@ -87,7 +85,7 @@ struct RawStream {
libqb_mutex *m; // we'll use a mutex to give exclusive access to resources used by both threads
bool stop; // set this to true to stop supply of samples completely (including silent samples)
static const size_t DEFAULT_SIZE = 1024; // this is almost twice the amout what miniaudio actually asks for in frameCount
static const size_t DEFAULT_SIZE = 1024; // this is almost twice the amount what miniaudio actually asks for in frameCount
// Delete default, copy and move constructors and assignments
RawStream() = delete;
@ -118,7 +116,7 @@ struct RawStream {
libqb_mutex_guard lock(m); // lock the mutex before accessing the vectors
consumer->cursor = 0; // reset the cursor
consumer->data.clear(); // clear the consumer vector
std::swap(consumer, producer); // quicky swap the Buffer pointers
std::swap(consumer, producer); // quickly swap the Buffer pointers
}
/// @brief This pushes a sample frame at the end of the queue. This is mutex protected and called by the main thread
@ -255,7 +253,7 @@ static ma_data_source_vtable rawStreamDataSourceVtable = {
/// @brief This creates, initializes and sets up a raw stream for playback
/// @param pmaEngine This should come from the QBPE sound engine
/// @param pmaSound This should come from a QBPE sound handle
/// @return Returns a pointer to a data souce if successful, NULL otherwise
/// @return Returns a pointer to a data source if successful, NULL otherwise
static RawStream *RawStreamCreate(ma_engine *pmaEngine, ma_sound *pmaSound) {
if (!pmaEngine || !pmaSound) { // these should not be NULL
AUDIO_DEBUG_PRINT("Invalid arguments");
@ -287,7 +285,7 @@ static RawStream *RawStreamCreate(ma_engine *pmaEngine, ma_sound *pmaSound) {
result = ma_sound_init_from_data_source(pmaEngine, &pRawStream->maDataSource, MA_SOUND_FLAG_NO_PITCH | MA_SOUND_FLAG_NO_SPATIALIZATION, NULL,
pmaSound); // attach data source to the ma_sound
if (result != MA_SUCCESS) {
AUDIO_DEBUG_PRINT("Error %i: failed to initalize sound from data source", result);
AUDIO_DEBUG_PRINT("Error %i: failed to initialize sound from data source", result);
delete pRawStream;
@ -423,7 +421,7 @@ class BufferMap {
}
};
/// @brief This is a PSG class that handles all kinds of sound generatation for BEEP, SOUND and PLAY
/// @brief This is a PSG class that handles all kinds of sound generation for BEEP, SOUND and PLAY
class PSG {
public:
/// @brief Various types of waveform that can be generated
@ -496,7 +494,7 @@ class PSG {
auto neededFrames = (ma_uint64)(waveDuration * rawStream->sampleRate);
if (!neededFrames || maWaveform.config.frequency >= 20000 || mixCursor + neededFrames > waveBuffer.size()) {
AUDIO_DEBUG_PRINT("Not generating any wavefrom. Frames = %llu, frequency = %lf, cursor = %llu", neededFrames, maWaveform.config.frequency,
AUDIO_DEBUG_PRINT("Not generating any waveform. Frames = %llu, frequency = %lf, cursor = %llu", neededFrames, maWaveform.config.frequency,
mixCursor);
return; // nothing to do
}
@ -769,7 +767,7 @@ class PSG {
if (processedChar == 'X') { // "X" + VARPTR$()
// A minimum of 3 bytes is need to read the address
if (currentState.length < 3) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
@ -791,12 +789,12 @@ class PSG {
continue;
} else if (currentChar == '=') { // "=" + VARPTR$()
if (dots) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
if (numberEntered) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
@ -816,7 +814,7 @@ class PSG {
*/
if (currentState.length < 3) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
@ -868,14 +866,14 @@ class PSG {
default:
// bit type?
if ((currentChar & 64) == 0) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
auto x2 = currentChar & 63;
if (x2 > 56) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
} // valid number of bits?
@ -897,7 +895,7 @@ class PSG {
}
if (d > 2147483647.0 || d < -2147483648.0) {
error(5); // out of range value!
error(QB_ERROR_ILLEGAL_FUNCTION_CALL); // out of range value!
return;
}
@ -906,7 +904,7 @@ class PSG {
continue;
} else if (currentChar >= '0' && currentChar <= '9') {
if (dots || numberEntered == 2) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
@ -920,7 +918,7 @@ class PSG {
continue;
} else if (currentChar == '.') {
if (followUp != 7 && followUp != 1 && followUp != 4) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
@ -932,14 +930,14 @@ class PSG {
follow_up:
if (followUp == 10) { // Q...
if (!numberEntered) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
numberEntered = 0;
if (number > 100) { // 0 - 100 ms
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
@ -950,14 +948,14 @@ class PSG {
break;
} else if (followUp == 9) { // @...
if (!numberEntered) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
numberEntered = 0;
if ((WaveformType)number <= WaveformType::NONE || (WaveformType)number >= WaveformType::COUNT) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
@ -969,14 +967,14 @@ class PSG {
break;
} else if (followUp == 8) { // V...
if (!numberEntered) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
numberEntered = 0;
if (number > MAX_MML_VOLUME) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
@ -990,7 +988,7 @@ class PSG {
if (numberEntered) {
numberEntered = 0;
if (number < 1 || number > 64) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
duration = 1.0 / (tempo / 60.0) * (4.0 / ((double)number));
@ -1027,7 +1025,7 @@ class PSG {
break;
} else if (followUp == 6) { // T...
if (!numberEntered) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
@ -1045,7 +1043,7 @@ class PSG {
break;
} else if (followUp == 5) { // M...
if (numberEntered) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
@ -1073,7 +1071,7 @@ class PSG {
background = false;
break;
default:
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
@ -1082,14 +1080,14 @@ class PSG {
continue;
} else if (followUp == 4) { // N...
if (!numberEntered) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
numberEntered = 0;
if (number > 84) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
@ -1098,14 +1096,14 @@ class PSG {
goto follow_up_1;
} else if (followUp == 3) { // O...
if (!numberEntered) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
numberEntered = 0;
if (number > MAX_OCTAVE) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
@ -1117,14 +1115,14 @@ class PSG {
break;
} else if (followUp == 2) { // L...
if (!numberEntered) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
numberEntered = 0;
if (number < MIN_LENGTH || number > MAX_LENGTH) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
@ -1137,7 +1135,7 @@ class PSG {
} else if (followUp == 1) { // A-G...
if (currentChar == '-') {
if (noteShifted || numberEntered) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
@ -1148,7 +1146,7 @@ class PSG {
}
if (currentChar == '+' || currentChar == '#') {
if (noteShifted || numberEntered) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
@ -1163,7 +1161,7 @@ class PSG {
numberEntered = 0;
if (number < 0 || number > 64) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
@ -1276,12 +1274,12 @@ class PSG {
continue;
}
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
if (numberEntered || followUp) {
error(5); // unhandled data
error(QB_ERROR_ILLEGAL_FUNCTION_CALL); // unhandled data
return;
}
@ -1536,7 +1534,7 @@ struct AudioEngine {
soundHandles[handle]->isUsed = false;
soundHandles[handle]->type = SoundHandle::Type::NONE;
// Save the free hanndle to lowestFreeHandle if it is lower than lowestFreeHandle
// Save the free handle to lowestFreeHandle if it is lower than lowestFreeHandle
if (handle < lowestFreeHandle)
lowestFreeHandle = handle;
@ -1588,13 +1586,13 @@ void sub_sound(double frequency, double lengthInClockTicks, double volume, doubl
return;
if ((frequency < 37.0 && frequency != 0) || frequency > 32767.0 || lengthInClockTicks < 0.0 || lengthInClockTicks > 65535.0) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
if (passed & 1) {
if (volume < PSG::MIN_VOLUME || volume > PSG::MAX_VOLUME) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
audioEngine.psg->SetAmplitude(volume);
@ -1602,7 +1600,7 @@ void sub_sound(double frequency, double lengthInClockTicks, double volume, doubl
if (passed & 2) {
if (panning < PSG::PAN_LEFT || panning > PSG::PAN_RIGHT) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
audioEngine.psg->SetPanning((float)panning);
@ -1610,7 +1608,7 @@ void sub_sound(double frequency, double lengthInClockTicks, double volume, doubl
if (passed & 4) {
if ((PSG::WaveformType)waveform <= PSG::WaveformType::NONE || (PSG::WaveformType)waveform >= PSG::WaveformType::COUNT) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
audioEngine.psg->SetWaveformType((PSG::WaveformType)waveform);
@ -1654,7 +1652,7 @@ void sub_play(const qbs *str) {
/// <summary>
/// This returns the sample rate from ma engine if ma is initialized.
/// </summary>
/// <returns>miniaudio sample rtate</returns>
/// <returns>miniaudio sample rate</returns>
int32_t func__sndrate() { return audioEngine.sampleRate; }
/// @brief Creates a ma_decoder and ma_sound from a memory buffer for a valid sound handle
@ -1722,7 +1720,7 @@ int32_t func__sndopen(qbs *fileName, qbs *requirements, int32_t passed) {
if (!reqs)
reqs = qbs_new(0, 0);
// Alocate a sound handle
// Allocate a sound handle
int32_t handle = audioEngine.CreateHandle();
if (handle < 1) // We are not expected to open files with handle 0
return INVALID_SOUND_HANDLE;
@ -1734,7 +1732,7 @@ int32_t func__sndopen(qbs *fileName, qbs *requirements, int32_t passed) {
if (passed && requirements->len)
qbs_set(reqs, qbs_ucase(requirements)); // Convert tmp str to perm str
// Set the flags to specifiy how we want the audio file to be opened
// Set the flags to specify how we want the audio file to be opened
if (passed && requirements->len && func_instr(1, reqs, qbs_new_txt(REQUIREMENT_STRING_STREAM), 1)) {
audioEngine.soundHandles[handle]->maFlags |= MA_SOUND_FLAG_STREAM; // Check if the user wants to stream the file
AUDIO_DEBUG_PRINT("Sound will stream");
@ -1757,8 +1755,8 @@ int32_t func__sndopen(qbs *fileName, qbs *requirements, int32_t passed) {
qbs_set(fileNameZ, qbs_add(fileName, qbs_new_txt_len("\0", 1))); // s1 = filename + CHR$(0)
// Forward the request to miniaudio to open the sound file
audioEngine.maResult = ma_sound_init_from_file(&audioEngine.maEngine, (const char *)fileNameZ->chr, audioEngine.soundHandles[handle]->maFlags, NULL,
NULL, &audioEngine.soundHandles[handle]->maSound);
audioEngine.maResult = ma_sound_init_from_file(&audioEngine.maEngine, filepath_fix_directory(fileNameZ), audioEngine.soundHandles[handle]->maFlags,
NULL, NULL, &audioEngine.soundHandles[handle]->maSound);
}
// If the sound failed to initialize, then free the handle and return INVALID_SOUND_HANDLE
@ -1782,7 +1780,7 @@ void sub__sndclose(int32_t handle) {
if (audioEngine.isInitialized && IS_SOUND_HANDLE_VALID(handle)) {
// If we have a raw stream then force it to push all it's data to miniaudio
// Note that this will take care of checking if the handle is a raw steam and other stuff
// So it is completly safe to call it this way
// So it is completely safe to call it this way
sub__sndrawdone(handle, true);
if (audioEngine.soundHandles[handle]->type == SoundHandle::Type::RAW)
@ -1826,7 +1824,7 @@ int32_t func__sndcopy(int32_t src_handle) {
} else if (audioEngine.soundHandles[src_handle]->maDecoder) {
AUDIO_DEBUG_PRINT("Doing custom sound copy for ma_decoder");
dst_handle = audioEngine.CreateHandle(); // alocate a sound handle
dst_handle = audioEngine.CreateHandle(); // allocate a sound handle
if (dst_handle < 1)
return INVALID_SOUND_HANDLE;
@ -1847,7 +1845,7 @@ int32_t func__sndcopy(int32_t src_handle) {
} else {
AUDIO_DEBUG_PRINT("Doing regular miniaudio sound copy");
dst_handle = audioEngine.CreateHandle(); // alocate a sound handle
dst_handle = audioEngine.CreateHandle(); // allocate a sound handle
if (dst_handle < 1)
return INVALID_SOUND_HANDLE;
@ -2182,7 +2180,7 @@ int32_t func__sndopenraw() {
if (!audioEngine.isInitialized)
return INVALID_SOUND_HANDLE;
// Alocate a sound handle
// Allocate a sound handle
int32_t handle = audioEngine.CreateHandle();
if (handle < 1)
return INVALID_SOUND_HANDLE;
@ -2415,7 +2413,7 @@ mem_block func__memsound(int32_t handle, int32_t targetChannel, int32_t passed)
AUDIO_DEBUG_PRINT("Format = %u, channels = %u, frames = %llu", maFormat, channels, sampleFrames);
// Setup type: This was not done in the old code
// But we are doing it here. By examing the type the user can now figure out if they have to use FP32 or integers
// But we are doing it here. By examining the type the user can now figure out if they have to use FP32 or integers
switch (maFormat) {
case ma_format::ma_format_f32:
mb.type = 4 + 256; // FP32
@ -2497,7 +2495,7 @@ void snd_init() {
// Get and save the engine sample rate. We will let miniaudio choose the device sample rate for us
// This ensures we get the lowest latency
// Set the resource manager decorder sample rate to the device sample rate (miniaudio engine bug?)
// Set the resource manager decoder sample rate to the device sample rate (miniaudio engine bug?)
audioEngine.maResourceManager.config.decodedSampleRate = audioEngine.sampleRate = ma_engine_get_sample_rate(&audioEngine.maEngine);
// Set the initialized flag as true

View file

@ -189,13 +189,13 @@ struct FontManager {
FT_Face face; // FreeType face object
FT_Pos monospaceWidth; // the monospace width (if font was loaded as monospace, else zero)
FT_Pos defaultHeight; // default (max) pixel height the user wants
FT_Pos baseline; // font baeline in pixels
FT_Pos baseline; // font baseline in pixels
int32_t options; // fonts options that were passed by QB64 while loading the font
/// @brief Manages a single glyph in a font
struct Glyph {
// Usually the bitmap size & metrics returned by FT for mono and gray can be the same
// But it's a bad idea to assume that is the case everytime
// But it's a bad idea to assume that is the case every time
struct Bitmap {
uint8_t *data; // pointer to the raw pixels
FT_Vector size; // bitmap width & height in pixels
@ -205,7 +205,7 @@ struct FontManager {
FT_UInt index; // glyph index
Bitmap bmpMono; // monochrome bitmap in 8-bit format
Bitmap bmpGray; // anti-aliased bitamp in 8-bit format
Bitmap bmpGray; // anti-aliased bitmap in 8-bit format
Bitmap *bitmap; // pointer to the currently selected bitmap (mono / gray)
// Delete copy and move constructors and assignments
@ -435,7 +435,7 @@ struct FontManager {
auto newGlyph = new Glyph;
if (!newGlyph) {
FONT_DEBUG_PRINT("Failed to allocate mmemory");
FONT_DEBUG_PRINT("Failed to allocate memory");
return nullptr; // failed to allocate memory
}
@ -573,7 +573,7 @@ struct FontManager {
if (h >= vectorSize) {
// Scan through the entire vector and return a slot that is not being used
// Ideally this should execute in extremely few (if at all) senarios
// Ideally this should execute in extremely few (if at all) scenarios
// Also, this loop should not execute if size is 0
for (h = 0; h < vectorSize; h++) {
if (!fonts[h]->isUsed) {
@ -1018,14 +1018,14 @@ int32_t func__UFontHeight(int32_t qb64_fh, int32_t passed) {
if (passed) {
// Check if a valid font handle was passed
if (!IS_VALID_QB64_FONT_HANDLE(qb64_fh)) {
error(258);
error(QB_ERROR_INVALID_HANDLE);
return 0;
}
} else {
qb64_fh = write_page->font; // else get the current write page font handle
}
// For buint-in fonts return the handle value (which is = font height)
// For built-in fonts return the handle value (which is = font height)
if (qb64_fh < 32)
return qb64_fh;
@ -1041,7 +1041,7 @@ int32_t func__UFontHeight(int32_t qb64_fh, int32_t passed) {
return fnt->defaultHeight;
}
/// @brief Returns the text widht in pixels
/// @brief Returns the text width in pixels
/// @param text The text to calculate the width for
/// @param utf_encoding The UTF encoding of the text (0 = ASCII, 8 = UTF-8, 16 - UTF-16, 32 = UTF-32)
/// @param qb64_fh A QB64 font handle (this can be a builtin font as well)
@ -1056,7 +1056,7 @@ int32_t func__UPrintWidth(const qbs *text, int32_t utf_encoding, int32_t qb64_fh
// Check UTF argument
if (passed & 1) {
if (!IS_VALID_UTF_ENCODING(utf_encoding)) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return 0;
}
} else {
@ -1066,7 +1066,7 @@ int32_t func__UPrintWidth(const qbs *text, int32_t utf_encoding, int32_t qb64_fh
// Check if a valid font handle was passed
if (passed & 2) {
if (!IS_VALID_QB64_FONT_HANDLE(qb64_fh)) {
error(258);
error(QB_ERROR_INVALID_HANDLE);
return 0;
}
} else {
@ -1122,14 +1122,14 @@ int32_t func__ULineSpacing(int32_t qb64_fh, int32_t passed) {
if (passed) {
// Check if a valid font handle was passed
if (!IS_VALID_QB64_FONT_HANDLE(qb64_fh)) {
error(258);
error(QB_ERROR_INVALID_HANDLE);
return 0;
}
} else {
qb64_fh = write_page->font; // else get the current write page font handle
}
// For buint-in fonts return the handle value (which is = font height)
// For built-in fonts return the handle value (which is = font height)
if (qb64_fh < 32)
return qb64_fh;
@ -1160,7 +1160,7 @@ void sub__UPrintString(int32_t start_x, int32_t start_y, const qbs *text, int32_
// Check if we are in text mode and generate an error if we are
if (write_page->text) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
@ -1177,7 +1177,7 @@ void sub__UPrintString(int32_t start_x, int32_t start_y, const qbs *text, int32_
// Check UTF argument
if (passed & 2) {
if (!IS_VALID_UTF_ENCODING(utf_encoding)) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return;
}
} else {
@ -1187,7 +1187,7 @@ void sub__UPrintString(int32_t start_x, int32_t start_y, const qbs *text, int32_
// Check if a valid font handle was passed
if (passed & 4) {
if (!IS_VALID_QB64_FONT_HANDLE(qb64_fh)) {
error(258);
error(QB_ERROR_INVALID_HANDLE);
return;
}
} else {
@ -1464,7 +1464,7 @@ void sub__UPrintString(int32_t start_x, int32_t start_y, const qbs *text, int32_
free(drawBuf);
}
/// @brief Calculate the starting pixel positions of each chancter to an array. First one being zero.
/// @brief Calculate the starting pixel positions of each codepoint to an array. First one being zero.
/// This also calculates the pixel position of the last + 1 character.
/// @param text Text for which the data needs to be calculated. This can be unicode encoded
/// @param arr A QB64 LONG array. This should be codepoints + 1 long. If the array is shorter additional calculated data is ignored
@ -1488,7 +1488,7 @@ int32_t func__UCharPos(const qbs *text, void *arr, int32_t utf_encoding, int32_t
// Check UTF argument
if (passed & 2) {
if (!IS_VALID_UTF_ENCODING(utf_encoding)) {
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return 0;
}
} else {
@ -1498,7 +1498,7 @@ int32_t func__UCharPos(const qbs *text, void *arr, int32_t utf_encoding, int32_t
// Check if a valid font handle was passed
if (passed & 4) {
if (!IS_VALID_QB64_FONT_HANDLE(qb64_fh)) {
error(258);
error(QB_ERROR_INVALID_HANDLE);
return 0;
}
} else {

View file

@ -49,13 +49,6 @@
// This is returned to the caller if something goes wrong while loading the image
#define INVALID_IMAGE_HANDLE -1
// TODO: We should have QB64 error code enums in some common place
// Maybe consolidate error() & friends, and these enums
#define ERROR_ILLEGAL_FUNCTION_CALL 5
#define ERROR_INTERNAL_ERROR 51
#define ERROR_BAD_FILE_NAME 64
#define ERROR_INVALID_HANDLE 258
#ifdef QB64_WINDOWS
# define ZERO_VARIABLE(_v_) ZeroMemory(&(_v_), sizeof(_v_))
#else
@ -294,7 +287,7 @@ static uint32_t *image_qoi_load_from_memory(const uint8_t *buffer, size_t size,
return pixels;
}
/// @brief Decodes an image file freom a file using the dr_pcx & stb_image libraries.
/// @brief Decodes an image file from a file using the dr_pcx & stb_image libraries.
/// @param fileName A valid filename
/// @param xOut Out: width in pixels. This cannot be NULL
/// @param yOut Out: height in pixels. This cannot be NULL
@ -386,7 +379,7 @@ static inline uint8_t image_clamp_component(int32_t n) { return n < 0 ? 0 : n >
/// @brief This takes in a 32bpp (BGRA) image raw data and spits out an 8bpp raw image along with it's 256 color (BGRA) palette.
/// @param src32 The source raw image data. This must be in BGRA format and not NULL
/// @param w The widht of the image in pixels
/// @param w The width of the image in pixels
/// @param h The height of the image in pixels
/// @param paletteOut A 256 color palette if the operation was successful. This cannot be NULL
/// @return A pointer to a 8bpp raw image or NULL if operation failed
@ -448,7 +441,7 @@ static uint8_t *image_convert_8bpp(const uint32_t *src32, int32_t w, int32_t h,
/// If the number of unique colors in the 32bpp image > 256, then the functions returns a NULL.
/// Unlike image_convert_8bpp(), no 'real' conversion takes place.
/// @param src The source raw image data. This must be in BGRA format and not NULL
/// @param w The widht of the image in pixels
/// @param w The width of the image in pixels
/// @param h The height of the image in pixels
/// @param paletteOut A 256 color palette if the operation was successful. This cannot be NULL
/// @return A pointer to a 8bpp raw image or NULL if operation failed
@ -545,7 +538,7 @@ static inline uint32_t image_swap_red_blue(uint32_t clr) { return ((clr & 0xFF00
/// @brief This function loads an image into memory and returns valid LONG image handle values that are less than -1
/// @param qbsFileName The filename or memory buffer (see requirements below) of the image
/// @param bpp 32 = 32bpp, 33 = 32bpp (hardware acclerated), 256=8bpp or 257=8bpp (without palette remap)
/// @param bpp 32 = 32bpp, 33 = 32bpp (hardware accelerated), 256=8bpp or 257=8bpp (without palette remap)
/// @param qbsRequirements A qbs that can contain one or more of: hardware, memory, adaptive
/// @param passed How many parameters were passed?
/// @return Valid LONG image handle values that are less than -1 or -1 on failure
@ -573,7 +566,7 @@ int32_t func__loadimage(qbs *qbsFileName, int32_t bpp, qbs *qbsRequirements, int
if ((bpp != 32) && (bpp != 256)) { // invalid BPP?
IMAGE_DEBUG_PRINT("Invalid bpp (0x%X)", bpp);
error(5);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
return INVALID_IMAGE_HANDLE;
}
} else {
@ -594,8 +587,6 @@ int32_t func__loadimage(qbs *qbsFileName, int32_t bpp, qbs *qbsRequirements, int
IMAGE_DEBUG_PRINT("Parsing requirements string: %s", requirements.c_str());
// requirements.find(REQUIREMENT_STRING_MEMORY) != std::string::npos
if (requirements.find("HARDWARE") != std::string::npos && bpp == 32) {
isHardwareImage = true;
IMAGE_DEBUG_PRINT("Hardware image selected");
@ -627,7 +618,7 @@ int32_t func__loadimage(qbs *qbsFileName, int32_t bpp, qbs *qbsRequirements, int
pixels = image_decode_from_memory(qbsFileName->chr, qbsFileName->len, &x, &y, scaler);
} else {
std::string fileName(reinterpret_cast<char *>(qbsFileName->chr), qbsFileName->len);
pixels = image_decode_from_file(fileName.c_str(), &x, &y, scaler);
pixels = image_decode_from_file(filepath_fix_directory(fileName), &x, &y, scaler);
}
if (!pixels)
@ -718,7 +709,7 @@ int32_t func__loadimage(qbs *qbsFileName, int32_t bpp, qbs *qbsRequirements, int
/// @param qbsFileName The file path name to save to
/// @param imageHandle Optional: The image handle. If omitted, then this is _DISPLAY()
/// @param qbsRequirements Optional: Extra format and setting arguments
/// @param passed Argument bitmask
/// @param passed Optional parameters
void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements, int32_t passed) {
enum struct SaveFormat { PNG = 0, QOI, BMP, TGA, JPG, HDR };
static const char *formatName[] = {"png", "qoi", "bmp", "tga", "jpg", "hdr"};
@ -728,7 +719,7 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,
if (!qbsFileName->len) { // empty file names not allowed
IMAGE_DEBUG_PRINT("Empty file name");
error(ERROR_BAD_FILE_NAME);
error(QB_ERROR_BAD_FILE_NAME);
return;
}
@ -743,11 +734,11 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,
imageHandle = -imageHandle;
if (imageHandle >= nextimg) {
error(ERROR_INVALID_HANDLE);
error(QB_ERROR_INVALID_HANDLE);
return;
}
if (!img[imageHandle].valid) {
error(ERROR_INVALID_HANDLE);
error(QB_ERROR_INVALID_HANDLE);
return;
}
}
@ -759,11 +750,11 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,
// Safety
if (imageHandle >= nextimg) {
error(ERROR_INVALID_HANDLE);
error(QB_ERROR_INVALID_HANDLE);
return;
}
if (!img[imageHandle].valid) {
error(ERROR_INVALID_HANDLE);
error(QB_ERROR_INVALID_HANDLE);
return;
}
}
@ -792,6 +783,7 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,
IMAGE_DEBUG_PRINT("Format selected: %s", formatName[(int)format]);
std::string fileName(reinterpret_cast<char *>(qbsFileName->chr), qbsFileName->len);
filepath_fix_directory(fileName);
// Check if fileName has a valid extension and add one if it does not have one
if (fileName.length() > 4) { // must be at least n.ext
@ -889,7 +881,7 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,
pixels.resize(width * height);
if (img[imageHandle].bits_per_pixel == 32) { // BGRA pixels
IMAGE_DEBUG_PRINT("Coverting BGRA surface to RGBA");
IMAGE_DEBUG_PRINT("Converting BGRA surface to RGBA");
auto p = img[imageHandle].offset32;
@ -898,7 +890,7 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,
++p;
}
} else { // indexed pixels
IMAGE_DEBUG_PRINT("Coverting BGRA indexed surface to RGBA");
IMAGE_DEBUG_PRINT("Converting BGRA indexed surface to RGBA");
auto p = img[imageHandle].offset;
for (size_t i = 0; i < pixels.size(); i++) {
@ -915,7 +907,7 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,
stbi_write_png_compression_level = 100;
if (!stbi_write_png(fileName.c_str(), width, height, sizeof(uint32_t), pixels.data(), 0)) {
IMAGE_DEBUG_PRINT("stbi_write_png() failed");
error(ERROR_ILLEGAL_FUNCTION_CALL);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
}
} break;
@ -928,28 +920,28 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,
if (!qoi_write(fileName.c_str(), pixels.data(), &desc)) {
IMAGE_DEBUG_PRINT("qoi_write() failed");
error(ERROR_ILLEGAL_FUNCTION_CALL);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
}
} break;
case SaveFormat::BMP: {
if (!stbi_write_bmp(fileName.c_str(), width, height, sizeof(uint32_t), pixels.data())) {
IMAGE_DEBUG_PRINT("stbi_write_bmp() failed");
error(ERROR_ILLEGAL_FUNCTION_CALL);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
}
} break;
case SaveFormat::TGA: {
if (!stbi_write_tga(fileName.c_str(), width, height, sizeof(uint32_t), pixels.data())) {
IMAGE_DEBUG_PRINT("stbi_write_tga() failed");
error(ERROR_ILLEGAL_FUNCTION_CALL);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
}
} break;
case SaveFormat::JPG: {
if (!stbi_write_jpg(fileName.c_str(), width, height, sizeof(uint32_t), pixels.data(), 100)) {
IMAGE_DEBUG_PRINT("stbi_write_jpg() failed");
error(ERROR_ILLEGAL_FUNCTION_CALL);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
}
} break;
@ -974,12 +966,12 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,
if (!stbi_write_hdr(fileName.c_str(), width, height, HDRComponents, HDRPixels.data())) {
IMAGE_DEBUG_PRINT("stbi_write_hdr() failed");
error(ERROR_ILLEGAL_FUNCTION_CALL);
error(QB_ERROR_ILLEGAL_FUNCTION_CALL);
}
} break;
default:
IMAGE_DEBUG_PRINT("Save handler not implemented");
error(ERROR_INTERNAL_ERROR);
error(QB_ERROR_INTERNAL_ERROR);
}
}

View file

@ -3,11 +3,13 @@
#include "compression.h"
#include "datetime.h"
#include "event.h"
#include "extended_math.h"
#include "filepath.h"
#include "filesystem.h"
#include "font.h"
#include "gui.h"
#include "image.h"
#include "rounding.h"
#include "extended_math.h"
extern int32 func__cinp(int32 toggle,
int32 passed); // Console INP scan code reader
@ -88,14 +90,9 @@ void requestKeyboardOverlayImage(int32 handle) {
// extern functions
extern qbs *func__dir(qbs *context);
extern int32 func__scaledwidth();
extern int32 func__scaledheight();
extern qbs *func__cwd();
extern qbs *func__startdir();
extern void sub__fps(double fps, int32 passed);
extern void sub__resize(int32 on_off, int32 stretch_smooth);
@ -178,8 +175,6 @@ extern int32 func_windowexists();
extern int32 func_screenicon();
extern int32 func_screenwidth();
extern int32 func_screenheight();
extern int32 func__borderwidth();
extern int32 func__titlebarheight();
extern void sub_screenicon();
extern void sub__console(int32);
extern int32 func__console();
@ -191,8 +186,6 @@ extern int32 func__hasfocus();
extern void set_foreground_window(ptrszint i);
extern qbs *func__title();
extern int32 func__handle();
extern int32 func__fileexists(qbs *);
extern int32 func__direxists(qbs *);
extern int32 func_stick(int32 i, int32 axis_group, int32 passed);
extern int32 func_strig(int32 i, int32 controller, int32 passed);
extern void sub__maptriangle(int32 cull_options, float sx1, float sy1,
@ -224,7 +217,6 @@ extern void field_get(int32 fileno, int64 seekpos, int32 passed);
extern void field_put(int32 fileno, int64 seekpos, int32 passed);
extern int32 func__keydown(int32 x);
extern int32 func__keyhit();
extern void sub_files(qbs *str, int32 passed);
extern int32 func_lpos(int32);
extern void sub__printimage(int32 i);
extern float func__mousemovementx(int32 context, int32 passed);
@ -246,7 +238,6 @@ extern qbs *func__clipboard();
extern int32 func__clipboardimage();
extern void sub__clipboardimage(int32 src);
extern int32 func__exit();
extern char *fixdir(qbs *);
extern void revert_input_check();
extern int32 func__openhost(qbs *);
extern int32 func__openconnection(int32);
@ -276,7 +267,6 @@ extern void sub__putimage(double f_dx1, double f_dy1, double f_dx2,
double f_sy1, double f_sx2, double f_sy2,
int32 passed);
extern int32 selectfont(int32 f, img_struct *im);
extern void sndsetup();
extern uint32 sib();
extern uint32 sib_mod0();
extern uint8 *rm8();
@ -299,10 +289,6 @@ extern void sub_defseg(int32 segment, int32 passed);
extern int32 func_peek(int32 offset);
extern void sub_poke(int32 offset, int32 value);
extern void more_return_points();
extern void qb64_generatesound(double f, double l, uint8 wait);
extern uint8 *soundwave(double frequency, double length, double volume,
double fadein, double fadeout);
extern int32 wavesize(double length);
extern qbs *qbs_new_descriptor();
extern void qbs_free_descriptor(qbs *str);
extern void qbs_free(qbs *str);
@ -524,14 +510,8 @@ extern qbs *func_input(int32 n, int32 i, int32 passed);
extern int32 func__statusCode(int32 handle);
extern double func_sqr(double value);
extern void snd_check();
extern qbs *func_command(int32 index, int32 passed);
extern int32 func__commandcount();
extern void sub_kill(qbs *str);
extern void sub_name(qbs *oldname, qbs *newname);
extern void sub_chdir(qbs *str);
extern void sub_mkdir(qbs *str);
extern void sub_rmdir(qbs *str);
extern long double pow2(long double x, long double y);
extern int32 func_freefile();
extern void sub__mousehide();
@ -1333,8 +1313,8 @@ void sub_chain(qbs *f) {
extensions_ready:
// normalize dir slashes
fixdir(f_exe);
fixdir(f_bas);
filepath_fix_directory(f_exe);
filepath_fix_directory(f_bas);
// get path (strip paths from f_exe & f_bas)
f_path->len = 0;

View file

@ -625,6 +625,29 @@ id.specialformat = "[?]"
id.hr_syntax = "FILES fileSpec$"
regid
clearid
id.n = qb64prefix$ + "Files"
id.musthave = "$"
id.subfunc = 1
id.callname = "func__files"
id.args = 1
id.arg = MKL$(STRINGTYPE - ISPOINTER)
id.specialformat = "[?]"
id.ret = STRINGTYPE - ISPOINTER
id.hr_syntax = "_FILES$([fileSpec$])"
regid
clearid
id.n = qb64prefix$ + "FullPath"
id.musthave = "$"
id.subfunc = 1
id.callname = "func__fullpath"
id.args = 1
id.arg = MKL$(STRINGTYPE - ISPOINTER)
id.ret = STRINGTYPE - ISPOINTER
id.hr_syntax = "_FULLPATH$(pathName$)"
regid
clearid
id.n = qb64prefix$ + "PrintImage": id.Dependency = DEPENDENCY_PRINTER
id.subfunc = 2

View file

@ -5,5 +5,5 @@ listOfKeywords$ = listOfKeywords$ + "_GLCOPYTEXSUBIMAGE2D@_GLCULLFACE@_GLDELETEL
listOfKeywords$ = listOfKeywords$ + "_GLPOPATTRIB@_GLPOPCLIENTATTRIB@_GLPOPMATRIX@_GLPOPNAME@_GLPRIORITIZETEXTURES@_GLPUSHATTRIB@_GLPUSHCLIENTATTRIB@_GLPUSHMATRIX@_GLPUSHNAME@_GLRASTERPOS2D@_GLRASTERPOS2DV@_GLRASTERPOS2F@_GLRASTERPOS2FV@_GLRASTERPOS2I@_GLRASTERPOS2IV@_GLRASTERPOS2S@_GLRASTERPOS2SV@_GLRASTERPOS3D@_GLRASTERPOS3DV@_GLRASTERPOS3F@_GLRASTERPOS3FV@_GLRASTERPOS3I@_GLRASTERPOS3IV@_GLRASTERPOS3S@_GLRASTERPOS3SV@_GLRASTERPOS4D@_GLRASTERPOS4DV@_GLRASTERPOS4F@_GLRASTERPOS4FV@_GLRASTERPOS4I@_GLRASTERPOS4IV@_GLRASTERPOS4S@_GLRASTERPOS4SV@_GLREADBUFFER@_GLREADPIXELS@_GLRECTD@_GLRECTDV@_GLRECTF@_GLRECTFV@_GLRECTI@_GLRECTIV@_GLRECTS@_GLRECTSV@_GLRENDERMODE@_GLROTATED@_GLROTATEF@_GLSCALED@_GLSCALEF@_GLSCISSOR@_GLSELECTBUFFER@_GLSHADEMODEL@_GLSTENCILFUNC@_GLSTENCILMASK@_GLSTENCILOP@_GLTEXCOORD1D@_GLTEXCOORD1DV@_GLTEXCOORD1F@_GLTEXCOORD1FV@_GLTEXCOORD1I@_GLTEXCOORD1IV@_GLTEXCOORD1S@_GLTEXCOORD1SV@_GLTEXCOORD2D@_GLTEXCOORD2DV@_GLTEXCOORD2F@_GLTEXCOORD2FV@_GLTEXCOORD2I@_GLTEXCOORD2IV@_GLTEXCOORD2S@_GLTEXCOORD2SV@_GLTEXCOORD3D@_GLTEXCOORD3DV@_GLTEXCOORD3F@_GLTEXCOORD3FV@_GLTEXCOORD3I@_GLTEXCOORD3IV@_GLTEXCOORD3S@_GLTEXCOORD3SV@_GLTEXCOORD4D@_GLTEXCOORD4DV@_GLTEXCOORD4F@_GLTEXCOORD4FV@_GLTEXCOORD4I@_GLTEXCOORD4IV@_GLTEXCOORD4S@_GLTEXCOORD4SV@_GLTEXCOORDPOINTER@_GLTEXENVF@_GLTEXENVFV@_GLTEXENVI@_GLTEXENVIV@_GLTEXGEND@_GLTEXGENDV@_GLTEXGENF@_GLTEXGENFV@_GLTEXGENI@_GLTEXGENIV@_GLTEXIMAGE1D@_GLTEXIMAGE2D@_GLTEXPARAMETERF@_GLTEXPARAMETERFV@_GLTEXPARAMETERI@_GLTEXPARAMETERIV@_GLTEXSUBIMAGE1D@_GLTEXSUBIMAGE2D@_GLTRANSLATED@_GLTRANSLATEF@_GLVERTEX2D@_GLVERTEX2DV@_GLVERTEX2F@_GLVERTEX2FV@_GLVERTEX2I@_GLVERTEX2IV@_GLVERTEX2S@_GLVERTEX2SV@_GLVERTEX3D@_GLVERTEX3DV@_GLVERTEX3F@_GLVERTEX3FV@_GLVERTEX3I@_GLVERTEX3IV@_GLVERTEX3S@_GLVERTEX3SV@_GLVERTEX4D@_GLVERTEX4DV@_GLVERTEX4F@_GLVERTEX4FV@_GLVERTEX4I@_GLVERTEX4IV@_GLVERTEX4S@_GLVERTEX4SV@_GLVERTEXPOINTER@_GLVIEWPORT@SMOOTH@STRETCH@_ANTICLOCKWISE@_BEHIND@_CLEAR@_FILLBACKGROUND@_GLUPERSPECTIVE@_HARDWARE@_HARDWARE1@_KEEPBACKGROUND@_NONE@_OFF@_ONLY@_ONLYBACKGROUND@_ONTOP@_SEAMLESS@_SMOOTH@_SMOOTHSHRUNK@_SMOOTHSTRETCHED@"
listOfKeywords$ = listOfKeywords$ + "_SOFTWARE@_SQUAREPIXELS@_STRETCH@_ALLOWFULLSCREEN@_ALL@_ECHO@_INSTRREV@_TRIM$@_ACCEPTFILEDROP@_FINISHDROP@_TOTALDROPPEDFILES@_DROPPEDFILE@_DROPPEDFILE$@_SHR@_SHL@_ROR@_ROL@"
listOfKeywords$ = listOfKeywords$ + "_DEFLATE$@_INFLATE$@_READBIT@_RESETBIT@_SETBIT@_TOGGLEBIT@$ASSERTS@_ASSERT@_CAPSLOCK@_NUMLOCK@_SCROLLLOCK@_TOGGLE@_CONSOLEFONT@_CONSOLECURSOR@_CONSOLEINPUT@_CINP@$NOPREFIX@$COLOR@$DEBUG@$EMBED@_EMBEDDED$@_ENVIRONCOUNT@$UNSTABLE@$MIDISOUNDFONT@"
listOfKeywords$ = listOfKeywords$ + "_NOTIFYPOPUP@_MESSAGEBOX@_INPUTBOX$@_SELECTFOLDERDIALOG$@_COLORCHOOSERDIALOG@_OPENFILEDIALOG$@_SAVEFILEDIALOG$@_SAVEIMAGE@"
listOfKeywords$ = listOfKeywords$ + "_NOTIFYPOPUP@_MESSAGEBOX@_INPUTBOX$@_SELECTFOLDERDIALOG$@_COLORCHOOSERDIALOG@_OPENFILEDIALOG$@_SAVEFILEDIALOG$@_SAVEIMAGE@_FILES$@_FULLPATH$@"
listOfKeywords$ = listOfKeywords$ + "_STATUSCODE@_SNDNEW@_SCALEDWIDTH@_SCALEDHEIGHT@_UFONTHEIGHT@_UPRINTWIDTH@_ULINESPACING@_UPRINTSTRING@_UCHARPOS@"

View file

@ -0,0 +1,59 @@
$CONSOLE:ONLY
OPTION _EXPLICIT
ON ERROR GOTO test_failed
CHDIR _STARTDIR$
PRINT "Creating directory temp_dir"
MKDIR "temp_dir"
PRINT "Changing to directory temp_dir"
CHDIR "temp_dir"
PRINT "Changing to parent directory"
CHDIR ".."
PRINT "Renaming temp_dir to dummy_dir"
NAME "temp_dir" AS "dummy_dir"
PRINT "_DIREXISTS(dummy_dir):"; _DIREXISTS("./dummy_dir")
PRINT "Creating a temporary file inside dummy_dir"
DIM fileName AS STRING: fileName = CreateDummyFile$("./dummy_dir/")
PRINT "_FILEEXISTS(fileName):"; _FILEEXISTS(fileName)
PRINT "Deleting fileName"
KILL fileName
PRINT "Creating 10 dummy files inside dummy_dir"
DIM i AS LONG: FOR i = 0 TO 9
fileName = CreateDummyFile$("./dummy_dir/")
NEXT i
PRINT "Deleting all 10 dummy files"
KILL "./dummy_dir/*.tmp"
PRINT "Deleting dummy_dir"
RMDIR "dummy_dir"
SYSTEM
test_failed:
PRINT "Test failed!"
SYSTEM 1
FUNCTION CreateDummyFile$ (directory AS STRING)
DO
DIM fileName AS STRING: fileName = directory + LTRIM$(STR$(100! * (TIMER + RND))) + ".tmp"
LOOP WHILE _FILEEXISTS(fileName)
DIM h AS LONG: h = FREEFILE
OPEN fileName FOR OUTPUT AS h
PRINT #h, "Delete me!"
CLOSE h
CreateDummyFile = fileName
END FUNCTION

View file

@ -0,0 +1,11 @@
Creating directory temp_dir
Changing to directory temp_dir
Changing to parent directory
Renaming temp_dir to dummy_dir
_DIREXISTS(dummy_dir):-1
Creating a temporary file inside dummy_dir
_FILEEXISTS(fileName):-1
Deleting fileName
Creating 10 dummy files inside dummy_dir
Deleting all 10 dummy files
Deleting dummy_dir