lccrt/lib/common/lccrt_fs.c

352 lines
8.4 KiB
C
Raw Normal View History

/**
* Part of the Lccrt Project, under the Apache License v2.0
* See http://www.apache.org/licenses/LICENSE-2.0.txt for license information.
* SPDX-License-Identifier: Apache-2.0
*
* lccrt_fs.c - работа с потоками данных.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "lccrt_real.h"
/**
* Разновидности потоков данных.
*/
typedef enum
{
LCCRT_FS_TYPE_FILE,
LCCRT_FS_TYPE_BUFFER,
LCCRT_FS_TYPE_LAST
} lccrt_fs_type_t;
/**
* Файловый поток данных.
*/
typedef struct lccrt_fs_r
{
lccrt_context_ptr ctx; /* etc */
lccrt_fs_type_t type; /* тип потока данных */
FILE *file;
char *buff;
uint64_t buff_size; /* размер буфера (для LCCRT_FS_TYPE_BUFFER) */
uint64_t syms_count; /* счетчик записанных или прочитанных символов */
int8_t is_self; /* флаг владением потоком данных */
int8_t is_error; /* флаг возникновения ошибки при работе с потоком данных */
lccrt_check_type_t type_check; /* средства динамического контроля типа данных */
} lccrt_fs_t;
lccrt_check_type_define( lccrt_fs_t);
/**
* Открытие потока данных.
*/
static lccrt_fs_ptr
lccrt_fs_new( lccrt_context_ptr ctx)
{
lccrt_fs_ptr r = 0;
r = lccrt_ctx_malloc( ctx, lccrt_fs_t);
memset( r, 0, sizeof( r[0]));
lccrt_check_type_init( r, lccrt_fs_t);
r->ctx = ctx;
r->type = LCCRT_FS_TYPE_LAST;
r->file = 0;
r->buff = 0;
r->buff_size = 0;
r->is_self = 0;
r->is_error = 0;
r->syms_count = 0;
return (r);
} /* lccrt_fs_new */
/**
* Открытие потока данных на основе файла.
*/
lccrt_fs_ptr
lccrt_fs_new_file( lccrt_context_ptr ctx, FILE *file)
{
lccrt_fs_ptr r = 0;
if ( file )
{
r = lccrt_fs_new( ctx);
r->type = LCCRT_FS_TYPE_FILE;
r->file = file;
}
return (r);
} /* lccrt_fs_new_file */
/**
* Открытие потока данных на основе файла.
*/
lccrt_fs_ptr
lccrt_fs_open_file( lccrt_context_ptr ctx, const char *name, int is_load)
{
lccrt_fs_ptr r = 0;
FILE *file = 0;
file = fopen( name, is_load ? "r" : "w");
if ( file )
{
r = lccrt_fs_new_file( ctx, file);
r->is_self = 1;
}
return (r);
} /* lccrt_fs_open_file */
/**
* Открытие потока данных на основе буфера в памяти.
*/
lccrt_fs_ptr
lccrt_fs_new_buffer( lccrt_context_ptr ctx, char *buff, uint64_t buff_size)
{
lccrt_fs_ptr r = 0;
r = lccrt_fs_new( ctx);
r->type = LCCRT_FS_TYPE_BUFFER;
if ( buff )
{
r->buff_size = buff_size;
r->buff = buff;
} else
{
r->is_self = 1;
r->buff_size = buff_size ? buff_size : 4096;
r->buff = lccrt_ctx_mallocn( ctx, char, r->buff_size);
}
return (r);
} /* lccrt_fs_new_buffer */
/**
* Закрытие потока данных.
*/
int
lccrt_fs_delete( lccrt_fs_ptr f)
{
int r = 0;
if ( f )
{
lccrt_check_type_assert( f, lccrt_fs_t);
if ( (f->type == LCCRT_FS_TYPE_FILE) )
{
if ( f->is_self )
{
r = fclose( f->file);
}
} else if ( (f->type == LCCRT_FS_TYPE_BUFFER) )
{
if ( f->is_self )
{
lccrt_ctx_free( f->ctx, f->buff);
}
} else
{
r = -1;
lccrt_assert( 0);
}
lccrt_check_type_done( f, lccrt_fs_t);
lccrt_ctx_free( f->ctx, f);
}
return (r);
} /* lccrt_fs_delete */
/**
* Получить значение поля.
*/
int
lccrt_fs_is_error( lccrt_fs_ptr f)
{
int r = 0;
lccrt_check_type_assert( f, lccrt_fs_t);
r = f->is_error;
return (r);
} /* lccrt_fs_is_error */
/**
* Получить значение поля.
*/
uint64_t
lccrt_fs_get_syms_count( lccrt_fs_ptr f)
{
uint64_t r = 0;
lccrt_check_type_assert( f, lccrt_fs_t);
r = f->syms_count;
return (r);
} /* lccrt_fs_get_syms_count */
/**
* Для потока данных на основе буфера дать ссылку на область данных
* (ссылка доступна только до первого действия с потоком данных).
*/
char *
lccrt_fs_get_buffer( lccrt_fs_ptr f)
{
char *r = f->buff;
lccrt_check_type_assert( f, lccrt_fs_t);
return (r);
} /* lccrt_fs_get_buffer */
/**
* Чтение символа из потока.
*/
int
lccrt_fs_get_sym( lccrt_fs_ptr f)
{
int r = EOF;
lccrt_check_type_assert( f, lccrt_fs_t);
if ( (f->type == LCCRT_FS_TYPE_FILE) )
{
r = fgetc( f->file);
if ( !feof( f->file) )
{
f->syms_count++;
}
} else if ( (f->type == LCCRT_FS_TYPE_BUFFER) )
{
if ( (f->syms_count < f->buff_size) )
{
r = f->buff[f->syms_count];
f->syms_count++;
}
} else
{
lccrt_assert( 0);
}
return (r);
} /* lccrt_fs_get_sym */
/**
* Форматированный вывод в поток данных.
*/
int
lccrt_fs_printf( lccrt_fs_ptr f, const char *fmt, ...)
{
va_list ap;
int r = 0;
lccrt_check_type_assert( f, lccrt_fs_t);
if ( !f->is_error )
{
if ( (f->type == LCCRT_FS_TYPE_FILE) )
{
va_start( ap, fmt);
r = vfprintf( f->file, fmt, ap);
va_end( ap);
} else if ( (f->type == LCCRT_FS_TYPE_BUFFER) )
{
int k = f->buff_size;
int j = f->syms_count;
lccrt_assert( f->syms_count <= f->buff_size);
lccrt_assert( f->buff_size > 0);
if ( !f->is_self )
{
va_start( ap, fmt);
r = vsnprintf( f->buff + j, k - j, fmt, ap);
va_end( ap);
if ( (r == k - j) )
{
int l = 0;
char *b = lccrt_ctx_mallocn( f->ctx, char, r + 1);
/* Проверяем, что размера буфера было достаточно для вывода
всех данных. */
va_start( ap, fmt);
l = vsnprintf( b, r + 1, fmt, ap);
va_end( ap);
lccrt_ctx_free( f->ctx, b);
if ( (r != l) )
{
/* Превышен допустимый размер буфера. */
f->is_error = 1;
}
}
} else
{
int is_work = 1;
while ( is_work )
{
va_start( ap, fmt);
r = vsnprintf( f->buff + j, k - j, fmt, ap);
va_end( ap);
if ( (r < k - j) )
{
/* Текущего размера буфера достаточно. */
is_work = 0;
} else
{
/* Необходимо увеличить размер буфера. */
f->buff_size = 2*f->buff_size;
f->buff = lccrt_ctx_realloc( f->ctx, f->buff, f->buff_size);
k = f->buff_size;
}
}
}
} else
{
lccrt_assert( 0);
r = -1;
}
if ( (r < 0) )
{
f->is_error = 1;
} else
{
f->syms_count += r;
}
}
return (r);
} /* lccrt_fs_printf */
/**
* Вывод на печать строки и символа '\0', после строки.
*/
int
lccrt_fs_print_name_str( lccrt_fs_ptr f, const char *name)
{
int r = 0;
if ( name )
{
int k = 0;
for ( k = 0; name[k]; ++k )
{
if ( (name[k] == 0)
|| (name[k] == '\\') )
{
r += lccrt_fs_printf( f, "\\");
}
r += lccrt_fs_printf( f, "%c", name[k]);
}
r += lccrt_fs_printf( f, "%c", 0);
}
return (r);
} /* lccrt_fs_print_name_str */