/* * JoeyDev * Copyright (C) 2018-2023 Scott Duensing * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include #include "common.h" #include "utils.h" #include "cwalk.h" #include "messages.h" #ifdef _WIN32 #define ourMkdir(p,m) mkdir(p) #else #define ourMkdir mkdir #endif typedef struct WindowListS { GtkWidget *key; WindowDataT *value; } WindowListT; static ArchiveT **_activeArchives = NULL; static WindowListT *_windowList = NULL; char __utilFilenameBuffer[FILENAME_MAX]; gboolean utilDecompressUpdate(gpointer userData); // Not static void utilAddTextToListBox(GtkListBox *list, char *text) { GtkWidget *row; GtkWidget *box; GtkWidget *label; row = gtk_list_box_row_new(); box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); gtk_widget_set_hexpand(box, TRUE); label = gtk_label_new(text); gtk_label_set_use_markup(GTK_LABEL(label), TRUE); // Add new row to the message box. gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0); gtk_container_add(GTK_CONTAINER(row), box); gtk_list_box_insert(GTK_LIST_BOX(list), row, -1); gtk_widget_show_all(list); } void utilClearContainer(GtkContainer *container) { GList *children; GList *iter; children = gtk_container_get_children(GTK_CONTAINER(container)); for (iter = children; iter != NULL; iter = g_list_next(iter)) { gtk_widget_destroy(GTK_WIDGET(iter->data)); } g_list_free(children); } char *utilCreateString(char *format, ...) { va_list args; char *string; va_start(args, format); string = utilCreateStringVArgs(format, args); va_end(args); return string; } __attribute__((__format__(__printf__, 1, 0))) char *utilCreateStringVArgs(char *format, va_list args) { va_list argsCopy; int32_t size; char *buffer = NULL; va_copy(argsCopy, args); size = vsnprintf(NULL, 0, format, argsCopy) + 1; va_end(argsCopy); buffer = calloc(1, (size_t)size); if (buffer) { vsnprintf(buffer, (size_t)size, format, args); } //debug("%s = %s\n", format, buffer); return buffer; } ArchiveT *utilDecompress(char *archive, char *outPath, archiveCallback callback, void *userData) { ArchiveT *a = NULL; int i; // Do not allow two of the same archive to be started. for (i=0; iarchive, archive) == 0) { // Return existing transfer handle. return _activeArchives[i]; } } a = NEW(ArchiveT); a->in = archive_read_new(); archive_read_support_filter_bzip2(a->in); archive_read_support_format_gnutar(a->in); a->out = archive_write_disk_new(); archive_write_disk_set_options(a->out, 0); archive_write_disk_set_standard_lookup(a->out); a->entry = NULL; a->result = 0; a->compression = 0; a->fileCount = 0; a->compressed = 0; a->uncompressed = 0; a->archive = utilFileBasename(archive); a->outPath = strdup(outPath); a->currentFile = NULL; a->copying = FALSE; a->finished = FALSE; a->success = FALSE; a->callback = callback; a->userData = userData; a->size = utilFileSize(archive); a->result = archive_read_open_filename(a->in, archive, 16384); if (a->result == ARCHIVE_OK) { arrput(_activeArchives, a); return a; } debug("%s\n", archive_error_string(a->in)); DEL(a->archive); DEL(a->outPath); DEL(a); return NULL; } gboolean utilDecompressUpdate(gpointer userData) { ArchiveT *a; const void *buff; size_t size; la_int64_t offset; int i; int toClose = -1; char *fullOutputPath; (void)userData; // Anything to process? for (i=0; ifinished) { a->success = FALSE; toClose = i; break; } // Update stats. if (a->in != NULL) { a->compressed = archive_filter_bytes(a->in, -1); a->uncompressed = archive_filter_bytes(a->in, 0); if (a->compressed > a->uncompressed) { a->compression = 0; } else { a->compression = (int)((a->uncompressed - a->compressed) * 100 / a->uncompressed); } a->fileCount = archive_file_count(a->in); } if (a->entry != NULL) { DEL(a->currentFile); a->currentFile = strdup(archive_entry_pathname(a->entry)); } // Are we unpacking, or do we need the next file? if (a->copying) { a->result = archive_read_data_block(a->in, &buff, &size, &offset); if (a->result == ARCHIVE_EOF) { a->copying = FALSE; a->result = archive_write_finish_entry(a->out); if (a->result < ARCHIVE_WARN) { a->finished = TRUE; a->success = FALSE; toClose = i; } break; } if (a->result < ARCHIVE_OK) { a->finished = TRUE; a->success = FALSE; toClose = i; break; } a->result = archive_write_data_block(a->out, buff, size, offset); if (a->result < ARCHIVE_OK) { a->finished = TRUE; a->success = FALSE; toClose = i; break; } } else { // Get next file to unpack. a->result = archive_read_next_header(a->in, &a->entry); if (a->result == ARCHIVE_EOF) { a->finished = TRUE; a->success = TRUE; toClose = i; break; } if (a->result < ARCHIVE_WARN) { a->finished = TRUE; a->success = FALSE; toClose = i; break; } // Add path to output filename. fullOutputPath = utilCreateString("%s%s", a->outPath, archive_entry_pathname(a->entry)); archive_entry_set_pathname(a->entry, fullOutputPath); DEL(fullOutputPath); // Create output file. a->result = archive_write_header(a->out, a->entry); if (a->result != ARCHIVE_OK) { a->finished = TRUE; a->success = FALSE; toClose = i; break; } // Is there data to decompress? if (archive_entry_size(a->entry) > 0) { // Start copying decompressed data. a->copying = TRUE; } } } if (toClose >= 0) { a = _activeArchives[toClose]; arrdel(_activeArchives, toClose); if (a->callback) { a->callback(a); } archive_read_close(a->in); archive_read_free(a->in); archive_write_close(a->out); archive_write_free(a->out); DEL(a->archive); DEL(a->outPath); DEL(a->currentFile); DEL(a); } return G_SOURCE_CONTINUE; } int utilDeleteTree(const char *path) { DIR *directory = opendir(path); char *filename = NULL; struct dirent *entry; int (*removeFunc)(const char *); if (directory) { while ((entry = readdir(directory))) { if (!strcmp(".", entry->d_name) || !strcmp("..", entry->d_name)) { continue; } filename = utilCreateString("%s/%s", path, entry->d_name); removeFunc = entry->d_type == DT_DIR ? utilDeleteTree : remove; if (removeFunc(filename)) { DEL(filename); closedir(directory); return -1; } DEL(filename); } if (closedir(directory)) { return -1; } } return remove(path); } char *utilDeobfuscateASCII(char *obfuscated) { char *deobfuscated = NULL; char *hostname; int i; int j; int c; // Valid ASCII 32 - 126 (94 chars) hostname = strdup(g_get_host_name()); // Decrypt. j = 0; deobfuscated = (char *)malloc((strlen(obfuscated) + 1) * sizeof(char)); for (i=0; i= strlen(hostname)) j = 0; } deobfuscated[i] = 0; DEL(hostname); return deobfuscated; } void utilDequote(char *string) { int x; if (string[0] == '"' || string[strlen(string) - 1] == '"') { string[strlen(string) - 1] = 0; for (x=1; x= 0 && path[name] != '/') name--; target = utilCreateString("%s%s", __resourcePath, &path[name + 1]); if (!utilFileExists(target)) { utilEnsureBufferSize(&buffer, (int *)&bytes, 8192); in = g_resources_open_stream(path, G_RESOURCE_LOOKUP_FLAGS_NONE, NULL); if (in) { out = fopen(target, "wb"); if (out) { do { read = g_input_stream_read(in, buffer, bytes, NULL, NULL); fwrite(buffer, read, 1, out); } while (read > 0); fclose(out); } else { debug("Unable to write resource %s!\n", target); } g_input_stream_close(in, NULL, NULL); g_object_unref(in); } else { debug("Resource %s not found!\n", path); } DEL(buffer); } DEL(target); } char *utilFileBasename(char *path) { const char *basename; size_t length; char *newString = NULL; // Returns filename without path. cwk_path_get_basename(path, &basename, &length); if (basename != NULL) newString = strdup(basename); return newString; } gboolean utilFileCopy(char *from, char *to) { FILE *in = NULL; FILE *out = NULL; gboolean result = FALSE; size_t bytes; in = fopen(from, "rb"); if (in) { out = fopen(to, "wb"); if (out) { while (!feof(in)) { bytes = fread(__utilFilenameBuffer, 1, FILENAME_MAX, in); if (bytes) { fwrite(__utilFilenameBuffer, 1, bytes, out); } } result = TRUE; fclose(out); } fclose(in); } return result; } gboolean utilFileExists(char *filename) { FILE *f = fopen(filename, "rb"); if (f) { fclose(f); return TRUE; } return FALSE; } gboolean utilFileOpen(WindowDataT *self, char *extension, char *what) { GtkWidget *dialog; GtkFileFilter *filter; char *title = utilCreateString("Open %s", what); char *warning = utilCreateString("You have unsaved changes. Open different %s?", what); char *files = utilCreateString("%s Files", what); gboolean result = TRUE; if (self->isDirty) { if (!utilQuestionDialog(self->window, title, warning)) { result = FALSE; } } if (result == TRUE) { dialog = gtk_file_chooser_dialog_new(title, GTK_WINDOW(self->window), GTK_FILE_CHOOSER_ACTION_OPEN, "_Cancel", GTK_RESPONSE_CANCEL, "_Open", GTK_RESPONSE_ACCEPT, NULL ); filter = gtk_file_filter_new(); gtk_file_filter_set_name(filter, files); gtk_file_filter_add_pattern(filter, extension); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { utilSetDirty((WindowDataT *)self, FALSE); DEL(self->filename); DEL(self->path); self->filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); utilUpdatePath(self); } else { result = FALSE; } gtk_widget_destroy(dialog); } DEL(files); DEL(warning); DEL(title); return result; } char *utilFilePath(char *filename) { size_t i; char *temp = NULL; if (filename) { // Derive the path from the filename. temp = strdup(filename); cwk_path_get_dirname(temp, &i); if (i > 0) temp[i] = 0; } return temp; } char *utilFileRemoveExtension(char *filename) { int x = (int)strlen(filename) - 1; char c; char *newString = NULL; while (x >= 0) { if (filename[x] == '.') break; x--; } if (x >= 0) { c = filename[x]; filename[x] = 0; newString = strdup(filename); filename[x] = c; } else { newString = strdup(filename); } return newString; } gboolean utilFileSaveAs(WindowDataT *self, char *extension, char *what) { gboolean result; result = utilFileSaveOtherAs(self, extension, what, &self->filename); utilUpdatePath(self); return result; } gboolean utilFileSaveOtherAs(WindowDataT *self, char *extension, char *what, char **filename) { GtkWidget *dialog; GtkFileFilter *filter; gboolean result = FALSE; char *files = utilCreateString("%s Files", what); size_t x; dialog = gtk_file_chooser_dialog_new("Save As", GTK_WINDOW(self->window), GTK_FILE_CHOOSER_ACTION_SAVE, "_Cancel", GTK_RESPONSE_CANCEL, "_Save", GTK_RESPONSE_ACCEPT, NULL); if (*filename != NULL) { memcpy(__utilFilenameBuffer, *filename, strlen(*filename) + 1); cwk_path_get_dirname(__utilFilenameBuffer, &x); if (x > 0) __utilFilenameBuffer[x] = 0; gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),__utilFilenameBuffer); } gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); filter = gtk_file_filter_new(); gtk_file_filter_set_name(filter, files); gtk_file_filter_add_pattern(filter, extension); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { DEL(*filename); *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); result = TRUE; } DEL(files); gtk_widget_destroy(dialog); return result; } size_t utilFileSize(char *filename) { size_t bytes = 0; FILE *f = fopen(filename, "rb"); if (f) { fseek(f, 0, SEEK_END); bytes = ftell(f); fclose(f); } return bytes; } GtkWidget *utilFindChildWidget(GtkWidget *parent, const gchar *name) { GList *children = NULL; GtkWidget *widget = NULL; // This works on widgets with names. If a widget is referenced by // utilGetWidgetsFromMemory, the name will be set to the ID of the // widget. If not, you need to assign a name yourself. if (g_strcmp0(gtk_widget_get_name(parent), name) == 0) { return parent; } if (GTK_IS_CONTAINER(parent)) { children = gtk_container_get_children(GTK_CONTAINER(parent)); } while (children != NULL) { widget = utilFindChildWidget(children->data, name); if (widget != NULL) { return widget; } children = children->next; } return NULL; } void utilForceUpdate(void) { //***TODO*** This is supposed to force any pending paint operations. // It doesn't work. //while (g_main_context_pending(NULL)) { g_main_context_iteration(NULL,FALSE); //} } char *utilGetToken(char *input, char *delimit, char *openblock, char *closeblock) { static char *token = NULL; char *lead = NULL; char *block = NULL; int iBlock = 0; int iBlockIndex = 0; // https://stackoverflow.com/questions/26187037/in-c-split-char-on-spaces-with-strtok-function-except-if-between-quotes if (input != NULL) { token = input; lead = input; } else { lead = token; if (*token == 0) lead = NULL; } while (*token != 0) { if (iBlock) { if (closeblock[iBlockIndex] == *token) { iBlock = 0; } token++; continue; } if ((block = strchr(openblock, *token)) != NULL) { iBlock = 1; iBlockIndex = (int)(block - openblock); token++; continue; } if (strchr(delimit, *token) != NULL) { *token = 0; token++; break; } token++; } return lead; } WindowDataT *utilGetWindowData(GtkWidget *window) { return hmget(_windowList, window); } gboolean utilGetWidgetsFromMemory(char *resource, char *name[], GtkWidget **widgets[], gpointer userData) { GtkBuilder *gtkBuilder; int x; gtkBuilder = gtk_builder_new(); if (gtk_builder_add_from_resource(gtkBuilder, resource, NULL) == 0) { printf("Failed to build UI '%s'!\n", name[0]); exit(1); } x = 0; while (name[x] != NULL) { *widgets[x] = GTK_WIDGET(gtk_builder_get_object(gtkBuilder, name[x])); // Set the widget name property to the ID we used to find it. gtk_widget_set_name(*widgets[x], name[x]); x++; } gtk_builder_connect_signals(gtkBuilder, userData); g_object_unref(G_OBJECT(gtkBuilder)); return TRUE; } void utilMessageDialog(GtkWidget *parent, char *title, char *message) { GtkWidget *dialog; dialog = gtk_message_dialog_new( GTK_WINDOW(parent), GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "%s", message); gtk_window_set_title(GTK_WINDOW(dialog), title); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); } gboolean utilMkDirP(const char *dir, const mode_t mode) { char *p = NULL; struct stat sb; size_t len; // Make copy of dir. len = strnlen(dir, FILENAME_MAX); if (len == 0 || len == FILENAME_MAX) { return -1; } memcpy(__utilFilenameBuffer, dir, len); __utilFilenameBuffer[len] = '\0'; // Remove trailing slash. if (__utilFilenameBuffer[len - 1] == UTIL_PATH_CHAR) { __utilFilenameBuffer[len - 1] = '\0'; } // Does it already exist? if (stat(__utilFilenameBuffer, &sb) == 0) { if (S_ISDIR(sb.st_mode)) { return TRUE; } } // Recursive mkdir. for (p = __utilFilenameBuffer + 1; *p; p++) { if (*p == UTIL_PATH_CHAR) { *p = 0; if (stat(__utilFilenameBuffer, &sb) != 0) { // Does not exist - create it. if (ourMkdir(__utilFilenameBuffer, mode) < 0) { return FALSE; } } else { if (!S_ISDIR(sb.st_mode)) { // Not a directory return FALSE; } } *p = UTIL_PATH_CHAR; } } // Check path if (stat(__utilFilenameBuffer, &sb) != 0) { // Does not exist - create it. if (ourMkdir(__utilFilenameBuffer, mode) < 0) { return FALSE; } } else { if (!S_ISDIR(sb.st_mode)) { // Not a directory return FALSE; } } return TRUE; } char *utilObfuscateASCII(char *clearText) { char *obfuscated = NULL; char *hostname; int i; int j; int c; // Valid ASCII 32 - 126 (94 chars) hostname = strdup(g_get_host_name()); // Obfuscate. j = 0; obfuscated = (char *)malloc((strlen(clearText) + 1) * sizeof(char)); for (i=0; i 94) c -= 94; obfuscated[i] = (char)(c + 32); j++; if (j >= strlen(hostname)) j = 0; } obfuscated[i] = 0; DEL(hostname); return obfuscated; } gboolean utilQuestionDialog(GtkWidget *parent, char *title, char *question) { GtkWidget *dialog; int response; dialog = gtk_message_dialog_new( GTK_WINDOW(parent), GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, "%s", question); gtk_window_set_title(GTK_WINDOW(dialog), title); response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); return (response == GTK_RESPONSE_OK) ? TRUE : FALSE; } void utilSetDirty(WindowDataT *self, gboolean dirty) { char *title; self->isDirty = dirty; if (dirty) { if (self->filename) { title = utilCreateString("%s - * %s", self->title, self->filename); } else { title = utilCreateString("%s - * (no name)", self->title); } } else { if (self->filename) { title = utilCreateString("%s - %s", self->title, self->filename); } else { title = utilCreateString("%s", self->title); } } gtk_window_set_title(GTK_WINDOW(self->window), title); DEL(title); } void utilShutdown(void) { g_idle_remove_by_data(utilDecompressUpdate); } void utilStartup(void) { g_idle_add(utilDecompressUpdate, utilDecompressUpdate); } void utilUpdatePath(WindowDataT *self) { DEL(self->path); self->path = utilFilePath(self->filename); } void utilWindowRegister(gpointer windowData) { WindowDataT *w = (WindowDataT *)windowData; // Grab title. w->title = strdup(gtk_window_get_title(GTK_WINDOW(w->window))); gtk_widget_show_all(w->window); utilForceUpdate(); hmput(_windowList, w->window, windowData); debug("Window Registered: %ld\n", hmlen(_windowList)); } int utilWindowsCloseAll(void) { WindowDataT *w; debug("Windows Left Open: %ld\n", hmlen(_windowList)); while (hmlen(_windowList) > 0) { w = (WindowDataT *)_windowList[0].value; if (w->closeWindow(w->window, w) == TRUE) { // User canceled closing. break; } } return utilWindowsOpen(); } int utilWindowsOpen(void) { return hmlen(_windowList); } gboolean utilWindowUnRegister(gpointer windowData) { int result; WindowDataT *w = (WindowDataT *)windowData; result = hmdel(_windowList, w->window); debug("Window Unregistered: %ld\n", hmlen(_windowList)); DEL(w->filename); DEL(w->path); DEL(w->title); return (result == 1) ? TRUE : FALSE; }