8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-24 21:23:04 +01:00
firebird-mirror/src/burp/spit.cpp
dimitr a5a8041e26 1) Fixed stdin/stdout redirection in win32 gsplit
2) Make the code more GCC4 friendly
2006-01-13 07:36:30 +00:00

1449 lines
36 KiB
C++

/*************************************************************************
**
** PROGRAM: JRD file split utility program
** MODULE: spit.cpp
** DESCRIPTION: Command line interpreter for backup file split/join
** utility program
**
**
**************************************************************************
* The contents of this file are subject to the Interbase Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy
* of the License at http://www.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
* or implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code was created by Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*/
#include "firebird.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "../jrd/common.h"
#include "../jrd/gdsassert.h"
#include <stdarg.h>
#include <fcntl.h>
#include <ctype.h>
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#include "../burp/spit.h"
#include "../burp/burpswi.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef WIN_NT
#include <io.h> // usage of non-ANSI open/read/write/close functions
#endif
static const int mode_read = O_RDONLY;
static const int mode_write = O_WRONLY | O_CREAT;
static const int mask = 0666;
// Definitions for GSPLIT
enum gsplit_option
{
IN_SW_SPIT_0 = 0, // the unknowable switch
IN_SW_SPIT_SP = 30, // splits back up files
IN_SW_SPIT_JT = 31 // joins back up files
};
static in_sw_tab_t spit_in_sw_table[] =
{
{IN_SW_SPIT_SP, 0, "SPLIT_BK_FILE", 0, 0, 0, FALSE, 0, 0, NULL},
{IN_SW_SPIT_JT, 0, "JOIN_BK_FILE", 0, 0, 0, FALSE, 0, 0, NULL},
{IN_SW_SPIT_0, 0, NULL, 0, 0, 0, FALSE, 0, 0, NULL}
};
/*************************************
** backup files header record
**************************************
*/
struct header_rec {
TEXT name[18];
TEXT date_time[30];
TEXT text1[11];
TEXT num[4];
TEXT text2[4];
TEXT total[4];
TEXT text3[2];
TEXT fl_name[MAX_FILE_NM_LEN];
};
const size_t header_rec_len = sizeof(header_rec);
static const char *header_rec_name = "InterBase/gsplit, ";
/*************************************
** backup files structure
**************************************
*/
struct b_fil {
b_fil *b_fil_next;
TEXT *b_fil_name;
SLONG b_fil_number;
double b_fil_size;
};
const size_t b_fil_len = sizeof(b_fil);
/*****************************************************
**
** local function declarations
**
******************************************************
*/
static int conv_ntoc(SLONG, TEXT *);
static int free_file_list(b_fil*);
static int final_flush_io_buff(UCHAR *, SLONG, FILE_DESC);
static int final_read_and_write(FILE_DESC, FILE_DESC,
const TEXT*, SLONG, UCHAR **, bool*);
static int flush_io_buff(UCHAR *, SLONG,
FILE_DESC, double, SLONG *, bool*);
static int get_file_name(const SCHAR *, double, b_fil**);
static int get_file_size(const SCHAR *, const SCHAR *, double *);
static int get_function_option(const SCHAR *, gsplit_option*, const SCHAR *,
const in_sw_tab_t* const);
static int gen_multy_bakup_files(b_fil*, FILE_DESC, SLONG);
static int set_hdr_str(TEXT *, const TEXT *, SLONG, SLONG);
static int join_multy_bakup_files(b_fil*);
static int print_clo(const TEXT *);
static int read_and_write(FILE_DESC, FILE_DESC,
const TEXT*, SLONG,
double, UCHAR **, bool*, double *, SLONG *);
static int read_and_write_for_join(FILE_DESC, const TEXT*,
UCHAR **, SLONG, SLONG *);
static int write_header(b_fil*, header_rec, FILE_DESC, TEXT *);
/*****************************************************
**
** M A I N P R O G R A M
**
******************************************************
*/
int main( int argc, char *argv[])
{
SLONG ret_cd, file_num = 0;
const SCHAR *prog_name = argv[0];
if (argc < 2) {
fprintf(stderr, "%s: No Command Line Option Specified\n",
argv[0]);
ret_cd = print_clo(prog_name);
return FB_FAILURE;
}
/************************
** Fields initialization
*************************
*/
b_fil* file_ptr = NULL;
b_fil* file_list = NULL;
b_fil* prev_file = NULL;
// Strange, never met a program with tenths of bytes... may need updating
// from double to int64.
double file_size = -1;
gsplit_option sw_replace = IN_SW_SPIT_0;
/*******************************
** Initialize in_sw_table table.
*******************************
*/
for (in_sw_tab_t* in_sw_tab = spit_in_sw_table; in_sw_tab->in_sw_name;
in_sw_tab++)
{
in_sw_tab->in_sw_state = FALSE;
}
/**********************************
** validating command line options
***********************************
*/
bool file_nm_sw = false;
const SCHAR* string = NULL;
const SCHAR* const* const end = argv + argc;
++argv;
while (argv < end)
{
string = *argv;
if (*string == '-') {
argv++;
ret_cd = get_function_option(prog_name, &sw_replace,
string, spit_in_sw_table);
if (ret_cd == FB_FAILURE) {
ret_cd = free_file_list(file_list);
return FB_FAILURE;
}
} // end of processing (*string == '-')
else { // processing function specific command line options
switch (sw_replace) {
case (IN_SW_SPIT_SP):
if (!file_nm_sw) { // process file name
file_size = 0;
file_num = file_num + 1;
if (file_num > MAX_NUM_OF_FILES) {
fprintf(stderr,
"%s: maximum of files is %d\n",
prog_name, MAX_NUM_OF_FILES);
ret_cd = print_clo(prog_name);
ret_cd = free_file_list(file_list);
return FB_FAILURE;
}
if (strlen(string) > MAX_FILE_NM_LEN) {
fprintf(stderr,
"%s: file name %s is too long\n",
prog_name, string);
fprintf(stderr,
"%s: maximum length of file name is %d bytes\n",
prog_name, MAX_FILE_NM_LEN);
ret_cd = print_clo(prog_name);
ret_cd = free_file_list(file_list);
return FB_FAILURE;
}
ret_cd = get_file_name(string, file_size, &file_ptr);
if (ret_cd == FB_FAILURE) {
ret_cd = free_file_list(file_list);
return FB_FAILURE;
}
file_nm_sw = true;
file_ptr->b_fil_number = file_num;
if (!file_list)
file_list = prev_file = file_ptr;
else {
prev_file->b_fil_next = file_ptr;
prev_file = file_ptr;
}
} // processing file name
else { // processing file size
file_nm_sw = false;
ret_cd = get_file_size(prog_name, string, &file_size);
if (ret_cd == FB_FAILURE) {
ret_cd = free_file_list(file_list);
return FB_FAILURE;
}
} // end of processing file size specification
file_ptr->b_fil_size = file_size;
break;
case (IN_SW_SPIT_JT):
ret_cd = get_file_name(string, file_size, &file_ptr);
if (ret_cd == FB_FAILURE) {
ret_cd = free_file_list(file_list);
return FB_FAILURE;
}
file_num = file_num + 1;
file_ptr->b_fil_number = file_num;
if (!file_list)
file_list = prev_file = file_ptr;
else {
prev_file->b_fil_next = file_ptr;
prev_file = file_ptr;
} // end of processing file size specification
break;
default:
fprintf(stderr, "%s: invalid option '%s'\n",
prog_name, string);
ret_cd = print_clo(prog_name);
ret_cd = free_file_list(file_list);
return FB_FAILURE;
break;
} // end of switch (sw_replace)
argv++;
} // processing function specific command line options
} // while (argv < end)
if (!file_list && sw_replace != IN_SW_SPIT_0) {
fprintf(stderr,
"%s: invalid option '%s', rest of parameters is missing\n",
prog_name, string);
ret_cd = print_clo(prog_name);
ret_cd = free_file_list(file_list);
return FB_FAILURE;
}
FILE_DESC input_file_desc;
switch (sw_replace) {
case (IN_SW_SPIT_SP):
input_file_desc = GBAK_STDIN_DESC;
ret_cd = gen_multy_bakup_files(file_list, input_file_desc, file_num);
if (ret_cd == FB_FAILURE) {
fprintf(stderr,
"%s: progam fails to generate multi-volumn back-up files\n",
prog_name);
ret_cd = free_file_list(file_list);
return FB_FAILURE;
}
break;
case (IN_SW_SPIT_JT):
ret_cd = join_multy_bakup_files(file_list);
if (ret_cd == FB_FAILURE) {
fprintf(stderr,
"%s: progam fails to join multi-volumn back-up files\n",
prog_name);
ret_cd = free_file_list(file_list);
return FB_FAILURE;
}
break;
default:
break;
}
/********************************************************
**
** free all the storage allocated for backup files
**
*********************************************************
*/
ret_cd = free_file_list(file_list);
return FB_SUCCESS;
} // end of main()
static int get_function_option(const SCHAR* prog_name,
gsplit_option* sw_replace,
const SCHAR* string,
const in_sw_tab_t* const in_sw_table)
{
/********************************************************************
**
** g e t _ f u n c t i o n _ o p t i o n
**
*********************************************************************
**
** Functional description:
**
** processing command line option and set proper function switch
**
*********************************************************************
*/
SLONG ret_cd;
if (strlen(string) == 1) {
fprintf(stderr, "%s: invalid option '%s'\n", prog_name, string);
ret_cd = print_clo(prog_name);
return FB_FAILURE;
}
//gsplit_option op_specified = *sw_replace; Commented, where is it used here???
const SCHAR* q;
const in_sw_tab_t* in_sw_tab;
for (in_sw_tab = in_sw_table; q = in_sw_tab->in_sw_name; in_sw_tab++)
{
SCHAR c;
for (const SCHAR* p = string + 1; c = *p++;)
if (UPPER(c) != *q++)
break;
if (!c) {
if (*sw_replace == IN_SW_SPIT_0) {
*sw_replace = (gsplit_option) in_sw_tab->in_sw;
return FB_SUCCESS;
}
else {
if (*sw_replace != in_sw_tab->in_sw) {
fprintf(stderr,
"%s: invalid option '%s', incompatible option\n",
prog_name, string);
ret_cd = print_clo(prog_name);
return FB_FAILURE;
}
else // compatible option
break;
}
} // end of if (!c)
} // end of for loop
if (!in_sw_tab->in_sw) {
fprintf(stderr, "%s: invalid option '%s'\n", prog_name, string);
ret_cd = print_clo(prog_name);
return FB_FAILURE;
}
return FB_SUCCESS;
}
static int get_file_name( const SCHAR * string, double file_size, b_fil** file_ptr)
{
/********************************************************************
**
** g e t _ f i l e _ n a m e
**
*********************************************************************
**
** Functional description:
**
** processing file name specification and returns file structure
** pointer.
**
*********************************************************************
*/
b_fil* const temp_ptr = (b_fil*) malloc(b_fil_len);
*file_ptr = temp_ptr;
temp_ptr->b_fil_name = (TEXT*) malloc(strlen(string) + 1);
temp_ptr->b_fil_next = NULL;
strcpy(temp_ptr->b_fil_name, string);
temp_ptr->b_fil_number = 0;
temp_ptr->b_fil_size = file_size;
return FB_SUCCESS;
}
static int get_file_size(const SCHAR* prog_name, const SCHAR* string, double* file_size)
{
/********************************************************************
**
** g e t _ f i l e _ s i z e
**
*********************************************************************
**
** Functional description:
**
** processing file size specification and returns calculated
** file size.
**
*********************************************************************
*/
SCHAR c;
SLONG ret_cd;
for (const SCHAR* p = string; c = *p++;)
{
if (c == '\0')
break;
if (isdigit(c))
*file_size = *file_size * 10 + (c - '0');
else {
if ((isalpha(c)) &&
(UPPER(c) == 'G' || UPPER(c) == 'K' || UPPER(c) == 'M'))
{
SLONG size_indicator = 0;
switch (UPPER(c)) {
case ('K'):
size_indicator = K_BYTES;
break;
case ('M'):
size_indicator = M_BYTES;
break;
case ('G'):
size_indicator = G_BYTES;
break;
default:
break;
} // end of switch( UPPER (c) )
*file_size = *file_size * size_indicator;
}
else { // invalid size indicator
fprintf(stderr,
"%s: invalid size indicator '%s'\n", prog_name,
string);
ret_cd = print_clo(prog_name);
return FB_FAILURE;
}
}
}
if (*file_size < MIN_FILE_SIZE) { // handling user specifies file size 0
fprintf(stderr,
"%s: invalid option '%s', minimum file size is 1 megabyte\n",
prog_name, string);
ret_cd = print_clo(prog_name);
return FB_FAILURE;
}
return FB_SUCCESS;
}
static int gen_multy_bakup_files(b_fil* file_list,
FILE_DESC input_file_desc, SLONG file_num)
{
/********************************************************************
**
** g e n _ m u l t y _ b a c k u p _ f i l e s
**
*********************************************************************
**
** Functional description:
**
** processing input data from stdin and splits the data into
** multiple back-up files.
**
** allocates an 16K bytes I/O buffer
** intilializes header record common fields
** do forever
** walk through the backup file chain
** intilializes header record unique fields
** open backup file
** writes out header record to backup file
** points to the next backup file in the chain
** calculates the actual file size ( minus header record length )
** if the actual file size less than 16K bytes
** set I/O size to actual file size
** otherwise
** set I/O size to 16K byte long
** when it is the last backup file
** reads data from standard input as much as indicated by I/O size
** and writes it out to the last backup file until no EOF.
** issues error message when disk space full condition is detected
** otherwise reads and writes to backup files util EOF
** if disk full cobdition is detected
** flush the remaining data in the I/O buffer to subsequence
** backup files
** go back to normal read and write process util EOF
**
*********************************************************************
*/
SLONG byte_write, ret_cd;
TEXT header_str[header_rec_len], num_arr[5];
header_rec hdr_rec;
// CVC: there's a can of worms here. First, this function assumes it can free
// the io_buffer's allocated memory without keeping a second copy of that pointer.
// However, io_buffer can't be declared UCHAR* const because its address is
// passed to final_read_and_write() and read_and_write() and both functions
// thus suggest, by taking a UCHAR** that they can change the pointer's address;
// but in practice they never affect it, so fixing those functions to take simply
// UCHAR* would allow the correct declaration for io_buffer to succeed.
//UCHAR* const io_buffer = (UCHAR *) malloc(IO_BUFFER_SIZE);
UCHAR* io_buffer = (UCHAR *) malloc(IO_BUFFER_SIZE);
if (!io_buffer) {
fprintf(stderr, "I/O buffer allocation failed\n");
return FB_FAILURE;
}
size_t pos;
for (pos = 0; pos < header_rec_len; pos++)
header_str[pos] = BLANK;
pos = 0;
ret_cd = set_hdr_str(header_str, header_rec_name,
pos, sizeof(hdr_rec.name));
size_t indx;
for (indx = 0; indx < sizeof(hdr_rec.name); indx++)
hdr_rec.name[indx] = BLANK;
pos = pos + sizeof(hdr_rec.name);
time_t clock = time(0); // was SLONG
ret_cd = set_hdr_str(header_str, ctime(&clock),
pos, sizeof(hdr_rec.date_time));
for (indx = 0; indx < sizeof(hdr_rec.date_time); indx++)
hdr_rec.date_time[indx] = BLANK;
pos = pos + sizeof(hdr_rec.date_time);
ret_cd = set_hdr_str(header_str, ", file No. ",
pos, sizeof(hdr_rec.text1));
for (indx = 0; indx < sizeof(hdr_rec.text1); indx++)
hdr_rec.text1[indx] = BLANK;
for (indx = 0; indx < sizeof(hdr_rec.num); indx++)
hdr_rec.num[indx] = BLANK;
pos = pos + sizeof(hdr_rec.text1) + sizeof(hdr_rec.num);
ret_cd = set_hdr_str(header_str, " of ", pos, sizeof(hdr_rec.text2));
for (indx = 0; indx < sizeof(hdr_rec.text2); indx++)
hdr_rec.text2[indx] = BLANK;
ret_cd = conv_ntoc(file_num, num_arr);
if (ret_cd == FB_FAILURE) {
free(io_buffer);
fprintf(stderr,
"gsplit could not convert numeric data to character data\n");
return FB_FAILURE;
}
num_arr[sizeof(num_arr) - 1] = TERMINAL;
pos = pos + sizeof(hdr_rec.text2);
ret_cd = set_hdr_str(header_str, num_arr, pos, sizeof(hdr_rec.total));
for (indx = 0; indx < sizeof(hdr_rec.total); indx++)
hdr_rec.total[indx] = BLANK;
pos = pos + sizeof(hdr_rec.total);
ret_cd = set_hdr_str(header_str, ", ", pos, sizeof(hdr_rec.text3));
for (indx = 0; indx < sizeof(hdr_rec.text3); indx++)
hdr_rec.text3[indx] = BLANK;
for (indx = 0; indx < sizeof(hdr_rec.fl_name); indx++)
hdr_rec.fl_name[indx] = BLANK;
FILE_DESC output_fl_desc = 0;
bool end_of_input = false, flush_done = false;
const TEXT* file_name = NULL;
SLONG io_size = 0;
b_fil* fl_ptr = file_list;
double byte_read = 0;
double file_size = 0;
while (true) {
if (fl_ptr != NULL) {
byte_read = 0;
byte_write = 0;
if (!fl_ptr->b_fil_next && (fl_ptr->b_fil_size == 0))
{
fl_ptr->b_fil_size = MIN_FILE_SIZE;
}
file_size = fl_ptr->b_fil_size - header_rec_len;
file_name = fl_ptr->b_fil_name;
output_fl_desc = open(file_name, mode_write, mask);
if (output_fl_desc == -1) {
free(io_buffer);
fprintf(stderr, "can not open back up file %s\n",
file_name);
return FB_FAILURE;
}
ret_cd =
write_header(fl_ptr, hdr_rec, output_fl_desc, header_str);
if (ret_cd == FB_FAILURE) {
free(io_buffer);
fprintf(stderr,
"could not write header record to file %s\n",
file_name);
return FB_FAILURE;
}
fl_ptr = fl_ptr->b_fil_next;
}
if (file_size < IO_BUFFER_SIZE)
io_size = (SLONG) file_size;
else
io_size = IO_BUFFER_SIZE;
if (!fl_ptr) {
while (!end_of_input) {
ret_cd = final_read_and_write(input_file_desc, output_fl_desc,
file_name, io_size, &io_buffer,
&end_of_input);
if (ret_cd == FB_FAILURE) {
free(io_buffer);
return FB_FAILURE;
}
if (end_of_input) {
free(io_buffer);
return FB_SUCCESS;
}
}
}
else {
while ((file_size > byte_read) && (fl_ptr != NULL)) {
ret_cd = read_and_write(input_file_desc, output_fl_desc,
file_name, io_size, file_size,
&io_buffer, &end_of_input,
&byte_read, &byte_write);
switch (ret_cd) {
case FB_FAILURE:
{
free(io_buffer);
return FB_FAILURE;
break;
}
case FILE_IS_FULL:
{
byte_read = 0; /* reset byte read count,
** prepare for next read
*/
UCHAR* remaining_io = io_buffer + byte_write;
SLONG remaining_io_len = IO_BUFFER_SIZE - byte_write;
while (!flush_done && (fl_ptr != NULL)) {
if (!fl_ptr->b_fil_next && fl_ptr->b_fil_size == 0)
fl_ptr->b_fil_size = MIN_FILE_SIZE;
file_size = fl_ptr->b_fil_size - header_rec_len;
file_name = fl_ptr->b_fil_name;
output_fl_desc =
open(file_name, mode_write, mask);
if (output_fl_desc == -1) {
free(io_buffer);
fprintf(stderr,
"can not open back up file %s\n",
file_name);
return FB_FAILURE;
}
ret_cd = write_header(fl_ptr, hdr_rec,
output_fl_desc, header_str);
if (ret_cd == FB_FAILURE) {
free(io_buffer);
fprintf(stderr,
"fail to write header rec to file %s\n",
file_name);
return FB_FAILURE;
}
fl_ptr = fl_ptr->b_fil_next;
if (!fl_ptr) {
ret_cd = final_flush_io_buff(remaining_io,
remaining_io_len,
output_fl_desc);
if (ret_cd == FB_FAILURE) {
fprintf(stderr,
"gsplit could not do backup due");
fprintf(stderr,
" to lack of space or I/O problem\n");
free(io_buffer);
return FB_FAILURE;
}
}
else { // got a lot of backup files
ret_cd = flush_io_buff(remaining_io,
remaining_io_len,
output_fl_desc,
file_size,
&byte_write,
&flush_done);
if (ret_cd == FB_FAILURE) {
fprintf(stderr,
"gsplit could not do backup due");
fprintf(stderr, " I/O problem\n");
free(io_buffer);
return FB_FAILURE;
}
if (flush_done) {
file_size = file_size - byte_write;
byte_write = 0;
}
else {
remaining_io = remaining_io + byte_write;
remaining_io_len = remaining_io_len -
byte_write;
}
}
} // end of while loop
break;
} // case FILE_IS_FULL
default:
break;
}
if (end_of_input) {
free(io_buffer);
return FB_SUCCESS;
}
}
}
} // end of while ( true )
}
static int read_and_write(FILE_DESC input_file_desc,
FILE_DESC output_fl_desc,
const TEXT* file_name,
SLONG io_size,
double file_size,
UCHAR** io_buffer,
bool* end_of_input,
double *byte_read,
SLONG* byte_write)
{
/********************************************************************
**
** r e a d _ a n d _ w r i t e
**
*********************************************************************
**
** Functional description:
**
** Read data from input file and write the data out to output
** file.
**
*********************************************************************
*/
SLONG read_cnt, last_read_size;
/********************************************************
** when number of byte read + number of byte goint to
** be read is greater then file size, then calculate
** the size for the last read and do the last read for
** the current backup file. Otherwise read as mush data
** as will fit in the current backup file.
**********************************************************
*/
if (*byte_read + io_size > file_size) {
last_read_size = (SLONG) (file_size - *byte_read);
read_cnt = read(input_file_desc, *io_buffer, last_read_size);
}
else
read_cnt = read(input_file_desc, *io_buffer, io_size);
switch (read_cnt) {
case (0): // no more data to be read
close(output_fl_desc);
*end_of_input = true;
*byte_read = *byte_read + read_cnt;
return FB_SUCCESS;
break;
case (-1): // read failed
close(output_fl_desc);
fprintf(stderr,
"fail to read input from stdin, errno = %d\n", errno);
return FB_FAILURE;
break;
default: // read ok
*byte_read = *byte_read + read_cnt;
break;
}
const SLONG write_cnt = write(output_fl_desc, *io_buffer, read_cnt);
switch (write_cnt) {
case (-1): // write failed
close(output_fl_desc);
return FB_FAILURE;
break;
default:
if (write_cnt == read_cnt) // write ok
return FB_SUCCESS;
else { // write less data then it reads in
close(output_fl_desc);
*byte_write = write_cnt;
return FILE_IS_FULL;
}
break;
}
}
static int final_read_and_write(FILE_DESC input_file_desc,
FILE_DESC output_fl_desc,
const TEXT* file_name,
SLONG io_size,
UCHAR** io_buffer,
bool* end_of_input)
{
/********************************************************************
**
** f i n a l _ r e a d _ a n d _ w r i t e
**
*********************************************************************
**
** Functional description:
**
** Read all data from input file and write the data out to output
** file.
**
*********************************************************************
*/
const SLONG read_cnt = read(input_file_desc, *io_buffer, io_size);
switch (read_cnt) {
case (0): // no more data to be read
close(output_fl_desc);
*end_of_input = true;
return FB_SUCCESS;
break;
case (-1): // read failed
close(output_fl_desc);
fprintf(stderr,
"problem when reading input file, errno = %d\n", errno);
return FB_FAILURE;
break;
default: // read ok
break;
}
const SLONG write_cnt = write(output_fl_desc, *io_buffer, read_cnt);
switch (write_cnt) {
case (-1): // write failed
close(output_fl_desc);
return FB_FAILURE;
break;
default:
if (write_cnt == read_cnt) // write ok
return FB_SUCCESS;
else { // write less data then it reads in
fprintf(stderr,
"There is no enough space to write to back up file %s\n",
file_name);
close(output_fl_desc);
return FB_FAILURE;
}
break;
}
}
static int join_multy_bakup_files( b_fil* file_list)
{
/********************************************************************
**
** j o i n _ m u l t y _ b a c k u p _ f i l e s
**
*********************************************************************
**
** Functional description:
**
** allocates I/O buffer and walks through backup files` chain.
** calls read_and_write_for_join routine to read data from
** backup file and to write data to standard output file
** which later to be processed by gbak. Finally frees up
** I/O buffer
**
*********************************************************************
*/
FILE_DESC output_fl_desc = GBAK_STDOUT_DESC;
// See comment near the beginning of gen_multy_bakup_files() as it
// also applies to read_and_write_for_join().
//UCHAR* const io_buffer = (UCHAR *) malloc(IO_BUFFER_SIZE);
UCHAR* io_buffer = (UCHAR *) malloc(IO_BUFFER_SIZE);
if (io_buffer == 0) {
fprintf(stderr, "I/O buffer allocation failed\n");
return FB_FAILURE;
}
SLONG cnt = 0, total_int = 0;
// Why two variables to achieve this simple loop?
b_fil* next_fl = NULL;
for (b_fil* fl_ptr = file_list; fl_ptr; fl_ptr = next_fl) {
cnt++;
next_fl = fl_ptr->b_fil_next;
const TEXT* file_name = fl_ptr->b_fil_name;
SLONG ret_cd =
read_and_write_for_join(output_fl_desc, file_name, &io_buffer,
cnt, &total_int);
if (ret_cd == FB_FAILURE) {
free(io_buffer);
return FB_FAILURE;
}
} // end of for loop
free(io_buffer);
return FB_SUCCESS;
}
static int read_and_write_for_join(FILE_DESC output_fl_desc,
const TEXT* file_name,
UCHAR ** io_buffer,
SLONG cnt,
SLONG * total_int)
{
/********************************************************************
**
** r e a d _ a n d _ w r i t e _ f o r _ j o i n
**
*********************************************************************
**
** Functional description:
**
** Reads data from backup files and writes to standard
** output file.
**
*********************************************************************
*/
TEXT num_arr[5], total_arr[5];
header_rec hdr_rec;
FILE_DESC input_fl_desc = open(file_name, mode_read);
if (input_fl_desc == -1) {
fprintf(stderr, "can not open input file %s\n", file_name);
return FB_FAILURE;
}
int read_cnt = read(input_fl_desc, *io_buffer, header_rec_len);
if (read_cnt != static_cast<int>(header_rec_len)) {
close(input_fl_desc);
fprintf(stderr,
"progam fails to read gsplit header record in back-up file%s\n",
file_name);
return FB_FAILURE;
}
const TEXT* char_ptr1 = reinterpret_cast<char*>(*io_buffer);
SLONG ret_cd = strncmp(char_ptr1, header_rec_name, sizeof(hdr_rec.name) - 1);
if (ret_cd != 0) {
close(input_fl_desc);
fprintf(stderr, "gsplit: expected GSPLIT description record\n");
fprintf(stderr,
"gsplit: Exiting before completion due to errors\n");
return FB_FAILURE;
}
SLONG skip_to_num = sizeof(hdr_rec.name) + sizeof(hdr_rec.date_time) +
sizeof(hdr_rec.text1);
SLONG skip_to_total = skip_to_num + sizeof(hdr_rec.num) + sizeof(hdr_rec.text2);
char_ptr1 = reinterpret_cast<char*>(*io_buffer + skip_to_num);
const TEXT* char_ptr2 = reinterpret_cast<char*>(*io_buffer + skip_to_total);
size_t indx;
for (indx = 0; indx < sizeof(hdr_rec.num); indx++) {
num_arr[indx] = *char_ptr1;
char_ptr1++;
if (cnt == 1) {
total_arr[indx] = *char_ptr2;
char_ptr2++;
}
}
num_arr[indx] = '\0';
const SLONG num_int = atoi(num_arr);
if (cnt == 1) {
total_arr[indx] = '\0';
*total_int = atoi(total_arr);
}
if ((num_int != cnt) || (num_int > *total_int)) {
close(input_fl_desc);
fprintf(stderr,
"gsplit: join backup file is out of sequence\n");
fprintf(stderr,
"gsplit: Exiting before completion due to errors\n");
return FB_FAILURE;
}
read_cnt = read(input_fl_desc, *io_buffer, IO_BUFFER_SIZE);
while (true) {
switch (read_cnt) {
case (0): // no more data to be read
close(input_fl_desc);
return FB_SUCCESS;
break;
case (-1): // read failed
close(input_fl_desc);
return FB_FAILURE;
break;
default: // this is the last read
break;
}
SLONG write_cnt = write(output_fl_desc, *io_buffer, read_cnt);
switch (write_cnt) {
case (-1): // write failed
close(input_fl_desc);
return FB_FAILURE;
break;
default:
fb_assert(write_cnt == read_cnt);
break;
}
read_cnt = read(input_fl_desc, *io_buffer, IO_BUFFER_SIZE);
} // end of while (true) loop
}
static int conv_ntoc( SLONG numeric_in, TEXT char_out[])
{
/********************************************************************
**
** c o n v _ n t o c
**
*********************************************************************
**
** Functional description:
**
** Convert 4-digit numeric data to a 4-byte character array.
**
*********************************************************************
*/
SLONG i = numeric_in;
SLONG indx = 3;
while (true) {
const SLONG mod = i % 10;
switch (mod) {
case (0):
char_out[indx] = '0';
break;
case (1):
char_out[indx] = '1';
break;
case (2):
char_out[indx] = '2';
break;
case (3):
char_out[indx] = '3';
break;
case (4):
char_out[indx] = '4';
break;
case (5):
char_out[indx] = '5';
break;
case (6):
char_out[indx] = '6';
break;
case (7):
char_out[indx] = '7';
break;
case (8):
char_out[indx] = '8';
break;
default:
char_out[indx] = '9';
break;
}
indx = indx - 1;
i = i / 10;
if (i <= 0)
break;
}
for (; indx >= 0; indx--) {
char_out[indx] = ' ';
}
return FB_SUCCESS;
}
static int write_header(b_fil* fl_ptr,
header_rec hdr_rec,
FILE_DESC output_fl_desc,
TEXT header_str[])
{
/********************************************************************
**
** w r i t e _ h e a d e r
**
*********************************************************************
**
** Functional description:
**
** writes out gsplit header record
**
** assigns backup file name to header record file name array
** calls conv_ntoc routine to convert numeric data to char data
** and assigns to header record file number field
** writes gsplit header record out to backup file
**
*********************************************************************
*/
TEXT num_arr[5];
SLONG end, indx;
SLONG ret_cd = conv_ntoc(fl_ptr->b_fil_number, num_arr);
if (ret_cd == FB_FAILURE) {
printf
("gsplit could not convert numeric data to character data\n");
return FB_FAILURE;
}
num_arr[sizeof(num_arr) - 1] = TERMINAL;
SLONG pos = sizeof(hdr_rec.name) + sizeof(hdr_rec.date_time) +
sizeof(hdr_rec.text1);
ret_cd = set_hdr_str(header_str, num_arr, pos, sizeof(hdr_rec.num));
const TEXT* file_name = fl_ptr->b_fil_name;
pos = sizeof(hdr_rec.name) + sizeof(hdr_rec.date_time) +
sizeof(hdr_rec.text1) + sizeof(hdr_rec.num) +
sizeof(hdr_rec.text2) + sizeof(hdr_rec.total) + sizeof(hdr_rec.text3);
ret_cd = set_hdr_str(header_str, file_name, pos, strlen(file_name));
SLONG write_cnt = write(output_fl_desc, header_str, header_rec_len);
switch (write_cnt)
{
case (-1): // write failed
close(output_fl_desc);
return FB_FAILURE;
break;
default:
end = pos + strlen(file_name);
for (indx = pos; indx < end; indx++)
header_str[indx] = BLANK;
return FB_SUCCESS;
break;
}
}
static int flush_io_buff(UCHAR* remaining_io,
SLONG remaining_io_len,
FILE_DESC output_fl_desc,
double file_size,
SLONG* byte_write,
bool* flush_done)
{
/********************************************************************
**
** f l u s h _ i o _ b u f f
**
*********************************************************************
**
** Functional description:
**
** flush out the remaining data in the I/O buffer
**
** when the file_size is truly has space to write out the remaining data
** then
** set and returns flush_done true
** otherwise
** closes the output file, set and returns flush_done false.
** otherwise
** we can only writes out as much data as file_size indicated
** if it was able to write out the remaining data
** then
** set and returns flush_done true
** otherwise
** closes the output file, set and returns flush_done false.
**
*********************************************************************
*/
SLONG write_cnt;
if (file_size > remaining_io_len) {
write_cnt = write(output_fl_desc, remaining_io, remaining_io_len);
}
else { /* file_size <= remaining_io_len */
write_cnt =
write(output_fl_desc, remaining_io, (unsigned int) file_size);
}
switch (write_cnt) {
case (-1): // write failed
close(output_fl_desc);
*flush_done = false;
return FB_FAILURE;
break;
default:
if (write_cnt == remaining_io_len) // write ok
*flush_done = true;
else { // could not write out all remaining data
close(output_fl_desc);
*flush_done = false;
}
*byte_write = write_cnt;
return FB_SUCCESS;
break;
}
}
static int final_flush_io_buff(UCHAR * remaining_io,
SLONG remaining_io_len,
FILE_DESC output_fl_desc)
{
/********************************************************************
**
** f i n a l _ f l u s h _ i o _ b u f f
**
*********************************************************************
**
** Functional description:
**
** flush out the remaining data in the I/O buffer
**
*********************************************************************
*/
SLONG write_cnt = write(output_fl_desc, remaining_io, remaining_io_len);
switch (write_cnt)
{
case (-1): // write failed
close(output_fl_desc);
return FB_FAILURE;
break;
default:
if (write_cnt == remaining_io_len) // write ok
return FB_SUCCESS;
else { // could not write out all remaining data
close(output_fl_desc);
return FB_FAILURE;
}
break;
}
}
static int print_clo(const TEXT* prog_name)
{
/********************************************************************
**
** p r i n t _ c l o
**
*********************************************************************
**
** Functional description:
**
** print out gsplit utility command line options
**
*********************************************************************
*/
fprintf(stderr, "%s: Command Line Options Are:\n", prog_name);
fprintf(stderr,
" gsplit -S[PLIT_BK_FILE] <file> <size>{k|m|g} [... <file> [<size>{k|m|g}]] or\n");
fprintf(stderr, " gsplit -J[OINT_BK_FILE] <file> [... <file>]\n");
fprintf(stderr,
"%s: option can be abbreviated to the unparenthesized characters\n",
prog_name);
fprintf(stderr,
"%s: Exiting before completion due to errors\n", prog_name);
return FB_SUCCESS;
}
static int set_hdr_str(TEXT header_str[], const TEXT * in_str, SLONG pos, SLONG len)
{
/********************************************************************
**
** s e t _ h d r _ s t r
**
*********************************************************************
**
** Functional description:
**
** initialyze header string
**
*********************************************************************
*/
const TEXT* t_str = in_str;
const SLONG end = pos + len;
for (SLONG indx = pos; indx < end; indx++) {
switch (*t_str) {
case (NEW_LINE):
case (TERMINAL):
header_str[indx] = ' ';
break;
default:
header_str[indx] = *t_str;
break;
}
*t_str++;
}
return FB_SUCCESS;
}
static int free_file_list( b_fil* file_list)
{
/********************************************************************
**
** f r e e _ f i l e _ l i s t
**
*********************************************************************
**
** Functional description:
**
** free all the storage allocated for backup files structure
**
*********************************************************************
*/
b_fil* next_file = NULL;
for (b_fil* file_ptr = file_list; file_ptr != NULL; file_ptr = next_file) {
next_file = file_ptr->b_fil_next;
free(file_ptr->b_fil_name);
free(file_ptr);
}
return FB_SUCCESS;
}