Overlay work.

This commit is contained in:
Scott Duensing 2024-02-18 17:34:47 -06:00
parent a5ef53c7b6
commit 9e95c1fab0
10 changed files with 583 additions and 52 deletions

1
.gitignore vendored
View file

@ -15,6 +15,7 @@ software/
*.sym
*.o
makemap
**/.builddir
# Crap for CrossOver
.windows-serial

View file

@ -0,0 +1,54 @@
#
# Copyright (c) 2024 Scott Duensing, scott@kangaroopunch.com
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# This is only to make my IDE happy.
# We can't actually build with it until I get llvm-mos integrated into
# toolchains. -- SCD
cmake_minimum_required(VERSION 3.22)
set(CMAKE_C_STANDARD 17)
project(overlay)
set(DEFINES ${CMAKE_SOURCE_DIR}/../../include)
set(F256LIB ${CMAKE_SOURCE_DIR}/../../f256lib)
set(OVERLAY_SOURCE
${F256LIB}/f256.h
${F256LIB}/f256.c
overlay.c
)
add_executable(${CMAKE_PROJECT_NAME}
${OVERLAY_SOURCE}
)
target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC
${CMAKE_SOURCE_DIR}
${DEFINES}
${F256LIB}
)
#target_link_libraries(${CMAKE_PROJECT_NAME}
# -lm
#)

46
examples/overlay/build.sh Executable file
View file

@ -0,0 +1,46 @@
#!/bin/bash -ex
#
# Copyright (c) 2024 Scott Duensing, scott@kangaroopunch.com
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
PROJECT=overlay
F256=$(pwd)/../..
LLVM=${F256}/llvm-mos
PATH=${LLVM}/bin:${PATH}
CLANG="mos-f256k-clang -I${F256}/include -I${F256}/f256lib -Os"
${CLANG} -c ${PROJECT}.c
${CLANG} -T f256.ld \
-Wl,-Map=overlay.map \
-o ${PROJECT} \
${PROJECT}.o
mv ${PROJECT} ${PROJECT}.pgz
llvm-nm ${PROJECT}.elf > ${PROJECT}.sym
llvm-objdump -d --print-imm-hex ${PROJECT}.elf > ${PROJECT}.lst
hexdump -C ${PROJECT}.pgz > ${PROJECT}.hex
python ${F256}/pgz-thunk.py ${PROJECT}.pgz

96
examples/overlay/f256.ld Normal file
View file

