diff --git a/CMakeLists.txt b/CMakeLists.txt index 63e5a8f..78e5546 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,7 @@ add_executable( src/lib/environ.c src/lib/strextra.c src/lib/readline.c + src/lib/sglob.c src/dos/beep.c src/dos/call.c src/dos/cd.c @@ -67,12 +68,11 @@ add_executable( src/unix/cal.c src/unix/head.c src/unix/hexdump.c - src/unix/tail.c) + src/unix/tail.c + ) if(WIN32) target_sources(fdbox PRIVATE - src/lib/win32/win32-glob.c - src/lib/win32/win32-glob.h src/lib/tc202/stdextra.c ) diff --git a/TCCONFIG.TC b/TCCONFIG.TC index e6b46fc..f49ad88 100644 Binary files a/TCCONFIG.TC and b/TCCONFIG.TC differ diff --git a/fdbox.prj b/fdbox.prj index ae23b43..d7d9011 100644 --- a/fdbox.prj +++ b/fdbox.prj @@ -3,11 +3,11 @@ src/applets.c src/help.c (src/help.h) src/lib/applet.c src/lib/args.c (src/lib/args.h) -src/lib/tc202/dos-glob.c (src/lib/tc202/dos-glob.h) src/lib/tc202/stdextra.c (src/lib/tc202/stdextra.h) src/lib/environ.c (src/lib/environ.h) src/lib/readline.c (src/lib/readline.h) src/lib/strextra.c (src/lib/strextra.h) +src/lib/sglob.c (src/lib/sglob.h) src/dos/beep.c (src/dos/beep.h) src/dos/call.c (src/dos/call.h) src/dos/cd.c (src/dos/cd.c) diff --git a/src/Makefile.dj2 b/src/Makefile.dj2 index be79464..e55f125 100644 --- a/src/Makefile.dj2 +++ b/src/Makefile.dj2 @@ -1,113 +1,123 @@ -# This makefile is for DJGPP - -CFLAGS=-I. -Ilib -Idos -O2 - -OBJECTS=applets.o main.o help.o \ - dos/beep.o \ - dos/call.o \ - dos/cd.o \ - dos/cls.o \ - dos/command.o \ - dos/copymove.o \ - dos/datetime.o \ - dos/del.o \ - dos/dir.o \ - dos/echo.o \ - dos/exit.o \ - dos/for.o \ - dos/goto.o \ - dos/if.o \ - dos/md.o \ - dos/rem.o \ - dos/path.o \ - dos/prompt.o \ - dos/reboot.o \ - dos/set.o \ - dos/shift.o \ - dos/type.o \ - dos/ver.o \ - lib/applet.o \ - lib/args.o \ - lib/strextra.o \ - lib/environ.o \ - -.PHONY: clean all - -all: fdbox tests - -clean: - del *.o - del dos\*.o - del lib\*.o - -fdbox: $(OBJECTS) - $(CC) $(OBJECTS) -o fdbox.exe - -tests: tests.o lib/applet.o lib/args.o lib/strextra.o lib/environ.o - $(CC) tests.o lib/applet.o lib/args.o lib/strextra.o lib/environ.o -o tests.exe - -################## - -main.o: main.c - -tests.o: tests.c - -applets.o: applets.c - -help.o: help.c - -dos/beep.o: dos/beep.c - -dos/call.o: dos/call.c - -dos/cd.o: dos/cd.c - -dos/cls.o: dos/cls.c - -dos/command.o: dos/command.c - -dos/copymove.o: dos/copymove.c - -dos/datetime.o: dos/datetime.c - -dos/del.o: dos/del.c - -dos/dir.o: dos/dir.c - -dos/echo.o: dos/echo.c - -dos/exit.o: dos/exit.c - -dos/for.o: dos/for.c - -dos/goto.o: dos/goto.c - -dos/if.o: dos/if.c - -dos/md.o: dos/md.c - -dos/path.o: dos/path.c - -dos/prompt.o: dos/prompt.c - -dos/reboot.o: dos/reboot.c - -dos/rem.o: dos/rem.c - -dos/set.o: dos/set.c - -dos/shift.o: dos/shift.c - -dos/type.o: dos/type.c - -dos/reboot.o: dos/reboot.c - -help.o: help.c - -lib/applet.o: lib/applet.c - -lib/args.o: lib/args.c - -lib/environ.o: lib/environ.c - -lib/strextra.o: lib/strextra.c +# This makefile is for DJGPP + +CFLAGS=-I. -Ilib -Idos -O2 + +OBJECTS=applets.o main.o help.o \ + dos/beep.o \ + dos/call.o \ + dos/cd.o \ + dos/cls.o \ + dos/command.o \ + dos/copymove.o \ + dos/datetime.o \ + dos/del.o \ + dos/dir.o \ + dos/echo.o \ + dos/exit.o \ + dos/for.o \ + dos/goto.o \ + dos/if.o \ + dos/md.o \ + dos/rem.o \ + dos/path.o \ + dos/prompt.o \ + dos/reboot.o \ + dos/set.o \ + dos/shift.o \ + dos/type.o \ + dos/ver.o \ + lib/applet.o \ + lib/args.o \ + lib/strextra.o \ + lib/sglob.o \ + lib/readline.o \ + lib/environ.o \ + dos/history.o \ + + +.PHONY: clean all + +all: fdbox tests + +clean: + del *.o + del dos\*.o + del lib\*.o + +fdbox: $(OBJECTS) + $(CC) $(OBJECTS) -o fdbox.exe + +tests: tests.o lib/applet.o lib/args.o lib/strextra.o lib/environ.o + $(CC) tests.o lib/applet.o lib/args.o lib/strextra.o lib/environ.o -o tests.exe + +################## + +main.o: main.c + +tests.o: tests.c + +applets.o: applets.c + +help.o: help.c + +dos/beep.o: dos/beep.c + +dos/call.o: dos/call.c + +dos/cd.o: dos/cd.c + +dos/cls.o: dos/cls.c + +dos/command.o: dos/command.c + +dos/copymove.o: dos/copymove.c + +dos/datetime.o: dos/datetime.c + +dos/del.o: dos/del.c + +dos/dir.o: dos/dir.c + +dos/echo.o: dos/echo.c + +dos/exit.o: dos/exit.c + +dos/for.o: dos/for.c + +dos/goto.o: dos/goto.c + +dos/if.o: dos/if.c + +dos/md.o: dos/md.c + +dos/path.o: dos/path.c + +dos/prompt.o: dos/prompt.c + +dos/reboot.o: dos/reboot.c + +dos/rem.o: dos/rem.c + +dos/set.o: dos/set.c + +dos/shift.o: dos/shift.c + +dos/type.o: dos/type.c + +dos/reboot.o: dos/reboot.c + +help.o: help.c + +lib/applet.o: lib/applet.c + +lib/args.o: lib/args.c + +lib/environ.o: lib/environ.c + +lib/strextra.o: lib/strextra.c + +lib/sglob.o: lib/sglob.c + +lib/readline.o: lib/readline.c + +dos/history.o: dos/history.c diff --git a/src/Makefile.ow b/src/Makefile.ow index 8a8a983..f0f30d2 100644 --- a/src/Makefile.ow +++ b/src/Makefile.ow @@ -1,133 +1,133 @@ -# This makefile is for OpenWatcom (this is GNU-Makefile) - -CFLAGS=-zq -bt=dos -d0 -2 -I. -Ilib -Idos -za99 -Ietc/ow/h -sg -CC=wcc -LD=wcl - -OBJECTS=applets.o main.o help.o \ - dos/beep.o \ - dos/call.o \ - dos/cd.o \ - dos/cls.o \ - dos/command.o \ - dos/copymove.o \ - dos/datetime.o \ - dos/del.o \ - dos/dir.o \ - dos/echo.o \ - dos/exit.o \ - dos/for.o \ - dos/goto.o \ - dos/history.o \ - dos/if.o \ - dos/md.o \ - dos/rem.o \ - dos/path.o \ - dos/prompt.o \ - dos/reboot.o \ - dos/set.o \ - dos/shift.o \ - dos/type.o \ - dos/ver.o \ - lib/applet.o \ - lib/args.o \ - lib/strextra.o \ - lib/environ.o \ - lib/readline.o \ - lib/tc202/dos-glob.o - -TEST_OBJECTS = tests.o \ - lib/applet.o \ - lib/args.o \ - lib/environ.o \ - lib/strextra.o \ - -.PHONY: clean all - -.c.o: - $(CC) -fo=$@ $(CFLAGS) $< - -all: tests fdbox - -clean: - rm -f *.o - rm -f dos\*.o - rm -f lib\*.o - -fdbox: $(OBJECTS) - $(LD) -lr $^ -fe=$@ - -tests: $(TEST_OBJECTS) - $(LD) -lr $^ -fe=$@ - -################## - -main.o: main.c - -tests.o: tests.c - -applets.o: applets.c - -help.o: help.c - -dos/beep.o: dos/beep.c - -dos/call.o: dos/call.c - -dos/cd.o: dos/cd.c - -dos/cls.o: dos/cls.c - -dos/command.o: dos/command.c - -dos/copymove.o: dos/copymove.c - -dos/datetime.o: dos/datetime.c - -dos/del.o: dos/del.c - -dos/dir.o: dos/dir.c - -dos/echo.o: dos/echo.c - -dos/exit.o: dos/exit.c - -dos/for.o: dos/for.c - -dos/goto.o: dos/goto.c - -dos/history.o: dos/history.c - -dos/if.o: dos/if.c - -dos/md.o: dos/md.c - -dos/path.o: dos/path.c - -dos/prompt.o: dos/prompt.c - -dos/reboot.o: dos/reboot.c - -dos/rem.o: dos/rem.c - -dos/set.o: dos/set.c - -dos/shift.o: dos/shift.c - -dos/type.o: dos/type.c - -dos/reboot.o: dos/reboot.c - -help.o: help.c - -lib/applet.o: lib/applet.c - -lib/args.o: lib/args.c - -lib/environ.o: lib/environ.c - -lib/strextra.o: lib/strextra.c - -lib/readline.o: lib/readline.c - -lib/tc202/dos-glob.o: lib/tc202/dos-glob.c +# This makefile is for OpenWatcom (this is GNU-Makefile) + +CFLAGS=-zq -bt=dos -d0 -2 -I. -Ilib -Idos -za99 -Ietc/ow/h -sg +CC=wcc +LD=wcl + +OBJECTS=applets.o main.o help.o \ + dos/beep.o \ + dos/call.o \ + dos/cd.o \ + dos/cls.o \ + dos/command.o \ + dos/copymove.o \ + dos/datetime.o \ + dos/del.o \ + dos/dir.o \ + dos/echo.o \ + dos/exit.o \ + dos/for.o \ + dos/goto.o \ + dos/history.o \ + dos/if.o \ + dos/md.o \ + dos/rem.o \ + dos/path.o \ + dos/prompt.o \ + dos/reboot.o \ + dos/set.o \ + dos/shift.o \ + dos/type.o \ + dos/ver.o \ + lib/sglob.o \ + lib/applet.o \ + lib/args.o \ + lib/strextra.o \ + lib/environ.o \ + lib/readline.o \ + +TEST_OBJECTS = tests.o \ + lib/applet.o \ + lib/args.o \ + lib/environ.o \ + lib/strextra.o \ + +.PHONY: clean all + +.c.o: + $(CC) -fo=$@ $(CFLAGS) $< + +all: fdbox tests + +clean: + rm -f *.o + rm -f dos\*.o + rm -f lib\*.o + +fdbox: $(OBJECTS) + $(LD) -lr $^ -fe=$@ + +tests: $(TEST_OBJECTS) + $(LD) -lr $^ -fe=$@ + +################## + +main.o: main.c + +tests.o: tests.c + +applets.o: applets.c + +help.o: help.c + +dos/beep.o: dos/beep.c + +dos/call.o: dos/call.c + +dos/cd.o: dos/cd.c + +dos/cls.o: dos/cls.c + +dos/command.o: dos/command.c + +dos/copymove.o: dos/copymove.c + +dos/datetime.o: dos/datetime.c + +dos/del.o: dos/del.c + +dos/dir.o: dos/dir.c + +dos/echo.o: dos/echo.c + +dos/exit.o: dos/exit.c + +dos/for.o: dos/for.c + +dos/goto.o: dos/goto.c + +dos/history.o: dos/history.c + +dos/if.o: dos/if.c + +dos/md.o: dos/md.c + +dos/path.o: dos/path.c + +dos/prompt.o: dos/prompt.c + +dos/reboot.o: dos/reboot.c + +dos/rem.o: dos/rem.c + +dos/set.o: dos/set.c + +dos/shift.o: dos/shift.c + +dos/type.o: dos/type.c + +dos/reboot.o: dos/reboot.c + +help.o: help.c + +lib/applet.o: lib/applet.c + +lib/args.o: lib/args.c + +lib/environ.o: lib/environ.c + +lib/strextra.o: lib/strextra.c + +lib/readline.o: lib/readline.c + +lib/tc202/dos-glob.o: lib/tc202/dos-glob.c diff --git a/src/dos/beep.c b/src/dos/beep.c index 9a9e73b..a764dfa 100644 --- a/src/dos/beep.c +++ b/src/dos/beep.c @@ -2,7 +2,6 @@ #include #include #include -#include #ifdef WIN32 // https://docs.microsoft.com/en-us/windows/desktop/api/utilapiset/nf-utilapiset-beep diff --git a/src/dos/call.c b/src/dos/call.c index 2d2389a..ca692ad 100644 --- a/src/dos/call.c +++ b/src/dos/call.c @@ -2,7 +2,6 @@ #include #include "dos/call.h" -#include "fdbox.h" /* This file is part of fdbox diff --git a/src/dos/cd.c b/src/dos/cd.c index ce4fe89..384a3cf 100644 --- a/src/dos/cd.c +++ b/src/dos/cd.c @@ -1,7 +1,5 @@ - #include #include -#include #if defined(_POSIX_C_SOURCE) || defined(__DJGPP__) || defined(__APPLE__) #include @@ -29,7 +27,6 @@ #endif #include "dos/cd.h" -#include "fdbox.h" /* This file is part of fdbox @@ -40,10 +37,13 @@ int command_cd(int argc, char *argv[]) { const char *path = argv[1]; if (argc == 1) { char cwd[128]; -#if defined(__linux__) || defined(__MSDOS__) || defined(__APPLE__) || defined(__WATCOMC__) +#if defined(__linux__) || defined(__MSDOS__) || defined(__APPLE__) || defined(__WATCOMC__) || defined(_POSIX_C_SOURCE) getcwd(cwd, 128); #elif defined(__WIN32__) DWORD err = GetCurrentDirectory(128, cwd); + if (err != 0) { + return EXIT_FAILURE; + } #else #error platform not supported yet #endif diff --git a/src/dos/cls.c b/src/dos/cls.c index 9be01ff..5c5047f 100644 --- a/src/dos/cls.c +++ b/src/dos/cls.c @@ -1,4 +1,3 @@ -#include #include #include "dos/cls.h" diff --git a/src/dos/command.c b/src/dos/command.c index f3ac183..b8c3d1a 100644 --- a/src/dos/command.c +++ b/src/dos/command.c @@ -95,7 +95,7 @@ int command_execute_line(const char *line) { if (command_args_split(line, &args)) { fprintf(stderr, "Command line parsing failed\n"); - return EXIT_SUCCESS; + return EXIT_FAILURE; } if (args.argc == 0) { @@ -109,7 +109,7 @@ int command_execute_line(const char *line) { return EXIT_FAILURE; } - cmd = find_applet(CASE_INSENSITVE, args.argv[0], commands); + cmd = find_applet(CASE_INSENSITIVE, args.argv[0], commands); if (cmd != NULL) { code = cmd->handler(args.argc, args.argv); errno = code; @@ -145,7 +145,6 @@ int command_command(int argc, char *argv[]) { } do { - char prompt[256]; const char *t; int l; @@ -154,8 +153,9 @@ int command_command(int argc, char *argv[]) { command_prompt(1, NULL); t = getenv("PROMPT"); } - get_prompt(t, prompt, 256); - printf("%s", prompt); + get_prompt(t, line, 256); + printf("%s", line); + fflush(stdout); l = read_line(line, 1024); if (l < 0) { return EXIT_FAILURE; diff --git a/src/dos/command.h b/src/dos/command.h index 37cdafc..af6fdc2 100644 --- a/src/dos/command.h +++ b/src/dos/command.h @@ -10,6 +10,6 @@ int command_command(int arc, char *argv[]); const char *help_command(); /* EXIT_FAILURE means exit was triggered */ -int command_execute_line(char *line); +int command_execute_line(const char *line); #endif //__command_h__ diff --git a/src/dos/copymove.c b/src/dos/copymove.c index bd4a169..b3fc264 100644 --- a/src/dos/copymove.c +++ b/src/dos/copymove.c @@ -19,28 +19,28 @@ For license - read license.txt */ #if defined(__TURBOC__) -#include "lib/tc202/dos-glob.h" #include "lib/tc202/stdbool.h" #include "lib/tc202/stdextra.h" +#include "lib/sglob.h" #endif #if defined(__WATCOMC__) -#include "lib/tc202/dos-glob.h" +#include "lib/sglob.h" #endif #if defined(_POSIX_C_SOURCE) || defined(__DJGPP__) || defined(__APPLE__) -#include +#include "lib/sglob.h" #include #include #include #endif #ifdef __WIN32__ -#include "lib/win32/dirent.h" -#include "lib/win32/win32-glob.h" #include #include #include +#include "lib/win32/dirent.h" +#include "lib/sglob.h" #endif struct copy_config { diff --git a/src/dos/del.c b/src/dos/del.c index f447ade..f59394a 100644 --- a/src/dos/del.c +++ b/src/dos/del.c @@ -15,8 +15,9 @@ For license - read license.txt */ #if defined(__TURBOC__) || defined(__WATCOMC__) -#include "lib/tc202/dos-glob.h" +#include #include "lib/tc202/stdextra.h" +#include "lib/sglob.h" #endif #if defined(_POSIX_C_SOURCE) || defined(__APPLE__) @@ -26,10 +27,10 @@ For license - read license.txt #endif #ifdef __WIN32__ -#include "lib/win32/dirent.h" -#include "lib/win32/win32-glob.h" #include #include +#include "lib/win32/dirent.h" +#include "lib/sglob.h" #endif struct del_config { diff --git a/src/dos/dir.c b/src/dos/dir.c index 9828bd9..323c17a 100644 --- a/src/dos/dir.c +++ b/src/dos/dir.c @@ -34,27 +34,28 @@ Places I look for inspiration: #include "lib/strextra.h" #if defined(_POSIX_C_SOURCE) || defined(__APPLE__) +#include "lib/sglob.h" #include #include -#include #include #endif #ifdef __WIN32__ #include "lib/win32/dirent.h" -#include "lib/win32/win32-glob.h" +#include "lib/sglob.h" #endif #if defined(__TURBOC__) -#include "lib/tc202/dos-glob.h" #include "lib/tc202/stdbool.h" #include "lib/tc202/stdextra.h" +#include "lib/sglob.h" +#include #endif #if defined(__WATCOMC__) -#include "lib/tc202/dos-glob.h" #include #include +#include "lib/sglob.h" #endif /*************************************************************************** @@ -62,13 +63,17 @@ Places I look for inspiration: ***************************************************************************/ #define SORT_NAME 0x01 -#define SORT_EXTENTION 0x02 +#define SORT_EXTENSION 0x02 #define SORT_DIRS 0x04 #define SORT_DATE 0x08 #define SORT_SIZE 0x10 -#define MAX_DIR_FILES 128 +#if defined(__WATCOMC__) +/* under watcom C - 256 files need 30k stack... still investigating... */ +#define MAX_DIR_ENTRY_FILES 256 +#else #define MAX_DIR_ENTRY_FILES 1024 +#endif struct dir_config { bool show_help; @@ -162,7 +167,7 @@ static void dir_display_dir(struct dir_config *config, const char *dir_name, memset(files, 0, sizeof(files)); for (i = 0; i < files2->count; i++) { glob_t globbuf = {0}; - glob(files2->file[i], GLOB_DOOFFS | GLOB_NOSORT, NULL, &globbuf); + serenity_glob(files2->file[i], GLOB_DOOFFS | GLOB_NOSORT, NULL, &globbuf); for (j = 0; j != globbuf.gl_pathc; j++) { const char *file_name = globbuf.gl_pathv[j]; requested_count++; @@ -175,7 +180,7 @@ static void dir_display_dir(struct dir_config *config, const char *dir_name, file_count++; } } - globfree(&globbuf); + serenity_globfree(&globbuf); } /* and sort if needed */ @@ -309,7 +314,7 @@ static void dir_config_print(struct dir_config *config) { if (flag_test(config->sort_order, SORT_NAME)) { printf("name "); } - if (flag_test(config->sort_order, SORT_EXTENTION)) { + if (flag_test(config->sort_order, SORT_EXTENSION)) { printf("extension "); } if (flag_test(config->sort_order, SORT_DIRS)) { @@ -382,7 +387,7 @@ static bool dir_parse_config(int argc, char *argv[], struct dir_config *config) flag_set(&config->sort_order, SORT_NAME, true); break; case 'e': - flag_set(&config->sort_order, SORT_EXTENTION, true); + flag_set(&config->sort_order, SORT_EXTENSION, true); break; case 'g': flag_set(&config->sort_order, SORT_DIRS, true); @@ -497,7 +502,7 @@ static int dir_file_comperator(const void *a, const void *b) { order -= 2; } } - if (flag_test(dir_file_order, SORT_EXTENTION)) { + if (flag_test(dir_file_order, SORT_EXTENSION)) { const char *ext1, *ext2; int v; ext1 = file_get_extension(file1->file_name); diff --git a/src/dos/exit.c b/src/dos/exit.c index 2134f50..ad54071 100644 --- a/src/dos/exit.c +++ b/src/dos/exit.c @@ -2,7 +2,6 @@ #include #include "dos/exit.h" -#include "fdbox.h" /* This file is part of fdbox diff --git a/src/dos/if.c b/src/dos/if.c index 72456c0..e47fb98 100644 --- a/src/dos/if.c +++ b/src/dos/if.c @@ -4,7 +4,6 @@ For license - read license.txt */ #include -#include #include #include #include @@ -16,24 +15,24 @@ For license - read license.txt #include "lib/strextra.h" #if defined(_POSIX_C_SOURCE) || defined(__APPLE__) -#include +#include "lib/sglob.h" #include #endif #ifdef __WIN32__ #include "lib/win32/dirent.h" -#include "lib/win32/win32-glob.h" +#include "lib/sglob.h" #include #endif #if defined(__TURBOC__) -#include "lib/tc202/dos-glob.h" +#include "lib/sglob.h" #include "lib/tc202/stdbool.h" #include "lib/tc202/stdextra.h" #endif #if defined(__WATCOMC__) -#include "lib/tc202/dos-glob.h" +#include "lib/sglob.h" #include #endif diff --git a/src/dos/md.c b/src/dos/md.c index 0910871..67bc59a 100644 --- a/src/dos/md.c +++ b/src/dos/md.c @@ -15,24 +15,21 @@ For license - read license.txt */ #ifdef __MSDOS__ -#include "lib/tc202/dos-glob.h" #include "lib/tc202/stdbool.h" #include "lib/tc202/stdextra.h" #endif #ifdef _POSIX_C_SOURCE -#include #include +#include #include #include #endif #ifdef __WIN32__ #include "lib/win32/dirent.h" -#include "lib/win32/win32-glob.h" #include #include -#include #endif struct mkdir_config { diff --git a/src/dos/path.c b/src/dos/path.c index 45c72f5..b73d8cd 100644 --- a/src/dos/path.c +++ b/src/dos/path.c @@ -2,7 +2,6 @@ #include #include "dos/path.h" -#include "fdbox.h" /* This file is part of fdbox diff --git a/src/dos/prompt.c b/src/dos/prompt.c index 7900484..eff45d7 100644 --- a/src/dos/prompt.c +++ b/src/dos/prompt.c @@ -2,7 +2,6 @@ #include #include "dos/prompt.h" -#include "fdbox.h" #if defined(__MSDOS__) || defined(__WIN32__) /* setenv is not supported under TC, implement this locally diff --git a/src/dos/reboot.c b/src/dos/reboot.c index f5c603b..1d2bfa8 100644 --- a/src/dos/reboot.c +++ b/src/dos/reboot.c @@ -2,7 +2,6 @@ #include #include "dos/reboot.h" -#include "fdbox.h" /* This file is part of fdbox diff --git a/src/dos/rem.c b/src/dos/rem.c index 5782536..223692e 100644 --- a/src/dos/rem.c +++ b/src/dos/rem.c @@ -1,4 +1,3 @@ -#include #include #include "dos/rem.h" diff --git a/src/dos/set.c b/src/dos/set.c index 20ded86..13ffa75 100644 --- a/src/dos/set.c +++ b/src/dos/set.c @@ -1,10 +1,7 @@ #include #include -#include #include "dos/set.h" -#include "fdbox.h" -#include "lib/tc202/stdextra.h" /* This file is part of fdbox @@ -19,7 +16,10 @@ For license - read license.txt #endif int command_set(int argc, char *argv[]) { + /* msdos and windows have this in stdlib.h */ +#if defined(_POSIX_C_SOURCE) || defined(__APPLE__) extern char **environ; +#endif char **s = environ; int r; const char *name = argv[1]; diff --git a/src/dos/shift.c b/src/dos/shift.c index 31906a5..f0e1d6e 100644 --- a/src/dos/shift.c +++ b/src/dos/shift.c @@ -2,7 +2,6 @@ #include #include "dos/shift.h" -#include "fdbox.h" /* This file is part of fdbox diff --git a/src/dos/type.c b/src/dos/type.c index 5b31a59..c3a7ed3 100644 --- a/src/dos/type.c +++ b/src/dos/type.c @@ -3,7 +3,6 @@ #include #include "dos/type.h" -#include "fdbox.h" #include "lib/args.h" #define LINE_PRINT "%5zd " @@ -56,9 +55,13 @@ int command_type(int argc, char *argv[]) { for (i = 0; i < config.global.files.count; i++) { const char *file_name = config.global.files.file[i]; - FILE *file = fopen(file_name, "r"); char line[1024]; size_t line_number = 0; + FILE *file = fopen(file_name, "r"); + + if (file == NULL) { + return EXIT_FAILURE; + } while (fgets(line, sizeof(line), file)) { line_number++; diff --git a/src/dos/ver.c b/src/dos/ver.c index fc25266..04c0185 100644 --- a/src/dos/ver.c +++ b/src/dos/ver.c @@ -1,6 +1,5 @@ #include #include -#include #include "dos/ver.h" #include "fdbox.h" @@ -10,11 +9,29 @@ This file is part of fdbox For license - read license.txt */ +/* https://sourceforge.net/p/predef/wiki/Compilers/ */ +#if defined(__GNUC__) +#define GCC_VERSION (__GNUC__ * 10000 \ + + __GNUC_MINOR__ * 100 \ + + __GNUC_PATCHLEVEL__) +# define COMPILER_NAME "gcc" +# define COMPILER_VERSION GCC_VERSION +#elif defined(__TURBOC__) +# define COMPILER_NAME "TurboC" +# define COMPILER_VERSION __TURBOC__ +#elif defined(__WATCOMC__) +# define COMPILER_NAME "OpenWatcom" +# define COMPILER_VERSION __WATCOMC__ +#else +# define COMPILER_NAME "???" +# define COMPILER_VERSION 0 +#endif + int command_ver(int argc, char *argv[]) { UNUSED(argc); UNUSED(argv); - printf("%s %s\n", "fdbox", FDBOX_VERSION_STR); + printf("%s %s (%s/%d)\n", "fdbox", FDBOX_VERSION_STR, COMPILER_NAME, COMPILER_VERSION); return EXIT_SUCCESS; } -const char *help_ver() { return "Display the vresion of the shell"; } +const char *help_ver() { return "Display the version of the shell"; } diff --git a/src/lib/applet.h b/src/lib/applet.h index 7edb4c8..16599d3 100644 --- a/src/lib/applet.h +++ b/src/lib/applet.h @@ -11,8 +11,8 @@ typedef const char *(*function_help)(); #include #endif -#define CASE_INSENSITVE false -#define CASE_SENSITVE true +#define CASE_INSENSITIVE false +#define CASE_SENSITIVE true struct applet { function_help help_text; diff --git a/src/lib/args.c b/src/lib/args.c index 30cf5ca..3ba08e1 100644 --- a/src/lib/args.c +++ b/src/lib/args.c @@ -64,7 +64,7 @@ void hexDump(char *desc, void *addr, int len) { printf(" %s\n", buff); } -void print_agrs(int argc, char *argv[]) { +void print_args(int argc, char *argv[]) { int i; if (argc == 0) { printf("No args\n"); @@ -175,7 +175,7 @@ bool command_split_args(char *full_cmd, size_t *argc, const char *argv[], size_t } bool command_merge_args(size_t argc, const char *argv[], char *line, size_t max_line_size) { - size_t i = 0, l = 0; + size_t i, l = 0; line[0] = 0; for (i = 0; i < argc; i++) { @@ -186,7 +186,7 @@ bool command_merge_args(size_t argc, const char *argv[], char *line, size_t max_ strncat(line, argv[i], max_line_size); strncat(line, " ", max_line_size); l += k; - }; + } return true; } diff --git a/src/lib/args.h b/src/lib/args.h index 7f7ff04..aad5271 100644 --- a/src/lib/args.h +++ b/src/lib/args.h @@ -26,7 +26,7 @@ For license - read license.txt #define ARG_MAX_FILES 128 void hexDump(char *desc, void *addr, int len); -void print_agrs(int argc, char *argv[]); +void print_args(int argc, char *argv[]); /* int dos_parseargs(int *argc, char **argv[], const char* template, char **output); */ struct command_glob { diff --git a/src/lib/environ.c b/src/lib/environ.c index 5f4a8ab..217f7e3 100644 --- a/src/lib/environ.c +++ b/src/lib/environ.c @@ -169,7 +169,6 @@ char *get_prompt(const char *prompt, char prompt_string[], size_t prompt_str_len void expand_string(const char *src, char *dest, size_t dest_length) { char var_name[100], *var_p = NULL; char *var_value; - size_t var_len; char *dest_p = dest; size_t dest_written = 0; @@ -181,8 +180,8 @@ void expand_string(const char *src, char *dest, size_t dest_length) { /* we start a new variable */ var_p = var_name; *var_p = 0; - var_len = 0; } else { + size_t var_len; /* we end a variable */ *var_p = 0; if (var_name[0] != '\0') { diff --git a/src/lib/readline.c b/src/lib/readline.c index e475a12..7b6d6a4 100644 --- a/src/lib/readline.c +++ b/src/lib/readline.c @@ -4,19 +4,13 @@ For license - read license.txt */ #include -#include #include #include #include -#include "lib/applet.h" -#include "lib/args.h" #include "lib/environ.h" #include "lib/readline.h" -#include "dos/history.h" -#include "dos/prompt.h" - #ifdef __TURBOC__ #include "dos.h" #include "lib/tc202/stdbool.h" @@ -40,8 +34,6 @@ void clrscr() { _clearscreen(_GCLEARSCREEN); } #endif #if defined(__WIN32__) -#include -#include #include #include diff --git a/src/lib/sglob.c b/src/lib/sglob.c new file mode 100644 index 0000000..581bf5c --- /dev/null +++ b/src/lib/sglob.c @@ -0,0 +1,470 @@ +/* +* This file is part of fdbox +* +* SerenityGlob is a multi platform port of the posix `glob()` api +* to MSDOS, Windows, SerenityOS and Posix systems. +* +* Copyright 2022 Diego Iastrubni +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* 3. Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* +* Implementation of a `glob()` for systems without glob. +* First implementation is aimed at @SerenityOS. However, +* porting this to Win32/Win64 or MSDOS should be trivial. +* +* Note that this code is not licensed under GPLv3 as the rest of +* the code fdbox. This code is licensed more permissive on purpose. +*/ + +#include +#include +#include + +#include "lib/strextra.h" +#include "sglob.h" + +#if !defined(__serenity__) +#define HAS_FNSTAT +#endif + +/* +#define GLOB_DEFAULT_FLAGS (FNM_CASEFOLD | FNM_NOESCAPE | FNM_PERIOD) + */ +#define GLOB_DEFAULT_FLAGS (FNM_NOESCAPE | FNM_PERIOD) + +struct file_entry { + char *file_name; + struct file_entry *next; +}; + +struct linked_file_list { + int count; + struct file_entry* head; + struct file_entry* tail; +}; + +/* TODO - what if malloc failed? */ +static void list_push_front(struct linked_file_list *list, const char *name) +{ + struct file_entry *next = malloc(sizeof(struct file_entry)); + next->file_name = strdup(name); + next->next = NULL; + if (list->head == NULL) { + list->head = next; + } else { + next->next = list->head; + list->head = next; + } + list->count ++; +} + +static void list_free(struct linked_file_list *list, int also_content) +{ + struct file_entry *next; + if (list == NULL) { + return; + } + next = list->head; + while (next != NULL) { + struct file_entry *p = next; + if (also_content) { + free(next->file_name); + } + next = next->next; + free(p); + } +} + +static int glob_inside_dir(const char *dir_name, const char *pattern, struct linked_file_list* list); + +static int entry_comparer(const void *pv1, const void *pv2) { + const char *pe1 = pv1; + const char *pe2 = pv2; + return strcmp(pe1, pe2); +} + +static int convert_list(struct linked_file_list files, glob_t *pglob) { + struct file_entry *entry; + int i; + + pglob->gl_pathv = malloc(sizeof(const char *) * files.count); + if (pglob->gl_pathv == NULL) { + return GLOB_NOSPACE; + } + entry = files.head; + for (i = 0; i < files.count; i++) { + pglob->gl_pathv[i] = entry->file_name; + entry = entry->next; + } + pglob->gl_pathc = files.count; + return 0; +} + +int serenity_glob(const char *pattern, int flags, void *unused, glob_t *pglob) { + char *dir = NULL; + const char *glob_pattern; + struct linked_file_list files; + int rc; + + files.head = NULL; + files.tail = NULL; + files.count = 0; + + glob_pattern = file_base_name(pattern); + dir = file_get_dir(pattern); + + /* TODO: should we test for flags? I see something weird here */ + if (!pattern /*|| flags != (flags & GLOB_FLAGS)*/ || unused || !pglob) { + errno = EINVAL; + return EINVAL; + } + + rc = glob_inside_dir(dir, glob_pattern, &files); + if (rc != 0) { + list_free(&files, 1); + free(dir); + return rc; + } + + /* warning in TC. pglob is passed by value. WTF? */ + if (convert_list(files, pglob) != 0) { + free(dir); + list_free(&files, 1); + return GLOB_NOSPACE; + } + rc = files.count == 0 ? GLOB_NOMATCH : 0; + list_free(&files, 0); + free(dir); + + if ((flags & GLOB_NOSORT) == 0) { + qsort(pglob->gl_pathv, pglob->gl_pathc, sizeof(char *), entry_comparer); + } + + return rc; +} + +#if defined(_POSIX_C_SOURCE) || defined(__APPLE__) +#include +#include + +#if defined(HAS_FNSTAT) +/* Most code here is borrowed from dash. The following code is licensed under + BSD license. +https://github.com/tklauser/dash/blob/afe0e0152e4dc12d84be3c02d6d62b0456d68580/src/expand.c +*/ + +#include +#else +#include +#define fnmatch(a, b, c) pmatch((a), (b)) + +char *prefix(const char *string, const char *pfx) +{ + while (*pfx) { + if (*pfx++ != *string++) + return 0; + } + return (char *) string; +} + +int ccmatch(const char *p, int chr, const char **r) +{ + static const struct class { + char name[10]; + int (*fn)(int); + } classes[] = { + { .name = ":alnum:]", .fn = isalnum }, + { .name = ":cntrl:]", .fn = iscntrl }, + { .name = ":lower:]", .fn = islower }, + { .name = ":space:]", .fn = isspace }, + { .name = ":alpha:]", .fn = isalpha }, + { .name = ":digit:]", .fn = isdigit }, + { .name = ":print:]", .fn = isprint }, + { .name = ":upper:]", .fn = isupper }, + { .name = ":blank:]", .fn = isblank }, + { .name = ":graph:]", .fn = isgraph }, + { .name = ":punct:]", .fn = ispunct }, + { .name = ":xdigit:]", .fn = isxdigit }, + }; + const struct class *class, *end; + + end = classes + sizeof(classes) / sizeof(classes[0]); + for (class = classes; class < end; class++) { + const char *q; + + q = prefix(p, class->name); + if (!q) + continue; + *r = q; + return class->fn(chr); + } + + *r = 0; + return 0; +} + +int pmatch(const char *pattern, const char *string) +{ + const char *p, *q; + char c; + + p = pattern; + q = string; + for (;;) { + switch (c = *p++) { + case '\0': + goto breakloop; + case '\\': + if (*p) { + c = *p++; + } + goto dft; + case '?': + if (*q++ == '\0') + return 0; + break; + case '*': + c = *p; + while (c == '*') + c = *++p; + if (c != '\\' && c != '?' && c != '*' && c != '[') { + while (*q != c) { + if (*q == '\0') + return 0; + q++; + } + } + do { + if (pmatch(p, q)) + return 1; + } while (*q++ != '\0'); + return 0; + case '[': { + const char *startp; + int invert, found; + char chr; + + startp = p; + invert = 0; + if (*p == '!') { + invert++; + p++; + } + found = 0; + chr = *q; + if (chr == '\0') + return 0; + c = *p++; + do { + if (!c) { + p = startp; + c = '['; + goto dft; + } + if (c == '[') { + const char *r; + + found |= !!ccmatch(p, chr, &r); + if (r) { + p = r; + continue; + } + } else if (c == '\\') + c = *p++; + if (*p == '-' && p[1] != ']') { + p++; + if (*p == '\\') + p++; + if (chr >= c && chr <= *p) + found = 1; + p++; + } else { + if (chr == c) + found = 1; + } + } while ((c = *p++) != ']'); + if (found == invert) + return 0; + q++; + break; + } + dft: default: + if (*q++ != c) + return 0; + break; + } + } +breakloop: + if (*q != '\0') + return 0; + return 1; +} +#endif + +/* https://bitbucket.org/szx/glob/src/master/glob_posix.cpp */ +int glob_inside_dir(const char *dir_name, const char *pattern, struct linked_file_list* list) +{ + struct dirent* entry; + DIR *dir; + + if (dir_name == NULL) { + dir_name = "."; + } + if (pattern == NULL) { + pattern = "*"; + } + dir = opendir(dir_name); + if (dir == NULL) { + return GLOB_ABORTED; + } + + while ((entry = readdir(dir)) != 0) { + int r = fnmatch(pattern, entry->d_name, GLOB_DEFAULT_FLAGS); + if (r != FNM_NOMATCH) { + list_push_front(list, entry->d_name); +#if 0 + if (S_ISDIR(entry->d_type)) { + next = glob_inside_dir(entry->d_name, pattern, NULL, NULL) + } +#endif + } + } + + return closedir(dir); +} +#elif defined(WIN32) +#define WIN32_LEAN_AND_MEAN 1 +#include + +int glob_inside_dir(const char *dir_name, const char *pattern, struct linked_file_list* list) +{ + if (dir_name == NULL) { + dir_name = "."; + } + if (pattern == NULL) { + pattern = "*"; + } + + WIN32_FIND_DATAA finddata; + HANDLE hFindfile = FindFirstFileA(pattern, &finddata); + bool failed = true; + + if (hFindfile != INVALID_HANDLE_VALUE) { + do { + list_push_front(list, finddata.cFileName); +#if 0 + if (finddata is directory) { + next = glob_inside_dir(finddata.cFileName, pattern, NULL, NULL) + } +#endif + } while (FindNextFileA(hFindfile, &finddata)); + failed = !FindClose(hFindfile); + } + + if (failed) { + return GLOB_ABORTED; + } + return 0; +} +#elif defined(__WATCOMC__) || defined(__TURBOC__) + +#include + +#if defined(__WATCOMC__) +# include +# include +# define findfirst _dos_findfirst +# define findnext _dos_findnext +# define ffblk find_t +# define FA_RDONLY _A_RDONLY +# define FA_HIDDEN _A_HIDDEN +# define FA_SYSTEM _A_SYSTEM +# define FA_DIREC _A_SUBDIR +# define GET_FILE_NAME(ff)(ff.name) +#elif defined (__TURBOC__) +# include "lib/tc202/stdbool.h" +# include +# define GET_FILE_NAME(ff)(ff.ff_name) +#elif + TODO: unsupported DOS compiler... +#endif + +#define ATTRIBUTES FA_RDONLY | FA_HIDDEN | FA_SYSTEM | FA_DIREC + +int glob_inside_dir(const char *dir_name, const char *pattern, struct linked_file_list* list) +{ + int err; + struct ffblk ff; + + if (dir_name == NULL) { + dir_name = "."; + } + if (pattern == NULL) { + pattern = "*.*"; + } + +#if defined(__WATCOMC__) + /* stupid watcom c, defines the function differently */ + err = _dos_findfirst(pattern, ATTRIBUTES, &ff); +#else + err = findfirst(pattern, &ff, ATTRIBUTES); +#endif + + if (err == 0) { + /* again stupid watcom c, file name member name is different */ + const char *file_name = GET_FILE_NAME(ff); + do { + list_push_front(list, file_name); +#if 0 + if (S_ISDIR(entry->d_type)) { + next = glob_inside_dir(entry->d_name, pattern, NULL, NULL) + } +#endif + err = findnext(&ff); + } while (err == 0); + } + + /* TODO: how should we find an error? */ + return 0; +} +#endif + +void serenity_globfree(glob_t *pglob) +{ + int i; + if (pglob == NULL) { + return; + } + for (i=0; i< pglob->gl_pathc; i++) { + free(pglob->gl_pathv[i]); + } + free(pglob->gl_pathv); + pglob->gl_pathv = NULL; + pglob->gl_pathc = 0; +} diff --git a/src/lib/sglob.h b/src/lib/sglob.h new file mode 100644 index 0000000..f889263 --- /dev/null +++ b/src/lib/sglob.h @@ -0,0 +1,101 @@ +/* + * This file is part of fdbox. + * + * SerenityGlob is a multi platform port of the posix `glob()` api + * to MSDOS, Windows, SerenityOS and Posix systems. + * + * Copyright 2022 Diego Iastrubni + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Implementation of a `glob()` for systems without glob. + * First implementation is aimed at @SerenityOS. However, + * porting this to Win32/Win64 or MSDOS should be trivial. + * + * Note that this code is not licensed under GPLv3 as the rest of + * the code fdbox. This code is licensed more permissive on purpose. + */ + +#ifndef SERENITY_GLOB_H +#define SERENITY_GLOB_H 1 + +#define GLOB_FLAGS (GLOB_NOCHECK) + +/* Bits set in the FLAGS argument to `glob' */ + +/* Posix */ +#define GLOB_ERR (1 << 0) +#define GLOB_MARK (1 << 1) +#define GLOB_NOSORT (1 << 2) +#define GLOB_DOOFFS (1 << 3) +#define GLOB_NOCHECK (1 << 4) +#define GLOB_APPEND (1 << 5) +#define GLOB_NOESCAPE (1 << 6) +#define GLOB_PERIOD (1 << 7) + +/* Posix2, GNU, BSD */ +#define GLOB_MAGCHAR (1 << 8) +#define GLOB_ALTDIRFUNC (1 << 9) +#define GLOB_BRACE (1 << 10) +#define GLOB_NOMAGIC (1 << 11) +#define GLOB_TILDE (1 << 12) +#define GLOB_ONLYDIR (1 << 13) +#define GLOB_TILDE_CHECK (1 << 14) + +/* Error returns from `glob' */ +#define GLOB_NOSPACE 1 +#define GLOB_ABORTED 2 +#define GLOB_NOMATCH 3 +#define GLOB_NOSYS 4 + +/* under Windows, this is already defined */ +#ifndef MAX_PATH +#define MAX_PATH 256 +#endif + +typedef struct glob_t { + unsigned gl_pathc; + char **gl_pathv; +} glob_t; + +int serenity_glob(const char *pattern, int flags, void *unused, glob_t *pglob); +void serenity_globfree(glob_t *pglob); + +/* these should not be here technically, they should be in STAT.H */ +#define __S_ISTYPE(mode, mask) (((mode)&S_IFMT) == (mask)) + +#ifndef S_ISDIR +#define S_ISDIR(mode) __S_ISTYPE((mode), S_IFDIR) +#endif + +#define glob serenity_glob +#define globfree serenity_globfree + +/* SERENITY_GLOB_H */ +#endif diff --git a/src/lib/strextra.c b/src/lib/strextra.c index 34bf807..01fdc19 100644 --- a/src/lib/strextra.c +++ b/src/lib/strextra.c @@ -65,6 +65,9 @@ char *file_get_dir(const char *file_name) { char *c, *d; int length; + if (file_name == NULL) { + return NULL; + } c = strrchr(file_name, '/'); if (c == NULL) { c = strrchr(file_name, '\\'); @@ -76,7 +79,7 @@ char *file_get_dir(const char *file_name) { length = c - file_name; d = malloc(length + 1); strncpy(d, file_name, length); - d[length+1] = 0; + d[length] = 0; return d; } diff --git a/src/lib/tc202/dos-glob.c b/src/lib/tc202/dos-glob.c deleted file mode 100644 index ee38949..0000000 --- a/src/lib/tc202/dos-glob.c +++ /dev/null @@ -1,155 +0,0 @@ -#include -#include -#include -#include -#include - -#include "lib/tc202/dos-glob.h" - -#if defined(__TURBOC__) -#include - -#elif defined(__WATCOMC__) - -#include - -#define findfirst _dos_findfirst -#define findnext _dos_findnext - -#define FA_RDONLY _A_RDONLY -#define FA_HIDDEN _A_HIDDEN -#define FA_SYSTEM _A_SYSTEM -#define FA_DIREC _A_SUBDIR -#endif - -#define ATTRIBUTES FA_RDONLY | FA_HIDDEN | FA_SYSTEM | FA_DIREC - -struct file_entry { - char name[MAX_PATH]; - struct file_entry *next; -}; - -static int insert(const char *path, const char *name, struct file_entry **phead) { - int len; - struct file_entry *cur = (struct file_entry *)malloc(sizeof(struct file_entry)); - if (!cur) { - return ENOMEM; - } - - /* todo: we need snprintf */ - len = sprintf(cur->name, "%s%s", path, name); - cur->name[MAX_PATH - 1] = 0; - cur->next = *phead; - *phead = cur; - - return len < 0 || len >= MAX_PATH ? ENAMETOOLONG : 0; -} - -static int entry_comparer(const void *pv1, const void *pv2) { - const struct file_entry *const *pe1 = pv1; - const struct file_entry *const *pe2 = pv2; - return stricmp((*pe1)->name, (*pe2)->name); -} - -int glob(const char *pattern, int flags, int (*errfunc)(const char *epath, int eerrno), - glob_t *pglob) { - char path[MAX_PATH]; - struct file_entry *head = NULL; - int err = 0, err2; - size_t len; - unsigned entries = 0; - -#if defined(__WATCOMC__) - struct find_t ff; -#else - struct ffblk ff; -#endif - /* - if (!pattern || flags != (flags & GLOB_FLAGS) || unused || !pglob) - { - errno = EINVAL; - return EINVAL; - } - */ - path[MAX_PATH - 1] = 0; - strncpy(path, pattern, MAX_PATH); - if (path[MAX_PATH - 1] != 0) { - errno = ENAMETOOLONG; - return ENAMETOOLONG; - } - - len = strlen(path); - while (len > 0 && path[len - 1] != '/' && path[len - 1] != '\\') - len--; - path[len] = 0; - -#if defined(__WATCOMC__) - err = _dos_findfirst(pattern, ATTRIBUTES, &ff); -#else - err = findfirst(pattern, &ff, ATTRIBUTES); -#endif - if (err != 0) { - if (flags & GLOB_NOCHECK) { - err = insert("", pattern, &head); - entries++; - } - } else { - do { -#if defined(__WATCOMC__) - const char *file_name = ff.name; -#else - const char *file_name = ff.ff_name; -#endif - - err2 = insert(path, file_name, &head); - entries++; - err = findnext(&ff); - } while (!err && !err2); - err = 0; - } - - if (err == 0) { - pglob->gl_pathv = malloc((entries + 1) * sizeof(char *)); - if (pglob->gl_pathv) { - pglob->gl_pathc = entries; - pglob->gl_pathv[entries] = NULL; - for (; head; head = head->next, entries--) - pglob->gl_pathv[entries - 1] = (char *)head; - - if ((flags & GLOB_NOCHECK) == 0 ) { - qsort(pglob->gl_pathv, pglob->gl_pathc, sizeof(char *), - entry_comparer); - } - } else { - pglob->gl_pathc = 0; - err = ENOMEM; - } - } else if (pglob) { - pglob->gl_pathc = 0; - pglob->gl_pathv = NULL; - } - - if (err) { - struct file_entry *cur; - while (head) { - cur = head; - head = head->next; - free(cur); - } - errno = err; - } - - return err; -} - -void globfree(glob_t *pglob) { - if (pglob) { - char **cur; - for (cur = pglob->gl_pathv; *cur; cur++) { - free(*cur); - } - - pglob->gl_pathc = 0; - pglob->gl_pathv = NULL; - } -} diff --git a/src/lib/tc202/dos-glob.h b/src/lib/tc202/dos-glob.h deleted file mode 100644 index 5feccf6..0000000 --- a/src/lib/tc202/dos-glob.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef GLOB_H -#define GLOB_H - -#ifdef __cplusplus -extern "C" { -#endif - -#define MAX_PATH 254 -#ifndef ENAMETOOLONG -#define ENAMETOOLONG -1 -#endif - -/* Bits set in the FLAGS argument to `glob' */ - -/* Posix */ -#define GLOB_ERR (1 << 0) -#define GLOB_MARK (1 << 1) -#define GLOB_NOSORT (1 << 2) -#define GLOB_DOOFFS (1 << 3) -#define GLOB_NOCHECK (1 << 4) -#define GLOB_APPEND (1 << 5) -#define GLOB_NOESCAPE (1 << 6) -#define GLOB_PERIOD (1 << 7) - -/* Posix2, GNU, BSD */ -#define GLOB_MAGCHAR (1 << 8) -#define GLOB_ALTDIRFUNC (1 << 9) -#define GLOB_BRACE (1 << 10) -#define GLOB_NOMAGIC (1 << 11) -#define GLOB_TILDE (1 << 12) -#define GLOB_ONLYDIR (1 << 13) -#define GLOB_TILDE_CHECK (1 << 14) - -/* Error returns from `glob' */ -#define GLOB_NOSPACE 1 -#define GLOB_ABORTED 2 -#define GLOB_NOMATCH 3 -#define GLOB_NOSYS 4 - -typedef struct glob_t { - unsigned gl_pathc; - char **gl_pathv; - int gl_offs; -} glob_t; - -int glob(const char *pattern, int flags, int (*errfunc)(const char *epath, int eerrno), - glob_t *pglob); -void globfree(glob_t *pglob); - -/* these should not be here technically, they should be in STAT.H */ -#define __S_ISTYPE(mode, mask) (((mode)&S_IFMT) == (mask)) - -#ifndef S_ISDIR -#define S_ISDIR(mode) __S_ISTYPE((mode), S_IFDIR) -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* ifndef GLOB_H */ diff --git a/src/lib/tc202/stdextra.c b/src/lib/tc202/stdextra.c index 9d722aa..40dc721 100644 --- a/src/lib/tc202/stdextra.c +++ b/src/lib/tc202/stdextra.c @@ -1,8 +1,5 @@ #include "lib/tc202/stdextra.h" -#include "fdbox.h" -#include -#include #include #include #include diff --git a/src/lib/tc202/stdextra.h b/src/lib/tc202/stdextra.h index d957b30..c1ffa38 100644 --- a/src/lib/tc202/stdextra.h +++ b/src/lib/tc202/stdextra.h @@ -2,6 +2,7 @@ #define STD_EXTRA #include +#include "fdbox.h" #if defined(__TURBOC__) /* This came in C99, as we do not have it - emulate it lamely */ diff --git a/src/lib/win32/win32-glob.c b/src/lib/win32/win32-glob.c deleted file mode 100644 index 61c7a80..0000000 --- a/src/lib/win32/win32-glob.c +++ /dev/null @@ -1,136 +0,0 @@ -/* libSoX minimal glob for MS-Windows: (c) 2009 SoX contributors - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "win32-glob.h" -#include -#include -#include -#define WIN32_LEAN_AND_MEAN 1 -#include - -typedef struct file_entry { - char name[MAX_PATH]; - struct file_entry *next; -} file_entry; - -static int insert(const char *path, const char *name, file_entry **phead) { - int len; - file_entry *cur = malloc(sizeof(file_entry)); - if (!cur) { - return ENOMEM; - } - - len = _snprintf(cur->name, MAX_PATH, "%s%s", path, name); - cur->name[MAX_PATH - 1] = 0; - cur->next = *phead; - *phead = cur; - - return len < 0 || len >= MAX_PATH ? ENAMETOOLONG : 0; -} - -static int entry_comparer(const void *pv1, const void *pv2) { - const file_entry *const *pe1 = pv1; - const file_entry *const *pe2 = pv2; - return _stricmp((*pe1)->name, (*pe2)->name); -} - -int glob(const char *pattern, int flags, void *unused, glob_t *pglob) { - char path[MAX_PATH]; - file_entry *head = NULL; - int err = 0; - size_t len; - unsigned entries = 0; - WIN32_FIND_DATAA finddata; - HANDLE hfindfile = FindFirstFileA(pattern, &finddata); - - // TODO - why this flag is broken? - if (!pattern /*|| flags != (flags & GLOB_FLAGS)*/ || unused || !pglob) { - errno = EINVAL; - return EINVAL; - } - - path[MAX_PATH - 1] = 0; - strncpy(path, pattern, MAX_PATH); - if (path[MAX_PATH - 1] != 0) { - errno = ENAMETOOLONG; - return ENAMETOOLONG; - } - - len = strlen(path); - while (len > 0 && path[len - 1] != '/' && path[len - 1] != '\\') - len--; - path[len] = 0; - - if (hfindfile == INVALID_HANDLE_VALUE) { - if (flags & GLOB_NOCHECK) { - err = insert("", pattern, &head); - entries++; - } - } else { - do { - err = insert(path, finddata.cFileName, &head); - entries++; - } while (!err && FindNextFileA(hfindfile, &finddata)); - - FindClose(hfindfile); - } - - if (err == 0) { - pglob->gl_pathv = malloc((entries + 1) * sizeof(char *)); - if (pglob->gl_pathv) { - pglob->gl_pathc = entries; - pglob->gl_pathv[entries] = NULL; - for (; head; head = head->next, entries--) - pglob->gl_pathv[entries - 1] = (char *)head; - if ((flags & GLOB_NOCHECK) == 0 ) { - qsort(pglob->gl_pathv, pglob->gl_pathc, sizeof(char *), - entry_comparer); - } - } else { - pglob->gl_pathc = 0; - err = ENOMEM; - } - } else if (pglob) { - pglob->gl_pathc = 0; - pglob->gl_pathv = NULL; - } - - if (err) { - file_entry *cur; - while (head) { - cur = head; - head = head->next; - free(cur); - } - - errno = err; - } - - return err; -} - -void globfree(glob_t *pglob) { - if (pglob) { - char **cur; - for (cur = pglob->gl_pathv; *cur; cur++) { - free(*cur); - } - - pglob->gl_pathc = 0; - pglob->gl_pathv = NULL; - } -} diff --git a/src/lib/win32/win32-glob.h b/src/lib/win32/win32-glob.h deleted file mode 100644 index 537df6d..0000000 --- a/src/lib/win32/win32-glob.h +++ /dev/null @@ -1,67 +0,0 @@ -/* libSoX minimal glob for MS-Windows: (c) 2009 SoX contributors - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef GLOB_H -#define GLOB_H 1 - -#define GLOB_FLAGS (GLOB_NOCHECK) - -/* Bits set in the FLAGS argument to `glob' */ - -/* Posix */ -#define GLOB_ERR (1 << 0) -#define GLOB_MARK (1 << 1) -#define GLOB_NOSORT (1 << 2) -#define GLOB_DOOFFS (1 << 3) -#define GLOB_NOCHECK (1 << 4) -#define GLOB_APPEND (1 << 5) -#define GLOB_NOESCAPE (1 << 6) -#define GLOB_PERIOD (1 << 7) - -/* Posix2, GNU, BSD */ -#define GLOB_MAGCHAR (1 << 8) -#define GLOB_ALTDIRFUNC (1 << 9) -#define GLOB_BRACE (1 << 10) -#define GLOB_NOMAGIC (1 << 11) -#define GLOB_TILDE (1 << 12) -#define GLOB_ONLYDIR (1 << 13) -#define GLOB_TILDE_CHECK (1 << 14) - -/* Error returns from `glob' */ -#define GLOB_NOSPACE 1 -#define GLOB_ABORTED 2 -#define GLOB_NOMATCH 3 -#define GLOB_NOSYS 4 - -typedef struct glob_t { - unsigned gl_pathc; - char **gl_pathv; -} glob_t; - -#ifdef __cplusplus -extern "C" { -#endif - -int glob(const char *pattern, int flags, void *unused, glob_t *pglob); - -void globfree(glob_t *pglob); - -#ifdef __cplusplus -} -#endif - -#endif /* ifndef GLOB_H */ diff --git a/src/main.c b/src/main.c index 8e3194d..cdd434b 100644 --- a/src/main.c +++ b/src/main.c @@ -12,25 +12,25 @@ extern struct applet commands[]; int main(int argc, char *argv[]) { - struct applet *cmd = find_applet(CASE_INSENSITVE, argv[1], commands); - struct command_config config; + struct applet *cmd = find_applet(CASE_INSENSITIVE, argv[1], commands); if (cmd != NULL) { /* shift argument list left, now applet name is argv[0] */ return cmd->handler(argc - 1, ++argv); - } - - command_config_init(&config); - command_config_parse(argc, argv, &config); + } else { + struct command_config config; + command_config_init(&config); + command_config_parse(argc, argv, &config); - if (config.show_help) { - command_help(argc - 1, ++argv); - return EXIT_SUCCESS; - } + if (config.show_help) { + command_help(argc - 1, ++argv); + return EXIT_SUCCESS; + } - /* OK - this is not verbose, but version */ - if (config.verbose) { - return command_ver(argc - 1, ++argv); + /* OK - this is not verbose, but version */ + if (config.verbose) { + return command_ver(argc - 1, ++argv); + } } readline_init(); diff --git a/src/tests.c b/src/tests.c index cd02a42..001b193 100644 --- a/src/tests.c +++ b/src/tests.c @@ -1,611 +1,611 @@ -#include "fdbox.h" -#include "lib/applet.h" -#include "lib/args.h" -#include "lib/environ.h" -#include "lib/strextra.h" -#include "lib/tc202/stdextra.h" - -#include -#include -#include -#include - -typedef bool (*function_test)(); - -bool test(const char *message, function_test unittest); -bool test_str_list(); -bool test_args(); -bool test_args_split(); -bool test_applets(); -bool test_strings(); -bool test_prompts(); -bool test_var_expand(); - -int main(int argc, char *argv[]) { - bool ok = true; - - ok &= test("applets", test_applets); - ok &= test("args", test_args); - ok &= test("strings", test_strings); - ok &= test("prompts", test_prompts); - ok &= test("variables expansion", test_var_expand); - ok &= test("argument split", test_args_split); - ok &= test_str_list(); - UNUSED(argc); - UNUSED(argv); - return ok ? EXIT_SUCCESS : EXIT_FAILURE; -} - -bool test(const char *message, function_test unittest) { - printf("Running test for %s:\n", message); - if (!unittest()) { - printf(" * %s - FAIL\n", message); - return false; - } - return true; -} - -////////////////////////// -// assert like functions. -// TODO : if we convert this to a macro, we will have line numbers - -bool verify_ptr_equals(const void *arg1, const void *arg2, const char *message) { - printf(" * Checking %s: ", message); - if (arg1 == arg2) { - printf("OK\n"); - return true; - } - printf("FAIL (expecting %p, got %p)\n", arg1, arg2); - return false; -} - -bool verify_int_equals(int arg1, int arg2, const char *message) { - printf(" * Checking %s: ", message); - if (arg1 == arg2) { - printf("OK\n"); - return true; - } - printf("FAIL (expecting %d, got %d)\n", arg1, arg2); - return false; -} - -bool verify_string_equals(const char *arg1, const char *arg2, const char *message) { - printf(" * Checking %s: ", message); - if (strcmp(arg1, arg2) == 0) { - printf("OK\n"); - return true; - } - printf("FAIL (expecting %s, got %s)\n", arg1, arg2); - return false; -} - -/////////////////////////////////////////// -bool test_args() { -#define MAX_ARGV 100 - bool ok = true; - char c2[256], *c3; - const char *argv[MAX_ARGV]; - size_t argc = 0; - bool parsing_ok; - - // basic argument parsing - can we create argc/argv? - /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */ - memset(c2, 'x', 256); - c3 = ""; - /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy) */ - strcpy(c2, c3); - parsing_ok = command_split_args(c2, &argc, argv, MAX_ARGV); - ok |= parsing_ok && verify_int_equals(0, argc, "no arguments"); - - /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */ - memset(c2, 'x', 256); - c3 = "asd asd"; - /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy) */ - strcpy(c2, c3); - parsing_ok = command_split_args(c2, &argc, argv, MAX_ARGV); - ok |= parsing_ok && verify_int_equals(2, argc, "2 arguments"); - - /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */ - memset(c2, 'x', 256); - c3 = "111 222 333 44 555 666"; - /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy) */ - strcpy(c2, c3); - parsing_ok = command_split_args(c2, &argc, argv, MAX_ARGV); - ok |= parsing_ok && verify_int_equals(6, argc, "6 arguments, with spaces"); - - /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */ - memset(c2, 'x', 256); - c3 = "dir /w"; - /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy) */ - strcpy(c2, c3); - parsing_ok = command_split_args(c2, &argc, argv, MAX_ARGV); - ok |= parsing_ok && verify_int_equals(2, argc, "2 args - dir /w"); - - /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */ - memset(c2, 'x', 256); - c3 = "dir /w /w"; - /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy) */ - strcpy(c2, c3); - parsing_ok = command_split_args(c2, &argc, argv, MAX_ARGV); - ok |= parsing_ok && verify_int_equals(3, argc, "3 args - dir /w /w"); - - /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */ - memset(c2, 'x', 256); - c3 = "dir /w /2"; - /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy) */ - strcpy(c2, c3); - parsing_ok = command_split_args(c2, &argc, argv, MAX_ARGV); - ok |= parsing_ok && verify_int_equals(3, argc, "3 args - dir /w /2"); - return ok; -} - -bool test_args_split() { - bool ok = true; - struct command_args args; - int rc; - - rc = command_args_split("", &args); - ok |= rc == EXIT_SUCCESS && verify_int_equals(0, args.argc, "no arguments"); - command_args_free(&args); - - rc = command_args_split("x", &args); - ok |= rc == EXIT_SUCCESS && verify_int_equals(1, args.argc, "single argument"); - command_args_free(&args); - - rc = command_args_split("asd asd", &args); - ok |= rc == EXIT_SUCCESS && verify_int_equals(2, args.argc, "2 arguments"); - command_args_free(&args); - - rc = command_args_split("one, two three!", &args); - ok |= rc == EXIT_SUCCESS && verify_int_equals(3, args.argc, "3 words"); - command_args_free(&args); - - rc = command_args_split("111 222 333 44 555 666", &args); - ok |= rc == EXIT_SUCCESS && verify_int_equals(6, args.argc, "6 arguments - spaces"); - command_args_free(&args); - - rc = command_args_split("'one', 'two'", &args); - ok |= rc == EXIT_SUCCESS && verify_int_equals(2, args.argc, "2 args, quoting"); - command_args_free(&args); - - rc = command_args_split("\"1 2\", 'three'", &args); - ok |= rc == EXIT_SUCCESS && verify_int_equals(2, args.argc, "2 args, double quoting"); - command_args_free(&args); - - rc = command_args_split("'t \" a", &args); - ok |= rc == EXIT_SUCCESS && verify_int_equals(1, args.argc, "1 arg, containing open quote"); - command_args_free(&args); - - rc = command_args_split("'one', \"two's three \"", &args); - ok |= rc == EXIT_SUCCESS && verify_int_equals(2, args.argc, "2 arg, containing quote"); - command_args_free(&args); - - rc = command_args_split("'one', \"two's three \" - four", &args); - ok |= rc == EXIT_SUCCESS && - verify_int_equals(4, args.argc, "3 words and '-', containing quote"); - command_args_free(&args); - - rc = command_args_split("dir /w", &args); - ok |= rc == EXIT_SUCCESS && verify_int_equals(2, args.argc, "2 args - dir /w"); - command_args_free(&args); - - rc = command_args_split("dir /w /w", &args); - ok |= rc == EXIT_SUCCESS && verify_int_equals(3, args.argc, "3 args - dir /w /w"); - command_args_free(&args); - - rc = command_args_split("dir /w /2", &args); - ok |= rc == EXIT_SUCCESS && verify_int_equals(3, args.argc, "3 args - dir /w /2"); - command_args_free(&args); - - rc = command_args_split("a=12", &args); - ok |= rc == EXIT_SUCCESS && verify_int_equals(3, args.argc, "3 args - a=12"); - command_args_free(&args); - - return ok; -} - -bool test_var_expand() { - char orig[256]; - char parsed[256]; - bool ok = true; - - strcpy(orig, ""); - expand_string(orig, parsed, sizeof(parsed)); - ok |= verify_string_equals(orig, parsed, "empty string"); - - strcpy(orig, "hello"); - expand_string(orig, parsed, sizeof(parsed)); - ok |= verify_string_equals(orig, parsed, "no vars"); - - strcpy(orig, "[%%]"); - expand_string(orig, parsed, sizeof(parsed)); - ok |= verify_string_equals("[]", parsed, "empty var name"); - - setenv("FOOBAR", "***", 1); - strcpy(orig, "%FOOBAR%"); - expand_string(orig, parsed, sizeof(parsed)); - ok |= verify_string_equals("***", parsed, "normal var"); - - setenv("ZOIP", "abc", 1); - strcpy(orig, "%FOOBAR%%ZOIP%"); - expand_string(orig, parsed, sizeof(parsed)); - ok |= verify_string_equals("***abc", parsed, "2 normal vars"); - - strcpy(orig, "%FOOBAR%[%ZOIP%]"); - expand_string(orig, parsed, sizeof(parsed)); - ok |= verify_string_equals("***[abc]", parsed, "2 normal vars (mixed)"); - - strcpy(orig, "%FOOBAR%"); - expand_string(orig, parsed, 1); - ok |= verify_string_equals("", parsed, "1 var, truncated"); - - strcpy(orig, "%FOOBAR%%ZOIP%"); - expand_string(orig, parsed, 7); - ok |= verify_string_equals("***abc", parsed, "2 vars un-truncated"); - - expand_string(orig, parsed, 6); - ok |= verify_string_equals("***", parsed, "2 vars truncated"); - - return ok; -} - -/////////// applets - -/* clang-format off */ -int applet1(int argc, char *argv[]) { return EXIT_SUCCESS; UNUSED(argc); UNUSED(argv); } - -int applet2(int argc, char *argv[]) { return EXIT_SUCCESS; UNUSED(argc); UNUSED(argv); } - -int applet3(int argc, char *argv[]) { return EXIT_SUCCESS; UNUSED(argc); UNUSED(argv); } -/* clang-format on */ - -bool test_applets() { - /* clang-format off */ - struct applet commands[] = { - {NULL, &applet1, "applet1"}, - {NULL, &applet2, "applet2"}, - {NULL, NULL, NULL} - }; - /* clang-format on */ - bool ok = true; - struct applet *c; - - c = find_applet(CASE_INSENSITVE, "applet1", commands); - ok &= verify_ptr_equals(c->handler, applet1, "applet 1 available"); - c = find_applet(CASE_SENSITVE, "applet2", commands); - ok &= verify_ptr_equals(c->handler, applet2, "applet 2 available"); - c = find_applet(CASE_INSENSITVE, "applet3", commands); - ok &= verify_ptr_equals(c, NULL, "applet 3 un-available"); - c = find_applet(CASE_INSENSITVE, "ApPleT2", commands); - ok &= verify_ptr_equals(c->handler, applet2, "applet 2 (ApPleT2) found (case insensitive)"); - return ok; -} - -bool test_string_lower() { - const char *c; - char str[100]; - bool ok = true; - - c = str_to_lower(strcpy(str, "NULL")); - ok &= verify_string_equals(c, "null", "normal lower"); - c = str_to_lower(strcpy(str, "qwertyuiopQWERTYUIOP")); - ok &= verify_string_equals(c, "qwertyuiopqwertyuiop", "normal lower"); - c = str_to_lower(strcpy(str, "")); - ok &= verify_string_equals(c, strcpy(str, ""), "lower to empty string"); - c = str_to_lower(strcpy(str, "123456")); - ok &= verify_string_equals(c, "123456", "numbers"); - - return ok; -} - -bool test_str_prefix() { - const char *c; - char str[100]; - bool ok = true; - - c = strcpy(str, "Lorem Ipsum"); - ok &= verify_int_equals(str_is_prefix(c, "L"), 1, "string starts with string (1)"); - ok &= verify_int_equals(str_is_prefix(c, "Lorem"), 1, "string starts with string (5)"); - ok &= verify_int_equals(str_is_prefix(c, "XXX"), 0, "string does not start with string"); - ok &= verify_int_equals(str_is_prefix(c, "Lorem Ipsum"), 1, "string starts with itself"); - ok &= verify_int_equals(str_is_prefix(c, ""), 1, "string starts with itself"); - - return ok; -} - -bool test_str_del() { - bool ok = true; - char c[1000]; - - strcpy(c, "hello"); - str_del_char(c, 0); - ok &= verify_string_equals("ello", c, "Deleting first char"); - - strcpy(c, "hello"); - str_del_char(c, 0); - ok &= verify_string_equals("ello", c, "Deleting first char"); - - strcpy(c, "hello"); - str_del_char(c, strlen(c) - 1); - ok &= verify_string_equals("hell", c, "Deleting last char"); - - strcpy(c, "hello"); - str_del_char(c, 3); - ok &= verify_string_equals("helo", c, "Deleting some char"); - - return ok; -} - -bool test_str_ins() { - bool ok = true; - char c[1000]; - - c[0] = 0; - str_ins_char(c, 1000, 'Y', 0); - ok &= verify_string_equals("Y", c, "insert a char to empty string"); - - strcpy(c, "hello"); - str_ins_char(c, 1000, '!', 5); - ok &= verify_string_equals("hello!", c, "insert a char at the end"); - - strcpy(c, "hello"); - str_ins_char(c, 1000, '!', 0); - ok &= verify_string_equals("!hello", c, "insert a char at the beginnig"); - - strcpy(c, "hello"); - str_ins_char(c, 1000, 'A', 1); - ok &= verify_string_equals("hAello", c, "insert in the middle"); - - strcpy(c, "hello"); - str_ins_char(c, 6, '!', 10000); - ok &= verify_string_equals("hello", c, "insert an out of bound char - silently fail"); - - strcpy(c, "124"); - str_ins_char(c, 5, '3', 2); - ok &= verify_string_equals("1234", c, "insert a char in the middle (exact buffer size)"); - - strcpy(c, "13"); - str_ins_char(c, 3, '2', 1); - ok &= verify_string_equals("123", c, "insert a char and the end (truncated)"); - - return ok; -} - -bool test_str_char_suffix() { - bool ok = true; - - ok &= verify_int_equals(str_ends_with("123", '3'), 1, "String ends (digit)"); - ok &= verify_int_equals(str_ends_with("3", '3'), 1, "String ends (digit)"); - ok &= verify_int_equals(str_ends_with("333", '3'), 1, "String ends (digit)"); - ok &= verify_int_equals(str_ends_with("aaa", 'a'), 1, "String ends (alpha)"); - ok &= verify_int_equals(str_ends_with("cba", 'a'), 1, "String ends (alpha)"); - ok &= verify_int_equals(str_ends_with("a", 'a'), 1, "String ends (alpha)"); - ok &= verify_int_equals(str_ends_with("A", 'a'), 0, "String not ends (alpha)"); - ok &= verify_int_equals(str_ends_with("Aa-", 'a'), 0, "String not ends (alpha)"); - ok &= verify_int_equals(str_ends_with("", 'a'), 0, "String not ends (alpha)"); - return ok; -} - -bool test_file_basename() { - char str[100]; - const char *c; - bool ok = true; - - strcpy(str, "file.txt"); - c = file_base_name(str); - ok &= verify_string_equals(c, "file.txt", "full file name"); - - strcpy(str, "c:\\windows\\drivers\\file.txt"); - c = file_base_name(str); - ok &= verify_string_equals(c, "file.txt", "windows path"); - - strcpy(str, "\\windows\\drivers\\file.txt"); - c = file_base_name(str); - ok &= verify_string_equals(c, "file.txt", "windows path (no drive)"); - - strcpy(str, "windows\\drivers\\file.txt"); - c = file_base_name(str); - ok &= verify_string_equals(c, "file.txt", "windows path (relative)"); - - strcpy(str, "..\\..\\windows\\file.txt"); - c = file_base_name(str); - ok &= verify_string_equals(c, "file.txt", "windows path (relative, parent)"); - - strcpy(str, "/tmp/file.txt"); - c = file_base_name(str); - ok &= verify_string_equals(c, "file.txt", "unix path"); - - strcpy(str, "tmp/file.txt"); - c = file_base_name(str); - ok &= verify_string_equals(c, "file.txt", "unix path (relative)"); - - strcpy(str, "../../../tmp/file.txt"); - c = file_base_name(str); - ok &= verify_string_equals(c, "file.txt", "unix path (relative, parent)"); - - strcpy(str, "c:/test/messedup/..\\windows.exe.manifest"); - c = file_base_name(str); - ok &= verify_string_equals(c, "windows.exe.manifest", "mixed path (unix/windows, parent)"); - - return ok; -} - -bool test_file_extensions() { - char str[100]; - const char *c; - bool ok = true; - - strcpy(str, "file.txt"); - c = file_get_extension(str); - ok &= verify_string_equals(c, "txt", "getting normal 8.3 extention"); - - strcpy(str, "/var/lib/file.txt"); - c = file_get_extension(str); - ok &= verify_string_equals(c, "txt", "getting normal 8.3 extention + path"); - - strcpy(str, "/var/lib/file.text"); - c = file_get_extension(str); - ok &= verify_string_equals(c, "text", "getting normal long extention + path"); - - strcpy(str, "/var/lib/file.txt.text.file.blabla"); - c = file_get_extension(str); - ok &= verify_string_equals(c, "blabla", "getting very long extention + path"); - - return ok; -} - -bool test_strings() { - bool ok = true; - ok &= test_string_lower(); - ok &= test_str_prefix(); - ok &= test_str_del(); - ok &= test_str_ins(); - ok &= test_str_char_suffix(); - ok &= test_file_basename(); - ok &= test_file_extensions(); - return ok; -} - -bool test_str_list() { - bool ok = true; - struct str_list list; - char *c1; - const char *c2; - - str_list_init(&list, 5); - c1 = str_list_pop(&list); - ok &= verify_ptr_equals(c1, NULL, "pop from empty list"); - - str_list_push(&list, "aaa"); - c1 = str_list_pop(&list); - ok &= verify_string_equals(c1, "aaa", "push and pop"); - free(c1); - c1 = str_list_pop(&list); - ok &= verify_ptr_equals(c1, NULL, "pop after push"); - - str_list_push(&list, "bbb"); - c2 = str_list_get(&list, 0); - ok &= verify_string_equals(c2, "bbb", "get from a single item list"); - c1 = str_list_pop(&list); - ok &= verify_string_equals(c1, "bbb", "pop after fetch"); - free(c1); - c1 = str_list_pop(&list); - ok &= verify_ptr_equals(c1, NULL, "pop after push, and get"); - - str_list_push(&list, "111"); - str_list_push(&list, "222"); - str_list_push(&list, "333"); - str_list_push(&list, "444"); - c1 = str_list_pop(&list); - free(c1); - c1 = str_list_pop(&list); - free(c1); - c1 = str_list_pop(&list); - free(c1); - c1 = str_list_pop(&list); - ok &= verify_string_equals(c1, "111", "pop after fetch"); - free(c1); - c1 = str_list_pop(&list); - ok &= verify_ptr_equals(c1, NULL, "pop after 4 pushes"); - - str_list_push(&list, "111"); - str_list_push(&list, "222"); - str_list_push(&list, "333"); - str_list_push(&list, "444"); - str_list_push(&list, "555"); - - c2 = str_list_get(&list, 0); - ok &= verify_string_equals(c2, "555", "fetch from 1/5 list"); - c2 = str_list_get(&list, 1); - ok &= verify_string_equals(c2, "444", "fetch from 2/5 list"); - c2 = str_list_get(&list, 2); - ok &= verify_string_equals(c2, "333", "fetch from 3/5 list"); - c2 = str_list_get(&list, 3); - ok &= verify_string_equals(c2, "222", "fetch from 4/5 list"); - c2 = str_list_get(&list, 4); - ok &= verify_string_equals(c2, "111", "fetch from 5/5 list"); - - str_list_push(&list, "666"); - c2 = str_list_get(&list, 0); - ok &= verify_string_equals(c2, "666", "fetch from 6/5 list"); - c2 = str_list_get(&list, 1); - ok &= verify_string_equals(c2, "555", "fetch from 6/5 list"); - c2 = str_list_get(&list, 2); - ok &= verify_string_equals(c2, "444", "fetch from 6/5 list"); - c2 = str_list_get(&list, 3); - ok &= verify_string_equals(c2, "333", "fetch from 6/5 list"); - c2 = str_list_get(&list, 4); - ok &= verify_string_equals(c2, "222", "fetch from 6/5 list"); - c2 = str_list_get(&list, 5); - ok &= verify_ptr_equals(c2, NULL, "fetch out of bounds"); - - c1 = str_list_pop(&list); - ok &= verify_string_equals(c1, "666", "pop from 6/5 list"); - free(c1); - c1 = str_list_pop(&list); - ok &= verify_string_equals(c1, "555", "pop from 6/5 list"); - free(c1); - c1 = str_list_pop(&list); - ok &= verify_string_equals(c1, "444", "pop from 6/5 list"); - free(c1); - c1 = str_list_pop(&list); - ok &= verify_string_equals(c1, "333", "pop from 6/5 list"); - free(c1); - c1 = str_list_pop(&list); - ok &= verify_string_equals(c1, "222", "pop from 6/5 list"); - free(c1); - c1 = str_list_pop(&list); - ok &= verify_ptr_equals(c1, NULL, "pop after 6/5 pushes"); - free(c1); - - str_list_free(&list); - - return ok; -} - -bool test_prompt_letter(char l, char k) { - bool ok = true; - char p1[16], p2[16]; - char message[100]; - int i; - - snprintf(p1, 16, "$%c", l); - get_prompt(p1, p2, 256); - i = strlen(p2); - snprintf(message, 100, "prompt size for $%c", l); - ok &= verify_int_equals(i, 1, message); - snprintf(message, 100, "char $%c = %d", l, k); - ok &= verify_int_equals(p2[0], k, message); - return ok; -} - -bool test_prompts() { - bool ok = true; - char prompt[100], message[200]; - - ok &= test_prompt_letter('$', '$'); - ok &= test_prompt_letter('_', '\n'); - ok &= test_prompt_letter('a', '&'); - ok &= test_prompt_letter('b', '|'); - ok &= test_prompt_letter('c', '('); - ok &= test_prompt_letter('e', 27); - ok &= test_prompt_letter('g', '>'); - ok &= test_prompt_letter('h', 8); - ok &= test_prompt_letter('l', '<'); - ok &= test_prompt_letter('q', '='); - ok &= test_prompt_letter('s', ' '); - - /* TODO */ - /* date */ - /* time */ - /* drive */ - - get_prompt("$V", prompt, 100); - ok &= verify_int_equals(strlen(prompt), strlen(FDBOX_VERSION_STR), - "testing prompt version length"); - snprintf(message, 200, "testing for prompt version, get %s, expected %s", prompt, - FDBOX_VERSION_STR); - ok &= verify_string_equals(prompt, FDBOX_VERSION_STR, message); - - return ok; -} +#include "fdbox.h" +#include "lib/applet.h" +#include "lib/args.h" +#include "lib/environ.h" +#include "lib/strextra.h" +#include "lib/tc202/stdextra.h" + +#include +#include +#include +#include + +typedef bool (*function_test)(); + +bool test(const char *message, function_test unittest); +bool test_str_list(); +bool test_args(); +bool test_args_split(); +bool test_applets(); +bool test_strings(); +bool test_prompts(); +bool test_var_expand(); + +int main(int argc, char *argv[]) { + bool ok = true; + + ok &= test("applets", test_applets); + ok &= test("args", test_args); + ok &= test("strings", test_strings); + ok &= test("prompts", test_prompts); + ok &= test("variables expansion", test_var_expand); + ok &= test("argument split", test_args_split); + ok &= test_str_list(); + UNUSED(argc); + UNUSED(argv); + return ok ? EXIT_SUCCESS : EXIT_FAILURE; +} + +bool test(const char *message, function_test unittest) { + printf("Running test for %s:\n", message); + if (!unittest()) { + printf(" * %s - FAIL\n", message); + return false; + } + return true; +} + +////////////////////////// +// assert like functions. +// TODO : if we convert this to a macro, we will have line numbers + +bool verify_ptr_equals(const void *arg1, const void *arg2, const char *message) { + printf(" * Checking %s: ", message); + if (arg1 == arg2) { + printf("OK\n"); + return true; + } + printf("FAIL (expecting %p, got %p)\n", arg1, arg2); + return false; +} + +bool verify_int_equals(int arg1, int arg2, const char *message) { + printf(" * Checking %s: ", message); + if (arg1 == arg2) { + printf("OK\n"); + return true; + } + printf("FAIL (expecting %d, got %d)\n", arg1, arg2); + return false; +} + +bool verify_string_equals(const char *arg1, const char *arg2, const char *message) { + printf(" * Checking %s: ", message); + if (strcmp(arg1, arg2) == 0) { + printf("OK\n"); + return true; + } + printf("FAIL (expecting %s, got %s)\n", arg1, arg2); + return false; +} + +/////////////////////////////////////////// +bool test_args() { +#define MAX_ARGV 100 + bool ok = true; + char c2[256], *c3; + const char *argv[MAX_ARGV]; + size_t argc = 0; + bool parsing_ok; + + // basic argument parsing - can we create argc/argv? + /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */ + memset(c2, 'x', 256); + c3 = ""; + /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy) */ + strcpy(c2, c3); + parsing_ok = command_split_args(c2, &argc, argv, MAX_ARGV); + ok |= parsing_ok && verify_int_equals(0, argc, "no arguments"); + + /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */ + memset(c2, 'x', 256); + c3 = "asd asd"; + /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy) */ + strcpy(c2, c3); + parsing_ok = command_split_args(c2, &argc, argv, MAX_ARGV); + ok |= parsing_ok && verify_int_equals(2, argc, "2 arguments"); + + /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */ + memset(c2, 'x', 256); + c3 = "111 222 333 44 555 666"; + /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy) */ + strcpy(c2, c3); + parsing_ok = command_split_args(c2, &argc, argv, MAX_ARGV); + ok |= parsing_ok && verify_int_equals(6, argc, "6 arguments, with spaces"); + + /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */ + memset(c2, 'x', 256); + c3 = "dir /w"; + /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy) */ + strcpy(c2, c3); + parsing_ok = command_split_args(c2, &argc, argv, MAX_ARGV); + ok |= parsing_ok && verify_int_equals(2, argc, "2 args - dir /w"); + + /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */ + memset(c2, 'x', 256); + c3 = "dir /w /w"; + /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy) */ + strcpy(c2, c3); + parsing_ok = command_split_args(c2, &argc, argv, MAX_ARGV); + ok |= parsing_ok && verify_int_equals(3, argc, "3 args - dir /w /w"); + + /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */ + memset(c2, 'x', 256); + c3 = "dir /w /2"; + /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy) */ + strcpy(c2, c3); + parsing_ok = command_split_args(c2, &argc, argv, MAX_ARGV); + ok |= parsing_ok && verify_int_equals(3, argc, "3 args - dir /w /2"); + return ok; +} + +bool test_args_split() { + bool ok = true; + struct command_args args; + int rc; + + rc = command_args_split("", &args); + ok |= rc == EXIT_SUCCESS && verify_int_equals(0, args.argc, "no arguments"); + command_args_free(&args); + + rc = command_args_split("x", &args); + ok |= rc == EXIT_SUCCESS && verify_int_equals(1, args.argc, "single argument"); + command_args_free(&args); + + rc = command_args_split("asd asd", &args); + ok |= rc == EXIT_SUCCESS && verify_int_equals(2, args.argc, "2 arguments"); + command_args_free(&args); + + rc = command_args_split("one, two three!", &args); + ok |= rc == EXIT_SUCCESS && verify_int_equals(3, args.argc, "3 words"); + command_args_free(&args); + + rc = command_args_split("111 222 333 44 555 666", &args); + ok |= rc == EXIT_SUCCESS && verify_int_equals(6, args.argc, "6 arguments - spaces"); + command_args_free(&args); + + rc = command_args_split("'one', 'two'", &args); + ok |= rc == EXIT_SUCCESS && verify_int_equals(2, args.argc, "2 args, quoting"); + command_args_free(&args); + + rc = command_args_split("\"1 2\", 'three'", &args); + ok |= rc == EXIT_SUCCESS && verify_int_equals(2, args.argc, "2 args, double quoting"); + command_args_free(&args); + + rc = command_args_split("'t \" a", &args); + ok |= rc == EXIT_SUCCESS && verify_int_equals(1, args.argc, "1 arg, containing open quote"); + command_args_free(&args); + + rc = command_args_split("'one', \"two's three \"", &args); + ok |= rc == EXIT_SUCCESS && verify_int_equals(2, args.argc, "2 arg, containing quote"); + command_args_free(&args); + + rc = command_args_split("'one', \"two's three \" - four", &args); + ok |= rc == EXIT_SUCCESS && + verify_int_equals(4, args.argc, "3 words and '-', containing quote"); + command_args_free(&args); + + rc = command_args_split("dir /w", &args); + ok |= rc == EXIT_SUCCESS && verify_int_equals(2, args.argc, "2 args - dir /w"); + command_args_free(&args); + + rc = command_args_split("dir /w /w", &args); + ok |= rc == EXIT_SUCCESS && verify_int_equals(3, args.argc, "3 args - dir /w /w"); + command_args_free(&args); + + rc = command_args_split("dir /w /2", &args); + ok |= rc == EXIT_SUCCESS && verify_int_equals(3, args.argc, "3 args - dir /w /2"); + command_args_free(&args); + + rc = command_args_split("a=12", &args); + ok |= rc == EXIT_SUCCESS && verify_int_equals(3, args.argc, "3 args - a=12"); + command_args_free(&args); + + return ok; +} + +bool test_var_expand() { + char orig[256]; + char parsed[256]; + bool ok = true; + + strcpy(orig, ""); + expand_string(orig, parsed, sizeof(parsed)); + ok |= verify_string_equals(orig, parsed, "empty string"); + + strcpy(orig, "hello"); + expand_string(orig, parsed, sizeof(parsed)); + ok |= verify_string_equals(orig, parsed, "no vars"); + + strcpy(orig, "[%%]"); + expand_string(orig, parsed, sizeof(parsed)); + ok |= verify_string_equals("[]", parsed, "empty var name"); + + setenv("FOOBAR", "***", 1); + strcpy(orig, "%FOOBAR%"); + expand_string(orig, parsed, sizeof(parsed)); + ok |= verify_string_equals("***", parsed, "normal var"); + + setenv("ZOIP", "abc", 1); + strcpy(orig, "%FOOBAR%%ZOIP%"); + expand_string(orig, parsed, sizeof(parsed)); + ok |= verify_string_equals("***abc", parsed, "2 normal vars"); + + strcpy(orig, "%FOOBAR%[%ZOIP%]"); + expand_string(orig, parsed, sizeof(parsed)); + ok |= verify_string_equals("***[abc]", parsed, "2 normal vars (mixed)"); + + strcpy(orig, "%FOOBAR%"); + expand_string(orig, parsed, 1); + ok |= verify_string_equals("", parsed, "1 var, truncated"); + + strcpy(orig, "%FOOBAR%%ZOIP%"); + expand_string(orig, parsed, 7); + ok |= verify_string_equals("***abc", parsed, "2 vars un-truncated"); + + expand_string(orig, parsed, 6); + ok |= verify_string_equals("***", parsed, "2 vars truncated"); + + return ok; +} + +/////////// applets + +/* clang-format off */ +int applet1(int argc, char *argv[]) { return EXIT_SUCCESS; UNUSED(argc); UNUSED(argv); } + +int applet2(int argc, char *argv[]) { return EXIT_SUCCESS; UNUSED(argc); UNUSED(argv); } + +int applet3(int argc, char *argv[]) { return EXIT_SUCCESS; UNUSED(argc); UNUSED(argv); } +/* clang-format on */ + +bool test_applets() { + /* clang-format off */ + struct applet commands[] = { + {NULL, &applet1, "applet1"}, + {NULL, &applet2, "applet2"}, + {NULL, NULL, NULL} + }; + /* clang-format on */ + bool ok = true; + struct applet *c; + + c = find_applet(CASE_INSENSITIVE, "applet1", commands); + ok &= verify_ptr_equals(c->handler, applet1, "applet 1 available"); + c = find_applet(CASE_SENSITIVE, "applet2", commands); + ok &= verify_ptr_equals(c->handler, applet2, "applet 2 available"); + c = find_applet(CASE_INSENSITIVE, "applet3", commands); + ok &= verify_ptr_equals(c, NULL, "applet 3 un-available"); + c = find_applet(CASE_INSENSITIVE, "ApPleT2", commands); + ok &= verify_ptr_equals(c->handler, applet2, "applet 2 (ApPleT2) found (case insensitive)"); + return ok; +} + +bool test_string_lower() { + const char *c; + char str[100]; + bool ok = true; + + c = str_to_lower(strcpy(str, "NULL")); + ok &= verify_string_equals(c, "null", "normal lower"); + c = str_to_lower(strcpy(str, "qwertyuiopQWERTYUIOP")); + ok &= verify_string_equals(c, "qwertyuiopqwertyuiop", "normal lower"); + c = str_to_lower(strcpy(str, "")); + ok &= verify_string_equals(c, strcpy(str, ""), "lower to empty string"); + c = str_to_lower(strcpy(str, "123456")); + ok &= verify_string_equals(c, "123456", "numbers"); + + return ok; +} + +bool test_str_prefix() { + const char *c; + char str[100]; + bool ok = true; + + c = strcpy(str, "Lorem Ipsum"); + ok &= verify_int_equals(str_is_prefix(c, "L"), 1, "string starts with string (1)"); + ok &= verify_int_equals(str_is_prefix(c, "Lorem"), 1, "string starts with string (5)"); + ok &= verify_int_equals(str_is_prefix(c, "XXX"), 0, "string does not start with string"); + ok &= verify_int_equals(str_is_prefix(c, "Lorem Ipsum"), 1, "string starts with itself"); + ok &= verify_int_equals(str_is_prefix(c, ""), 1, "string starts with itself"); + + return ok; +} + +bool test_str_del() { + bool ok = true; + char c[1000]; + + strcpy(c, "hello"); + str_del_char(c, 0); + ok &= verify_string_equals("ello", c, "Deleting first char"); + + strcpy(c, "hello"); + str_del_char(c, 0); + ok &= verify_string_equals("ello", c, "Deleting first char"); + + strcpy(c, "hello"); + str_del_char(c, strlen(c) - 1); + ok &= verify_string_equals("hell", c, "Deleting last char"); + + strcpy(c, "hello"); + str_del_char(c, 3); + ok &= verify_string_equals("helo", c, "Deleting some char"); + + return ok; +} + +bool test_str_ins() { + bool ok = true; + char c[1000]; + + c[0] = 0; + str_ins_char(c, 1000, 'Y', 0); + ok &= verify_string_equals("Y", c, "insert a char to empty string"); + + strcpy(c, "hello"); + str_ins_char(c, 1000, '!', 5); + ok &= verify_string_equals("hello!", c, "insert a char at the end"); + + strcpy(c, "hello"); + str_ins_char(c, 1000, '!', 0); + ok &= verify_string_equals("!hello", c, "insert a char at the beginnig"); + + strcpy(c, "hello"); + str_ins_char(c, 1000, 'A', 1); + ok &= verify_string_equals("hAello", c, "insert in the middle"); + + strcpy(c, "hello"); + str_ins_char(c, 6, '!', 10000); + ok &= verify_string_equals("hello", c, "insert an out of bound char - silently fail"); + + strcpy(c, "124"); + str_ins_char(c, 5, '3', 2); + ok &= verify_string_equals("1234", c, "insert a char in the middle (exact buffer size)"); + + strcpy(c, "13"); + str_ins_char(c, 3, '2', 1); + ok &= verify_string_equals("123", c, "insert a char and the end (truncated)"); + + return ok; +} + +bool test_str_char_suffix() { + bool ok = true; + + ok &= verify_int_equals(str_ends_with("123", '3'), 1, "String ends (digit)"); + ok &= verify_int_equals(str_ends_with("3", '3'), 1, "String ends (digit)"); + ok &= verify_int_equals(str_ends_with("333", '3'), 1, "String ends (digit)"); + ok &= verify_int_equals(str_ends_with("aaa", 'a'), 1, "String ends (alpha)"); + ok &= verify_int_equals(str_ends_with("cba", 'a'), 1, "String ends (alpha)"); + ok &= verify_int_equals(str_ends_with("a", 'a'), 1, "String ends (alpha)"); + ok &= verify_int_equals(str_ends_with("A", 'a'), 0, "String not ends (alpha)"); + ok &= verify_int_equals(str_ends_with("Aa-", 'a'), 0, "String not ends (alpha)"); + ok &= verify_int_equals(str_ends_with("", 'a'), 0, "String not ends (alpha)"); + return ok; +} + +bool test_file_basename() { + char str[100]; + const char *c; + bool ok = true; + + strcpy(str, "file.txt"); + c = file_base_name(str); + ok &= verify_string_equals(c, "file.txt", "full file name"); + + strcpy(str, "c:\\windows\\drivers\\file.txt"); + c = file_base_name(str); + ok &= verify_string_equals(c, "file.txt", "windows path"); + + strcpy(str, "\\windows\\drivers\\file.txt"); + c = file_base_name(str); + ok &= verify_string_equals(c, "file.txt", "windows path (no drive)"); + + strcpy(str, "windows\\drivers\\file.txt"); + c = file_base_name(str); + ok &= verify_string_equals(c, "file.txt", "windows path (relative)"); + + strcpy(str, "..\\..\\windows\\file.txt"); + c = file_base_name(str); + ok &= verify_string_equals(c, "file.txt", "windows path (relative, parent)"); + + strcpy(str, "/tmp/file.txt"); + c = file_base_name(str); + ok &= verify_string_equals(c, "file.txt", "unix path"); + + strcpy(str, "tmp/file.txt"); + c = file_base_name(str); + ok &= verify_string_equals(c, "file.txt", "unix path (relative)"); + + strcpy(str, "../../../tmp/file.txt"); + c = file_base_name(str); + ok &= verify_string_equals(c, "file.txt", "unix path (relative, parent)"); + + strcpy(str, "c:/test/messedup/..\\windows.exe.manifest"); + c = file_base_name(str); + ok &= verify_string_equals(c, "windows.exe.manifest", "mixed path (unix/windows, parent)"); + + return ok; +} + +bool test_file_extensions() { + char str[100]; + const char *c; + bool ok = true; + + strcpy(str, "file.txt"); + c = file_get_extension(str); + ok &= verify_string_equals(c, "txt", "getting normal 8.3 extention"); + + strcpy(str, "/var/lib/file.txt"); + c = file_get_extension(str); + ok &= verify_string_equals(c, "txt", "getting normal 8.3 extention + path"); + + strcpy(str, "/var/lib/file.text"); + c = file_get_extension(str); + ok &= verify_string_equals(c, "text", "getting normal long extention + path"); + + strcpy(str, "/var/lib/file.txt.text.file.blabla"); + c = file_get_extension(str); + ok &= verify_string_equals(c, "blabla", "getting very long extention + path"); + + return ok; +} + +bool test_strings() { + bool ok = true; + ok &= test_string_lower(); + ok &= test_str_prefix(); + ok &= test_str_del(); + ok &= test_str_ins(); + ok &= test_str_char_suffix(); + ok &= test_file_basename(); + ok &= test_file_extensions(); + return ok; +} + +bool test_str_list() { + bool ok = true; + struct str_list list; + char *c1; + const char *c2; + + str_list_init(&list, 5); + c1 = str_list_pop(&list); + ok &= verify_ptr_equals(c1, NULL, "pop from empty list"); + + str_list_push(&list, "aaa"); + c1 = str_list_pop(&list); + ok &= verify_string_equals(c1, "aaa", "push and pop"); + free(c1); + c1 = str_list_pop(&list); + ok &= verify_ptr_equals(c1, NULL, "pop after push"); + + str_list_push(&list, "bbb"); + c2 = str_list_get(&list, 0); + ok &= verify_string_equals(c2, "bbb", "get from a single item list"); + c1 = str_list_pop(&list); + ok &= verify_string_equals(c1, "bbb", "pop after fetch"); + free(c1); + c1 = str_list_pop(&list); + ok &= verify_ptr_equals(c1, NULL, "pop after push, and get"); + + str_list_push(&list, "111"); + str_list_push(&list, "222"); + str_list_push(&list, "333"); + str_list_push(&list, "444"); + c1 = str_list_pop(&list); + free(c1); + c1 = str_list_pop(&list); + free(c1); + c1 = str_list_pop(&list); + free(c1); + c1 = str_list_pop(&list); + ok &= verify_string_equals(c1, "111", "pop after fetch"); + free(c1); + c1 = str_list_pop(&list); + ok &= verify_ptr_equals(c1, NULL, "pop after 4 pushes"); + + str_list_push(&list, "111"); + str_list_push(&list, "222"); + str_list_push(&list, "333"); + str_list_push(&list, "444"); + str_list_push(&list, "555"); + + c2 = str_list_get(&list, 0); + ok &= verify_string_equals(c2, "555", "fetch from 1/5 list"); + c2 = str_list_get(&list, 1); + ok &= verify_string_equals(c2, "444", "fetch from 2/5 list"); + c2 = str_list_get(&list, 2); + ok &= verify_string_equals(c2, "333", "fetch from 3/5 list"); + c2 = str_list_get(&list, 3); + ok &= verify_string_equals(c2, "222", "fetch from 4/5 list"); + c2 = str_list_get(&list, 4); + ok &= verify_string_equals(c2, "111", "fetch from 5/5 list"); + + str_list_push(&list, "666"); + c2 = str_list_get(&list, 0); + ok &= verify_string_equals(c2, "666", "fetch from 6/5 list"); + c2 = str_list_get(&list, 1); + ok &= verify_string_equals(c2, "555", "fetch from 6/5 list"); + c2 = str_list_get(&list, 2); + ok &= verify_string_equals(c2, "444", "fetch from 6/5 list"); + c2 = str_list_get(&list, 3); + ok &= verify_string_equals(c2, "333", "fetch from 6/5 list"); + c2 = str_list_get(&list, 4); + ok &= verify_string_equals(c2, "222", "fetch from 6/5 list"); + c2 = str_list_get(&list, 5); + ok &= verify_ptr_equals(c2, NULL, "fetch out of bounds"); + + c1 = str_list_pop(&list); + ok &= verify_string_equals(c1, "666", "pop from 6/5 list"); + free(c1); + c1 = str_list_pop(&list); + ok &= verify_string_equals(c1, "555", "pop from 6/5 list"); + free(c1); + c1 = str_list_pop(&list); + ok &= verify_string_equals(c1, "444", "pop from 6/5 list"); + free(c1); + c1 = str_list_pop(&list); + ok &= verify_string_equals(c1, "333", "pop from 6/5 list"); + free(c1); + c1 = str_list_pop(&list); + ok &= verify_string_equals(c1, "222", "pop from 6/5 list"); + free(c1); + c1 = str_list_pop(&list); + ok &= verify_ptr_equals(c1, NULL, "pop after 6/5 pushes"); + free(c1); + + str_list_free(&list); + + return ok; +} + +bool test_prompt_letter(char l, char k) { + bool ok = true; + char p1[16], p2[16]; + char message[100]; + int i; + + snprintf(p1, 16, "$%c", l); + get_prompt(p1, p2, 256); + i = strlen(p2); + snprintf(message, 100, "prompt size for $%c", l); + ok &= verify_int_equals(i, 1, message); + snprintf(message, 100, "char $%c = %d", l, k); + ok &= verify_int_equals(p2[0], k, message); + return ok; +} + +bool test_prompts() { + bool ok = true; + char prompt[100], message[200]; + + ok &= test_prompt_letter('$', '$'); + ok &= test_prompt_letter('_', '\n'); + ok &= test_prompt_letter('a', '&'); + ok &= test_prompt_letter('b', '|'); + ok &= test_prompt_letter('c', '('); + ok &= test_prompt_letter('e', 27); + ok &= test_prompt_letter('g', '>'); + ok &= test_prompt_letter('h', 8); + ok &= test_prompt_letter('l', '<'); + ok &= test_prompt_letter('q', '='); + ok &= test_prompt_letter('s', ' '); + + /* TODO */ + /* date */ + /* time */ + /* drive */ + + get_prompt("$V", prompt, 100); + ok &= verify_int_equals(strlen(prompt), strlen(FDBOX_VERSION_STR), + "testing prompt version length"); + snprintf(message, 200, "testing for prompt version, get %s, expected %s", prompt, + FDBOX_VERSION_STR); + ok &= verify_string_equals(prompt, FDBOX_VERSION_STR, message); + + return ok; +}