Функция, которая у вас есть, проверяет, можно ли открыть файл, но не работает для некоторых файлов, которые существуют, но у вас нет прав на открытие.Я бы использовал stat
вместо этого.Чтобы объединить путь и имя файла, вы можете использовать строковые функции.
Обычные API Unix C мрачны.Требуется много усилий, чтобы правильно выполнить простейшие вещи - и даже тогда я не уверен, что не забыл какой-то Unix-изм, такой как обработка сигналов или некоторые непонятные случаи ошибок.То есть вещи, которые довольно просты для понимания в современном C ++.
Я бы хотел, чтобы кто-то разработал современный API системы C и реализовал его как минимум для Linux, чтобы наши страдания закончились ...
Обычно для конкатенации строк требуется API более высокого уровня при сохранении некоторого здравого смысла.Таким образом, в приведенном ниже примере для построения строки используется класс strbuilder
.Это делает вещи расплывчатыми и позволяет избежать наиболее распространенных ошибок.
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
struct strbuilder {
unsigned items, item;
size_t length, *lengths;
char *str, *dst;
};
bool strbuilder_pass(struct strbuilder *builder, int *rc);
void strcat_str(struct strbuilder *builder, const char *src);
void strcat_c_ifnone(struct strbuilder *builder, char c);
bool strbuilder_is_freed(const struct strbuilder *builder);
int fileExists(const char *path, const char *filename)
{
const char pathSep = '/';
int rc;
struct strbuilder bld = {0};
while (strbuilder_pass(&bld, &rc))
{
strcat_str(&bld, path);
strcat_c_ifnone(&bld, pathSep);
strcat_str(&bld, filename);
if (!rc)
{
struct stat statbuf;
printf("path = %s\n", bld.str);
rc = stat(bld.str, &statbuf);
}
}
assert(strbuilder_is_freed(&bld));
return rc;
}
int main()
{
int rc = fileExists("/", "dev");
assert(rc == 0);
return 0;
}
Создание строки контролируется функцией strbuilder_pass
, которая продвигает состояние построителя строки через пять проходов операции:
- Определите количество элементов, ширина которых должна быть сохранена (избегает необходимости вызывать strlen дважды).
- Подготовьте вектор хранения длины.Определите необходимую длину буфера.
- Подготовьте буфер выходной строки.Объединить элементы в буфер.
- Использовать буфер выходной строки.
- Освободить буфер выходной строки.
Этот API не особенно особенный, но подходит для этоговариант использования.Некоторый другой специальный подход тоже подойдет, но это ИМХО немного более элегантно.
void strbuilder_free(struct strbuilder *builder)
{
free(builder->lengths);
free(builder->str);
memset(builder, 0, sizeof(*builder));
}
bool strbuilder_pass(struct strbuilder *builder, int *rc)
{
if (!builder->length) {// start of pass 1
builder->length = 1; /*term*/
*rc = EAGAIN;
return true;
}
else if (!builder->lengths) // end of pass 1
{
builder->lengths = malloc(sizeof(*builder->lengths) * builder->items);
if (builder->lengths)
return true;
*rc = ENOMEM;
}
else if (!builder->str) // end of pass 2
{
builder->dst = (builder->str = malloc(builder->length));
builder->item = 0;
builder->length = 0;
if (builder->dst) {
*builder->dst = '\0';
return true;
}
*rc = ENOMEM;
}
else if (builder->dst) // end of pass 3
{
while (*builder->dst) { // include optional content
builder->dst++; // skip
builder->length++;
}
builder->dst = NULL;
*rc = 0;
return true;
}
else if (!builder->dst) // end of pass 4 (if any)
{}
else {
*rc = EINVAL;
}
strbuilder_free(builder);
return false;
}
void strcat_str(struct strbuilder *builder, const char *src)
{
if (!src)
return;
if (!builder->lengths) // pass 1
builder->items ++;
else if (!builder->str) // pass 2
{
size_t len = strlen(src);
builder->lengths[builder->item++] = len;
builder->length += len;
}
else if (builder->dst) // pass 3
{
size_t len = builder->lengths[builder->item++];
if (*builder->dst && (!len || *builder->dst != *src))
{
builder->dst++;
builder->length++;
}
memcpy(builder->dst, src, len);
builder->dst += len;
builder->length += len;
*builder->dst = '\0';
}
}
void strcat_c_ifnone(struct strbuilder *builder, char c)
{
if (!builder->lengths) {} // pass 1
else if (!builder->str) // pass 2
{
if (c) builder->length ++;
}
else if (builder->dst) // pass 3
{
if (!builder->length || builder->dst[-1] != c)
*(builder->dst) = c;
}
}
bool strbuilder_is_freed(const struct strbuilder *builder)
{
return !builder || (!builder->lengths && !builder->str);
}