From 9e95c1fab04e8e678b956845f8f11947e7d6baba Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Sun, 18 Feb 2024 17:34:47 -0600 Subject: [PATCH] Overlay work. --- .gitignore | 1 + examples/overlay/CMakeLists.txt | 54 ++++ examples/overlay/build.sh | 46 ++++ examples/overlay/f256.ld | 96 +++++++ examples/overlay/foenixmgr.ini | 7 + examples/overlay/overlay.c | 41 +++ .../mos-platform/CMakeLists.txt | 4 +- tools/overlay/src/overlay.c | 240 ++++++++++++++---- tools/shared/util.c | 137 ++++++++++ tools/shared/util.h | 9 + 10 files changed, 583 insertions(+), 52 deletions(-) create mode 100644 examples/overlay/CMakeLists.txt create mode 100755 examples/overlay/build.sh create mode 100644 examples/overlay/f256.ld create mode 100644 examples/overlay/foenixmgr.ini create mode 100644 examples/overlay/overlay.c diff --git a/.gitignore b/.gitignore index e854154..44db582 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ software/ *.sym *.o makemap +**/.builddir # Crap for CrossOver .windows-serial diff --git a/examples/overlay/CMakeLists.txt b/examples/overlay/CMakeLists.txt new file mode 100644 index 0000000..788ba1d --- /dev/null +++ b/examples/overlay/CMakeLists.txt @@ -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 +#) diff --git a/examples/overlay/build.sh b/examples/overlay/build.sh new file mode 100755 index 0000000..02e3e9d --- /dev/null +++ b/examples/overlay/build.sh @@ -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 diff --git a/examples/overlay/f256.ld b/examples/overlay/f256.ld new file mode 100644 index 0000000..0dc74ee --- /dev/null +++ b/examples/overlay/f256.ld @@ -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) +} diff --git a/examples/overlay/foenixmgr.ini b/examples/overlay/foenixmgr.ini new file mode 100644 index 0000000..c6ab319 --- /dev/null +++ b/examples/overlay/foenixmgr.ini @@ -0,0 +1,7 @@ +[DEFAULT] +port=/dev/ttyUSB1 +labels=sample.lbl +flash_address=380000 +chunk_size=1024 +cpu=65c02 +data_rate=6000000 \ No newline at end of file diff --git a/examples/overlay/overlay.c b/examples/overlay/overlay.c new file mode 100644 index 0000000..5e4643c --- /dev/null +++ b/examples/overlay/overlay.c @@ -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 + + +#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; +} diff --git a/llvm-mos-sdk-patches/mos-platform/CMakeLists.txt b/llvm-mos-sdk-patches/mos-platform/CMakeLists.txt index cc2d103..60bf2b8 100644 --- a/llvm-mos-sdk-patches/mos-platform/CMakeLists.txt +++ b/llvm-mos-sdk-patches/mos-platform/CMakeLists.txt @@ -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) diff --git a/tools/overlay/src/overlay.c b/tools/overlay/src/overlay.c index ecfb528..05fc7be 100644 --- a/tools/overlay/src/overlay.c +++ b/tools/overlay/src/overlay.c @@ -25,6 +25,7 @@ #include #include #include +#include #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_" - found = false; - if (pos > strlen(segDef)) { - found = true; - for (x=0; x 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_" + found = false; + if (pos > strlen(segDef)) { + found = true; + for (x=0; x 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; xd_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; } diff --git a/tools/shared/util.c b/tools/shared/util.c index 40c5720..6f6e815 100644 --- a/tools/shared/util.c +++ b/tools/shared/util.c @@ -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(©, 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); diff --git a/tools/shared/util.h b/tools/shared/util.h index 4727911..23e99ed 100644 --- a/tools/shared/util.h +++ b/tools/shared/util.h @@ -26,6 +26,11 @@ #include +#include +#include + + +#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);