Newer
Older
/*
* BRLTTY - A background process providing access to the console screen (when in
* text mode) for a blind person using a refreshable braille display.
*
* Copyright (C) 1995-2018 by The BRLTTY Developers.
*
* BRLTTY comes with ABSOLUTELY NO WARRANTY.
*
* This is free software, placed 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. Please see the file LICENSE-LGPL for details.
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
#include "prologue.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#ifdef HAVE_SYS_FILE_H
#include <sys/file.h>
#endif /* HAVE_SYS_FILE_H */
#include "async_wait.h"
#include "charset.h"
#include "program.h"
int
isPathDelimiter (const char character) {
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
}
int
isAbsolutePath (const char *path) {
if (isPathDelimiter(path[0])) return 1;
#if defined(__MINGW32__) || defined(__MSDOS__)
if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", toupper(path[0])) && (path[1] == ':')) return 1;
#endif /* defined(__MINGW32__) || defined(__MSDOS__) */
return 0;
}
static size_t
stripPathDelimiter (const char *path, size_t length) {
while (length) {
if (!isPathDelimiter(path[length-1])) break;
length -= 1;
}
return length;
}
char *
getPathDirectory (const char *path) {
size_t length = strlen(path);
size_t end = stripPathDelimiter(path, length);
if (end) {
while (--end)
if (isPathDelimiter(path[end-1]))
break;
if ((length = end))
if ((end = stripPathDelimiter(path, length)))
length = end;
}
if (!length) length = strlen(path = ".");
{
if (directory) {
memcpy(directory, path, length);
directory[length] = 0;
return directory;
}
}
const char *
locatePathName (const char *path) {
const char *name = path + strlen(path);
while (name != path) {
if (isPathDelimiter(*--name)) {
++name;
break;
}
}
return name;
}
const char *
locatePathExtension (const char *path) {
const char *name = locatePathName(path);
const char *extension = strrchr(name, '.');
if (extension && extension[1]) {
const char *c = extension;
while (c > name) {
if (*--c != '.') {
return extension;
}
}
}
return NULL;
}
int
isExplicitPath (const char *path) {
return locatePathName(path) != path;
}
char *
joinPath (const char *const *components, unsigned int count) {
const char *const *component = components + count;
unsigned int size = (count * 2) - 1;
const char *strings[size];
unsigned int first = size;
while (component != components) {
const char *next = *--component;
if (next && *next) {
if ((first != size) && !isPathDelimiter(next[strlen(next)-1])) {
strings[--first] = "/";
}
strings[--first] = next;
if (isAbsolutePath(next)) break;
}
}
return joinStrings(&strings[first], size-first);
}
char *
makePath (const char *directory, const char *file) {
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
const char *const components[] = {directory, file};
return joinPath(components, ARRAY_COUNT(components));
}
int
hasFileExtension (const char *path, const char *extension) {
const char *tail = locatePathExtension(path);
if (!tail) return 0;
return strcmp(tail, extension) == 0;
}
char *
replaceFileExtension (const char *path, const char *extension) {
const char *oldExtension = locatePathExtension(path);
if (oldExtension) {
size_t headLength = oldExtension - path;
size_t extensionLength = strlen(extension);
char *newPath = malloc(headLength + extensionLength + 1);
if (newPath) {
char *byte = newPath;
byte = mempcpy(byte, path, headLength);
byte = mempcpy(byte, extension, extensionLength);
*byte = 0;
return newPath;
} else {
logMallocError();
} else {
logMessage(LOG_WARNING, "path has no extension: %s", path);
ensureFileExtension (const char *path, const char *extension) {
const char *strings[2];
int count = 0;
strings[count++] = path;
if (extension && !locatePathExtension(path)) strings[count++] = extension;
return joinStrings(strings, count);
}
char *
makeFilePath (const char *directory, const char *name, const char *extension) {
char *path = NULL;
char *file = ensureFileExtension(name, extension);
if (file) {
path = makePath(directory, file);
free(file);
}
return path;
}
#else /* F_OK */
errno = ENOSYS;
return 0;
#endif /* F_OK */
testFilePath (const char *path) {
#ifdef S_ISREG
struct stat status;
if (stat(path, &status) != -1) {
if (S_ISREG(status.st_mode)) return 1;
errno = EEXIST;
}
#else /* S_ISREG */
int result = open(path, O_RDONLY);
if (result != -1) {
close(result);
return 1;
}
#endif /* S_ISREG */
return 0;
}
int
testProgramPath (const char *path) {
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
#if defined(__MINGW32__)
{
const char *extension = locatePathExtension(path);
if (extension) {
static char **extensions = NULL;
if (!extensions) {
const char *string = getenv("PATHEXT");
{
static char *noExtensions[] = {NULL};
extensions = noExtensions;
}
if (string) {
char **strings = splitString(string, ';', NULL);
if (strings) extensions = strings;
}
}
{
char **x = extensions;
while (*x) {
if (strcasecmp(*x, extension) == 0) return 1;
x += 1;
}
}
}
}
return 0;
#elif defined(X_OK)
#else /* X_OK */
errno = ENOSYS;
return 0;
#endif /* X_OK */
}
int
testDirectoryPath (const char *path) {
#ifdef S_ISDIR
if (stat(path, &status) != -1) {
if (S_ISDIR(status.st_mode)) return 1;
errno = EEXIST;
}
#else /* S_ISDIR */
errno = ENOSYS;
#endif /* S_ISDIR */
return 0;
}
int
createDirectory (const char *path) {
#if defined(GRUB_RUNTIME)
errno = EROFS;
#else /* make directory */
if (mkdir(path
#ifndef __MINGW32__
,(S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
#endif /* __MINGW32__ */
) != -1) return 1;
#endif /* make directory */
logMessage(LOG_WARNING, "%s: %s: %s",
gettext("cannot create directory"),
path, strerror(errno));
return 0;
}
int
ensureDirectory (const char *path) {
if (testDirectoryPath(path)) return 1;
if (errno == EEXIST) {
logMessage(LOG_ERR, "not a directory: %s", path);
logMessage(LOG_ERR, "cannot access directory: %s: %s", path, strerror(errno));
} else {
{
char *directory = getPathDirectory(path);
if (!directory) return 0;
{
int exists = ensureDirectory(directory);
free(directory);
if (!exists) return 0;
}
}
logMessage(LOG_NOTICE, "directory created: %s", path);
return 1;
}
static void
setDirectory (const char **variable, const char *directory) {
*variable = directory;
}
static const char *
getDirectory (const char *const *variable) {
if (*variable && **variable) {
if (ensureDirectory(*variable)) {
return *variable;
}
}
static char *
makeDirectoryPath (const char *const *variable, const char *file) {
const char *directory = getDirectory(variable);
if (directory) return makePath(directory, file);
return NULL;
}
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
static const char *updatableDirectory = NULL;
void
setUpdatableDirectory (const char *directory) {
setDirectory(&updatableDirectory, directory);
}
const char *
getUpdatableDirectory (void) {
return getDirectory(&updatableDirectory);
}
char *
makeUpdatablePath (const char *file) {
return makeDirectoryPath(&updatableDirectory, file);
}
static const char *writableDirectory = NULL;
void
setWritableDirectory (const char *directory) {
setDirectory(&writableDirectory, directory);
}
const char *
getWritableDirectory (void) {
return getDirectory(&writableDirectory);
}
char *
makeWritablePath (const char *file) {
return makeDirectoryPath(&writableDirectory, file);
}
#if defined(GRUB_RUNTIME)
errno = ENOSYS;
#else /* get working directory */
size_t size = 0X80;
char *buffer = NULL;
{
char *newBuffer = realloc(buffer, size<<=1);
if (!newBuffer) {
logMallocError();
break;
}
buffer = newBuffer;
}
if (getcwd(buffer, size)) return buffer;
if (errno != ERANGE) {
logSystemError("getcwd");
break;
}
}
#endif /* get working directory */
logMessage(LOG_WARNING, "%s: %s",
gettext("cannot get working directory"),
strerror(errno));
setWorkingDirectory (const char *path) {
#if defined(GRUB_RUNTIME)
errno = ENOSYS;
#else /* set working directory */
if (chdir(path) != -1) return 1;
#endif /* set working directory */
logMessage(LOG_WARNING, "%s: %s: %s",
gettext("cannot set working directory"),
return 0;
}
char *
getHomeDirectory (void) {
#if defined(GRUB_RUNTIME)
#else /* get home directory */
if (path && *path) {
if ((path = strdup(path))) return path;
logMallocError();
}
#endif /* get home directory */
return NULL;
static char *
makeOverridePath (const char *base, int xdg) {
return makePath(base, (xdg? PACKAGE_TARNAME: ("." PACKAGE_TARNAME)));
}
static int
addOverridePath (char **paths, size_t *index, const char *base, int xdg) {
char *path = makeOverridePath(base, xdg);
if (!path) return 0;
logMessage(LOG_INFO, "Override Directory: %s", path);
paths[(*index)++] = path;
return 1;
}
const char *const *
getAllOverrideDirectories (void) {
static const char *const *overrideDirectories = NULL;
if (!overrideDirectories) {
const char *secondaryList = getenv("XDG_CONFIG_DIRS");
int secondaryCount;
char **secondaryBases = splitString(((secondaryList && *secondaryList)? secondaryList: "/etc/xdg"), ':', &secondaryCount);
if (secondaryBases) {
size_t count = 1 + secondaryCount + 1;
char **paths;
if ((paths = malloc(sizeof(*paths) * (count + 1)))) {
size_t index = 0;
{
const char *primary = getenv("XDG_CONFIG_HOME");
if (primary && *primary) {
if (!addOverridePath(paths, &index, primary, 1)) goto done;
} else {
char *home = getHomeDirectory();
if (home) {
char *base = *home? makePath(home, ".config"): NULL;
if (base) {
int added = addOverridePath(paths, &index, base, 1);
if (!added) goto done;
}
}
}
}
if (!index) {
char *primary = strdup("");
if (!primary) {
logMallocError();
goto done;
}
paths[index++] = primary;
}
{
char **base = secondaryBases;
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
while (*base) {
if (**base) {
if (!addOverridePath(paths, &index, *base, 1)) {
break;
}
} else {
count -= 1;
}
base += 1;
}
if (*base) goto done;
}
{
int added = 0;
char *home = getHomeDirectory();
if (home && *home) {
if (addOverridePath(paths, &index, home, 0)) added = 1;
} else {
char *current = getWorkingDirectory();
if (current) {
if (addOverridePath(paths, &index, current, 0)) added = 1;
free(current);
}
if (home) free(home);
if (!added) goto done;
done:
paths[index] = NULL;
if (index == count) {
overrideDirectories = (const char *const *)paths;
} else {
deallocateStrings(paths);
deallocateStrings(secondaryBases);
if (!overrideDirectories) logMessage(LOG_WARNING, "no override directories");
return overrideDirectories;
}
const char *
getPrimaryOverrideDirectory (void) {
const char *const *directories = getAllOverrideDirectories();
if (directories) {
const char *directory = directories[0];
if (directory && *directory) return directory;
}
logMessage(LOG_WARNING, "no primary override directory");
return NULL;
}
#if defined(F_SETLK)
static int
modifyFileLock (int file, int action, short type) {
struct flock lock;
memset(&lock, 0, sizeof(lock));
lock.l_type = type;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
do {
if (fcntl(file, action, &lock) != -1) return 1;
} while (errno == EINTR);
if (errno == EACCES) errno = EAGAIN;
if (errno != EAGAIN) logSystemError("fcntl[struct flock *]");
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
return 0;
}
static int
lockFile (int file, int exclusive, int wait) {
return modifyFileLock(file, (wait? F_SETLKW: F_SETLK), (exclusive? F_WRLCK: F_RDLCK));
}
int
acquireFileLock (int file, int exclusive) {
return lockFile(file, exclusive, 1);
}
int
attemptFileLock (int file, int exclusive) {
return lockFile(file, exclusive, 0);
}
int
releaseFileLock (int file) {
return modifyFileLock(file, F_SETLK, F_UNLCK);
}
#elif defined(LOCK_EX)
static int
modifyFileLock (int file, int operation) {
do {
if (flock(file, operation) != -1) return 1;
} while (errno == EINTR);
#ifdef EWOULDBLOCK
if (errno == EWOULDBLOCK) errno = EAGAIN;
#endif /* EWOULDBLOCK */
if (errno == EACCES) errno = EAGAIN;
if (errno != EAGAIN) logSystemError("flock");
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
return 0;
}
int
acquireFileLock (int file, int exclusive) {
return modifyFileLock(file, (exclusive? LOCK_EX: LOCK_SH));
}
int
attemptFileLock (int file, int exclusive) {
return modifyFileLock(file, ((exclusive? LOCK_EX: LOCK_SH) | LOCK_NB));
}
int
releaseFileLock (int file) {
return modifyFileLock(file, LOCK_UN);
}
#elif defined(F_LOCK)
static int
modifyRegionLock (int file, int command, off_t length) {
do {
if (lockf(file, command, length) != -1) return 1;
} while (errno == EINTR);
if (errno == EACCES) errno = EAGAIN;
if (errno != EAGAIN) logSystemError("lockf");
return 0;
}
static int
modifyFileLock (int file, int command) {
off_t offset;
if ((offset = lseek(file, 0, SEEK_CUR)) == -1) {
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
} else if (modifyRegionLock(file, command, 0)) {
if (!offset) return 1;
if (modifyRegionLock(file, command, -offset)) return 1;
}
return 0;
}
int
acquireFileLock (int file, int exclusive) {
return modifyFileLock(file, F_LOCK);
}
int
attemptFileLock (int file, int exclusive) {
return modifyFileLock(file, F_TLOCK);
}
int
releaseFileLock (int file) {
return modifyFileLock(file, F_ULOCK);
}
#elif defined(__MINGW32__)
#include <io.h>
#include <sys/locking.h>
#include <limits.h>
static int
modifyFileLock (int file, int mode) {
int ok = 0;
off_t offset;
if ((offset = lseek(file, 0, SEEK_CUR)) != -1) {
if (lseek(file, 0, SEEK_SET) != -1) {
int wait;
if (mode == _LK_LOCK) {
mode = _LK_NBLCK;
wait = 1;
} else if (mode == _LK_RLCK) {
mode = _LK_NBRLCK;
wait = 1;
} else {
wait = 0;
}
while (1) {
if (_locking(file, mode, LONG_MAX) != -1) {
ok = 1;
break;
}
if (errno != EACCES) {
}
if (lseek(file, offset, SEEK_SET) == -1) {
}
return ok;
}
int
acquireFileLock (int file, int exclusive) {
return modifyFileLock(file, (exclusive? _LK_LOCK: _LK_RLCK));
}
int
attemptFileLock (int file, int exclusive) {
return modifyFileLock(file, (exclusive? _LK_NBLCK: _LK_NBRLCK));
}
int
releaseFileLock (int file) {
return modifyFileLock(file, _LK_UNLCK);
}
#else /* file locking */
#warning file lock support not available on this platform
int
acquireFileLock (int file, int exclusive) {
}
int
attemptFileLock (int file, int exclusive) {
}
int
releaseFileLock (int file) {
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
static void
exitProgramStream (void *data) {
FILE **stream = data;
if (*stream) {
fclose(*stream);
*stream = NULL;
}
}
void
registerProgramStream (const char *name, FILE **stream) {
onProgramExit(name, exitProgramStream, stream);
}
FILE *
openFile (const char *path, const char *mode, int optional) {
FILE *file = fopen(path, mode);
if (file) {
logMessage(LOG_DEBUG, "file opened: %s fd=%d", path, fileno(file));
} else {
logMessage((optional && (errno == ENOENT))? LOG_DEBUG: LOG_ERR,
"cannot open file: %s: %s", path, strerror(errno));
}
return file;
}
int
readLine (FILE *file, char **buffer, size_t *size) {
char *line;
if (ferror(file)) return 0;
if (feof(file)) return 0;
if (!*size) {
if (!(*buffer = malloc(*size = 0X80))) {
logMallocError();
return 0;
}
}
if ((line = fgets(*buffer, *size, file))) {
size_t length = strlen(line); /* Line length including new-line. */
/* No trailing new-line means that the buffer isn't big enough. */
while (line[length-1] != '\n') {
/* If necessary, extend the buffer. */
if ((*size - (length + 1)) == 0) {
size_t newSize = *size << 1;
char *newBuffer = realloc(*buffer, newSize);
if (!newBuffer) {
logMallocError();
return 0;
}
*buffer = newBuffer;
*size = newSize;
}
/* Read the rest of the line into the end of the buffer. */
if (!(line = fgets(&(*buffer)[length], *size-length, file))) {
if (!ferror(file)) return 1;
return 0;
}
length += strlen(line); /* New total line length. */
line = *buffer; /* Point to the beginning of the line. */
}
if (--length > 0)
if (line[length-1] == '\r')
--length;
line[length] = 0; /* Remove trailing new-line. */
return 1;
} else if (ferror(file)) {
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
}
return 0;
}
/* Process each line of an input text file safely.
* This routine handles the actual reading of the file,
* insuring that the input buffer is always big enough,
* and calls a caller-supplied handler once for each line in the file.
* The caller-supplied data pointer is passed straight through to the handler.
*/
int
processLines (FILE *file, LineHandler handleLine, void *data) {
unsigned int lineNumber = 0;
char *buffer = NULL;
size_t bufferSize = 0;
while (readLine(file, &buffer, &bufferSize)) {
char *line = buffer;
if (!lineNumber++) {
static const char utf8ByteOrderMark[] = {0XEF, 0XBB, 0XBF};
static const unsigned int length = sizeof(utf8ByteOrderMark);
if (strncmp(line, utf8ByteOrderMark, length) == 0) line += length;
}
if (!handleLine(line, data)) break;
}
if (buffer) free(buffer);
return !ferror(file);
}
STR_BEGIN_FORMATTER(formatInputError, const char *file, const int *line, const char *format, va_list arguments)
if (file) STR_PRINTF("%s", file);
if (line) STR_PRINTF("[%d]", *line);
if (STR_LENGTH) STR_PRINTF(": ");
STR_VPRINTF(format, arguments);
STR_END_FORMATTER
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#ifdef __MINGW32__
ssize_t
readFileDescriptor (FileDescriptor fileDescriptor, void *buffer, size_t size) {
{
DWORD count;
if (ReadFile(fileDescriptor, buffer, size, &count, NULL)) return count;
}
setSystemErrno();
return -1;
}
ssize_t
writeFileDescriptor (FileDescriptor fileDescriptor, const void *buffer, size_t size) {
{
DWORD count;
if (WriteFile(fileDescriptor, buffer, size, &count, NULL)) return count;
}
setSystemErrno();
return -1;
}
const char *
getNamedPipeDirectory (void) {
return "//./pipe";
}
int
createAnonymousPipe (FileDescriptor *pipeInput, FileDescriptor *pipeOutput) {
SECURITY_ATTRIBUTES attributes;
ZeroMemory(&attributes, sizeof(attributes));
attributes.nLength = sizeof(attributes);
attributes.bInheritHandle = TRUE;
attributes.lpSecurityDescriptor = NULL;
if (CreatePipe(pipeOutput, pipeInput, &attributes, 0)) {
return 1;
} else {
logWindowsSystemError("CreatePipe");
}
return 0;
}