3045 lines
82 KiB
C
3045 lines
82 KiB
C
/*
|
|
* Copyright (C) 1999-2004, Rildo Pragana, Ferran Pegueroles, Jim Noeth,
|
|
* Bernard Giroud, David Essex, Andrew Cameron.
|
|
* Copyright (C) 1993, 1991 Rildo Pragana.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation; either version 2.1,
|
|
* or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; see the file COPYING.LIB. If
|
|
* not, write to the Free Software Foundation, Inc., 59 Temple Place,
|
|
* Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/*
|
|
*
|
|
* Cobol Compiler Library -- File Handling Module
|
|
*
|
|
*/
|
|
|
|
/* Permissible statements -- table from CD-1.2 (cobol draft) pp. 450
|
|
* -----------------------------------------------------------------
|
|
* acc. mode | statement | -------- open mode ------
|
|
* | | input output i-o extend
|
|
* ----------+------------+--------------------------
|
|
* line | read | X
|
|
* sequential| write | X X
|
|
* ----------+------------+--------------------------
|
|
* sequential| read | X X
|
|
* | write | X X
|
|
* | rewrite | X
|
|
* ----------+------------+--------------------------
|
|
* sequential| delete | X
|
|
* (relative | |
|
|
* & indexed| start | X X
|
|
* files) | |
|
|
* ----------+------------+--------------------------
|
|
* random | read | X X
|
|
* | write | X X
|
|
* | rewrite | X
|
|
* | start |
|
|
* | delete | X
|
|
* ----------+------------+--------------------------
|
|
* dynamic | read | X X
|
|
* | write | X X
|
|
* | rewrite | X
|
|
* | start | X X
|
|
* | delete | X
|
|
* ----------+------------+--------------------------
|
|
*/
|
|
|
|
/*
|
|
ANSI 2000x FILE STATUS CODES
|
|
---------------------------
|
|
See http://www.ncits.org/tc_home/j4.htm for details
|
|
|
|
*/
|
|
|
|
#include "htcoblib.h"
|
|
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
#ifdef USE_LOCKSERVER
|
|
|
|
#include "flckclient.h"
|
|
|
|
extern struct fd_filename name_info[];
|
|
extern char *temp_key;
|
|
extern char *lockserverhost;
|
|
|
|
#endif
|
|
|
|
int ignore_lock = 0;
|
|
#define bcounter 5
|
|
#if defined(SunOS)
|
|
va_list __builtin_va_alist;
|
|
#endif
|
|
|
|
int need_lock = 0;
|
|
|
|
#define RETURN_STATUS(x) do { save_status (f, x); return(x); } while (0)
|
|
|
|
#ifdef USE_MF_COMPATABILITY
|
|
#define TCOB_EXTERNFILE_MFCOMP 1
|
|
#endif
|
|
|
|
#ifdef __MINGW32__
|
|
|
|
#ifndef LT_DIRSEP_CHAR
|
|
#define LT_DIRSEP_CHAR '\\'
|
|
#endif
|
|
#ifndef LT_DIRSEP_STR
|
|
#define LT_DIRSEP_STR "\\"
|
|
#endif
|
|
|
|
#else
|
|
|
|
#ifndef LT_DIRSEP_CHAR
|
|
#define LT_DIRSEP_CHAR '/'
|
|
#endif
|
|
#ifndef LT_DIRSEP_STR
|
|
#define LT_DIRSEP_STR "/"
|
|
#endif
|
|
|
|
#endif
|
|
|
|
static void save_status(struct file_desc *f, int status)
|
|
{
|
|
f->file_status[0] = status / 10 + '0';
|
|
f->file_status[1] = status % 10 + '0';
|
|
}
|
|
|
|
#ifdef WANT_ALL_DYNAMIC_LIBS
|
|
static DB *db_open_stub( const char *s, int i, int j, DBTYPE d, const void *p );
|
|
static DB *(* db_open)( const char *, int, int, DBTYPE, const void * ) =
|
|
db_open_stub;
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| db_open_stub |
|
|
| |
|
|
\*------------------------------------------------------------------------*/
|
|
|
|
static DB * db_open_stub( const char *s, int i, int j, DBTYPE d, const void *p )
|
|
{
|
|
char *libname = "libdb.so";
|
|
void *handle = dlopen(libname,RTLD_LAZY);
|
|
if (!handle)
|
|
{
|
|
fprintf(stderr,"*ERROR* loading %s: %s\n",libname,dlerror());
|
|
return NULL;
|
|
}
|
|
db_open = dlsym(handle,"dbopen");
|
|
return (* db_open)( s,i,j,d,p );
|
|
}
|
|
#else
|
|
#define db_open dbopen
|
|
#endif
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| tcob_check_varying |
|
|
| |
|
|
\*------------------------------------------------------------------------*/
|
|
|
|
int tcob_check_varying(struct file_desc *f, struct fld_desc *min_desc,
|
|
char *min_value, struct fld_desc *max_desc, char *max_value,
|
|
struct fld_desc *reclen_desc, char *reclen_value)
|
|
{
|
|
int mi_value;
|
|
int mx_value;
|
|
int rc_len;
|
|
char temp[20];
|
|
struct fld_desc *t;
|
|
char *t_value;
|
|
char *b;
|
|
|
|
t = (struct fld_desc *) malloc(sizeof(struct fld_desc *));
|
|
t_value = (char *) malloc(sizeof(char *));
|
|
b = (char *) malloc(sizeof(char *));
|
|
memset(temp, 0, 19);
|
|
if (min_desc == NULL)
|
|
{
|
|
mi_value = 0;
|
|
}
|
|
else
|
|
{
|
|
strncpy(temp, min_value, min_desc->len);
|
|
mi_value = atoi(temp);
|
|
}
|
|
memset(temp, 0, 19);
|
|
strncpy(temp, max_value, max_desc->len);
|
|
mx_value = atoi(temp);
|
|
memset(temp, 0, 19);
|
|
t->len = reclen_desc->len;
|
|
t->type = DTYPE_DISPLAY;
|
|
t->decimals = reclen_desc->decimals;
|
|
t->all = reclen_desc->all;
|
|
t->just_r = reclen_desc->just_r;
|
|
t->reserved = reclen_desc->reserved;
|
|
t->pic = b;
|
|
tcob_move(reclen_desc, reclen_value, t, t_value);
|
|
strncpy(temp, t_value, t->len);
|
|
rc_len = atoi(temp);
|
|
if ((rc_len <= mx_value) && (rc_len >= mi_value))
|
|
{
|
|
f->reclen = rc_len;
|
|
}
|
|
else
|
|
{
|
|
f->reclen = -1;
|
|
return 99;
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#ifdef TCOB_EXTERNFILE_MFCOMP
|
|
|
|
/*-----------------------------------------------------------*\
|
|
* map_file_name *
|
|
* *
|
|
* When the programmer wrote the COBOL SELECT statement, *
|
|
* we assume he intended the User to define an Environment *
|
|
* Variable that will tell the program where to find the *
|
|
* file. *
|
|
* *
|
|
* There are several possibilities: *
|
|
* *
|
|
* 1. The programmer wants the User to specify the base *
|
|
* directory for the data. In this case, the *
|
|
* programmer would have put something like *
|
|
* "DATA/Stock/ProductFile" in the SELECT clause. *
|
|
* *
|
|
* In this case, we would expect the User to set an *
|
|
* Environment Variable named "$DD_DATA". *
|
|
* Example (i) *
|
|
* DD_DATA=/usr/local/AccountsData *
|
|
* In this case, the full name of the file will *
|
|
* be: /usr/local/AccountsData/Stock/ProductFile *
|
|
* *
|
|
* However, Users like to do dumb things so we also *
|
|
* check to see whether they set up an environment *
|
|
* Variable named "$DATA". *
|
|
* Example (ii) *
|
|
* DATA=/u/Accts *
|
|
* In this case, the full name of the file will *
|
|
* be : /u/Accts/Stock/ProductFile *
|
|
* *
|
|
* (Note that these two examples prove that dumb Users *
|
|
* always use shorter, less meaningful names than the *
|
|
* theoretically possible intellegent User.) *
|
|
* *
|
|
* 2. The programmer wants the User to specify the full *
|
|
* file name. In this case, the programmer would have *
|
|
* put something like "PRODUCTFILE" in his SELECT *
|
|
* statement. *
|
|
* *
|
|
* In this case, we would like the User to have set *
|
|
* up an Environment Variable named "$DD_PRODUCTFILE". *
|
|
* Example (iii) *
|
|
* DD_PRODUCTFILE=/u/Data/Stock/ProductFile *
|
|
* In this case, the full name of the file will *
|
|
* simply be : /u/Data/Stock/ProductFile *
|
|
* *
|
|
* Again, we need to cater for the less intelligent *
|
|
* User, so we also check to see whether he named the *
|
|
* Environment Variable "$PRODUCTFILE". *
|
|
* Example (iv) *
|
|
* PRODUCTFILE=/ProductFile *
|
|
* In this case, the full name of the file will *
|
|
* simply be : /ProductFile *
|
|
* (Note that example (iv) is a true to life example *
|
|
* showing just how dumb Users can be. It takes a *
|
|
* truly ingenious kind of studidity to manage to *
|
|
* set your data files up in the root directory.) *
|
|
* *
|
|
* 3. (a) The programmer wants to hard-code the name of *
|
|
* file himself and hence will have something *
|
|
* like "/Data/Stock/ProductFile" in his *
|
|
* SELECT statement. *
|
|
* (b) The programmer really wanted the User to *
|
|
* specify an Environment Variable, but the User *
|
|
* stuffed up the installation process and there *
|
|
* is no Environment Variable to be found. *
|
|
* *
|
|
* In this case, we can do nothing except to use the *
|
|
* name the programmer typed in the SELECT statement *
|
|
* as the actual name of the file. *
|
|
* That is, we actually look for a file called : *
|
|
* "/Data/Stock/ProductFile". *
|
|
* *
|
|
* (Note that in this case we can be confident that *
|
|
* "/Data" is a separate partition and not a *
|
|
* directory in the root partition - Yeah, sure!) *
|
|
* *
|
|
\*-----------------------------------------------------------*/
|
|
char *map_file_name( char *evname )
|
|
{
|
|
int slash_pos;
|
|
int len, lenDD, lenSuffix;
|
|
char *file_name;
|
|
char *evDataDir, *dataDir, *file_Suffix;
|
|
|
|
file_name = NULL;
|
|
|
|
// fprintf(stderr, "debug: map_file_name: LT_DIRSEP_STR=%s;\n", LT_DIRSEP_STR);
|
|
|
|
/* First we search through the file name specified
|
|
* by the Cobol programmer and find the first
|
|
* occurance of the character "/".
|
|
* We will then use this to divide the name into
|
|
* the "Data Directory" and the "File Suffix".
|
|
* ( If there is no "/" in the name, then we treat
|
|
* the entire name as the "Data Directory" and
|
|
* assume a NULL File Suffix. I know it sounds
|
|
* crazy, but no less crazy than assuming a Null
|
|
* Data Directory).
|
|
*/
|
|
slash_pos = strcspn(evname, "/\\"); //LT_DIRSEP_STR);
|
|
|
|
/* Now we cheat a little by setting file_suffix
|
|
* to point to the same string as "evname",
|
|
* but starting half way through.
|
|
* This is naughty, and we must be careful that
|
|
* we don't go changing the contents of evname.
|
|
*/
|
|
file_Suffix = evname + slash_pos;
|
|
lenSuffix = strlen(file_Suffix);
|
|
|
|
/* Next we start forming up the name of the
|
|
* Environment Variable we wepect to find.
|
|
* We allocate enough room to hold the
|
|
* prefix for the environment variable
|
|
* :- "DD_"
|
|
* as well as the "Name" and the terminating
|
|
* Null character.
|
|
*/
|
|
evDataDir = malloc(slash_pos + 4);
|
|
|
|
strncpy(evDataDir, "DD_", 3);
|
|
strncpy(evDataDir + 3, evname, slash_pos);
|
|
evDataDir[slash_pos + 3] = 0;
|
|
|
|
/* Now lets see if the Environment Variable
|
|
* has been set up/
|
|
*/
|
|
dataDir = getenv(evDataDir);
|
|
|
|
/* If we failed to find an environment variable named
|
|
* "$DD_DataDir" then we don't give up yet...
|
|
*
|
|
* We will see whether the User has defined an
|
|
* environment variable named "$DataDir".
|
|
* Obviously, To do this, we just drop the first three
|
|
* characters from "DD_DataDir".
|
|
*/
|
|
if (dataDir == NULL)
|
|
{
|
|
evDataDir = evDataDir + 3;
|
|
dataDir = getenv(evDataDir);
|
|
}
|
|
|
|
/* If we found an environment variable containing the
|
|
* Data Directory, then we go ahead and use it.
|
|
* That is, we glue the file Suffix onto the end
|
|
* of the Data Directory to give us the full name
|
|
* of the file.
|
|
*/
|
|
if (dataDir != NULL)
|
|
{
|
|
lenDD = strlen(dataDir);
|
|
file_name = malloc(lenDD + lenSuffix + 1);
|
|
strcpy(file_name, dataDir);
|
|
strcpy(file_name + lenDD, file_Suffix);
|
|
}
|
|
|
|
else
|
|
/* If we failed to find an environment variable
|
|
* containing the name of the Data Directory,
|
|
* then we assume that the Programmer has
|
|
* Hard-Coded the file name.
|
|
* That is, we assume the name of the Environment
|
|
* Variable is really the name of the file itself.
|
|
*/
|
|
{
|
|
len = strlen(evname);
|
|
file_name = malloc(len+1);
|
|
strncpy(file_name,evname,len);
|
|
file_name[len] = '\0';
|
|
}
|
|
|
|
//fprintf(stderr, "debug: map_file_name: file_name=%s;\n", file_name);
|
|
|
|
return(file_name);
|
|
|
|
}
|
|
#endif
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| tcob_open |
|
|
| |
|
|
\*------------------------------------------------------------------------*/
|
|
|
|
int tcob_open(struct file_desc *f, char *record, char *fname, int mode)
|
|
{
|
|
DBTYPE type = DB_BTREE;
|
|
void *infop = NULL;
|
|
int sflags = S_IRUSR | S_IWUSR;
|
|
int oflags = 0;
|
|
char *filename, *evname;
|
|
#ifndef TCOB_EXTERNFILE_MFCOMP
|
|
char *pt;
|
|
#endif
|
|
char alt_filename[128];
|
|
int alt_key_no;
|
|
int len;
|
|
|
|
BTREEINFO alt_key;
|
|
alt_key.flags = 0;
|
|
alt_key.cachesize = 0;
|
|
alt_key.maxkeypage = 0;
|
|
alt_key.minkeypage = 0;
|
|
alt_key.psize = 0;
|
|
alt_key.compare = NULL;
|
|
alt_key.prefix = NULL;
|
|
alt_key.lorder = 0;
|
|
|
|
/* Check the correct structure */
|
|
if (f->vers_id < RTL_FILE_VERSION)
|
|
{
|
|
fprintf(stderr, "You need to recompile your program\n");
|
|
fprintf(stderr, "Version mismatch; structure %x, RTL %x\n", f->vers_id,
|
|
RTL_FILE_VERSION);
|
|
RETURN_STATUS(99);
|
|
}
|
|
f->read_done = 0;
|
|
/*
|
|
BEWARE:
|
|
fname points to a field storage (non null-terminated)
|
|
we must copy fname to a C string and terminate it with a \0
|
|
and also trim spaces at the end.
|
|
|
|
NOTE:
|
|
This is a hack to accommodate the 'EXTERNAL' clause in
|
|
the SELECT statement.
|
|
|
|
The 'access_mode' in the file structure 'struct file_desc' is
|
|
used to indicate how the value stored in 'fname' is used.
|
|
It can be used to define a filename variable or a environment
|
|
variable.
|
|
|
|
If the access_mode < ACCEV_ENVAR (5), then 'fname' is used to
|
|
define a filename. Else 'fname' is used as an environment
|
|
variable which is used to determined the actual filename.
|
|
If no environment variable is found or is empty, then the
|
|
filename defaults to the value stored in 'fname'.
|
|
*/
|
|
f->eof_hit = 0;
|
|
if (f->access_mode < ACCEV_ENVAR)
|
|
{
|
|
len = f->fname_desc->len;
|
|
filename = malloc(len + 1);
|
|
memmove(filename, fname, len);
|
|
|
|
// printf("Will try open %s\n",filename);
|
|
|
|
do
|
|
{
|
|
filename[len--] = 0;
|
|
} while (filename[len] == ' ');
|
|
evname = NULL;
|
|
}
|
|
else
|
|
{
|
|
len = f->fname_desc->len;
|
|
evname = malloc(len + 1);
|
|
memmove(evname, fname, len);
|
|
do
|
|
{
|
|
evname[len--] = 0;
|
|
} while (evname[len] == ' ');
|
|
|
|
/* Get environment variable, if it exists */
|
|
#ifdef TCOB_EXTERNFILE_MFCOMP
|
|
filename = map_file_name(evname);
|
|
#else
|
|
if ((pt = getenv(evname)) != NULL)
|
|
{
|
|
len = strlen(pt);
|
|
if (len > 0)
|
|
{
|
|
filename = malloc(len + 1);
|
|
strncpy(filename, pt, len);
|
|
}
|
|
else
|
|
{
|
|
len = strlen(evname);
|
|
filename = malloc(len + 1);
|
|
strncpy(filename, evname, len);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
len = strlen(evname);
|
|
filename = malloc(len + 1);
|
|
strncpy(filename, evname, len);
|
|
}
|
|
filename[len] = '\0';
|
|
#endif
|
|
}
|
|
|
|
/* Check to see if the file is already open. If so return
|
|
File Status 41 in according to the Ansi 85 Standard. */
|
|
if (f->dbp != NULL)
|
|
RETURN_STATUS(41);
|
|
|
|
switch (mode)
|
|
{
|
|
case FMOD_INPUT:
|
|
oflags = O_RDONLY;
|
|
break;
|
|
case FMOD_IO:
|
|
if (f->organization == ORG_LINESEQUENTIAL)
|
|
{
|
|
/* Line Sequential does not Support Mode IO */
|
|
RETURN_STATUS(37);
|
|
}
|
|
oflags = O_RDWR;
|
|
if (f->optional)
|
|
oflags = O_CREAT | O_RDWR;
|
|
break;
|
|
case FMOD_OUTPUT:
|
|
/* libdb doesn't support O_WRONLY */
|
|
oflags = O_CREAT | O_TRUNC | O_RDWR;
|
|
break;
|
|
case FMOD_EXTEND:
|
|
oflags = O_RDWR | O_APPEND;
|
|
if (f->optional)
|
|
oflags = O_CREAT | O_RDWR | O_APPEND;
|
|
break;
|
|
}
|
|
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
|
oflags |= O_BINARY;
|
|
#endif
|
|
#ifdef USE_SYNC
|
|
if (f->organization != ORG_INDEXED)
|
|
oflags |= O_SYNC;
|
|
#endif
|
|
if (f->organization == ORG_INDEXED)
|
|
{
|
|
type = DB_BTREE;
|
|
}
|
|
else if (f->organization == ORG_RELATIVE)
|
|
{
|
|
/* type= DB_RECNO; */
|
|
}
|
|
if (f->organization == ORG_INDEXED)
|
|
{
|
|
struct altkey_desc *akd;
|
|
alt_key_no = 1;
|
|
|
|
for (akd = (struct altkey_desc *) (f + 1); akd->offset != -1; akd++)
|
|
{
|
|
/* printf("Alternate key: offset=%d, descriptor pic=0x%08x, dupl=%d\n",
|
|
akd->offset, (int)akd->descriptor->pic, akd->duplicates); */
|
|
if (akd->duplicates > 0)
|
|
{
|
|
alt_key.flags = R_DUP;
|
|
}
|
|
else
|
|
{
|
|
alt_key.flags = 0;
|
|
}
|
|
sprintf(alt_filename, "%s%d", filename, alt_key_no);
|
|
akd->alt_dbp = db_open(alt_filename, oflags, sflags, type, &alt_key);
|
|
if (!akd->alt_dbp)
|
|
{
|
|
if (errno == EINVAL)
|
|
{
|
|
f->dbp = NULL;
|
|
RETURN_STATUS(37);
|
|
}
|
|
if (errno == ENOENT)
|
|
{
|
|
f->dbp = NULL;
|
|
RETURN_STATUS(35);
|
|
}
|
|
f->dbp = NULL;
|
|
RETURN_STATUS(91);
|
|
}
|
|
alt_key_no++;
|
|
}
|
|
f->dbp = db_open(filename, oflags, sflags, type, infop);
|
|
}
|
|
/* otherwise it is sequential or relative, save its file handle, converted */
|
|
else if ((f->organization == ORG_LINESEQUENTIAL) && (mode == FMOD_INPUT))
|
|
f->dbp = (void *) fopen(filename, "rb");
|
|
else
|
|
{
|
|
f->dbp = (void *) open(filename, oflags, sflags);
|
|
if ((int) f->dbp == -1)
|
|
f->dbp = 0;
|
|
}
|
|
|
|
// free(filename);
|
|
if (evname != NULL)
|
|
free(evname);
|
|
|
|
if (!f->dbp)
|
|
{
|
|
/* Check to see if optional */
|
|
if (f->optional && mode == FMOD_INPUT)
|
|
{
|
|
f->dbp = NULL;
|
|
f->file_missing = 1;
|
|
}
|
|
else
|
|
{
|
|
if (errno == EINVAL)
|
|
{
|
|
f->dbp = NULL;
|
|
free(filename);
|
|
RETURN_STATUS(37);
|
|
}
|
|
if (errno == ENOENT)
|
|
{
|
|
f->dbp = NULL;
|
|
free(filename);
|
|
RETURN_STATUS(35);
|
|
}
|
|
f->dbp = NULL;
|
|
free(filename);
|
|
RETURN_STATUS(91);
|
|
}
|
|
}
|
|
/* save mode to check later (read,write,start,...) */
|
|
f->open_mode = mode;
|
|
/* save the file in the list of open files for the proces */
|
|
tcob_add_file_list(f);
|
|
#ifdef USE_LOCKSERVER
|
|
insert_filename((int)(f->dbp),filename);
|
|
#endif
|
|
free(filename);
|
|
RETURN_STATUS(0);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| tcob_close_real |
|
|
| |
|
|
\*------------------------------------------------------------------------*/
|
|
|
|
int tcob_close_real(struct file_desc *f, char *record)
|
|
{
|
|
|
|
f->read_done = 0;
|
|
/* Check to see if file is open. If not return File Status 42
|
|
In accordance with the Cobol 85 Standard. */
|
|
|
|
if (f->dbp == NULL)
|
|
{
|
|
if (f->optional && f->file_missing)
|
|
return 0;
|
|
return 42;
|
|
}
|
|
|
|
/* If there is a start not yet used delete it */
|
|
|
|
if (f->start_record != NULL)
|
|
{
|
|
free(f->start_record);
|
|
f->start_record = NULL;
|
|
}
|
|
|
|
if (f->organization == ORG_INDEXED)
|
|
{
|
|
struct altkey_desc *akd;
|
|
for (akd = (struct altkey_desc *) (f + 1); akd->offset != -1; akd++)
|
|
{
|
|
akd->alt_dbp->close(akd->alt_dbp);
|
|
}
|
|
f->dbp->close(f->dbp);
|
|
}
|
|
else
|
|
{
|
|
/* if ((f->organization == ORG_LINESEQUENTIAL || f->organization == ORG_SEQUENTIAL) && f->open_mode == FMOD_OUTPUT */
|
|
if ((f->organization == ORG_LINESEQUENTIAL) && (f->open_mode
|
|
== FMOD_OUTPUT) && (f->with_advancing) && (f->adv_before == 0))
|
|
{
|
|
/* write( (int)f->dbp, "\x0a", 1 ); */
|
|
}
|
|
if ((f->organization == ORG_LINESEQUENTIAL) && (f->open_mode
|
|
== FMOD_INPUT))
|
|
fclose((FILE *) f->dbp);
|
|
else
|
|
close((int) f->dbp);
|
|
}
|
|
#ifdef USE_LOCKSERVER
|
|
lockserver_release_specific_lock(fd_filename((int)f->dbp), "*");
|
|
remove_filename((int)f->dbp);
|
|
#endif
|
|
f->dbp = NULL;
|
|
return 0;
|
|
}
|
|
|
|
int tcob_close(struct file_desc *f, char *record)
|
|
{
|
|
int retval;
|
|
retval = tcob_close_real(f, record);
|
|
if (retval == 0)
|
|
tcob_remove_file_list(f); /* remove from the list of open files */
|
|
RETURN_STATUS(retval);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| tcob_file_init |
|
|
| |
|
|
\*------------------------------------------------------------------------*/
|
|
|
|
int tcob_file_init(struct file_desc *f, char *record)
|
|
{
|
|
#ifdef DEBUG_RTS
|
|
fprintf(stderr, "file_init: \n");
|
|
#endif
|
|
tcob_close(f, record);
|
|
f->open_mode = 0;
|
|
f->file_missing = 0;
|
|
return 0;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| tcob_setlock |
|
|
| |
|
|
\*------------------------------------------------------------------------*/
|
|
void tcob_setlock()
|
|
{
|
|
/* this just make aware we need to lock current record,
|
|
used before a read */
|
|
need_lock = 1;
|
|
}
|
|
|
|
static int lock_record(int fd, int len)
|
|
{
|
|
#if defined(__MINGW32__)
|
|
return -1;
|
|
#else
|
|
struct flock lk;
|
|
lk.l_type = F_WRLCK;
|
|
lk.l_whence = SEEK_CUR;
|
|
lk.l_start = 0;
|
|
lk.l_len = len;
|
|
lk.l_pid = getpid();
|
|
need_lock = 0;
|
|
return (fcntl(fd,F_SETLK,&lk));
|
|
#endif
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| tcob_unlock |
|
|
| |
|
|
| BEWARE THIS ONLY WORKS WITH RELATIVE FILES FOR NOW!!! |
|
|
| ANY OTHER USE MAY CRASH YOUR RUNTIME ENVIRONMENT. |
|
|
\*------------------------------------------------------------------------*/
|
|
void tcob_unlock(struct file_desc *f)
|
|
{
|
|
#ifdef USE_LOCKSERVER
|
|
if(f->organization == ORG_INDEXED)
|
|
{
|
|
lockserver_release_specific_lock(fd_filename((int)f->dbp), "*");
|
|
return;
|
|
}
|
|
#endif
|
|
#if defined(__MINGW32__)
|
|
return;
|
|
#else
|
|
{
|
|
struct flock lk;
|
|
lk.l_type = F_UNLCK;
|
|
lk.l_whence = SEEK_CUR;
|
|
lk.l_start = 0;
|
|
lk.l_len = f->reclen;
|
|
lk.l_pid = getpid();
|
|
fcntl((int)f->dbp,F_SETLK,&lk);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| tcob_ignorelock |
|
|
| |
|
|
\*------------------------------------------------------------------------*/
|
|
void tcob_ignorelock()
|
|
{
|
|
/* this just make aware we need to ignore locks on the current record,
|
|
used before a read */
|
|
ignore_lock = 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| tcob_read |
|
|
| |
|
|
\*------------------------------------------------------------------------*/
|
|
|
|
int tcob_read(struct file_desc *f, char *record, ...)
|
|
{
|
|
int result;
|
|
recno_t recno;
|
|
struct fld_desc *fkey;
|
|
char *keybuf;
|
|
struct fld_desc *reclen_desc;
|
|
char *reclen_buf = NULL;
|
|
DBT key, data;
|
|
DBT save_key;
|
|
va_list args;
|
|
int offset;
|
|
char rsize[bcounter];
|
|
int rlen;
|
|
char *temp_record;
|
|
struct fld_desc *t;
|
|
char t_value[bcounter];
|
|
char *b;
|
|
int lockstat;
|
|
|
|
f->read_done = 0;
|
|
/* Check to see if file is open. If not return File Status 47
|
|
In accordance with the Cobol 85 Standard. */
|
|
|
|
if (f->dbp == NULL)
|
|
{
|
|
if (f->optional && f->file_missing)
|
|
RETURN_STATUS(10);
|
|
RETURN_STATUS(47);
|
|
}
|
|
|
|
/* Check to see that the record length is valid */
|
|
if (f->reclen == -1)
|
|
RETURN_STATUS(99);
|
|
|
|
/* Check the mode the file is opened in to make sure that read
|
|
is Allowed */
|
|
if (((f->open_mode != FMOD_INPUT) && (f->open_mode != FMOD_IO)))
|
|
RETURN_STATUS(47);
|
|
|
|
/* Check that we didn't already hit eof */
|
|
if ((f->open_mode == FMOD_INPUT || f->open_mode == FMOD_IO) && f->eof_hit)
|
|
RETURN_STATUS(46);
|
|
|
|
/* check if reclen was given */
|
|
va_start(args,record);
|
|
reclen_desc = va_arg(args, struct fld_desc *);
|
|
if (reclen_desc != NULL)
|
|
reclen_buf = va_arg(args, char *);
|
|
|
|
/* If there is a start record outstanding use it to fulfill the read */
|
|
if (f->start_record != NULL)
|
|
{
|
|
memmove(record, f->start_record, f->reclen);
|
|
free(f->start_record);
|
|
f->start_record = NULL;
|
|
f->read_done = 1;
|
|
#ifdef USE_LOCKSERVER
|
|
if (ignore_lock)
|
|
{
|
|
ignore_lock=0;
|
|
}
|
|
else
|
|
if (f->organization == ORG_INDEXED)
|
|
{
|
|
key.data = record+f->rec_index;
|
|
key.size = f->ixd_desc->len;
|
|
/* Check to see if the record is Locked*/
|
|
temp_key = (char *) malloc (sizeof(char) * (key.size+1));
|
|
strncpy(temp_key,key.data,key.size);
|
|
temp_key[key.size]='\0';
|
|
result=get_lockserver_lock_owner(fd_filename((int)f->dbp),temp_key);
|
|
free(temp_key);
|
|
if(result>0)
|
|
{
|
|
/* This record is locked by another user. Return the Bad news*/
|
|
need_lock = 0;
|
|
RETURN_STATUS(51);
|
|
}
|
|
/* Lock the Record */
|
|
if(need_lock)
|
|
{
|
|
temp_key = (char *) malloc (sizeof(char) * (key.size+1));
|
|
strncpy(temp_key,key.data,key.size);
|
|
temp_key[key.size]='\0';
|
|
result=set_lockserver_lock(fd_filename((int)f->dbp),temp_key);
|
|
free(temp_key);
|
|
need_lock=0;
|
|
if(result>0)
|
|
{
|
|
/* This record is locked by another user. Return the Bad news*/
|
|
need_lock = 0;
|
|
RETURN_STATUS(51);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
RETURN_STATUS(0);
|
|
}
|
|
|
|
switch (f->organization)
|
|
{
|
|
case ORG_RELATIVE:
|
|
recno = va_arg(args,recno_t);
|
|
va_end(args);
|
|
if (recno < 1)
|
|
RETURN_STATUS(23);
|
|
// lseek( (int)f->dbp, recno * f->reclen, SEEK_SET);
|
|
lseek((int) f->dbp, ((recno) * ((f->reclen))), SEEK_SET);
|
|
if (need_lock)
|
|
{
|
|
if ((lockstat = lock_record((int) f->dbp, f->reclen)) != 0)
|
|
{
|
|
RETURN_STATUS(51); /* not able to lock */
|
|
}
|
|
}
|
|
result = read((int) f->dbp, record, f->reclen);
|
|
if (record[0] == '\0')
|
|
RETURN_STATUS(23);
|
|
if (result == f->reclen)
|
|
f->read_done = 1;
|
|
RETURN_STATUS(0);
|
|
if (reclen_desc != NULL)
|
|
{
|
|
sprintf(reclen_buf, "%0*i", (int) reclen_desc->len, result);
|
|
f->read_done = 1;
|
|
RETURN_STATUS(0);
|
|
}
|
|
RETURN_STATUS(30);
|
|
case ORG_INDEXED:
|
|
fkey = va_arg(args, struct fld_desc *);
|
|
keybuf = va_arg(args, char *);
|
|
va_end(args);
|
|
key.data = record + f->rec_index;
|
|
save_key.data = record + f->rec_index;
|
|
key.size = f->ixd_desc->len;
|
|
save_key.size = f->ixd_desc->len;
|
|
if (fkey == NULL)
|
|
{
|
|
f->key_in_use = NULL;
|
|
}
|
|
else
|
|
{
|
|
struct altkey_desc *akd;
|
|
for (akd = (struct altkey_desc *) (f + 1); akd->offset != -1; akd++)
|
|
{
|
|
if (akd->descriptor->pic == fkey->pic)
|
|
{
|
|
f->key_in_use = akd;
|
|
key.data = keybuf;
|
|
key.size = akd->descriptor->len;
|
|
result = akd->alt_dbp->seq(akd->alt_dbp, &key, &data,
|
|
R_CURSOR);
|
|
if (result)
|
|
RETURN_STATUS(23);
|
|
if (memcmp(key.data, keybuf, key.size) != 0)
|
|
RETURN_STATUS(23);
|
|
key.data = data.data;
|
|
save_key.data = data.data;
|
|
key.size = f->ixd_desc->len;
|
|
save_key.size = f->ixd_desc->len;
|
|
}
|
|
}
|
|
}
|
|
#ifdef USE_LOCKSERVER
|
|
if (ignore_lock)
|
|
{
|
|
ignore_lock=0;
|
|
}
|
|
else
|
|
{
|
|
/* Check to see if the record is Locked*/
|
|
temp_key = (char *) malloc (sizeof(char) * (key.size+1));
|
|
strncpy(temp_key,key.data,key.size);
|
|
temp_key[key.size]='\0';
|
|
result=get_lockserver_lock_owner(fd_filename((int)f->dbp),temp_key);
|
|
free(temp_key);
|
|
if(result>0)
|
|
{
|
|
/* This record is locked by another user. Return the Bad news*/
|
|
need_lock = 0;
|
|
RETURN_STATUS(51);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
result = f->dbp->seq(f->dbp, &key, &data, R_CURSOR);
|
|
if (result)
|
|
RETURN_STATUS(23); /* should have a better error info here */
|
|
if ((int) data.size < f->reclen)
|
|
{
|
|
if (reclen_desc != NULL)
|
|
{
|
|
sprintf(reclen_buf, "%0*i", (int) reclen_desc->len, result);
|
|
f->read_done = 1;
|
|
#ifdef USE_LOCKSERVER
|
|
/* Lock the Record */
|
|
if(need_lock)
|
|
{
|
|
temp_key = (char *) malloc (sizeof(char) * (key.size+1));
|
|
strncpy(temp_key,key.data,key.size);
|
|
temp_key[key.size]='\0';
|
|
result=set_lockserver_lock(fd_filename((int)f->dbp),temp_key);
|
|
free(temp_key);
|
|
need_lock=0;
|
|
if(result>0)
|
|
{
|
|
/* This record is locked by another user. Return the Bad news*/
|
|
need_lock = 0;
|
|
RETURN_STATUS(51);
|
|
}
|
|
|
|
}
|
|
#endif
|
|
RETURN_STATUS(0);
|
|
}
|
|
else
|
|
RETURN_STATUS(23);
|
|
}
|
|
if (memcmp(save_key.data, (char *) data.data + f->rec_index,
|
|
save_key.size) != 0)
|
|
RETURN_STATUS(23);
|
|
memmove(record, data.data, f->reclen);
|
|
f->read_done = 1;
|
|
#ifdef USE_LOCKSERVER
|
|
/* Lock the Record */
|
|
if(need_lock)
|
|
{
|
|
temp_key = (char *) malloc (sizeof(char) * (key.size+1));
|
|
strncpy(temp_key,key.data,key.size);
|
|
temp_key[key.size]='\0';
|
|
result=set_lockserver_lock(fd_filename((int)f->dbp),temp_key);
|
|
free(temp_key);
|
|
need_lock=0;
|
|
if(result>0)
|
|
{
|
|
/* This record is locked by another user. Return the Bad news*/
|
|
need_lock = 0;
|
|
RETURN_STATUS(51);
|
|
}
|
|
|
|
}
|
|
#endif
|
|
RETURN_STATUS(0);
|
|
case ORG_LINESEQUENTIAL:
|
|
{
|
|
char *tmpbuf, *s;
|
|
/* If the file is LINE SEQUENTIAL we need to read in the <NL> as well */
|
|
tmpbuf = malloc(f->reclen + 2);
|
|
memset(tmpbuf, 0, (f->reclen) + 2);
|
|
memset(record, ' ', f->reclen);
|
|
fgets(tmpbuf, f->reclen + 2, (void *) f->dbp);
|
|
printf("Record readed: [%s], size: [%d]\n",tmpbuf,strlen(tmpbuf));
|
|
if ((int) tmpbuf[0] == (int) NULL)
|
|
{
|
|
free(tmpbuf);
|
|
RETURN_STATUS(10);
|
|
}
|
|
s = strchr(tmpbuf, '\n');
|
|
if (s == NULL)
|
|
{
|
|
/* tmpbuf[f->reclen] = ' '; */
|
|
memmove(record, tmpbuf, f->reclen);
|
|
}
|
|
else
|
|
{
|
|
offset = s - tmpbuf;
|
|
memset(s, ' ', (f->reclen) - offset);
|
|
memmove(record, tmpbuf, f->reclen);
|
|
}
|
|
free(tmpbuf);
|
|
RETURN_STATUS(0);
|
|
}
|
|
default: /* sequential files */
|
|
if (reclen_desc != NULL)
|
|
{
|
|
/* Variable Length Sequential File */
|
|
result = read((int) f->dbp, rsize, bcounter);
|
|
if (result == 0)
|
|
RETURN_STATUS(10);
|
|
rlen = atoi(rsize);
|
|
f->reclen = rlen - bcounter;
|
|
temp_record = malloc(f->reclen + bcounter);
|
|
memset(temp_record, ' ', rlen);
|
|
result = read((int) f->dbp, temp_record, rlen);
|
|
if (result == 0)
|
|
RETURN_STATUS(10);
|
|
memmove(record, temp_record, result - bcounter);
|
|
sprintf(t_value, "%0*i", bcounter, result - bcounter);
|
|
t = (struct fld_desc *) malloc(sizeof(struct fld_desc*));
|
|
b = (char *) malloc(sizeof(char *));
|
|
t->len = bcounter;
|
|
t->type = DTYPE_DISPLAY;
|
|
t->decimals = 0;
|
|
t->all = reclen_desc->all;
|
|
t->just_r = reclen_desc->just_r;
|
|
t->reserved = reclen_desc->reserved;
|
|
t->pic = b;
|
|
tcob_move(t, t_value, reclen_desc, reclen_buf);
|
|
RETURN_STATUS(0);
|
|
}
|
|
else
|
|
{
|
|
result = read((int) f->dbp, record, f->reclen);
|
|
}
|
|
if (result == f->reclen)
|
|
RETURN_STATUS(0);
|
|
if (result == 0)
|
|
RETURN_STATUS(10);
|
|
RETURN_STATUS(30);
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| tcob_read_next |
|
|
| |
|
|
\*------------------------------------------------------------------------*/
|
|
|
|
int tcob_read_next(struct file_desc *f, char *record, ...)
|
|
{
|
|
int result;
|
|
int flags = R_NEXT;
|
|
#ifdef USE_LOCKSERVER
|
|
int reverse_flag=R_PREV;
|
|
#endif
|
|
DBT key, data;
|
|
va_list args;
|
|
struct fld_desc *reclen_desc;
|
|
char *reclen_buf;
|
|
|
|
/* Check to see if file is open. If not return File Status 47
|
|
In accordance with the Cobol 85 Standard. */
|
|
|
|
if (f->dbp == NULL)
|
|
{
|
|
if (f->optional && f->file_missing)
|
|
RETURN_STATUS(10);
|
|
RETURN_STATUS(47);
|
|
}
|
|
|
|
/* Check the mode the file is opened in to make sure that read
|
|
is Allowed */
|
|
if (((f->open_mode != FMOD_INPUT) && (f->open_mode != FMOD_IO)))
|
|
RETURN_STATUS(47);
|
|
|
|
/* Check that we didn't already hit eof */
|
|
if ((f->open_mode == FMOD_INPUT || f->open_mode == FMOD_IO) && f->eof_hit)
|
|
RETURN_STATUS(46);
|
|
|
|
/* check if reclen was given */
|
|
va_start(args,record);
|
|
reclen_desc = va_arg(args, struct fld_desc *);
|
|
if (reclen_desc != NULL)
|
|
reclen_buf = va_arg(args, char *);
|
|
|
|
/* If there is a start record outstanding use it to fulfill the read */
|
|
if (f->start_record != NULL)
|
|
{
|
|
memmove(record, f->start_record, f->reclen);
|
|
free(f->start_record);
|
|
f->start_record = NULL;
|
|
#ifdef USE_LOCKSERVER
|
|
if (ignore_lock)
|
|
{
|
|
ignore_lock=0;
|
|
}
|
|
else
|
|
if (f->organization == ORG_INDEXED)
|
|
{
|
|
key.data = record+f->rec_index;
|
|
key.size = f->ixd_desc->len;
|
|
/* Check to see if the record is Locked*/
|
|
temp_key = (char *) malloc (sizeof(char) * (key.size+1));
|
|
strncpy(temp_key,key.data,key.size);
|
|
temp_key[key.size]='\0';
|
|
result=get_lockserver_lock_owner(fd_filename((int)f->dbp),temp_key);
|
|
free(temp_key);
|
|
if(result>0)
|
|
{
|
|
/* This record is locked by another user. Return the Bad news*/
|
|
need_lock = 0;
|
|
RETURN_STATUS(51);
|
|
}
|
|
/* Lock the Record */
|
|
if(need_lock)
|
|
{
|
|
temp_key = (char *) malloc (sizeof(char) * (key.size+1));
|
|
strncpy(temp_key,key.data,key.size);
|
|
temp_key[key.size]='\0';
|
|
result=set_lockserver_lock(fd_filename((int)f->dbp),temp_key);
|
|
free(temp_key);
|
|
need_lock=0;
|
|
if(result>0)
|
|
{
|
|
/* This record is locked by another user. Return the Bad news*/
|
|
need_lock = 0;
|
|
RETURN_STATUS(51);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
f->read_done = 1;
|
|
RETURN_STATUS(0);
|
|
}
|
|
|
|
switch (f->organization)
|
|
{
|
|
case ORG_SEQUENTIAL:
|
|
result = read((int) f->dbp, record, f->reclen);
|
|
if (result <= 0)
|
|
RETURN_STATUS(10); /* what errors should I return? */
|
|
f->read_done = 1;
|
|
RETURN_STATUS(0);
|
|
case ORG_RELATIVE:
|
|
result = 1;
|
|
record[0] = '\0';
|
|
while (record[0] == '\0' && result > 0)
|
|
{
|
|
result = read((int) f->dbp, record, f->reclen);
|
|
}
|
|
if (result <= 0)
|
|
RETURN_STATUS(10); /* what errors should I return? */
|
|
f->read_done = 1;
|
|
RETURN_STATUS(0);
|
|
case ORG_INDEXED:
|
|
if (f->key_in_use != NULL)
|
|
{
|
|
struct altkey_desc *akd;
|
|
akd = (struct altkey_desc *) (f->key_in_use);
|
|
result = akd->alt_dbp->seq(akd->alt_dbp, &key, &data, flags);
|
|
if (result)
|
|
RETURN_STATUS(10);
|
|
key.data = data.data;
|
|
key.size = f->ixd_desc->len;
|
|
|
|
#ifdef USE_LOCKSERVER
|
|
if(ignore_lock)
|
|
{
|
|
ignore_lock=0;
|
|
}
|
|
else
|
|
{
|
|
/* Check to see if the record is Locked*/
|
|
temp_key = (char *) malloc (sizeof(char) * (key.size+1));
|
|
strncpy(temp_key,key.data,key.size);
|
|
temp_key[key.size]='\0';
|
|
result=get_lockserver_lock_owner(fd_filename((int)f->dbp),temp_key);
|
|
free(temp_key);
|
|
if(result>0)
|
|
{
|
|
/* This record is locked by another user. Roll Back and Return the Bad news*/
|
|
result=akd->alt_dbp->seq(akd->alt_dbp,&key,&data,reverse_flag);
|
|
need_lock = 0;
|
|
RETURN_STATUS(51);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
flags = 0;
|
|
result = f->dbp->get(f->dbp, &key, &data, flags);
|
|
if (result)
|
|
RETURN_STATUS(10); /* should have a better error info here */
|
|
if ((int) data.size < f->reclen)
|
|
RETURN_STATUS(10);
|
|
memmove(record, data.data, f->reclen);
|
|
f->read_done = 1;
|
|
#ifdef USE_LOCKSERVER
|
|
/* Lock the Record */
|
|
if(need_lock)
|
|
{
|
|
temp_key = (char *) malloc (sizeof(char) * (key.size+1));
|
|
strncpy(temp_key,key.data,key.size);
|
|
temp_key[key.size]='\0';
|
|
result=set_lockserver_lock(fd_filename((int)f->dbp),temp_key);
|
|
free(temp_key);
|
|
need_lock=0;
|
|
if(result>0)
|
|
{
|
|
/* This record is locked by another user. Roll Back and Return the Bad news*/
|
|
result=akd->alt_dbp->seq(akd->alt_dbp,&key,&data,reverse_flag);
|
|
need_lock = 0;
|
|
RETURN_STATUS(51);
|
|
}
|
|
|
|
}
|
|
#endif
|
|
RETURN_STATUS(0);
|
|
}
|
|
result = f->dbp->seq(f->dbp, &key, &data, flags);
|
|
if (result)
|
|
{
|
|
f->eof_hit = 1;
|
|
RETURN_STATUS(10); /* should have a better error info here */
|
|
}
|
|
if ((int) data.size < f->reclen)
|
|
{
|
|
f->eof_hit = 1;
|
|
RETURN_STATUS(98); /* partial record read */
|
|
}
|
|
memmove(record, data.data, f->reclen);
|
|
f->read_done = 1;
|
|
#ifdef USE_LOCKSERVER
|
|
/* Lock the Record */
|
|
if(need_lock)
|
|
{
|
|
temp_key = (char *) malloc (sizeof(char) * (key.size+1));
|
|
strncpy(temp_key,key.data,key.size);
|
|
temp_key[key.size]='\0';
|
|
result=set_lockserver_lock(fd_filename((int)f->dbp),temp_key);
|
|
free(temp_key);
|
|
need_lock=0;
|
|
if(result>0)
|
|
{
|
|
/* This record is locked by another user. Roll Back and Return the Bad news*/
|
|
result = f->dbp->seq(f->dbp,&key,&data,reverse_flag);
|
|
need_lock = 0;
|
|
RETURN_STATUS(51);
|
|
}
|
|
|
|
}
|
|
#endif
|
|
RETURN_STATUS(0);
|
|
}
|
|
RETURN_STATUS(99);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| tcob_read_prev |
|
|
| |
|
|
\*------------------------------------------------------------------------*/
|
|
int tcob_read_prev(struct file_desc *f, char *record, ...)
|
|
{
|
|
int result;
|
|
int flags = R_PREV;
|
|
#ifdef USE_LOCKSERVER
|
|
int reverse_flag=R_NEXT;
|
|
#endif
|
|
DBT key, data;
|
|
va_list args;
|
|
struct fld_desc *reclen_desc;
|
|
char *reclen_buf;
|
|
|
|
/* Check to see if file is open. If not return File Status 47
|
|
In accordance with the Cobol 85 Standard. */
|
|
|
|
if (f->dbp == NULL)
|
|
{
|
|
if (f->optional && f->file_missing)
|
|
RETURN_STATUS(10);
|
|
RETURN_STATUS(47);
|
|
}
|
|
|
|
/* Check the mode the file is opened in to make sure that read
|
|
is Allowed */
|
|
if (((f->open_mode != FMOD_INPUT) && (f->open_mode != FMOD_IO)))
|
|
RETURN_STATUS(47);
|
|
|
|
/* check if reclen was given */
|
|
va_start(args,record);
|
|
reclen_desc = va_arg(args, struct fld_desc *);
|
|
if (reclen_desc != NULL)
|
|
reclen_buf = va_arg(args, char *);
|
|
|
|
/* If there is a start record outstanding use it to fulfill the read */
|
|
if (f->start_record != NULL)
|
|
{
|
|
memmove(record, f->start_record, f->reclen);
|
|
free(f->start_record);
|
|
f->start_record = NULL;
|
|
#ifdef USE_LOCKSERVER
|
|
if(ignore_lock)
|
|
{
|
|
ignore_lock=0;
|
|
}
|
|
else
|
|
if (f->organization == ORG_INDEXED)
|
|
{
|
|
key.data = record+f->rec_index;
|
|
key.size = f->ixd_desc->len;
|
|
/* Check to see if the record is Locked*/
|
|
temp_key = (char *) malloc (sizeof(char) * (key.size+1));
|
|
strncpy(temp_key,key.data,key.size);
|
|
temp_key[key.size]='\0';
|
|
result=get_lockserver_lock_owner(fd_filename((int)f->dbp),temp_key);
|
|
free(temp_key);
|
|
if(result>0)
|
|
{
|
|
/* This record is locked by another user. Return the Bad news*/
|
|
need_lock = 0;
|
|
RETURN_STATUS(51);
|
|
}
|
|
/* Lock the Record */
|
|
if(need_lock)
|
|
{
|
|
temp_key = (char *) malloc (sizeof(char) * (key.size+1));
|
|
strncpy(temp_key,key.data,key.size);
|
|
temp_key[key.size]='\0';
|
|
result=set_lockserver_lock(fd_filename((int)f->dbp),temp_key);
|
|
free(temp_key);
|
|
need_lock=0;
|
|
if(result>0)
|
|
{
|
|
/* This record is locked by another user. Return the Bad news*/
|
|
need_lock = 0;
|
|
RETURN_STATUS(51);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
RETURN_STATUS(0);
|
|
}
|
|
|
|
switch (f->organization)
|
|
{
|
|
case ORG_SEQUENTIAL:
|
|
/* Need some logic here to figure out if at beginning of file */
|
|
/* As a result of the previous Read Previous */
|
|
lseek((int) f->dbp, (2 * ((f->reclen) * -1)), SEEK_CUR);
|
|
result = read((int) f->dbp, record, f->reclen);
|
|
if (result <= 0)
|
|
RETURN_STATUS(10);
|
|
f->read_done = 1;
|
|
RETURN_STATUS(0);
|
|
case ORG_RELATIVE:
|
|
result = 1;
|
|
record[0] = '\0';
|
|
while (record[0] == '\0' && result > 0)
|
|
{
|
|
if (lseek((int) f->dbp, (2 * ((f->reclen) * -1)), SEEK_CUR)
|
|
== 0)
|
|
RETURN_STATUS(10);
|
|
result = read((int) f->dbp, record, f->reclen);
|
|
}
|
|
if (result <= 0)
|
|
RETURN_STATUS(10);
|
|
f->read_done = 1;
|
|
RETURN_STATUS(0);
|
|
case ORG_INDEXED:
|
|
if (f->key_in_use != NULL)
|
|
{
|
|
struct altkey_desc *akd;
|
|
akd = (struct altkey_desc *) (f->key_in_use);
|
|
result = akd->alt_dbp->seq(akd->alt_dbp, &key, &data, flags);
|
|
if (result)
|
|
RETURN_STATUS(10);
|
|
key.data = data.data;
|
|
key.size = f->ixd_desc->len;
|
|
#ifdef USE_LOCKSERVER
|
|
if(ignore_lock)
|
|
{
|
|
ignore_lock=0;
|
|
}
|
|
else
|
|
{
|
|
/* Check to see if the record is Locked*/
|
|
temp_key = (char *) malloc (sizeof(char) * (key.size+1));
|
|
strncpy(temp_key,key.data,key.size);
|
|
temp_key[key.size]='\0';
|
|
result=get_lockserver_lock_owner(fd_filename((int)f->dbp),temp_key);
|
|
free(temp_key);
|
|
if(result>0)
|
|
{
|
|
/* This record is locked by another user. Roll Back and Return the Bad news*/
|
|
result=akd->alt_dbp->seq(akd->alt_dbp,&key,&data,reverse_flag);
|
|
need_lock = 0;
|
|
RETURN_STATUS(51);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
flags = 0;
|
|
result = f->dbp->get(f->dbp, &key, &data, flags);
|
|
if (result)
|
|
RETURN_STATUS(10); /* should have a better error info here */
|
|
if ((int) data.size < f->reclen)
|
|
RETURN_STATUS(10);
|
|
memmove(record, data.data, f->reclen);
|
|
f->read_done = 1;
|
|
#ifdef USE_LOCKSERVER
|
|
/* Lock the Record */
|
|
if(need_lock)
|
|
{
|
|
temp_key = (char *) malloc (sizeof(char) * (key.size+1));
|
|
strncpy(temp_key,key.data,key.size);
|
|
temp_key[key.size]='\0';
|
|
result=set_lockserver_lock(fd_filename((int)f->dbp),temp_key);
|
|
free(temp_key);
|
|
need_lock=0;
|
|
if(result>0)
|
|
{
|
|
/* This record is locked by another user. Roll Back and Return the Bad news*/
|
|
result=akd->alt_dbp->seq(akd->alt_dbp,&key,&data,reverse_flag);
|
|
need_lock = 0;
|
|
RETURN_STATUS(51);
|
|
}
|
|
|
|
}
|
|
#endif
|
|
RETURN_STATUS(0);
|
|
}
|
|
result = f->dbp->seq(f->dbp, &key, &data, flags);
|
|
if (result)
|
|
RETURN_STATUS(10); /* should have a better error info here */
|
|
if ((int) data.size < f->reclen)
|
|
RETURN_STATUS(10);
|
|
memmove(record, data.data, f->reclen);
|
|
f->read_done = 1;
|
|
#ifdef USE_LOCKSERVER
|
|
/* Lock the Record */
|
|
if(need_lock)
|
|
{
|
|
temp_key = (char *) malloc (sizeof(char) * (key.size+1));
|
|
strncpy(temp_key,key.data,key.size);
|
|
temp_key[key.size]='\0';
|
|
result=set_lockserver_lock(fd_filename((int)f->dbp),temp_key);
|
|
free(temp_key);
|
|
need_lock=0;
|
|
if(result>0)
|
|
{
|
|
/* This record is locked by another user. Roll Back and Return the Bad news*/
|
|
result = f->dbp->seq(f->dbp,&key,&data,reverse_flag);
|
|
need_lock = 0;
|
|
RETURN_STATUS(51);
|
|
}
|
|
|
|
}
|
|
#endif
|
|
RETURN_STATUS(0);
|
|
}
|
|
RETURN_STATUS(99);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| tcob_write |
|
|
| |
|
|
\*------------------------------------------------------------------------*/
|
|
|
|
int tcob_write(struct file_desc *f, char *record, ...)
|
|
{
|
|
int result;
|
|
recno_t recno;
|
|
int flags = 0;
|
|
DBT key, data;
|
|
va_list args;
|
|
char *tmpbuf;
|
|
int offset;
|
|
struct fld_desc *reclen_desc;
|
|
char *reclen_buf;
|
|
unsigned int slen = 0;
|
|
unsigned int elen = 0;
|
|
char sclen[bcounter];
|
|
char eclen[bcounter];
|
|
|
|
/* Check to see if file is open. If not return File Status 48
|
|
In accordance with the Cobol 85 Standard. */
|
|
|
|
if (f->dbp == NULL)
|
|
return 48;
|
|
|
|
/* Check to see that the record length is valid */
|
|
if (f->reclen == -1)
|
|
return 99;
|
|
|
|
/* Check the mode the file is opened in to make sure that write
|
|
is Allowed */
|
|
if (((f->open_mode != FMOD_OUTPUT) && (f->open_mode != FMOD_IO)
|
|
&& (f->open_mode != FMOD_EXTEND)))
|
|
return 48;
|
|
|
|
/* check if reclen was given */
|
|
va_start(args,record);
|
|
reclen_desc = va_arg(args, struct fld_desc *);
|
|
if (reclen_desc != NULL)
|
|
reclen_buf = va_arg(args, char *);
|
|
|
|
data.data = record;
|
|
data.size = f->reclen;
|
|
switch (f->organization)
|
|
{
|
|
case ORG_INDEXED:
|
|
key.data = record + f->rec_index;
|
|
key.size = f->ixd_desc->len;
|
|
result = f->dbp->put(f->dbp, &key, &data, R_NOOVERWRITE);
|
|
if (result == 1)
|
|
return 22;
|
|
else if (result)
|
|
return 99;
|
|
/* If the main write was successfull then we proceed to
|
|
write out the alternate keys. Any Failure will mean that
|
|
we have to delete the writes we have just done.
|
|
*/
|
|
{
|
|
struct altkey_desc *akd;
|
|
for (akd = (struct altkey_desc *) (f + 1); akd->offset != -1; akd++)
|
|
{
|
|
key.data = record + akd->offset;
|
|
key.size = akd->descriptor->len;
|
|
data.data = record + f->rec_index;
|
|
data.size = f->ixd_desc->len;
|
|
result
|
|
= akd->alt_dbp->put(akd->alt_dbp, &key, &data,
|
|
flags);
|
|
#ifdef USE_SYNC
|
|
result=akd->alt_dbp->sync(akd->alt_dbp,0);
|
|
#endif
|
|
/* If an error occurs we need code to back out.
|
|
Will be done later.
|
|
*/
|
|
}
|
|
}
|
|
#ifdef USE_SYNC
|
|
result = f->dbp->sync(f->dbp,0);
|
|
#endif
|
|
break;
|
|
case ORG_RELATIVE:
|
|
/* va_start(args,record); */
|
|
recno = va_arg(args,recno_t);
|
|
va_end(args);
|
|
if (recno < 1)
|
|
return 23;
|
|
// lseek((int)f->dbp, recno * f->reclen, SEEK_SET);
|
|
lseek((int) f->dbp, ((recno) * ((f->reclen))), SEEK_SET);
|
|
result = write((int) f->dbp, record, f->reclen);
|
|
if (!result)
|
|
return 99; /* what errors should I return? */
|
|
break;
|
|
case ORG_LINESEQUENTIAL:
|
|
/* Create buffer with room for the string and a line feed */
|
|
tmpbuf = malloc(f->reclen + 1);
|
|
memmove(tmpbuf, record, f->reclen);
|
|
|
|
for (offset = f->reclen - 1; offset >= 0; offset--)
|
|
{
|
|
/* Do not output trailing spaces and nulls */
|
|
if ((tmpbuf[offset] != ' ') && (tmpbuf[offset] != '\0'))
|
|
{
|
|
tmpbuf[offset + 1] = '\n';
|
|
result = write((int) f->dbp, tmpbuf, offset + 2);
|
|
}
|
|
else if (offset == 0)
|
|
{ /* Blank line */
|
|
result = write((int) f->dbp, "\n", 1);
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* Handle any errors writing to file */
|
|
if (!result)
|
|
{
|
|
free(tmpbuf);
|
|
return 99; /* FIXME: what error code? */
|
|
}
|
|
break;
|
|
}
|
|
free(tmpbuf);
|
|
break;
|
|
default:
|
|
if (reclen_desc != NULL)
|
|
{
|
|
/* If this is a variable length record write out the length */
|
|
char *temp_record = (char *) malloc(f->reclen);
|
|
slen = f->reclen + bcounter;
|
|
elen = slen;
|
|
sprintf(sclen, "%0*i", bcounter, slen);
|
|
sprintf(eclen, "%0*i", bcounter, elen);
|
|
memmove(temp_record, record, f->reclen);
|
|
result = write((int) f->dbp, sclen, bcounter);
|
|
result = write((int) f->dbp, temp_record, f->reclen);
|
|
result = write((int) f->dbp, eclen, bcounter);
|
|
free(temp_record);
|
|
}
|
|
else
|
|
{
|
|
result = write((int) f->dbp, record, f->reclen);
|
|
}
|
|
if (!result)
|
|
return 99; /* what errors should I return? */
|
|
break;
|
|
}
|
|
f->read_done = 0;
|
|
return 0;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| tcob_delete |
|
|
| |
|
|
\*------------------------------------------------------------------------*/
|
|
|
|
int tcob_delete(struct file_desc *f, char *record, ...)
|
|
{
|
|
int result;
|
|
recno_t recno;
|
|
int flags = 0;
|
|
va_list args;
|
|
DBT key;
|
|
|
|
/* Check to see if file is open. If not return File Status 49
|
|
In accordance with the Cobol 85 Standard. */
|
|
|
|
if (f->dbp == NULL)
|
|
return 49;
|
|
|
|
/* Check to see that the record length is valid */
|
|
if (f->reclen == -1)
|
|
return 99;
|
|
|
|
/* Check the mode the file is opened in to make sure that delete
|
|
is Allowed */
|
|
if (f->open_mode != FMOD_IO)
|
|
return 49;
|
|
|
|
switch (f->organization)
|
|
{
|
|
case ORG_INDEXED:
|
|
key.data = record + f->rec_index;
|
|
key.size = f->ixd_desc->len;
|
|
|
|
#ifdef USE_LOCKSERVER
|
|
/* Check to see if the record is Locked*/
|
|
temp_key = (char *) malloc (sizeof(char) * (key.size+1));
|
|
strncpy(temp_key,key.data,key.size);
|
|
temp_key[key.size]='\0';
|
|
result=get_lockserver_lock_owner(fd_filename((int)f->dbp),temp_key);
|
|
free(temp_key);
|
|
if(result>0)
|
|
{
|
|
/* This record is locked by another user. Return the Bad news*/
|
|
RETURN_STATUS(51);
|
|
}
|
|
#endif
|
|
|
|
result = f->dbp->del(f->dbp, &key, flags);
|
|
if (result != 0)
|
|
return 23;
|
|
#ifdef USE_SYNC
|
|
result = f->dbp->sync(f->dbp,0);
|
|
#endif
|
|
#ifdef USE_LOCKSERVER
|
|
/* Unlock the Record if it is Locked */
|
|
temp_key = (char *) malloc (sizeof(char) * (key.size+1));
|
|
strncpy(temp_key,key.data,key.size);
|
|
temp_key[key.size]='\0';
|
|
result=lockserver_release_specific_lock(fd_filename((int)f->dbp),temp_key);
|
|
free(temp_key);
|
|
if(result>0)
|
|
{
|
|
/* This record was not locked */
|
|
}
|
|
#endif
|
|
{
|
|
struct altkey_desc *akd;
|
|
for (akd = (struct altkey_desc *) (f + 1); akd->offset != -1; akd++)
|
|
{
|
|
key.data = record + akd->offset;
|
|
key.size = akd->descriptor->len;
|
|
result = akd->alt_dbp->del(akd->alt_dbp, &key, flags);
|
|
#ifdef USE_SYNC
|
|
result=akd->alt_dbp->sync(akd->alt_dbp,0);
|
|
#endif
|
|
}
|
|
}
|
|
break;
|
|
case ORG_RELATIVE:
|
|
va_start(args,record);
|
|
recno = va_arg(args,recno_t);
|
|
va_end(args);
|
|
if (recno < 1)
|
|
return 23;
|
|
/* recno = recno - 1; */
|
|
lseek((int) f->dbp, recno * f->reclen, SEEK_SET);
|
|
memset(record, 0, f->reclen);
|
|
result = write((int) f->dbp, record, f->reclen);
|
|
if (!result)
|
|
return 99; /* what errors should I return? */
|
|
break;
|
|
default:
|
|
return 37;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| tcob_start |
|
|
| |
|
|
\*------------------------------------------------------------------------*/
|
|
|
|
int tcob_start(struct file_desc *f, char *record, int cond, ...)
|
|
{
|
|
int result;
|
|
recno_t recno;
|
|
int flags = R_CURSOR;
|
|
DBT key, data;
|
|
va_list args;
|
|
int alternate = 0;
|
|
struct fld_desc *r;
|
|
char *key_ptr;
|
|
char new_record[f->reclen];
|
|
|
|
f->eof_hit = 0; /* additon AL : reset eof if already positioned */
|
|
f->read_done = 0;
|
|
/* Check to see if file is open. If not return File Status 47
|
|
In accordance with the Cobol 85 Standard. */
|
|
|
|
if (f->dbp == NULL)
|
|
return 47;
|
|
|
|
/* Check to see that the record length is valid */
|
|
if (f->reclen == -1)
|
|
return 99;
|
|
|
|
/* Check the mode the file is opened in to make sure that start
|
|
is Allowed */
|
|
if (((f->open_mode != FMOD_INPUT) && (f->open_mode != FMOD_IO)))
|
|
return 47;
|
|
|
|
switch (f->organization)
|
|
{
|
|
case ORG_RELATIVE:
|
|
va_start(args,cond);
|
|
recno = va_arg(args,recno_t);
|
|
va_end(args);
|
|
switch (cond)
|
|
{
|
|
case 1: /* Equal to */
|
|
if (lseek((int) f->dbp, ((recno) * ((f->reclen))), SEEK_SET)
|
|
> 0)
|
|
{
|
|
result = read((int) f->dbp, new_record, f->reclen);
|
|
if (new_record[0] == '\0')
|
|
return 23;
|
|
if (result == f->reclen)
|
|
{
|
|
f->start_record = malloc(f->reclen);
|
|
memmove(f->start_record, new_record, f->reclen);
|
|
return 0;
|
|
}
|
|
}
|
|
break;
|
|
case 2: /* Less Than */
|
|
if (recno < 1)
|
|
return 23;
|
|
recno = recno - 1;
|
|
if (lseek((int) f->dbp, ((recno) * ((f->reclen))), SEEK_SET)
|
|
> 0)
|
|
{
|
|
result = read((int) f->dbp, new_record, f->reclen);
|
|
if (new_record[0] == '\0')
|
|
{
|
|
result = 1;
|
|
while (new_record[0] == '\0' && result > 0)
|
|
{
|
|
recno--;
|
|
lseek((int) f->dbp, recno * f->reclen, SEEK_SET);
|
|
result = read((int) f->dbp, new_record,
|
|
f->reclen);
|
|
}
|
|
if (result <= 0)
|
|
return 23;
|
|
}
|
|
if (result == f->reclen)
|
|
{
|
|
f->start_record = malloc(f->reclen);
|
|
memmove(f->start_record, new_record, f->reclen);
|
|
return 0;
|
|
}
|
|
}
|
|
break;
|
|
case 3: /* Less than or equal to */
|
|
if (lseek((int) f->dbp, ((recno) * ((f->reclen))), SEEK_SET)
|
|
> 0)
|
|
{
|
|
result = read((int) f->dbp, new_record, f->reclen);
|
|
if (new_record[0] == '\0')
|
|
{
|
|
result = 1;
|
|
while (new_record[0] == '\0' && result > 0)
|
|
{
|
|
recno--;
|
|
lseek((int) f->dbp, ((recno) * ((f->reclen))),
|
|
SEEK_SET);
|
|
result = read((int) f->dbp, new_record,
|
|
f->reclen);
|
|
}
|
|
if (result <= 0)
|
|
return 23;
|
|
}
|
|
if (result == f->reclen)
|
|
{
|
|
f->start_record = malloc(f->reclen);
|
|
memmove(f->start_record, new_record, f->reclen);
|
|
return 0;
|
|
}
|
|
}
|
|
break;
|
|
case 4: /* Greater Than */
|
|
recno++;
|
|
if (lseek((int) f->dbp, ((recno) * ((f->reclen))), SEEK_SET)
|
|
> 0)
|
|
{
|
|
result = read((int) f->dbp, new_record, f->reclen);
|
|
if (new_record[0] == '\0')
|
|
{
|
|
result = 1;
|
|
while (new_record[0] == '\0' && result > 0)
|
|
{
|
|
result = read((int) f->dbp, new_record,
|
|
f->reclen);
|
|
recno++;
|
|
}
|
|
if (result <= 0)
|
|
return 23;
|
|
}
|
|
if (result == f->reclen)
|
|
{
|
|
f->start_record = malloc(f->reclen);
|
|
memmove(f->start_record, new_record, f->reclen);
|
|
return 0;
|
|
}
|
|
}
|
|
break;
|
|
case 5: /* Greater than or Equal to */
|
|
if (lseek((int) f->dbp, ((recno) * ((f->reclen))), SEEK_SET)
|
|
> 0)
|
|
{
|
|
result = read((int) f->dbp, new_record, f->reclen);
|
|
if (new_record[0] == '\0')
|
|
{
|
|
result = 1;
|
|
while (new_record[0] == '\0' && result > 0)
|
|
{
|
|
result = read((int) f->dbp, new_record,
|
|
f->reclen);
|
|
recno++;
|
|
}
|
|
if (result <= 0)
|
|
return 23;
|
|
}
|
|
if (result == f->reclen)
|
|
{
|
|
f->start_record = malloc(f->reclen);
|
|
memmove(f->start_record, new_record, f->reclen);
|
|
return 0;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
return 99;
|
|
}
|
|
return 23;
|
|
case ORG_INDEXED:
|
|
{
|
|
struct altkey_desc *akd;
|
|
va_start(args,cond);
|
|
r = va_arg(args,struct fld_desc *);
|
|
key_ptr = va_arg(args,char *);
|
|
va_end(args);
|
|
f->key_in_use = NULL;
|
|
for (akd = (struct altkey_desc *) (f + 1); akd->offset != -1; akd++)
|
|
{
|
|
if (akd->descriptor == r)
|
|
{
|
|
f->key_in_use = akd;
|
|
alternate = 1;
|
|
break;
|
|
}
|
|
}
|
|
switch (cond)
|
|
{
|
|
case 1: /* Equal To */
|
|
if (alternate == 1)
|
|
{
|
|
key.data = key_ptr;
|
|
key.size = akd->descriptor->len;
|
|
result = akd->alt_dbp->seq(akd->alt_dbp, &key, &data,
|
|
flags);
|
|
if (result)
|
|
return 23;
|
|
if (memcmp(key.data, key_ptr, key.size) != 0)
|
|
return 23;
|
|
key.data = data.data;
|
|
key.size = f->ixd_desc->len;
|
|
flags = 0;
|
|
result = f->dbp->get(f->dbp, &key, &data, flags);
|
|
if (result)
|
|
return 23;
|
|
if ((int) data.size < f->reclen)
|
|
return 23;
|
|
memmove(new_record, data.data, f->reclen);
|
|
}
|
|
else
|
|
{
|
|
key.data = record + f->rec_index;
|
|
key.size = f->ixd_desc->len;
|
|
result = f->dbp->seq(f->dbp, &key, &data, flags);
|
|
if (result)
|
|
return 23; /* should have a better error info here */
|
|
if ((int) data.size < f->reclen)
|
|
return 23;
|
|
memmove(new_record, data.data, f->reclen);
|
|
if (memcmp(record + f->rec_index, new_record
|
|
+ f->rec_index, key.size) != 0)
|
|
return 23;
|
|
}
|
|
f->start_record = malloc(f->reclen);
|
|
memmove(f->start_record, new_record, f->reclen);
|
|
break;
|
|
case 2: /* Less than */
|
|
if (alternate == 1)
|
|
{
|
|
key.data = key_ptr;
|
|
key.size = akd->descriptor->len;
|
|
result = akd->alt_dbp->seq(akd->alt_dbp, &key, &data,
|
|
flags);
|
|
if (result)
|
|
{
|
|
flags = R_LAST;
|
|
result = akd->alt_dbp->seq(akd->alt_dbp, &key,
|
|
&data, flags);
|
|
if (result)
|
|
return 23;
|
|
}
|
|
/* If result greater than or equal Key read prev record */
|
|
if (memcmp(key.data, key_ptr, key.size) > -1)
|
|
{
|
|
flags = R_PREV;
|
|
result = akd->alt_dbp->seq(akd->alt_dbp, &key,
|
|
&data, flags);
|
|
if (result)
|
|
return 23;
|
|
}
|
|
key.data = data.data;
|
|
key.size = f->ixd_desc->len;
|
|
flags = 0;
|
|
result = f->dbp->get(f->dbp, &key, &data, flags);
|
|
if (result)
|
|
return 23;
|
|
if ((int) data.size < f->reclen)
|
|
return 23;
|
|
memmove(new_record, data.data, f->reclen);
|
|
}
|
|
else
|
|
{
|
|
key.data = record + f->rec_index;
|
|
key.size = f->ixd_desc->len;
|
|
result = f->dbp->seq(f->dbp, &key, &data, flags);
|
|
if (result)
|
|
{
|
|
flags = R_LAST;
|
|
result = f->dbp->seq(f->dbp, &key, &data, flags);
|
|
if (result)
|
|
return 23; /* should have a better error info here */
|
|
}
|
|
if ((int) data.size < f->reclen)
|
|
return 23;
|
|
memmove(new_record, data.data, f->reclen);
|
|
/* If result greater than or equal key read prev record */
|
|
if (memcmp(record + f->rec_index, new_record
|
|
+ f->rec_index, key.size) > -1)
|
|
{
|
|
flags = R_PREV;
|
|
result = f->dbp->seq(f->dbp, &key, &data, flags);
|
|
if (result)
|
|
return 23; /* should have a better error info here */
|
|
if ((int) data.size < f->reclen)
|
|
return 23;
|
|
memmove(new_record, data.data, f->reclen);
|
|
}
|
|
}
|
|
f->start_record = malloc(f->reclen);
|
|
memmove(f->start_record, new_record, f->reclen);
|
|
break;
|
|
case 3: /* Less than or Equal To */
|
|
if (alternate == 1)
|
|
{
|
|
key.data = key_ptr;
|
|
key.size = akd->descriptor->len;
|
|
result = akd->alt_dbp->seq(akd->alt_dbp, &key, &data,
|
|
flags);
|
|
if (result)
|
|
{
|
|
flags = R_LAST;
|
|
result = akd->alt_dbp->seq(akd->alt_dbp, &key,
|
|
&data, flags);
|
|
if (result)
|
|
return 23;
|
|
}
|
|
/* If result greater than Key read prev record */
|
|
if (memcmp(key.data, key_ptr, key.size) > 0)
|
|
{
|
|
flags = R_PREV;
|
|
result = akd->alt_dbp->seq(akd->alt_dbp, &key,
|
|
&data, flags);
|
|
if (result)
|
|
return 23;
|
|
}
|
|
key.data = data.data;
|
|
key.size = f->ixd_desc->len;
|
|
flags = 0;
|
|
result = f->dbp->get(f->dbp, &key, &data, flags);
|
|
if (result)
|
|
return 23;
|
|
if ((int) data.size < f->reclen)
|
|
return 23;
|
|
memmove(new_record, data.data, f->reclen);
|
|
}
|
|
else
|
|
{
|
|
key.data = record + f->rec_index;
|
|
key.size = f->ixd_desc->len;
|
|
result = f->dbp->seq(f->dbp, &key, &data, flags);
|
|
if (result)
|
|
{
|
|
flags = R_LAST;
|
|
result = f->dbp->seq(f->dbp, &key, &data, flags);
|
|
if (result)
|
|
return 23; /* should have a better error info here */
|
|
}
|
|
if ((int) data.size < f->reclen)
|
|
return 23;
|
|
memmove(new_record, data.data, f->reclen);
|
|
/* If result greater than key read prev record */
|
|
if (memcmp(record + f->rec_index, new_record
|
|
+ f->rec_index, key.size) > 0)
|
|
{
|
|
flags = R_PREV;
|
|
result = f->dbp->seq(f->dbp, &key, &data, flags);
|
|
if (result)
|
|
return 23; /* should have a better error info here */
|
|
if ((int) data.size < f->reclen)
|
|
return 23;
|
|
memmove(new_record, data.data, f->reclen);
|
|
}
|
|
}
|
|
f->start_record = malloc(f->reclen);
|
|
memmove(f->start_record, new_record, f->reclen);
|
|
break;
|
|
case 4: /* Greater than */
|
|
if (alternate == 1)
|
|
{
|
|
key.data = key_ptr;
|
|
key.size = akd->descriptor->len;
|
|
result = akd->alt_dbp->seq(akd->alt_dbp, &key, &data,
|
|
flags);
|
|
if (result)
|
|
return 23;
|
|
/* If result equals Key read next record */
|
|
/* if(memcmp(data.data,key_ptr,akd->descriptor->len)==0) */
|
|
if (memcmp(key.data, key_ptr, key.size) == 0)
|
|
{
|
|
flags = R_NEXT;
|
|
result = akd->alt_dbp->seq(akd->alt_dbp, &key,
|
|
&data, flags);
|
|
if (result)
|
|
return 23;
|
|
}
|
|
key.data = data.data;
|
|
key.size = f->ixd_desc->len;
|
|
flags = 0;
|
|
result = f->dbp->get(f->dbp, &key, &data, flags);
|
|
if (result)
|
|
return 23;
|
|
if ((int) data.size < f->reclen)
|
|
return 23;
|
|
memmove(new_record, data.data, f->reclen);
|
|
}
|
|
else
|
|
{
|
|
key.data = record + f->rec_index;
|
|
key.size = f->ixd_desc->len;
|
|
result = f->dbp->seq(f->dbp, &key, &data, flags);
|
|
if (result)
|
|
return 23; /* should have a better error info here */
|
|
if ((int) data.size < f->reclen)
|
|
return 23;
|
|
memmove(new_record, data.data, f->reclen);
|
|
/* If result equals key read next record */
|
|
if (memcmp(record + f->rec_index, new_record
|
|
+ f->rec_index, key.size) == 0)
|
|
{
|
|
flags = R_NEXT;
|
|
result = f->dbp->seq(f->dbp, &key, &data, flags);
|
|
if (result)
|
|
return 23; /* should have a better error info here */
|
|
if ((int) data.size < f->reclen)
|
|
return 23;
|
|
memmove(new_record, data.data, f->reclen);
|
|
}
|
|
}
|
|
f->start_record = malloc(f->reclen);
|
|
memmove(f->start_record, new_record, f->reclen);
|
|
break;
|
|
case 5: /* Greater than or Equal To */
|
|
if (alternate == 1)
|
|
{
|
|
key.data = key_ptr;
|
|
key.size = akd->descriptor->len;
|
|
result = akd->alt_dbp->seq(akd->alt_dbp, &key, &data,
|
|
flags);
|
|
if (result)
|
|
return 23;
|
|
key.data = data.data;
|
|
key.size = f->ixd_desc->len;
|
|
flags = 0;
|
|
result = f->dbp->get(f->dbp, &key, &data, flags);
|
|
if (result)
|
|
return 23;
|
|
if ((int) data.size < f->reclen)
|
|
return 23;
|
|
memmove(new_record, data.data, f->reclen);
|
|
}
|
|
else
|
|
{
|
|
key.data = record + f->rec_index;
|
|
key.size = f->ixd_desc->len;
|
|
result = f->dbp->seq(f->dbp, &key, &data, flags);
|
|
if (result)
|
|
return 23; /* should have a better error info here */
|
|
if ((int) data.size < f->reclen)
|
|
return 23;
|
|
memmove(new_record, data.data, f->reclen);
|
|
}
|
|
f->start_record = malloc(f->reclen);
|
|
memmove(f->start_record, new_record, f->reclen);
|
|
break;
|
|
case 6:
|
|
default:
|
|
return 99;
|
|
}
|
|
return 0;
|
|
}
|
|
default: /* sequential files */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| tcob_save_status |
|
|
| |
|
|
\*------------------------------------------------------------------------*/
|
|
|
|
int tcob_save_status(char *status, int rt)
|
|
{
|
|
status[0] = rt / 10 + '0';
|
|
status[1] = rt % 10 + '0';
|
|
return rt;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| tcob_rewrite |
|
|
| |
|
|
\*------------------------------------------------------------------------*/
|
|
|
|
int tcob_rewrite(struct file_desc *f, char *record, ...)
|
|
{
|
|
int result;
|
|
int read_done = f->read_done;
|
|
recno_t recno;
|
|
DBT key, data;
|
|
va_list args;
|
|
char *NL = "\n";
|
|
struct fld_desc *reclen_desc;
|
|
char *reclen_buf;
|
|
|
|
f->read_done = 0;
|
|
|
|
/* Check to see if file is open. If not return File Status 49
|
|
In accordance with the Cobol 85 Standard. */
|
|
|
|
if (f->dbp == NULL)
|
|
return 49;
|
|
|
|
/* Check to see that the record length is valid */
|
|
if (f->reclen == -1)
|
|
return 99;
|
|
|
|
/* Check the mode the file is opened in to make sure that rewrite
|
|
is Allowed */
|
|
if (f->open_mode != FMOD_IO)
|
|
return 49;
|
|
|
|
/* Check that we didn't already hit eof */
|
|
if ((f->open_mode == FMOD_IO) && f->eof_hit)
|
|
RETURN_STATUS(43);
|
|
|
|
/* Check that we had a previous successful read */
|
|
if (!read_done)
|
|
RETURN_STATUS(43);
|
|
|
|
/* check if reclen was given */
|
|
va_start(args,record);
|
|
reclen_desc = va_arg(args, struct fld_desc *);
|
|
if (reclen_desc != NULL)
|
|
reclen_buf = va_arg(args, char *);
|
|
|
|
switch (f->organization)
|
|
{
|
|
case ORG_INDEXED:
|
|
break;
|
|
case ORG_RELATIVE:
|
|
break;
|
|
case ORG_SEQUENTIAL:
|
|
lseek((int) f->dbp, ((f->reclen) * -1), SEEK_CUR);
|
|
break;
|
|
case ORG_LINESEQUENTIAL:
|
|
/* Rewrite No longer supported on Line Sequential files */
|
|
return 30;
|
|
/* lseek((int)f->dbp, ((f->reclen+1)* -1), SEEK_CUR); */
|
|
/* break; */
|
|
default:
|
|
return 30;
|
|
}
|
|
|
|
data.data = record;
|
|
data.size = f->reclen;
|
|
/* Save the new record */
|
|
switch (f->organization)
|
|
{
|
|
case ORG_INDEXED:
|
|
{
|
|
struct altkey_desc *akd;
|
|
char *newdata_data;
|
|
unsigned int newdata_size;
|
|
|
|
newdata_data = (char *) malloc(f->reclen);
|
|
memmove(newdata_data, record, f->reclen);
|
|
newdata_size = f->reclen;
|
|
key.data = record + f->rec_index;
|
|
key.size = f->ixd_desc->len;
|
|
|
|
#ifdef USE_LOCKSERVER
|
|
/* Check to see if the record is Locked*/
|
|
temp_key = (char *) malloc (sizeof(char) * (key.size+1));
|
|
strncpy(temp_key,key.data,key.size);
|
|
temp_key[key.size]='\0';
|
|
result=get_lockserver_lock_owner(fd_filename((int)f->dbp),temp_key);
|
|
free(temp_key);
|
|
if(result>0)
|
|
{
|
|
/* This record is locked by another user. Return the Bad news*/
|
|
RETURN_STATUS(51);
|
|
}
|
|
#endif
|
|
|
|
/* Get the origional Record so we can delete the
|
|
Alternate Keys if there is any change */
|
|
result = f->dbp->get(f->dbp, &key, &data, 0);
|
|
memmove(record, data.data, f->reclen);
|
|
if (result)
|
|
{
|
|
free(newdata_data);
|
|
return 23;
|
|
}
|
|
for (akd = (struct altkey_desc *) (f + 1); akd->offset != -1; akd++)
|
|
{
|
|
key.data = record + akd->offset;
|
|
key.size = akd->descriptor->len;
|
|
result = akd->alt_dbp->del(akd->alt_dbp, &key, 0);
|
|
#ifdef USE_SYNC
|
|
result=akd->alt_dbp->sync(akd->alt_dbp,0);
|
|
#endif
|
|
}
|
|
memmove(record, newdata_data, newdata_size);
|
|
key.data = record + f->rec_index;
|
|
key.size = f->ixd_desc->len;
|
|
data.data = newdata_data;
|
|
data.size = newdata_size;
|
|
/* Rewrite the Main Record */
|
|
result = f->dbp->put(f->dbp, &key, &data, 0);
|
|
#ifdef __MINGW32__
|
|
int flags = R_PREV;
|
|
result = f->dbp->seq(f->dbp, &key, &data, flags);//NILONILO
|
|
//f->dbp->get(f->dbp, &key, &data, 0); //NILO
|
|
#endif
|
|
free(newdata_data);
|
|
if (result)
|
|
return 99; /* ? error code to be determined */
|
|
#ifdef USE_SYNC
|
|
result = f->dbp->sync(f->dbp,0);
|
|
#endif
|
|
#ifdef USE_LOCKSERVER
|
|
/* Unlock the Record if it is Locked */
|
|
temp_key = (char *) malloc (sizeof(char) * (key.size+1));
|
|
strncpy(temp_key,key.data,key.size);
|
|
temp_key[key.size]='\0';
|
|
result=lockserver_release_specific_lock(fd_filename((int)f->dbp),temp_key);
|
|
free(temp_key);
|
|
if(result>0)
|
|
{
|
|
/* This record was not locked */
|
|
}
|
|
#endif
|
|
/* Rewrite the Alternate Keys */
|
|
for (akd = (struct altkey_desc *) (f + 1); akd->offset != -1; akd++)
|
|
{
|
|
key.data = record + akd->offset;
|
|
key.size = akd->descriptor->len;
|
|
data.data = record + f->rec_index;
|
|
data.size = f->ixd_desc->len;
|
|
result = akd->alt_dbp->put(akd->alt_dbp, &key, &data, 0);
|
|
#ifdef USE_SYNC
|
|
result=akd->alt_dbp->sync(akd->alt_dbp,0);
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
case ORG_RELATIVE:
|
|
/* va_start(args,record); */
|
|
recno = va_arg(args,recno_t);
|
|
va_end(args);
|
|
lseek((int) f->dbp, ((recno) * ((f->reclen))), SEEK_SET);
|
|
result = write((int) f->dbp, record, f->reclen);
|
|
if (!result)
|
|
return 99; /* what errors should I return? */
|
|
break;
|
|
case ORG_LINESEQUENTIAL:
|
|
result = write((int) f->dbp, record, f->reclen);
|
|
if (!result)
|
|
return 99; /* what errors should I return? */
|
|
result = write((int) f->dbp, NL, 1);
|
|
if (!result)
|
|
return 99; /* what errors should I return? */
|
|
break;
|
|
default:
|
|
result = write((int) f->dbp, record, f->reclen);
|
|
if (!result)
|
|
return 99; /* what errors should I return? */
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| tcob_write_adv |
|
|
| |
|
|
\*------------------------------------------------------------------------*/
|
|
|
|
int tcob_write_adv(struct file_desc *f, char *record, int opt, ...)
|
|
{
|
|
int result;
|
|
va_list args;
|
|
struct fld_desc *cnt_desc;
|
|
char *cnt_buf;
|
|
int lines = 0;
|
|
int len;
|
|
struct fld_desc *reclen_desc;
|
|
char *reclen_buf;
|
|
|
|
/* Check to see if file is open. If not return File Status 48
|
|
In accordance with the Cobol 85 Standard. */
|
|
|
|
if (f->dbp == NULL)
|
|
return 48;
|
|
|
|
/* Check the mode the file is opened in to make sure that write
|
|
is Allowed */
|
|
if (((f->open_mode != FMOD_OUTPUT) && (f->open_mode != FMOD_IO)
|
|
&& (f->open_mode != FMOD_EXTEND)))
|
|
return 48;
|
|
|
|
/* check if reclen was given */
|
|
va_start(args,opt);
|
|
reclen_desc = va_arg(args, struct fld_desc *);
|
|
if (reclen_desc != NULL)
|
|
reclen_buf = va_arg(args, char *);
|
|
|
|
f->with_advancing = 1;
|
|
f->adv_before = 0;
|
|
/* va_start( args, opt ); */
|
|
|
|
if (opt > 0)
|
|
{
|
|
cnt_desc = va_arg( args, struct fld_desc * );
|
|
cnt_buf = va_arg( args,char * );
|
|
lines = tcob_get_index(cnt_desc, cnt_buf);
|
|
}
|
|
len = f->reclen;
|
|
if (f->organization == ORG_LINESEQUENTIAL)
|
|
{
|
|
/* removing trailling blanks */
|
|
while (len > 0 && record[len - 1] == ' ')
|
|
len--;
|
|
}
|
|
|
|
switch (opt)
|
|
{
|
|
/* 1: write before advancing N lines */
|
|
case 1:
|
|
result = write((int) f->dbp, record, len);
|
|
write((int) f->dbp, "\x0d", 1);
|
|
for (; lines > 0; lines--)
|
|
write((int) f->dbp, "\x0a", 1);
|
|
break;
|
|
|
|
/* 2: write after advancing N lines */
|
|
case 2:
|
|
for (; lines > 0; lines--)
|
|
write((int) f->dbp, "\x0a", 1);
|
|
result = write((int) f->dbp, record, len);
|
|
write((int) f->dbp, "\x0d", 1);
|
|
break;
|
|
|
|
/* -1: before advancing page */
|
|
case -1:
|
|
result = write((int) f->dbp, record, len);
|
|
write((int) f->dbp, "\x0c", 1);
|
|
break;
|
|
|
|
/* -2: after advancing page */
|
|
case -2:
|
|
write((int) f->dbp, "\x0c", 1);
|
|
result = write((int) f->dbp, record, len);
|
|
write((int) f->dbp, "\x0d", 1);
|
|
break;
|
|
|
|
/* only normal write (error?) */
|
|
default:
|
|
result = write((int) f->dbp, record, len);
|
|
if (f->organization == ORG_LINESEQUENTIAL)
|
|
{
|
|
write((int) f->dbp, "\x0a", 1);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (result == len)
|
|
RETURN_STATUS(0);
|
|
RETURN_STATUS(result);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| tcob_sort_open |
|
|
| |
|
|
\*------------------------------------------------------------------------*/
|
|
|
|
int tcob_sort_open(struct file_desc *f, char *record, char *fname)
|
|
{
|
|
DBTYPE type = DB_BTREE;
|
|
int sflags = S_IRUSR | S_IWUSR;
|
|
|
|
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
|
int oflags = O_CREAT | O_RDWR | O_BINARY;
|
|
#else
|
|
int oflags = O_CREAT | O_RDWR;
|
|
#endif
|
|
|
|
/* char *filename; */
|
|
int len;
|
|
BTREEINFO b;
|
|
|
|
b.flags = R_DUP;
|
|
b.cachesize = 0;
|
|
b.maxkeypage = 0;
|
|
b.minkeypage = 0;
|
|
b.psize = 0;
|
|
b.compare = NULL;
|
|
b.prefix = NULL;
|
|
b.lorder = 0;
|
|
|
|
/*
|
|
beware: fname points to a field storage (non null-terminated)
|
|
we must copy fname to a C string and terminate it with a \0
|
|
and also trim spaces at the end.
|
|
*/
|
|
len = f->fname_desc->len;
|
|
/*
|
|
filename = malloc(len+1);
|
|
memmove(filename,fname,len);
|
|
do {
|
|
filename[len--] = 0;
|
|
} while (filename[len] == ' ');
|
|
*/
|
|
type = DB_BTREE;
|
|
f->dbp = db_open(NULL, oflags, sflags, type, &b);
|
|
if (!f->dbp)
|
|
{
|
|
if (errno == EINVAL)
|
|
RETURN_STATUS(37);
|
|
RETURN_STATUS(30);
|
|
}
|
|
RETURN_STATUS(0);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| tcob_sort_release |
|
|
| |
|
|
\*------------------------------------------------------------------------*/
|
|
|
|
int tcob_sort_release(struct file_desc *f, char *record, char *sd, ...)
|
|
{
|
|
int result;
|
|
DBT key, data;
|
|
va_list args;
|
|
char *key1, *key_ptr;
|
|
unsigned int i;
|
|
|
|
data.data = record;
|
|
data.size = f->reclen;
|
|
|
|
key_ptr = key1 = (char *) malloc(f->reclen);
|
|
va_start(args, sd);
|
|
for (i = 0; sd[i]; i += 2)
|
|
{
|
|
unsigned int element_size = (unsigned char) sd[i + 1];
|
|
char *fld = va_arg (args, char *);
|
|
|
|
memcpy(key_ptr, fld, element_size);
|
|
if (sd[i] == 2)
|
|
{
|
|
/* If the key is descending then for each character we
|
|
perform a binary complement giving the final value.
|
|
This way we can combine the Ascending and Descending
|
|
Keys into one long key. The result is the final key and
|
|
saves us from having to run sort.
|
|
*/
|
|
unsigned int j;
|
|
for (j = 0; j < element_size; j++)
|
|
key_ptr[j] = ~key_ptr[j];
|
|
}
|
|
key_ptr += element_size;
|
|
}
|
|
va_end(args);
|
|
|
|
key.data = key1;
|
|
key.size = key_ptr - key1;
|
|
|
|
result = f->dbp->put(f->dbp, &key, &data, 0);
|
|
free(key1);
|
|
if (!result) /* success */
|
|
RETURN_STATUS(0);
|
|
else
|
|
/* failure */
|
|
RETURN_STATUS(99);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| tcob_sort_return |
|
|
| |
|
|
\*------------------------------------------------------------------------*/
|
|
|
|
int tcob_sort_return(struct file_desc *f, char *record)
|
|
{
|
|
int result;
|
|
DBT key, data;
|
|
int flags = R_NEXT;
|
|
|
|
result = f->dbp->seq(f->dbp, &key, &data, flags);
|
|
if (result)
|
|
RETURN_STATUS(10);
|
|
if ((int) data.size < f->reclen)
|
|
RETURN_STATUS(10);
|
|
memmove(record, data.data, f->reclen);
|
|
RETURN_STATUS(0);
|
|
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| tcob_sort_using |
|
|
| |
|
|
\*------------------------------------------------------------------------*/
|
|
|
|
int tcob_sort_using(struct file_desc *f1, char *fname1, ...)
|
|
{
|
|
int result, idx;
|
|
int sflags = S_IRUSR | S_IWUSR;
|
|
DBT key, data;
|
|
va_list args;
|
|
unsigned int cnt;
|
|
unsigned int file_num = 0;
|
|
unsigned int key_num;
|
|
struct file_desc *temp_f;
|
|
char *temp_fname;
|
|
struct file_desc *sort_file;
|
|
char *sd;
|
|
char *record, *rec;
|
|
struct file_desc *f[TCOB_MAX_PATHLN];
|
|
char *fname[TCOB_MAX_PATHLN];
|
|
char *fld[TCOB_MAX_PATHLN];
|
|
|
|
va_start(args, fname1);
|
|
f[file_num] = f1;
|
|
fname[file_num] = fname1;
|
|
temp_f = f1;
|
|
while (temp_f != NULL)
|
|
{
|
|
temp_f = va_arg(args, struct file_desc *);
|
|
if (temp_f == NULL)
|
|
break;
|
|
temp_fname = va_arg(args, char *);
|
|
file_num++;
|
|
f[file_num] = temp_f;
|
|
fname[file_num] = temp_fname;
|
|
}
|
|
sort_file = va_arg(args, struct file_desc *);
|
|
record = va_arg(args, char *);
|
|
sd = va_arg(args, char *);
|
|
for (key_num = 0; key_num < (strlen(sd) / 2); key_num++)
|
|
{
|
|
fld[key_num] = va_arg(args, char * );
|
|
}
|
|
va_end(args);
|
|
|
|
for (cnt = 0; cnt <= file_num; cnt++)
|
|
{
|
|
char *filename;
|
|
unsigned int adjusted_reclen;
|
|
int len;
|
|
int oflags = O_RDONLY;
|
|
|
|
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
|
oflags |= O_BINARY;
|
|
#endif
|
|
|
|
/*
|
|
beware: fname points to a field storage (non null-terminated)
|
|
we must copy fname to a C string and terminate it with a \0
|
|
and also trim spaces at the end.
|
|
*/
|
|
len = f[cnt]->fname_desc->len;
|
|
filename = malloc(len + 1);
|
|
memmove(filename, fname[cnt], len);
|
|
do
|
|
{
|
|
filename[len--] = 0;
|
|
} while (filename[len] == ' ');
|
|
|
|
adjusted_reclen = f[cnt]->reclen;
|
|
|
|
if (f[cnt]->organization != ORG_LINESEQUENTIAL)
|
|
{
|
|
f[cnt]->dbp = (void *) open(filename, oflags, sflags);
|
|
free(filename);
|
|
if (!f[cnt]->dbp)
|
|
{
|
|
if (errno == EINVAL)
|
|
{
|
|
f[cnt]->dbp = NULL;
|
|
return 37;
|
|
}
|
|
if (errno == ENOENT)
|
|
{
|
|
f[cnt]->dbp = NULL;
|
|
return 35;
|
|
}
|
|
f[cnt]->dbp = NULL;
|
|
return 91;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* create read buffer which can include record delimiters CR, LF */
|
|
rec = (char *) malloc(f1->reclen + 3);
|
|
if (rec == NULL)
|
|
{
|
|
free(filename);
|
|
return 91;
|
|
}
|
|
f[cnt]->dbp = (void *) fopen(filename, "r");
|
|
free(filename);
|
|
if (f[cnt]->dbp == NULL)
|
|
return 91;
|
|
adjusted_reclen = adjusted_reclen + 2;
|
|
}
|
|
|
|
/*
|
|
FIXME:
|
|
This code will result in a error condition
|
|
when EOF is reached, regarless.
|
|
*/
|
|
result = 0;
|
|
while (result == 0)
|
|
{
|
|
unsigned int i;
|
|
char *key1, *key_ptr;
|
|
|
|
if (f[cnt]->organization != ORG_LINESEQUENTIAL)
|
|
{
|
|
result = read((int) f[cnt]->dbp, record, adjusted_reclen);
|
|
if (result == 0)
|
|
{
|
|
close((int) f[cnt]->dbp);
|
|
f[cnt]->dbp = NULL;
|
|
result = 99;
|
|
break;
|
|
}
|
|
if (result != f[cnt]->reclen)
|
|
{
|
|
close((int) f[cnt]->dbp);
|
|
f[cnt]->dbp = NULL;
|
|
result = 99;
|
|
return 30;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (fgets(rec, adjusted_reclen, (FILE *) f[cnt]->dbp) == NULL)
|
|
{
|
|
fclose((FILE *) f[cnt]->dbp);
|
|
f[cnt]->dbp = NULL;
|
|
result = 99;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
remove trailing CR, LF and pad short record
|
|
length with blanks
|
|
*/
|
|
len = strlen(rec);
|
|
idx = 0;
|
|
while ((len > 0) && (idx < 2))
|
|
{
|
|
if ((rec[len - 1] == '\r') || (rec[len - 1] == '\n'))
|
|
{
|
|
rec[len - 1] = '\0';
|
|
len--;
|
|
}
|
|
idx++;
|
|
}
|
|
while (len <= f[cnt]->reclen)
|
|
{
|
|
rec[len - 1] = ' ';
|
|
len++;
|
|
}
|
|
memcpy(record, rec, f[cnt]->reclen);
|
|
result = f[cnt]->reclen;
|
|
}
|
|
}
|
|
|
|
data.data = record;
|
|
data.size = f[cnt]->reclen;
|
|
|
|
key_ptr = key1 = (char *) malloc(f1->reclen);
|
|
for (i = 0; sd[i]; i += 2)
|
|
{
|
|
unsigned int element_size = (unsigned char) sd[i + 1];
|
|
|
|
memcpy(key_ptr, fld[i >> 1], element_size);
|
|
if (sd[i] == 2)
|
|
{
|
|
/* If the key is descending then for each
|
|
character we perform a binary complement
|
|
giving the final value. This way we can
|
|
combine the Ascending and Descending keys
|
|
into one long key. The result is the final
|
|
key and saves us from having to run sort.
|
|
*/
|
|
unsigned int j;
|
|
for (j = 0; j < element_size; j++)
|
|
key_ptr[j] = ~key_ptr[j];
|
|
}
|
|
key_ptr += element_size;
|
|
}
|
|
|
|
key.data = key1;
|
|
key.size = key_ptr - key1;
|
|
|
|
result = sort_file->dbp->put(sort_file->dbp, &key, &data, 0);
|
|
free(key1);
|
|
if (result) /* failure */
|
|
return 99;
|
|
}
|
|
free(rec);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*\
|
|
| |
|
|
| tcob_sort_giving |
|
|
| |
|
|
\*------------------------------------------------------------------------*/
|
|
|
|
int tcob_sort_giving(struct file_desc *f1, char *fname1, ...)
|
|
{
|
|
int result;
|
|
int sflags = S_IRUSR | S_IWUSR;
|
|
DBT key, data;
|
|
int flags = R_NEXT;
|
|
struct file_desc *f[TCOB_MAX_PATHLN];
|
|
char *fname[TCOB_MAX_PATHLN];
|
|
int file_num = 0;
|
|
int cnt = 0;
|
|
char *temp_fname;
|
|
struct file_desc *temp_f;
|
|
char *filename;
|
|
va_list args;
|
|
struct file_desc *sort_file;
|
|
char *record, **rec, *pt;
|
|
int len;
|
|
|
|
va_start(args, *fname1);
|
|
f[file_num] = f1;
|
|
fname[file_num] = fname1;
|
|
temp_f = f1;
|
|
while (temp_f != NULL)
|
|
{
|
|
temp_f = va_arg(args, struct file_desc *);
|
|
if (temp_f == NULL)
|
|
break;
|
|
temp_fname = va_arg(args, char *);
|
|
file_num++;
|
|
f[file_num] = temp_f;
|
|
fname[file_num] = temp_fname;
|
|
}
|
|
sort_file = va_arg(args, struct file_desc *);
|
|
record = va_arg(args, char *);
|
|
va_end(args);
|
|
rec = (char **) malloc(sizeof(char*) * file_num);
|
|
for (cnt = 0; cnt <= file_num; cnt++)
|
|
{
|
|
int oflags = O_CREAT | O_TRUNC | O_RDWR;
|
|
|
|
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
|
oflags |= O_BINARY;
|
|
#endif
|
|
|
|
/* beware: fname points to a field storage (non null-terminated)
|
|
we must copy fname to a C string and terminate it with a \0
|
|
and also trim spaces at the end. */
|
|
len = f[cnt]->fname_desc->len;
|
|
filename = malloc(len + 1);
|
|
memmove(filename, fname[cnt], len);
|
|
do
|
|
{
|
|
filename[len--] = 0;
|
|
} while (filename[len] == ' ');
|
|
|
|
if (f[cnt]->organization != ORG_LINESEQUENTIAL)
|
|
{
|
|
rec[cnt] = NULL;
|
|
}
|
|
else
|
|
{
|
|
rec[cnt] = (char*) malloc(f[cnt]->reclen + 3);
|
|
}
|
|
f[cnt]->dbp = (void *) open(filename, oflags, sflags);
|
|
free(filename);
|
|
if (!f[cnt]->dbp)
|
|
{
|
|
if (errno == EINVAL)
|
|
{
|
|
f[cnt]->dbp = NULL;
|
|
return 37;
|
|
}
|
|
if (errno == ENOENT)
|
|
{
|
|
f[cnt]->dbp = NULL;
|
|
return 35;
|
|
}
|
|
f[cnt]->dbp = NULL;
|
|
return 91;
|
|
}
|
|
result = 0;
|
|
}
|
|
result = 0;
|
|
while (result == 0)
|
|
{
|
|
result = sort_file->dbp->seq(sort_file->dbp, &key, &data, flags);
|
|
if (result)
|
|
{
|
|
result = 10;
|
|
break;
|
|
}
|
|
if ((int) data.size < sort_file->reclen)
|
|
{
|
|
result = 10;
|
|
break;
|
|
}
|
|
memmove(record, data.data, sort_file->reclen);
|
|
for (cnt = 0; cnt <= file_num; cnt++)
|
|
{
|
|
if (f[cnt]->organization != ORG_LINESEQUENTIAL)
|
|
{
|
|
result = write((int) f[cnt]->dbp, record, f[cnt]->reclen);
|
|
if (!result)
|
|
return 99;
|
|
else
|
|
result = 0;
|
|
}
|
|
else
|
|
{
|
|
memmove(rec[cnt], record, f[cnt]->reclen);
|
|
len = f[cnt]->reclen;
|
|
pt = rec[cnt];
|
|
pt[len--] = '\0';
|
|
while ((pt[len] == ' ') && (len >= 0))
|
|
{
|
|
pt[len] = '\0';
|
|
len--;
|
|
}
|
|
len = strlen(pt);
|
|
pt[len++] = '\n';
|
|
pt[len] = '\0';
|
|
result = write((int) f[cnt]->dbp, rec[cnt], len);
|
|
if (!result)
|
|
return 99;
|
|
else
|
|
result = 0;
|
|
}
|
|
}
|
|
}
|
|
if (result != 10)
|
|
return 99;
|
|
for (cnt = 0; cnt <= file_num; cnt++)
|
|
{
|
|
if (f[cnt]->dbp != NULL)
|
|
{
|
|
close((int) f[cnt]->dbp);
|
|
f[cnt]->dbp = NULL;
|
|
}
|
|
if (rec[cnt] != NULL)
|
|
{
|
|
free(rec[cnt]);
|
|
rec[cnt] = NULL;
|
|
}
|
|
}
|
|
free(rec);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* EOF fileio.c */
|