@ -0,0 +1,96 @@
/* fake C Stack */
PROVIDE(__stack = 0xA000);
/* entry point to my program */
PROVIDE(__f256_start = 0x300);
/* page size of a block of memory */
PROVIDE(__BLOCK_SIZE = 0x2000);
/* swappable block address */
PROVIDE(__SLOT_ADDR = 0xA000);
/* f256k uses first 16 bytes of ZP for mmu control? */
__rc0 = 0x10;
INCLUDE imag-regs.ld
ASSERT(__rc0 == 0x10, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x2f, "Inconsistent zero page map.")
MEMORY {
/* kernel uses 0xf0-0xff for parameter passing */
zp : ORIGIN = __rc31 + 1, LENGTH = 0xF0 - (__rc31 + 1)
ram (rw) : ORIGIN = __f256_start, LENGTH = 0xA000-__f256_start
}
/* LMAs */
__block8_lma = ( 8<<24)|__SLOT_ADDR;
__block9_lma = ( 9<<24)|__SLOT_ADDR;
__block10_lma = (10<<24)|__SLOT_ADDR;
__block11_lma = (11<<24)|__SLOT_ADDR;
__block12_lma = (12<<24)|__SLOT_ADDR;
__block13_lma = (13<<24)|__SLOT_ADDR;
__block14_lma = (14<<24)|__SLOT_ADDR;
__block15_lma = (15<<24)|__SLOT_ADDR;
__block16_lma = (16<<24)|__SLOT_ADDR;
__block17_lma = (17<<24)|__SLOT_ADDR;
__block18_lma = (18<<24)|__SLOT_ADDR;
__block19_lma = (19<<24)|__SLOT_ADDR;
__block20_lma = (20<<24)|__SLOT_ADDR;
__block21_lma = (21<<24)|__SLOT_ADDR;
__block22_lma = (22<<24)|__SLOT_ADDR;
__block23_lma = (23<<24)|__SLOT_ADDR;
MEMORY {
block8 : ORIGIN = __block8_lma, LENGTH = __BLOCK_SIZE
block9 : ORIGIN = __block9_lma, LENGTH = __BLOCK_SIZE
block10 : ORIGIN = __block10_lma, LENGTH = __BLOCK_SIZE
block11 : ORIGIN = __block11_lma, LENGTH = __BLOCK_SIZE
block12 : ORIGIN = __block12_lma, LENGTH = __BLOCK_SIZE
block13 : ORIGIN = __block13_lma, LENGTH = __BLOCK_SIZE
block14 : ORIGIN = __block14_lma, LENGTH = __BLOCK_SIZE
block15 : ORIGIN = __block15_lma, LENGTH = __BLOCK_SIZE
block16 : ORIGIN = __block16_lma, LENGTH = __BLOCK_SIZE
block17 : ORIGIN = __block17_lma, LENGTH = __BLOCK_SIZE
block18 : ORIGIN = __block18_lma, LENGTH = __BLOCK_SIZE
block19 : ORIGIN = __block19_lma, LENGTH = __BLOCK_SIZE
block20 : ORIGIN = __block20_lma, LENGTH = __BLOCK_SIZE
block21 : ORIGIN = __block21_lma, LENGTH = __BLOCK_SIZE
block22 : ORIGIN = __block22_lma, LENGTH = __BLOCK_SIZE
block23 : ORIGIN = __block23_lma, LENGTH = __BLOCK_SIZE
}
REGION_ALIAS("c_writeable", ram)
REGION_ALIAS("c_readonly", ram)
SECTIONS {
INCLUDE c.ld
.block8 : { *(.block8 .block8.*) } >block8 end_block8 = .;
.block9 : { *(.block9 .block9.*) } >block9 end_block9 = .;
.block10 : { *(.block10 .block10.*) } >block10 end_block10 = .;
.block11 : { *(.block11 .block11.*) } >block11 end_block11 = .;
.block12 : { *(.block12 .block12.*) } >block12 end_block12 = .;
.block13 : { *(.block13 .block13.*) } >block13 end_block13 = .;
.block14 : { *(.block14 .block14.*) } >block14 end_block14 = .;
.block15 : { *(.block15 .block15.*) } >block15 end_block15 = .;
.block16 : { *(.block16 .block16.*) } >block16 end_block16 = .;
.block17 : { *(.block17 .block17.*) } >block17 end_block17 = .;
.block18 : { *(.block18 .block18.*) } >block18 end_block18 = .;
.block19 : { *(.block19 .block19.*) } >block19 end_block19 = .;
.block20 : { *(.block20 .block20.*) } >block20 end_block20 = .;
.block21 : { *(.block21 .block21.*) } >block21 end_block21 = .;
.block22 : { *(.block22 .block22.*) } >block22 end_block22 = .;
.block23 : { *(.block23 .block23.*) } >block23 end_block23 = .;
}
OUTPUT_FORMAT {
BYTE(0x5A) /* pgZ */
/* ram segment */
SHORT(ORIGIN(ram)) /* where to load it, 24 bits */
BYTE(0x00)
SHORT(__bss_start-ORIGIN(ram)) /* size to load */
BYTE(0x00)
TRIM(ram)
INCLUDE output.ld
/* Launch the program, at _start */
SHORT(_start)
LONG(0)
}

View file

@ -0,0 +1,7 @@
[DEFAULT]
port=/dev/ttyUSB1
labels=sample.lbl
flash_address=380000
chunk_size=1024
cpu=65c02
data_rate=6000000

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2024 Scott Duensing, scott@kangaroopunch.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "f256.h"
#include "f256.c"
#include <stdarg.h>
#define test(...) ( *(volatile byte *)(0x000D) = 8; REAL_test(__VA_ARGS__); *(volatile byte *)(0x000D) = 5; )
void test(int arg1, int arg2) {
}
int main(int argc, char *argv[]) {
test(test);
return 0;
}

