mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-13 02:42:09 +02:00
255 lines
6.4 KiB
C
255 lines
6.4 KiB
C
#include "io.h"
|
|
#include "util/stringUtils.h"
|
|
#include "util/unused.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <termios.h>
|
|
#include <poll.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
|
|
#if __has_include(<wordexp.h>)
|
|
#include <wordexp.h>
|
|
#endif
|
|
|
|
static void createSubfolders(const char* fileName)
|
|
{
|
|
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
|
|
|
|
char *token = NULL;
|
|
while((token = strchr(fileName, '/')) != NULL)
|
|
{
|
|
ffStrbufAppendNS(&path, (uint32_t)(token - fileName + 1), fileName);
|
|
mkdir(path.chars, S_IRWXU | S_IRGRP | S_IROTH);
|
|
fileName = token + 1;
|
|
}
|
|
}
|
|
|
|
bool ffWriteFileData(const char* fileName, size_t dataSize, const void* data)
|
|
{
|
|
int openFlagsModes = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC;
|
|
int openFlagsRights = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
|
|
|
int FF_AUTO_CLOSE_FD fd = open(fileName, openFlagsModes, openFlagsRights);
|
|
if(fd == -1)
|
|
{
|
|
if (errno == ENOENT)
|
|
{
|
|
createSubfolders(fileName);
|
|
fd = open(fileName, openFlagsModes, openFlagsRights);
|
|
if(fd == -1)
|
|
return false;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
return write(fd, data, dataSize) > 0;
|
|
}
|
|
|
|
static inline void readWithLength(int fd, FFstrbuf* buffer, uint32_t length)
|
|
{
|
|
ffStrbufEnsureFixedLengthFree(buffer, length);
|
|
ssize_t bytesRead = 0;
|
|
while(
|
|
length > 0 && (bytesRead = read(fd, buffer->chars + buffer->length, length)) > 0
|
|
) {
|
|
buffer->length += (uint32_t) bytesRead;
|
|
length -= (uint32_t) bytesRead;
|
|
}
|
|
}
|
|
|
|
static inline void readUntilEOF(int fd, FFstrbuf* buffer)
|
|
{
|
|
ffStrbufEnsureFree(buffer, 31);
|
|
uint32_t available = ffStrbufGetFree(buffer);
|
|
ssize_t bytesRead = 0;
|
|
while(
|
|
(bytesRead = read(fd, buffer->chars + buffer->length, available)) > 0
|
|
) {
|
|
buffer->length += (uint32_t) bytesRead;
|
|
if((uint32_t) bytesRead == available)
|
|
ffStrbufEnsureFree(buffer, buffer->allocated - 1); // Doubles capacity every round. -1 for the null byte.
|
|
available = ffStrbufGetFree(buffer);
|
|
}
|
|
}
|
|
|
|
bool ffAppendFDBuffer(int fd, FFstrbuf* buffer)
|
|
{
|
|
struct stat fileInfo;
|
|
if(fstat(fd, &fileInfo) != 0)
|
|
return false;
|
|
|
|
if (fileInfo.st_size > 0)
|
|
readWithLength(fd, buffer, (uint32_t)fileInfo.st_size);
|
|
else
|
|
readUntilEOF(fd, buffer);
|
|
|
|
buffer->chars[buffer->length] = '\0';
|
|
|
|
return buffer->length > 0;
|
|
}
|
|
|
|
ssize_t ffReadFileData(const char* fileName, size_t dataSize, void* data)
|
|
{
|
|
int FF_AUTO_CLOSE_FD fd = open(fileName, O_RDONLY | O_CLOEXEC);
|
|
if(fd == -1)
|
|
return -1;
|
|
|
|
return ffReadFDData(fd, dataSize, data);
|
|
}
|
|
|
|
bool ffAppendFileBuffer(const char* fileName, FFstrbuf* buffer)
|
|
{
|
|
int FF_AUTO_CLOSE_FD fd = open(fileName, O_RDONLY | O_CLOEXEC);
|
|
if(fd == -1)
|
|
return false;
|
|
|
|
return ffAppendFDBuffer(fd, buffer);
|
|
}
|
|
|
|
bool ffPathExpandEnv(FF_MAYBE_UNUSED const char* in, FF_MAYBE_UNUSED FFstrbuf* out)
|
|
{
|
|
bool result = false;
|
|
|
|
#if __has_include(<wordexp.h>) // https://github.com/termux/termux-packages/pull/7056
|
|
|
|
wordexp_t exp;
|
|
if(wordexp(in, &exp, 0) != 0)
|
|
return false;
|
|
|
|
if (exp.we_wordc == 1)
|
|
{
|
|
result = true;
|
|
ffStrbufSetS(out, exp.we_wordv[0]);
|
|
}
|
|
|
|
wordfree(&exp);
|
|
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
const char* ffGetTerminalResponse(const char* request, const char* format, ...)
|
|
{
|
|
struct termios oldTerm, newTerm;
|
|
if(tcgetattr(STDIN_FILENO, &oldTerm) == -1)
|
|
return "tcgetattr(STDIN_FILENO, &oldTerm) failed";
|
|
|
|
newTerm = oldTerm;
|
|
newTerm.c_lflag &= (tcflag_t) ~(ICANON | ECHO);
|
|
if(tcsetattr(STDIN_FILENO, TCSANOW, &newTerm) == -1)
|
|
return "tcsetattr(STDIN_FILENO, TCSANOW, &newTerm)";
|
|
|
|
fputs(request, stdout);
|
|
fflush(stdout);
|
|
|
|
//Give the terminal 35ms to respond
|
|
if(poll(&(struct pollfd) { .fd = STDIN_FILENO, .events = POLLIN }, 1, FF_IO_TERM_RESP_WAIT_MS) <= 0)
|
|
{
|
|
tcsetattr(STDIN_FILENO, TCSANOW, &oldTerm);
|
|
return "poll() timeout or failed";
|
|
}
|
|
|
|
char buffer[512];
|
|
ssize_t bytesRead = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);
|
|
|
|
tcsetattr(STDIN_FILENO, TCSANOW, &oldTerm);
|
|
|
|
if(bytesRead <= 0)
|
|
return "read(STDIN_FILENO, buffer, sizeof(buffer) - 1) failed";
|
|
|
|
buffer[bytesRead] = '\0';
|
|
|
|
va_list args;
|
|
va_start(args, format);
|
|
vsscanf(buffer, format, args);
|
|
va_end(args);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
bool ffSuppressIO(bool suppress)
|
|
{
|
|
static bool init = false;
|
|
static int origOut = -1;
|
|
static int origErr = -1;
|
|
static int nullFile = -1;
|
|
|
|
if(!init)
|
|
{
|
|
if(!suppress)
|
|
return true;
|
|
|
|
origOut = dup(STDOUT_FILENO);
|
|
origErr = dup(STDERR_FILENO);
|
|
nullFile = open("/dev/null", O_WRONLY | O_CLOEXEC);
|
|
init = true;
|
|
}
|
|
|
|
if(nullFile == -1)
|
|
return false;
|
|
|
|
fflush(stdout);
|
|
fflush(stderr);
|
|
|
|
dup2(suppress ? nullFile : origOut, STDOUT_FILENO);
|
|
dup2(suppress ? nullFile : origErr, STDERR_FILENO);
|
|
return true;
|
|
}
|
|
|
|
void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indentation, const char* folderName, bool pretty)
|
|
{
|
|
DIR* dir = opendir(folder->chars);
|
|
if(dir == NULL)
|
|
return;
|
|
|
|
uint32_t folderLength = folder->length;
|
|
|
|
if(pretty && folderName != NULL)
|
|
{
|
|
for(uint8_t i = 0; i < indentation - 1; i++)
|
|
fputs(" | ", stdout);
|
|
printf("%s/\n", folderName);
|
|
}
|
|
|
|
struct dirent* entry;
|
|
|
|
while((entry = readdir(dir)) != NULL)
|
|
{
|
|
if(entry->d_type == DT_DIR)
|
|
{
|
|
if(ffStrEquals(entry->d_name, ".") || ffStrEquals(entry->d_name, ".."))
|
|
continue;
|
|
|
|
ffStrbufAppendS(folder, entry->d_name);
|
|
ffStrbufAppendC(folder, '/');
|
|
listFilesRecursively(baseLength, folder, (uint8_t) (indentation + 1), entry->d_name, pretty);
|
|
ffStrbufSubstrBefore(folder, folderLength);
|
|
continue;
|
|
}
|
|
|
|
if (pretty)
|
|
{
|
|
for(uint8_t i = 0; i < indentation; i++)
|
|
fputs(" | ", stdout);
|
|
}
|
|
else
|
|
{
|
|
fputs(folder->chars + baseLength, stdout);
|
|
}
|
|
|
|
puts(entry->d_name);
|
|
}
|
|
|
|
closedir(dir);
|
|
}
|
|
|
|
void ffListFilesRecursively(const char* path, bool pretty)
|
|
{
|
|
FF_STRBUF_AUTO_DESTROY folder = ffStrbufCreateS(path);
|
|
ffStrbufEnsureEndsWithC(&folder, '/');
|
|
listFilesRecursively(folder.length, &folder, 0, NULL, pretty);
|
|
}
|