352 lines
8.4 KiB
C
352 lines
8.4 KiB
C
/**
|
||
* 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 */
|