View file

@ -51,8 +51,8 @@ add_subdirectory(atari2600-4k)
add_subdirectory(atari2600-3e)
add_subdirectory(atari8-common)
add_subdirectory(atari8-dos)
add_subdirectory(atari8-stdcart)
add_subdirectory(atari8-xegs)
add_subdirectory(atari8-cart-std)
add_subdirectory(atari8-cart-xegs)
add_subdirectory(commodore)
add_subdirectory(c64)
add_subdirectory(c128)

View file

@ -25,6 +25,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <dirent.h>
#include "util.h"
@ -39,7 +40,7 @@ char *_bankName[BLOCK_COUNT]; // Far blocks (8+), shifted down 8.
void findBank(char *name);
void parseCFile(char *filename, FILE *trampoline, int swapSlot);
void parseCFile(char *filename, char *targetFile, FILE *trampoline, char *trampolineFile, int swapSlot);
void trimEnd(char *string);
@ -72,24 +73,32 @@ void findBank(char *name) {
}
void parseCFile(char *filename, FILE *trampoline, int swapSlot) {
void parseCFile(char *filename, char *targetFile, FILE *trampoline, char *trampolineFile, int swapSlot) {
FILE *in;
FILE *out;
int c;
int brackets = 0;
int pos = 0;
char buffer[BUFFER_SIZE];
char *temp;
char *newCFile;
char *start;
char *b;
bool found;
char *segDef = "#define SEGMENT_";
int x;
int brackets = 0;
int crSinceStart = 0;
int pos = 0;
char *segDef = "#define SEGMENT_";
temp = utilGetLastPathComponent(filename);
newCFile = utilReplaceExtension(temp, ".tc");
free(temp);
/*
* This parser sucks.
*
* - It only handles C, not C++.
* - It should scan for function names backwards from the '(' and not by skipping possible modifiers like 'static' and 'struct'.
* - It can only handle single-line function definitions with the opening '{' on the same line as the function.
* - It can be borked by comments.
* - It always generates trampolines, not just when they're needed.
*
* Someone should fix it. :-)
*/
in = fopen(filename, "rt");
if (in == NULL) {
@ -98,47 +107,42 @@ void parseCFile(char *filename, FILE *trampoline, int swapSlot) {
exit(1);
}
out = fopen(newCFile, "wt");
out = fopen(targetFile, "wt");
if (out == NULL) {
fclose(in);
fclose(trampoline);
fprintf(stderr, "Cannot create %s!\n", newCFile);
free(newCFile);
fprintf(stderr, "Cannot create %s!\n", targetFile);
free(targetFile);
exit(1);
}
printf("Processing %s -> %s...\n", filename, targetFile);
// Add trampoline include automatically.
fprintf(out, "#include \"%s\"\n\n", trampolineFile);
do {
// Read next byte from C input file.
if ((c = fgetc(in)) == EOF) break;
// Count brackets so we know if we're inside a function or not.
if (c == '{') brackets++;
if (c == '{') {
brackets++;
crSinceStart = 0;
}
if (c == '}') brackets--;
// End of line?
if ((c == 13) || (c == 10)) {
crSinceStart++;
// End the line and trim the tail.
buffer[pos] = 0;
trimEnd(buffer);
if (brackets != 1) {
// Is this a segment definition? "#define SEGMENT_<something>"
found = false;
if (pos > strlen(segDef)) {
found = true;
for (x=0; x<strlen(segDef); x++) {
if (buffer[x] != segDef[x]) {
found = false;
break;
}
}
}
if (found) {
// New segment!
findBank(&buffer[strlen(segDef)]);
} else {
// Just barf it out again.
fprintf(out, "%s\n", buffer);
}
} else {
if ((brackets == 1) && (crSinceStart == 1)) {
// Is the last character a '{'? If we're not in a far memory bank, short-circuit this entire mess.
if ((_currentBank > 0) && (buffer[strlen(buffer) - 1] == '{')) {
// Remove it and any spaces.
@ -175,12 +179,12 @@ void parseCFile(char *filename, FILE *trampoline, int swapSlot) {
// Write out new function definition.
fprintf(out, "%s FAR%d_%s {\n", buffer, 8, start);
// Create trampoline macro.
fprintf(trampoline, "#define %s(...) ({\n"
"\t\tunsigned char ___mmu = (unsigned char)*(volatile unsigned char *)%#06x;\n"
"\t\t*(volatile unsigned char *)%#06x = %d;\n"
"\t\tFAR%d_%s(__VA_ARGS__);\n"
"\t\t*(volatile unsigned char *)%#06x = ___mmu;\n"
"\t})\n\n", temp, swapSlot, swapSlot, _currentBank, _currentBank, temp, swapSlot);
fprintf(trampoline, "#define %s(...) ({ \\\n"
"\t\tunsigned char ___mmu = (unsigned char)*(volatile unsigned char *)%#06x; \\\n"
"\t\t*(volatile unsigned char *)%#06x = %d; \\\n"
"\t\tFAR%d_%s(__VA_ARGS__); \\\n"
"\t\t*(volatile unsigned char *)%#06x = ___mmu; \\\n"
"\t})\n\n", temp, swapSlot, swapSlot, _currentBank, _currentBank, temp, swapSlot);
free(temp);
} else {
// Not a function. Write as is. (Put the '{' back.)
@ -190,20 +194,46 @@ void parseCFile(char *filename, FILE *trampoline, int swapSlot) {
// Didn't find '{'.
fprintf(out, "%s\n", buffer);
}
}
fflush(out);
} else { // Brackets
// Is this a segment definition? "#define SEGMENT_<something>"
found = false;
if (pos > strlen(segDef)) {
found = true;
for (x=0; x<strlen(segDef); x++) {
if (buffer[x] != segDef[x]) {
found = false;
break;
}
}
}
if (found) {
// New segment!
findBank(&buffer[strlen(segDef)]);
}
// Write to output in case they depend on the define for some odd reason.
fprintf(out, "%s\n", buffer);
} // Brackets.
// Reset buffer for next line.
pos = 0;
} else {
// Makes debugging nicer.
fflush(out);
} else { // EOL
// Add to buffer.
buffer[pos++] = c;
}
} // EOL
} while (1);
fclose(in);
free(newCFile);
}
@ -224,27 +254,135 @@ void trimEnd(char *string) {
int main(int argc, char *argv[]) {
FILE *out;
int x;
int nearSlot;
char *targetDir;
char *sourceDir;
char *trampolineFile;
char *linkerFile;
char *cFile;
char *targetFile;
char *thisDir;
int sourceDirOffset;
int thisOffset;
DIR *dir;
struct dirent *dirent;
/*
* Command line:
*
* ./overlay [nearSlot#] [targetDir] [sourceDir1] ... {sourceDirX}
*
*/
if (argc < 4) {
printf("Usage: %s [nearSlot#] [targetDir] [sourceDir1] ... {sourceDirX}\n", argv[0]);
return 1;
}
// Find near memory slot to swap into.
nearSlot = atoi(argv[1]);
if (nearSlot > 7) {
printf("ERROR! Only slots 0-7 are in near memory.\n");
return 1;
}
if (nearSlot == 0) {
printf("WARNING! Slot 0 contains the zero page, stack, and MMU!\n");
}
if ((nearSlot == 6) || (nearSlot == 7)) {
printf("WARNING! Slots 6 and 7 contain the microkernel!\n");
}
// Does the target directory exist?
targetDir = strdup(argv[2]);
utilFixPathSeparators(&targetDir, false);
if (!utilMkDirP(targetDir, 0777)) {
printf("ERROR! Cannot create target directory %s!\n", targetDir);
return 1;
}
// Create trampoline for all C files.
out = fopen("trampoline.h", "wt");
trampolineFile = utilCreateString("%s%ctrampoline.h", targetDir, utilGetPathSeparator());
out = fopen(trampolineFile, "wt");
if (out == NULL) {
fprintf(stderr, "Cannot create trampoline.h!\n");
fprintf(stderr, "ERROR! Cannot create %s!\n", trampolineFile);
free(trampolineFile);
free(targetDir);
return 1;
}
fprintf(out, "// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.\n\n");
fprintf(out, "#ifndef TRAMPOLINE_H\n");
fprintf(out, "#define TRAMPOLINE_H\n\n");
// Find common inital path so we can remove it later.
sourceDir = strdup(argv[x]);
utilFixPathSeparators(&sourceDir, true);
sourceDirOffset = strlen(sourceDir);
if (argc > 4) {
for (x=4; x<argc; x++) {
thisOffset = 0;
thisDir = strdup(argv[x]);
utilFixPathSeparators(&thisDir, true);
for (thisOffset=0; thisOffset < (strlen(sourceDir) < strlen(thisDir) ? strlen(sourceDir) : strlen(thisDir)); thisOffset++) {
if (sourceDir[thisOffset] != thisDir[thisOffset]) break;
}
if (thisOffset < sourceDirOffset) sourceDirOffset = thisOffset - 1;
free(thisDir);
}
}
free(sourceDir);
// Do all the C files in a project, one at a time.
parseCFile("/home/scott/code/f256/tools/overlay/interpreter.c", out, 0x000D);
for (x=3; x<argc; x++) {
sourceDir = strdup(argv[x]);
utilFixPathSeparators(&sourceDir, true);
if ((dir = opendir(sourceDir)) == NULL) {
fprintf(stderr, "ERROR! Cannot open directory %s!\n", argv[x]);
free(trampolineFile);
free(targetDir);
free(sourceDir);
return 1;
}
for (;;) {
if ((dirent = readdir(dir)) == NULL) break;
if ((dirent->d_type == DT_LNK) || (dirent->d_type == DT_REG)) {
// Is this a C file?
if ((dirent->d_name[strlen(dirent->d_name) - 2] == '.') && (dirent->d_name[strlen(dirent->d_name) - 1] == 'c')) {
cFile = utilCreateString("%s%s", sourceDir, dirent->d_name);
targetFile = utilCreateString("%s%s%s", targetDir, &sourceDir[sourceDirOffset], dirent->d_name);
thisDir = utilGetUpToLastPathComponent(targetFile);
utilFixPathSeparators(&thisDir, false);
if (!utilMkDirP(thisDir, 0777)) {
printf("ERROR! Cannot create target directory %s!\n", thisDir);
free(thisDir);
free(trampolineFile);
free(targetDir);
free(sourceDir);
free(cFile);
closedir(dir);
return 1;
}
free(thisDir);
parseCFile(cFile, targetFile, out, trampolineFile, nearSlot + 8);
free(cFile);
free(targetFile);
}
}
}
closedir(dir);
free(sourceDir);
}
fprintf(out, "#endif // TRAMPOLINE_H\n");
fclose(out);
free(trampolineFile);
// Generate linker script data.
out = fopen("output.ld", "wt");
linkerFile = utilCreateString("%s%coutput.ld", targetDir, utilGetPathSeparator());
out = fopen(linkerFile, "wt");
if (out == NULL) {
fprintf(stderr, "Cannot create output.ld!\n");
fprintf(stderr, "ERROR! Cannot create %s!\n", linkerFile);
free(targetDir);
free(linkerFile);
return 1;
}
for (x=0; x<_totalBanks; x++) {
@ -255,11 +393,13 @@ int main(int argc, char *argv[]) {
fprintf(out, " TRIM(block%d)\n\n", x+8);
}
fclose(out);
free(linkerFile);
// Clean up.
for (x=0; x<_totalBanks; x++) {
free(_bankName[x]);
}
free(targetDir);
return 0;
}

View file

@ -60,6 +60,52 @@ char *utilCreateStringVArgs(char *format, va_list args) {
}
void utilFixPathSeparators(char **path, bool slash) {
int32_t i = 0;
int32_t j = 0;
char *work = *path;
char *temp = NULL;
// Flip path separators to whatever our OS wants & remove repeated separators.
while (work[i] != 0) {
// Correct separator
if (work[i] == '\\' || work[i] == '/') {
// Was the prior character a seprator?
if (j == 0) {
work[j++] = utilGetPathSeparator();
} else {
if (work[j - 1] != utilGetPathSeparator()) {
// No, accept it.
work[j++] = utilGetPathSeparator();
}
}
} else {
work[j++] = work[i];
}
i++;
}
work[j] = 0;
if (slash) {
// Does this string end with a path separator?
if (work[strlen(work) - 1] != utilGetPathSeparator()) {
// No - append one.
temp = strdup(work);
free(work);
work = malloc(sizeof(char) * (strlen(temp) + 2));
strcpy(work, temp);
work[strlen(temp)] = utilGetPathSeparator();
work[strlen(temp) + 1] = 0;
free(temp);
}
}
*path = work;
}
char *utilGetLastPathComponent(char *pathname) {
static char *start;
int32_t x;
@ -77,6 +123,97 @@ char *utilGetLastPathComponent(char *pathname) {
}
char utilGetPathSeparator(void) {
#ifdef _WIN32
return '\\';
#else
return '/';
#endif
}
char *utilGetUpToLastPathComponent(char *pathname) {
static char *copy = NULL;
bool dumb = false; // Using (copy == NULL) below didn't work after optimizations, so enter the dummy.
int32_t x;
x = (int32_t)(strlen(pathname) - strlen(utilGetLastPathComponent(pathname))) - 1;
if (x < 0) x = 0;
if (dumb) {
free(copy);
copy = NULL;
} else {
dumb = true;
}
copy = strdup(pathname);
copy[x] = 0;
utilFixPathSeparators(&copy, true);
return copy;
}
bool utilMkDirP(const char *dir, const mode_t mode) {
char tmp[UTIL_PATH_MAX];
char *p = NULL;
struct stat sb;
size_t len;
// Make copy of dir.
len = strnlen(dir, UTIL_PATH_MAX);
if (len == 0 || len == UTIL_PATH_MAX) {
return -1;
}
memcpy(tmp, dir, len);
tmp[len] = '\0';
// Remove trailing slash.
if (tmp[len - 1] == utilGetPathSeparator()) {
tmp[len - 1] = '\0';
}
// Does it already exist?
if (stat(tmp, &sb) == 0) {
if (S_ISDIR (sb.st_mode)) {
return true;
}
}
// Recursive mkdir.
for (p = tmp + 1; *p; p++) {
if (*p == utilGetPathSeparator()) {
*p = 0;
if (stat(tmp, &sb) != 0) {
// Does not exist - create it.
if (mkdir(tmp, mode) < 0) {
return false;
}
} else {
if (!S_ISDIR(sb.st_mode)) {
// Not a directory
return false;
}
}
*p = utilGetPathSeparator();
}
}
// Check path
if (stat(tmp, &sb) != 0) {
// Does not exist - create it.
if (mkdir(tmp, mode) < 0) {
return false;
}
} else {
if (!S_ISDIR(sb.st_mode)) {
// Not a directory
return false;
}
}
return true;
}
char *utilReplaceExtension(char *org, char *new_ext) {
char *ext;
char *tmp = strdup(org);

View file

@ -26,6 +26,11 @@
#include <stdarg.h>
#include <stdbool.h>
#include <sys/stat.h>
#define UTIL_PATH_MAX 1024
typedef unsigned char byte;
@ -39,7 +44,11 @@ typedef struct colorS {
char *utilCreateString(char *format, ...);
char *utilCreateStringVArgs(char *format, va_list args);
void utilFixPathSeparators(char **path, bool slash);
char *utilGetLastPathComponent(char *pathname);
char utilGetPathSeparator(void);
char *utilGetUpToLastPathComponent(char *pathname);
bool utilMkDirP(const char *dir, const mode_t mode);
char *utilReplaceExtension(char *org, char *new_ext);
int utilStricmp(char *a, char *b);