2279 lines
60 KiB
C
2279 lines
60 KiB
C
/*
|
|
* $LynxId: HTVMS_WaisUI.c,v 1.21 2020/01/21 22:00:50 tom Exp $
|
|
* HTVMS_WAISUI.c
|
|
*
|
|
* Adaptation for Lynx by F.Macrides (macrides@sci.wfeb.edu)
|
|
*
|
|
* 30-May-1994 FM Initial version.
|
|
*
|
|
*----------------------------------------------------------------------*/
|
|
|
|
/*
|
|
* Routines originally from UI.c -- FM
|
|
*
|
|
*----------------------------------------------------------------------*/
|
|
/* WIDE AREA INFORMATION SERVER SOFTWARE:
|
|
* No guarantees or restrictions. See the readme file for the full standard
|
|
* disclaimer.
|
|
*
|
|
* Brewster@think.com
|
|
*/
|
|
|
|
/*
|
|
* this is a simple ui toolkit for building other ui's on top.
|
|
* -brewster
|
|
*
|
|
* top level functions:
|
|
* generate_search_apdu
|
|
* generate_retrieval_apdu
|
|
* interpret_message
|
|
*
|
|
*/
|
|
|
|
/* to do:
|
|
* generate multiple queries for long documents.
|
|
* this will crash if the file being retrieved is larger than 100k.
|
|
* do log_write()
|
|
*
|
|
*/
|
|
|
|
#include <HTUtils.h>
|
|
|
|
#ifdef VMS
|
|
#include <HTVMS_WaisUI.h>
|
|
#include <HTVMS_WaisProt.h>
|
|
#include <HTTCP.h>
|
|
|
|
#undef MAXINT /* we don't need it here, and www_tcp.h may conflict */
|
|
#include <math.h>
|
|
|
|
#include <LYexit.h>
|
|
#include <LYLeaks.h>
|
|
|
|
void log_write(char *s GCC_UNUSED)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
/* returns a pointer in the buffer of the first free byte.
|
|
if it overflows, then NULL is returned
|
|
*/
|
|
char *generate_search_apdu(char *buff, /* buffer to hold the apdu */
|
|
long *buff_len, /* length of the buffer changed to reflect new data written */
|
|
char *seed_words, /* string of the seed words */
|
|
char *database_name,
|
|
DocObj **docobjs,
|
|
long maxDocsRetrieved)
|
|
{
|
|
/* local variables */
|
|
|
|
SearchAPDU *search3;
|
|
char *end_ptr;
|
|
static char *database_names[2] =
|
|
{"", 0};
|
|
any refID;
|
|
WAISSearch *query;
|
|
|
|
refID.size = 1;
|
|
refID.bytes = "3";
|
|
|
|
database_names[0] = database_name;
|
|
query = makeWAISSearch(seed_words,
|
|
docobjs, /* DocObjsPtr */
|
|
0,
|
|
1, /* DateFactor */
|
|
0, /* BeginDateRange */
|
|
0, /* EndDateRange */
|
|
maxDocsRetrieved
|
|
);
|
|
|
|
search3 = makeSearchAPDU(30,
|
|
5000, /* should be large */
|
|
30,
|
|
1, /* replace indicator */
|
|
"", /* result set name */
|
|
database_names, /* database name */
|
|
QT_RelevanceFeedbackQuery, /* query_type */
|
|
0, /* element name */
|
|
NULL, /* reference ID */
|
|
query);
|
|
|
|
end_ptr = writeSearchAPDU(search3, buff, buff_len);
|
|
|
|
CSTFreeWAISSearch(query);
|
|
freeSearchAPDU(search3);
|
|
return (end_ptr);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
/* returns a pointer into the buffer of the next free byte.
|
|
if it overflowed, then NULL is returned
|
|
*/
|
|
|
|
char *generate_retrieval_apdu(char *buff,
|
|
long *buff_len, /* length of the buffer changed to reflect new data written */
|
|
any *docID,
|
|
long chunk_type,
|
|
long start,
|
|
long end,
|
|
char *type,
|
|
char *database_name)
|
|
{
|
|
SearchAPDU *search;
|
|
char *end_ptr;
|
|
|
|
static char *database_names[2];
|
|
static char *element_names[3];
|
|
any refID;
|
|
|
|
DocObj *DocObjs[2];
|
|
any *query; /* changed from char* by brewster */
|
|
|
|
if (NULL == type)
|
|
type = s_strdup("TEXT");
|
|
|
|
database_names[0] = database_name;
|
|
database_names[1] = NULL;
|
|
|
|
element_names[0] = " ";
|
|
element_names[1] = ES_DocumentText;
|
|
element_names[2] = NULL;
|
|
|
|
refID.size = 1;
|
|
refID.bytes = "3";
|
|
|
|
switch (chunk_type) {
|
|
case CT_line:
|
|
DocObjs[0] = makeDocObjUsingLines(docID, type, start, end);
|
|
break;
|
|
case CT_byte:
|
|
DocObjs[0] = makeDocObjUsingBytes(docID, type, start, end);
|
|
break;
|
|
}
|
|
DocObjs[1] = NULL;
|
|
|
|
query = makeWAISTextQuery(DocObjs);
|
|
search = makeSearchAPDU(10, 16, 15,
|
|
1, /* replace indicator */
|
|
"FOO", /* result set name */
|
|
database_names, /* database name */
|
|
QT_TextRetrievalQuery, /* query_type */
|
|
element_names, /* element name */
|
|
&refID, /* reference ID */
|
|
query);
|
|
end_ptr = writeSearchAPDU(search, buff, buff_len);
|
|
CSTFreeWAISTextQuery(query);
|
|
freeSearchAPDU(search);
|
|
return (end_ptr);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
/* this is a safe version of unix 'read' it does all the checking
|
|
* and looping necessary
|
|
* to those trying to modify the transport code to use non-UNIX streams:
|
|
* This is the function to modify!
|
|
*/
|
|
static long read_from_stream(int d, char *buf, long nbytes)
|
|
{
|
|
long didRead;
|
|
long toRead = nbytes;
|
|
long totalRead = 0; /* paranoia */
|
|
|
|
while (toRead > 0) {
|
|
didRead = NETREAD(d, buf, (int) toRead);
|
|
if (didRead == HT_INTERRUPTED)
|
|
return (HT_INTERRUPTED);
|
|
if (didRead == -1) /* error */
|
|
return (-1);
|
|
if (didRead == 0) /* eof */
|
|
return (-2); /* maybe this should return 0? */
|
|
toRead -= didRead;
|
|
buf += didRead;
|
|
totalRead += didRead;
|
|
}
|
|
if (totalRead != nbytes) /* we overread for some reason */
|
|
return (-totalRead); /* bad news */
|
|
return (totalRead);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
/* returns the length of the response, 0 if an error */
|
|
|
|
static long transport_message(long connection,
|
|
char *request_message,
|
|
long request_length,
|
|
char *response_message,
|
|
long response_buffer_length)
|
|
{
|
|
WAISMessage header;
|
|
long response_length;
|
|
int rv;
|
|
|
|
/* Write out message. Read back header. Figure out response length. */
|
|
|
|
if (request_length + HEADER_LENGTH !=
|
|
NETWRITE(connection, request_message,
|
|
(int) (request_length + HEADER_LENGTH)))
|
|
return 0;
|
|
|
|
/* read for the first '0' */
|
|
|
|
while (1) {
|
|
rv = read_from_stream(connection, response_message, 1);
|
|
if (rv == HT_INTERRUPTED)
|
|
return HT_INTERRUPTED;
|
|
if (rv < 0)
|
|
return 0;
|
|
if ('0' == response_message[0])
|
|
break;
|
|
}
|
|
|
|
rv = read_from_stream(connection, response_message + 1, HEADER_LENGTH - 1);
|
|
if (rv == HT_INTERRUPTED)
|
|
return HT_INTERRUPTED;
|
|
if (rv < 0)
|
|
return 0;
|
|
|
|
readWAISPacketHeader(response_message, &header);
|
|
{
|
|
char length_array[11];
|
|
|
|
LYStrNCpy(length_array, header.msg_len, 10);
|
|
response_length = atol(length_array);
|
|
/*
|
|
if(verbose){
|
|
printf("WAIS header: '%s' length_array: '%s'\n",
|
|
response_message, length_array);
|
|
}
|
|
*/
|
|
if (response_length > response_buffer_length) {
|
|
/* we got a message that is too long, therefore empty the message out,
|
|
and return 0 */
|
|
long i;
|
|
|
|
for (i = 0; i < response_length; i++) {
|
|
rv = read_from_stream(connection,
|
|
response_message + HEADER_LENGTH,
|
|
1);
|
|
if (rv == HT_INTERRUPTED)
|
|
return HT_INTERRUPTED;
|
|
if (rv < 0)
|
|
return 0;
|
|
}
|
|
return (0);
|
|
}
|
|
}
|
|
rv = read_from_stream(connection,
|
|
response_message + HEADER_LENGTH,
|
|
response_length);
|
|
if (rv == HT_INTERRUPTED)
|
|
return HT_INTERRUPTED;
|
|
if (rv < 0)
|
|
return 0;
|
|
return (response_length);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
/* returns the number of bytes written. 0 if an error */
|
|
long interpret_message(char *request_message,
|
|
long request_length, /* length of the buffer */
|
|
char *response_message,
|
|
long response_buffer_length,
|
|
long connection,
|
|
boolean verbose GCC_UNUSED)
|
|
{
|
|
long response_length;
|
|
|
|
/* ?
|
|
if(verbose){
|
|
printf ("sending");
|
|
if(hostname_internal && strlen(hostname_internal) > 0)
|
|
printf(" to host %s", hostname_internal);
|
|
if(service_name && strlen(service_name) > 0)
|
|
printf(" for service %s", service_name);
|
|
printf("\n");
|
|
twais_dsply_rsp_apdu(request_message + HEADER_LENGTH,
|
|
request_length);
|
|
}
|
|
|
|
*/
|
|
|
|
writeWAISPacketHeader(request_message,
|
|
request_length,
|
|
(long) 'z', /* Z39.50 */
|
|
"wais ", /* server name */
|
|
(long) NO_COMPRESSION, /* no compression */
|
|
(long) NO_ENCODING, (long) HEADER_VERSION);
|
|
if (connection != 0) {
|
|
response_length = transport_message(connection, request_message,
|
|
request_length,
|
|
response_message,
|
|
response_buffer_length);
|
|
if (response_length == HT_INTERRUPTED)
|
|
return (HT_INTERRUPTED);
|
|
} else
|
|
return (0);
|
|
|
|
return (response_length);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
/* modifies the string to exclude all seeker codes. sets length to
|
|
the new length. */
|
|
static char *delete_seeker_codes(char *string, long *length)
|
|
{
|
|
long original_count; /* index into the original string */
|
|
long new_count = 0; /* index into the collapsed string */
|
|
|
|
for (original_count = 0; original_count < *length; original_count++) {
|
|
if (27 == string[original_count]) {
|
|
/* then we have an escape code */
|
|
/* if the next letter is '(' or ')', then ignore two letters */
|
|
if ('(' == string[original_count + 1] ||
|
|
')' == string[original_count + 1])
|
|
original_count += 1; /* it is a term marker */
|
|
else
|
|
original_count += 4; /* it is a paragraph marker */
|
|
} else
|
|
string[new_count++] = string[original_count];
|
|
}
|
|
*length = new_count;
|
|
return (string);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
#if defined(VMS) && defined(__GNUC__) /* 10-AUG-1995 [pr] */
|
|
/*
|
|
Workaround for an obscure bug in gcc's 2.6.[123] and 2.7.0 vax/vms port;
|
|
sometimes global variables will end up not being defined properly,
|
|
causing first gas to assume they're routines, then the linker to complain
|
|
about unresolved symbols, and finally the program to reference the wrong
|
|
objects (provoking ACCVIO). It's triggered by the specific ordering of
|
|
variable usage in the source code, hence rarely appears. This bug is
|
|
fixed in gcc 2.7.1, and was not present in 2.6.0 and earlier.
|
|
|
|
Make a reference to VAXCRTL's _ctype_[], and also one to this dummy
|
|
variable itself to prevent any "defined but not used" warning.
|
|
*/
|
|
static __const void *__const ctype_dummy[] =
|
|
{&_ctype_, &ctype_dummy};
|
|
#endif /* VMS && __GNUC__ */
|
|
|
|
/* returns a pointer to a string with good stuff */
|
|
char *trim_junk(char *headline)
|
|
{
|
|
long length = strlen(headline) + 1; /* include the trailing null */
|
|
size_t i;
|
|
|
|
headline = delete_seeker_codes(headline, &length);
|
|
/* delete leading spaces */
|
|
for (i = 0; i < strlen(headline); i++) {
|
|
if (isprint(headline[i])) {
|
|
break;
|
|
}
|
|
}
|
|
headline = headline + i;
|
|
/* delete trailing stuff */
|
|
for (i = strlen(headline) - 1; i > 0; i--) {
|
|
if (isprint(headline[i])) {
|
|
break;
|
|
}
|
|
headline[i] = '\0';
|
|
}
|
|
return (headline);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
/*
|
|
* Routines originally from ZProt.c -- FM
|
|
*
|
|
*----------------------------------------------------------------------*/
|
|
/* WIDE AREA INFORMATION SERVER SOFTWARE:`
|
|
* No guarantees or restrictions. See the readme file for the full standard
|
|
* disclaimer.
|
|
*
|
|
* 3.26.90 Harry Morris, morris@think.com
|
|
* 3.30.90 Harry Morris - Changed any->bits to any->bytes
|
|
* 4.11.90 HWM - generalized conditional includes (see c-dialect.h)
|
|
*/
|
|
|
|
#define RESERVE_SPACE_FOR_HEADER(spaceLeft) \
|
|
*spaceLeft -= HEADER_LEN;
|
|
|
|
#define RELEASE_HEADER_SPACE(spaceLeft) \
|
|
if (*spaceLeft > 0) \
|
|
*spaceLeft += HEADER_LEN;
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
InitResponseAPDU *makeInitResponseAPDU(boolean result,
|
|
boolean search,
|
|
boolean present,
|
|
boolean deleteIt,
|
|
boolean accessControl,
|
|
boolean resourceControl,
|
|
long prefSize,
|
|
long maxMsgSize,
|
|
char *auth,
|
|
char *id,
|
|
char *name,
|
|
char *version,
|
|
any *refID,
|
|
void *userInfo)
|
|
/* build an initResponse APDU with user specified information */
|
|
{
|
|
InitResponseAPDU *init = (InitResponseAPDU *) s_malloc((size_t) sizeof(InitResponseAPDU));
|
|
|
|
init->PDUType = initResponseAPDU;
|
|
init->Result = result;
|
|
init->willSearch = search;
|
|
init->willPresent = present;
|
|
init->willDelete = deleteIt;
|
|
init->supportAccessControl = accessControl;
|
|
init->supportResourceControl = resourceControl;
|
|
init->PreferredMessageSize = prefSize;
|
|
init->MaximumRecordSize = maxMsgSize;
|
|
init->IDAuthentication = s_strdup(auth);
|
|
init->ImplementationID = s_strdup(id);
|
|
init->ImplementationName = s_strdup(name);
|
|
init->ImplementationVersion = s_strdup(version);
|
|
init->ReferenceID = duplicateAny(refID);
|
|
init->UserInformationField = userInfo; /* not copied! */
|
|
|
|
return (init);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
void freeInitResponseAPDU(InitResponseAPDU *init)
|
|
/* free an initAPDU */
|
|
{
|
|
s_free(init->IDAuthentication);
|
|
s_free(init->ImplementationID);
|
|
s_free(init->ImplementationName);
|
|
s_free(init->ImplementationVersion);
|
|
freeAny(init->ReferenceID);
|
|
s_free(init);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *writeInitResponseAPDU(InitResponseAPDU *init, char *buffer, long *len)
|
|
/* write the initResponse to a buffer, adding system information */
|
|
{
|
|
char *buf = buffer + HEADER_LEN; /* leave room for the header-length-indicator */
|
|
long size;
|
|
bit_map *optionsBM = NULL;
|
|
|
|
RESERVE_SPACE_FOR_HEADER(len);
|
|
|
|
buf = writePDUType(init->PDUType, buf, len);
|
|
buf = writeBoolean(init->Result, buf, len);
|
|
buf = writeProtocolVersion(buf, len);
|
|
|
|
optionsBM = makeBitMap((unsigned long) 5, init->willSearch, init->willPresent,
|
|
init->willDelete, init->supportAccessControl,
|
|
init->supportResourceControl);
|
|
buf = writeBitMap(optionsBM, DT_Options, buf, len);
|
|
freeBitMap(optionsBM);
|
|
|
|
buf = writeNum(init->PreferredMessageSize,
|
|
DT_PreferredMessageSize,
|
|
buf,
|
|
len);
|
|
buf = writeNum(init->MaximumRecordSize,
|
|
DT_MaximumRecordSize,
|
|
buf,
|
|
len);
|
|
buf = writeString(init->IDAuthentication,
|
|
DT_IDAuthentication,
|
|
buf,
|
|
len);
|
|
buf = writeString(init->ImplementationID,
|
|
DT_ImplementationID,
|
|
buf,
|
|
len);
|
|
buf = writeString(init->ImplementationName,
|
|
DT_ImplementationName,
|
|
buf,
|
|
len);
|
|
buf = writeString(init->ImplementationVersion,
|
|
DT_ImplementationVersion,
|
|
buf,
|
|
len);
|
|
buf = writeAny(init->ReferenceID,
|
|
DT_ReferenceID,
|
|
buf,
|
|
len);
|
|
|
|
/* go back and write the header-length-indicator */
|
|
RELEASE_HEADER_SPACE(len);
|
|
size = buf - buffer - HEADER_LEN;
|
|
writeBinaryInteger(size, HEADER_LEN, buffer, len);
|
|
|
|
if (init->UserInformationField != NULL)
|
|
buf = writeInitResponseInfo(init, buf, len);
|
|
|
|
return (buf);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *readInitResponseAPDU(InitResponseAPDU **init, char *buffer)
|
|
{
|
|
char *buf = buffer;
|
|
boolean search, present, delete, accessControl, resourceControl;
|
|
long prefSize, maxMsgSize;
|
|
char *auth, *id, *name, *version;
|
|
long size;
|
|
pdu_type pduType;
|
|
bit_map *versionBM = NULL;
|
|
bit_map *optionsBM = NULL;
|
|
boolean result;
|
|
any *refID = NULL;
|
|
void *userInfo = NULL;
|
|
|
|
auth = id = name = version = NULL;
|
|
refID = NULL;
|
|
|
|
/* read required part */
|
|
buf = readBinaryInteger(&size, HEADER_LEN, buf);
|
|
buf = readPDUType(&pduType, buf);
|
|
buf = readBoolean(&result, buf);
|
|
buf = readBitMap(&versionBM, buf);
|
|
buf = readBitMap(&optionsBM, buf);
|
|
buf = readNum(&prefSize, buf);
|
|
buf = readNum(&maxMsgSize, buf);
|
|
|
|
/* decode optionsBM */
|
|
search = bitAtPos(0, optionsBM);
|
|
present = bitAtPos(1, optionsBM);
|
|
delete = bitAtPos(2, optionsBM);
|
|
accessControl = bitAtPos(3, optionsBM);
|
|
resourceControl = bitAtPos(4, optionsBM);
|
|
|
|
/* read optional part */
|
|
while (buf < (buffer + size + HEADER_LEN)) {
|
|
data_tag tag = peekTag(buf);
|
|
|
|
switch (tag) {
|
|
case DT_IDAuthentication:
|
|
buf = readString(&auth, buf);
|
|
break;
|
|
case DT_ImplementationID:
|
|
buf = readString(&id, buf);
|
|
break;
|
|
case DT_ImplementationName:
|
|
buf = readString(&name, buf);
|
|
break;
|
|
case DT_ImplementationVersion:
|
|
buf = readString(&version, buf);
|
|
break;
|
|
case DT_ReferenceID:
|
|
buf = readAny(&refID, buf);
|
|
break;
|
|
default:
|
|
freeBitMap(versionBM);
|
|
freeBitMap(optionsBM);
|
|
s_free(auth);
|
|
s_free(id);
|
|
s_free(name);
|
|
s_free(version);
|
|
freeAny(refID);
|
|
REPORT_READ_ERROR(buf);
|
|
break;
|
|
}
|
|
}
|
|
|
|
buf = readInitResponseInfo(&userInfo, buf);
|
|
if (buf == NULL) {
|
|
freeBitMap(versionBM);
|
|
freeBitMap(optionsBM);
|
|
s_free(auth);
|
|
s_free(id);
|
|
s_free(name);
|
|
s_free(version);
|
|
freeAny(refID);
|
|
}
|
|
RETURN_ON_NULL(buf);
|
|
|
|
/* construct the basic init object */
|
|
*init = makeInitResponseAPDU(result,
|
|
search,
|
|
present,
|
|
delete,
|
|
accessControl,
|
|
resourceControl,
|
|
prefSize,
|
|
maxMsgSize,
|
|
auth,
|
|
id,
|
|
name,
|
|
version,
|
|
refID,
|
|
userInfo);
|
|
|
|
freeBitMap(versionBM);
|
|
freeBitMap(optionsBM);
|
|
s_free(auth);
|
|
s_free(id);
|
|
s_free(name);
|
|
s_free(version);
|
|
freeAny(refID);
|
|
|
|
return (buf);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
InitResponseAPDU *replyToInitAPDU(InitAPDU * init, boolean result, void *userInfo)
|
|
/* respond to an init message in the default way - echoing back
|
|
the init info
|
|
*/
|
|
{
|
|
InitResponseAPDU *initResp;
|
|
|
|
initResp = makeInitResponseAPDU(result,
|
|
init->willSearch,
|
|
init->willPresent,
|
|
init->willDelete,
|
|
init->supportAccessControl,
|
|
init->supportResourceControl,
|
|
init->PreferredMessageSize,
|
|
init->MaximumRecordSize,
|
|
init->IDAuthentication,
|
|
defaultImplementationID(),
|
|
defaultImplementationName(),
|
|
defaultImplementationVersion(),
|
|
init->ReferenceID,
|
|
userInfo);
|
|
return (initResp);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
SearchAPDU *makeSearchAPDU(long small,
|
|
long large,
|
|
long medium,
|
|
boolean replace,
|
|
char *name,
|
|
char **databases,
|
|
char *type,
|
|
char **elements,
|
|
any *refID,
|
|
void *queryInfo)
|
|
{
|
|
char *ptr = NULL;
|
|
long i;
|
|
SearchAPDU *query = (SearchAPDU *) s_malloc((size_t) sizeof(SearchAPDU));
|
|
|
|
query->PDUType = searchAPDU;
|
|
query->SmallSetUpperBound = small;
|
|
query->LargeSetLowerBound = large;
|
|
query->MediumSetPresentNumber = medium;
|
|
query->ReplaceIndicator = replace;
|
|
query->ResultSetName = s_strdup(name);
|
|
query->DatabaseNames = NULL;
|
|
if (databases != NULL) {
|
|
for (i = 0, ptr = databases[i]; ptr != NULL; ptr = databases[++i]) {
|
|
if (query->DatabaseNames == NULL)
|
|
query->DatabaseNames = (char **) s_malloc((size_t) (sizeof(char
|
|
*)
|
|
* 2));
|
|
|
|
else
|
|
query->DatabaseNames = (char **) s_realloc((char *) query->DatabaseNames,
|
|
(size_t) (sizeof(char
|
|
*) *
|
|
(i + 2)));
|
|
|
|
query->DatabaseNames[i] = s_strdup(ptr);
|
|
query->DatabaseNames[i + 1] = NULL;
|
|
}
|
|
}
|
|
query->QueryType = s_strdup(type);
|
|
query->ElementSetNames = NULL;
|
|
if (elements != NULL) {
|
|
for (i = 0, ptr = elements[i]; ptr != NULL; ptr = elements[++i]) {
|
|
if (query->ElementSetNames == NULL)
|
|
query->ElementSetNames =
|
|
(char **) s_malloc((size_t) (sizeof(char *) * 2));
|
|
|
|
else
|
|
query->ElementSetNames = (char **) s_realloc((char *) query->ElementSetNames,
|
|
(size_t) (sizeof(char
|
|
*) *
|
|
(i + 2)));
|
|
|
|
query->ElementSetNames[i] = s_strdup(ptr);
|
|
query->ElementSetNames[i + 1] = NULL;
|
|
}
|
|
}
|
|
query->ReferenceID = duplicateAny(refID);
|
|
query->Query = queryInfo; /* not copied! */
|
|
return (query);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
void freeSearchAPDU(SearchAPDU *query)
|
|
{
|
|
s_free(query->ResultSetName);
|
|
s_free(query->QueryType);
|
|
doList((void **) query->DatabaseNames, fs_free); /* can't use the macro here ! */
|
|
s_free(query->DatabaseNames);
|
|
doList((void **) query->ElementSetNames, fs_free); /* can't use the macro here ! */
|
|
s_free(query->ElementSetNames);
|
|
freeAny(query->ReferenceID);
|
|
s_free(query);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
#define DB_DELIMITER "\037" /* hex 1F occurs between each database name */
|
|
#define ES_DELIMITER_1 "\037" /* separates database name from element name */
|
|
#define ES_DELIMITER_2 "\036" /* hex 1E separates <db,es> groups from one another */
|
|
|
|
char *writeSearchAPDU(SearchAPDU *query, char *buffer, long *len)
|
|
{
|
|
char *buf = buffer + HEADER_LEN; /* leave room for the header-length-indicator */
|
|
long size, i;
|
|
char *ptr = NULL;
|
|
char *scratch = NULL;
|
|
|
|
RESERVE_SPACE_FOR_HEADER(len);
|
|
|
|
buf = writePDUType(query->PDUType, buf, len);
|
|
buf = writeBinaryInteger(query->SmallSetUpperBound, (size_t) 3, buf, len);
|
|
buf = writeBinaryInteger(query->LargeSetLowerBound, (size_t) 3, buf, len);
|
|
buf = writeBinaryInteger(query->MediumSetPresentNumber, (size_t) 3, buf, len);
|
|
buf = writeBoolean(query->ReplaceIndicator, buf, len);
|
|
buf = writeString(query->ResultSetName, DT_ResultSetName, buf, len);
|
|
/* write database names */
|
|
if (query->DatabaseNames != NULL) {
|
|
for (i = 0, scratch = NULL, ptr = query->DatabaseNames[i]; ptr != NULL;
|
|
ptr = query->DatabaseNames[++i]) {
|
|
if (scratch == NULL)
|
|
scratch = s_strdup(ptr);
|
|
else {
|
|
size_t newScratchSize = (size_t) (strlen(scratch) +
|
|
strlen(ptr) + 2);
|
|
|
|
scratch = (char *) s_realloc(scratch, newScratchSize);
|
|
s_strncat(scratch, DB_DELIMITER, 2, newScratchSize);
|
|
s_strncat(scratch, ptr, strlen(ptr) + 1, newScratchSize);
|
|
}
|
|
}
|
|
buf = writeString(scratch, DT_DatabaseNames, buf, len);
|
|
s_free(scratch);
|
|
}
|
|
buf = writeString(query->QueryType, DT_QueryType, buf, len);
|
|
/* write element set names */
|
|
if (query->ElementSetNames != NULL) {
|
|
for (i = 0, scratch = NULL, ptr = query->ElementSetNames[i];
|
|
ptr != NULL;
|
|
ptr = query->ElementSetNames[++i]) {
|
|
if (scratch == NULL) {
|
|
if (query->ElementSetNames[i + 1] == NULL) /* there is a single element set name */
|
|
{
|
|
scratch = (char *) s_malloc((size_t) strlen(ptr) + 2);
|
|
StrNCpy(scratch, ES_DELIMITER_1, 2);
|
|
s_strncat(scratch, ptr, strlen(ptr) + 1, strlen(ptr) + 2);
|
|
} else { /* this is the first of a series of element set names */
|
|
size_t newScratchSize = (size_t) (strlen(ptr) +
|
|
strlen(query->ElementSetNames[i
|
|
+ 1])
|
|
+ 2);
|
|
|
|
scratch = s_strdup(ptr); /* the database name */
|
|
ptr = query->ElementSetNames[++i]; /* the element set name */
|
|
scratch = (char *) s_realloc(scratch, newScratchSize);
|
|
s_strncat(scratch, ES_DELIMITER_1, 2, newScratchSize);
|
|
s_strncat(scratch, ptr, strlen(ptr) + 1, newScratchSize);
|
|
}
|
|
} else {
|
|
char *esPtr = query->ElementSetNames[++i]; /* the element set name */
|
|
size_t newScratchSize = (size_t) (strlen(scratch) +
|
|
strlen(ptr) +
|
|
strlen(esPtr) +
|
|
3);
|
|
|
|
scratch = (char *) s_realloc(scratch, newScratchSize);
|
|
s_strncat(scratch, ES_DELIMITER_2, 2, newScratchSize);
|
|
s_strncat(scratch, ptr, strlen(ptr) + 1, newScratchSize);
|
|
s_strncat(scratch, ES_DELIMITER_1, 2, newScratchSize);
|
|
s_strncat(scratch, esPtr, strlen(esPtr) + 1, newScratchSize);
|
|
}
|
|
}
|
|
buf = writeString(scratch, DT_ElementSetNames, buf, len);
|
|
s_free(scratch);
|
|
}
|
|
buf = writeAny(query->ReferenceID, DT_ReferenceID, buf, len);
|
|
|
|
/* go back and write the header-length-indicator */
|
|
RELEASE_HEADER_SPACE(len);
|
|
size = buf - buffer - HEADER_LEN;
|
|
writeBinaryInteger(size, HEADER_LEN, buffer, len);
|
|
|
|
if (query->Query != NULL)
|
|
buf = writeSearchInfo(query, buf, len);
|
|
|
|
return (buf);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
SearchResponseAPDU *makeSearchResponseAPDU(long result,
|
|
long count,
|
|
long recordsReturned,
|
|
long nextPos,
|
|
long resultStatus,
|
|
long presentStatus,
|
|
any *refID,
|
|
void *records)
|
|
{
|
|
SearchResponseAPDU *query =
|
|
(SearchResponseAPDU *) s_malloc((size_t) sizeof(SearchResponseAPDU));
|
|
|
|
query->PDUType = searchResponseAPDU;
|
|
query->SearchStatus = result;
|
|
query->ResultCount = count;
|
|
query->NumberOfRecordsReturned = recordsReturned;
|
|
query->NextResultSetPosition = nextPos;
|
|
query->ResultSetStatus = resultStatus;
|
|
query->PresentStatus = presentStatus;
|
|
query->ReferenceID = duplicateAny(refID);
|
|
query->DatabaseDiagnosticRecords = records;
|
|
return (query);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
void freeSearchResponseAPDU(SearchResponseAPDU *queryResponse)
|
|
{
|
|
freeAny(queryResponse->ReferenceID);
|
|
s_free(queryResponse);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *writeSearchResponseAPDU(SearchResponseAPDU *queryResponse, char *buffer,
|
|
long *len)
|
|
{
|
|
char *buf = buffer + HEADER_LEN; /* leave room for the header-length-indicator */
|
|
long size;
|
|
|
|
RESERVE_SPACE_FOR_HEADER(len);
|
|
|
|
buf = writePDUType(queryResponse->PDUType,
|
|
buf,
|
|
len);
|
|
buf = writeBinaryInteger(queryResponse->SearchStatus,
|
|
(size_t) 1,
|
|
buf,
|
|
len);
|
|
buf = writeBinaryInteger(queryResponse->ResultCount,
|
|
(size_t) 3,
|
|
buf,
|
|
len);
|
|
buf = writeBinaryInteger(queryResponse->NumberOfRecordsReturned,
|
|
(size_t) 3,
|
|
buf,
|
|
len);
|
|
buf = writeBinaryInteger(queryResponse->NextResultSetPosition,
|
|
(size_t) 3,
|
|
buf,
|
|
len);
|
|
buf = writeNum(queryResponse->ResultSetStatus,
|
|
DT_ResultSetStatus,
|
|
buf,
|
|
len);
|
|
buf = writeNum(queryResponse->PresentStatus,
|
|
DT_PresentStatus,
|
|
buf,
|
|
len);
|
|
buf = writeAny(queryResponse->ReferenceID,
|
|
DT_ReferenceID,
|
|
buf,
|
|
len);
|
|
|
|
/* go back and write the header-length-indicator */
|
|
RELEASE_HEADER_SPACE(len);
|
|
size = buf - buffer - HEADER_LEN;
|
|
writeBinaryInteger(size, HEADER_LEN, buffer, len);
|
|
|
|
if (queryResponse->DatabaseDiagnosticRecords != NULL)
|
|
buf = writeSearchResponseInfo(queryResponse, buf, len);
|
|
|
|
return (buf);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *readSearchResponseAPDU(SearchResponseAPDU **queryResponse, char *buffer)
|
|
{
|
|
char *buf = buffer;
|
|
long size;
|
|
pdu_type pduType;
|
|
long result, count, recordsReturned, nextPos;
|
|
long resultStatus, presentStatus;
|
|
any *refID = NULL;
|
|
void *userInfo = NULL;
|
|
|
|
/* read required part */
|
|
buf = readBinaryInteger(&size, HEADER_LEN, buf);
|
|
buf = readPDUType(&pduType, buf);
|
|
buf = readBinaryInteger(&result, (size_t) 1, buf);
|
|
buf = readBinaryInteger(&count, (size_t) 3, buf);
|
|
buf = readBinaryInteger(&recordsReturned, (size_t) 3, buf);
|
|
buf = readBinaryInteger(&nextPos, (size_t) 3, buf);
|
|
|
|
resultStatus = presentStatus = UNUSED;
|
|
refID = NULL;
|
|
|
|
/* read optional part */
|
|
while (buf < (buffer + size + HEADER_LEN)) {
|
|
data_tag tag = peekTag(buf);
|
|
|
|
switch (tag) {
|
|
case DT_ResultSetStatus:
|
|
buf = readNum(&resultStatus, buf);
|
|
break;
|
|
case DT_PresentStatus:
|
|
buf = readNum(&presentStatus, buf);
|
|
break;
|
|
case DT_ReferenceID:
|
|
buf = readAny(&refID, buf);
|
|
break;
|
|
default:
|
|
freeAny(refID);
|
|
REPORT_READ_ERROR(buf);
|
|
break;
|
|
}
|
|
}
|
|
|
|
buf = readSearchResponseInfo(&userInfo, buf);
|
|
if (buf == NULL)
|
|
freeAny(refID);
|
|
RETURN_ON_NULL(buf);
|
|
|
|
/* construct the search object */
|
|
*queryResponse = makeSearchResponseAPDU(result,
|
|
count,
|
|
recordsReturned,
|
|
nextPos,
|
|
(long) resultStatus,
|
|
(long) presentStatus,
|
|
refID,
|
|
userInfo);
|
|
|
|
freeAny(refID);
|
|
|
|
return (buf);
|
|
}
|
|
|
|
/*
|
|
* Routines originally from ZUtil.c -- FM
|
|
*
|
|
*----------------------------------------------------------------------*/
|
|
/* WIDE AREA INFORMATION SERVER SOFTWARE:
|
|
* No guarantees or restrictions. See the readme file for the full standard
|
|
* disclaimer.
|
|
*
|
|
* 3.26.90 Harry Morris, morris@think.com
|
|
* 3.30.90 Harry Morris - Changed any->bits to any->bytes
|
|
* 4.11.90 HWM - fixed include file names, changed
|
|
* - writeCompressedIntegerWithPadding() to
|
|
* writeCompressedIntWithPadding()
|
|
* - generalized conditional includes (see c-dialect.h)
|
|
* 3.7.91 Jonny Goldman. Replaced "short" in makeBitMap with "int" line 632.
|
|
*/
|
|
|
|
char *readErrorPosition = NULL; /* pos where buf stopped making sense */
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* A note on error handling
|
|
read - these are low level routines, they do not check the type tags
|
|
which (sometimes) precede the data (this is done by the higher
|
|
level functions which call these functions). There is no
|
|
attempt made to check that the reading does not exceed the read
|
|
buffer. Such cases should be very rare and usually will be
|
|
caught by the calling functions. (note - it is unlikely that
|
|
a series of low level reads will go far off the edge without
|
|
triggering a type error. However, it is possible for a single
|
|
bad read in an array function (eg. readAny) to attempt to read a
|
|
large amount, possibly causing a segmentation violation or out
|
|
of memory condition.
|
|
*/
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
diagnosticRecord *makeDiag(boolean surrogate, char *code, char *addInfo)
|
|
{
|
|
diagnosticRecord *diag =
|
|
(diagnosticRecord *) s_malloc((size_t) sizeof(diagnosticRecord));
|
|
|
|
diag->SURROGATE = surrogate;
|
|
MemCpy(diag->DIAG, code, DIAGNOSTIC_CODE_SIZE);
|
|
diag->ADDINFO = s_strdup(addInfo);
|
|
|
|
return (diag);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
void freeDiag(diagnosticRecord * diag)
|
|
{
|
|
if (diag != NULL) {
|
|
if (diag->ADDINFO != NULL)
|
|
s_free(diag->ADDINFO);
|
|
s_free(diag);
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
#define END_OF_RECORD 0x1D
|
|
|
|
char *writeDiag(diagnosticRecord * diag, char *buffer, long *len)
|
|
/* diagnostics (as per Appendix D) have a very weird format - this changes
|
|
in SR-1
|
|
*/
|
|
{
|
|
char *buf = buffer;
|
|
long length;
|
|
|
|
if (diag == NULL) /* handle unspecified optional args */
|
|
return (buf);
|
|
|
|
buf = writeTag(DT_DatabaseDiagnosticRecords, buf, len);
|
|
CHECK_FOR_SPACE_LEFT(0, len);
|
|
|
|
length = 3;
|
|
if (diag->ADDINFO != NULL)
|
|
length += strlen(diag->ADDINFO);
|
|
|
|
if (length >= 0xFFFF) /* make sure the length is reasonable */
|
|
{
|
|
length = 0xFFFF - 1;
|
|
diag->ADDINFO[0xFFFF - 3 - 1] = '\0';
|
|
}
|
|
|
|
buf = writeBinaryInteger(length, 2, buf, len);
|
|
|
|
CHECK_FOR_SPACE_LEFT(1, len);
|
|
buf[0] = diag->DIAG[0];
|
|
buf++;
|
|
|
|
CHECK_FOR_SPACE_LEFT(1, len);
|
|
buf[0] = diag->DIAG[1];
|
|
buf++;
|
|
|
|
if (length > 3) {
|
|
CHECK_FOR_SPACE_LEFT(3, len);
|
|
MemCpy(buf, diag->ADDINFO, length - 3);
|
|
buf += length - 3;
|
|
}
|
|
|
|
CHECK_FOR_SPACE_LEFT(1, len);
|
|
buf[0] = diag->SURROGATE;
|
|
buf++;
|
|
|
|
CHECK_FOR_SPACE_LEFT(1, len);
|
|
buf[0] = END_OF_RECORD;
|
|
buf++;
|
|
|
|
return (buf);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *readDiag(diagnosticRecord ** diag, char *buffer)
|
|
{
|
|
char *buf = buffer;
|
|
diagnosticRecord *d = (diagnosticRecord *) s_malloc((size_t) sizeof(diagnosticRecord));
|
|
data_tag tag;
|
|
long len;
|
|
|
|
buf = readTag(&tag, buf);
|
|
|
|
buf = readBinaryInteger(&len, 2, buf);
|
|
|
|
d->DIAG[0] = buf[0];
|
|
d->DIAG[1] = buf[1];
|
|
d->DIAG[2] = '\0';
|
|
|
|
if (len > 3) {
|
|
d->ADDINFO = (char *) s_malloc((size_t) (len - 3 + 1));
|
|
MemCpy(d->ADDINFO, (char *) (buf + 2), len - 3);
|
|
d->ADDINFO[len - 3] = '\0';
|
|
} else
|
|
d->ADDINFO = NULL;
|
|
|
|
d->SURROGATE = buf[len - 1];
|
|
|
|
*diag = d;
|
|
|
|
return (buf + len + 1);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
#define continueBit 0x80
|
|
#define dataMask 0x7F
|
|
#define dataBits 7
|
|
|
|
char *writeCompressedInteger(unsigned long num, char *buf, long *len)
|
|
/* write a binary integer in the format described on p. 40.
|
|
this might be sped up
|
|
*/
|
|
{
|
|
char byte;
|
|
unsigned long i;
|
|
unsigned long size;
|
|
|
|
size = writtenCompressedIntSize(num);
|
|
CHECK_FOR_SPACE_LEFT(size, len);
|
|
|
|
for (i = size - 1; i != 0; i--) {
|
|
byte = num & dataMask;
|
|
if (i != (size - 1)) /* turn on continue bit */
|
|
byte = (char) (byte | continueBit);
|
|
buf[i] = byte;
|
|
num = num >> dataBits; /* don't and here */
|
|
}
|
|
|
|
return (buf + size);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *readCompressedInteger(unsigned long *num, char *buf)
|
|
/* read a binary integer in the format described on p. 40.
|
|
this might be sped up
|
|
*/
|
|
{
|
|
long i = 0;
|
|
unsigned char byte;
|
|
|
|
*num = 0;
|
|
|
|
do {
|
|
byte = buf[i++];
|
|
*num = *num << dataBits;
|
|
*num += (byte & dataMask);
|
|
}
|
|
while (byte & continueBit);
|
|
|
|
return (buf + i);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
#define pad 128 /* high bit is set */
|
|
|
|
char *writeCompressedIntWithPadding(unsigned long num,
|
|
unsigned long size,
|
|
char *buffer,
|
|
long *len)
|
|
/* Like writeCompressedInteger, except writes padding (128) to make
|
|
sure that size bytes are used. This can be read correctly by
|
|
readCompressedInteger()
|
|
*/
|
|
{
|
|
char *buf = buffer;
|
|
unsigned long needed, padding;
|
|
long i;
|
|
|
|
CHECK_FOR_SPACE_LEFT(size, len);
|
|
|
|
needed = writtenCompressedIntSize(num);
|
|
padding = size - needed;
|
|
i = padding - 1;
|
|
|
|
for (i = padding - 1; i >= 0; i--) {
|
|
buf[i] = pad;
|
|
}
|
|
|
|
buf = writeCompressedInteger(num, buf + padding, len);
|
|
|
|
return (buf);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
unsigned long writtenCompressedIntSize(unsigned long num)
|
|
/* return the number of bytes needed to represent the value num in
|
|
compressed format. currently limited to 4 bytes
|
|
*/
|
|
{
|
|
if (num < CompressedInt1Byte)
|
|
return (1);
|
|
else if (num < CompressedInt2Byte)
|
|
return (2);
|
|
else if (num < CompressedInt3Byte)
|
|
return (3);
|
|
else
|
|
return (4);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *writeTag(data_tag tag, char *buf, long *len)
|
|
/* write out a data tag */
|
|
{
|
|
return (writeCompressedInteger(tag, buf, len));
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *readTag(data_tag *tag, char *buf)
|
|
/* read a data tag */
|
|
{
|
|
return (readCompressedInteger(tag, buf));
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
unsigned long writtenTagSize(data_tag tag)
|
|
{
|
|
return (writtenCompressedIntSize(tag));
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
data_tag peekTag(char *buf)
|
|
/* read a data tag without advancing the buffer */
|
|
{
|
|
data_tag tag;
|
|
|
|
readTag(&tag, buf);
|
|
return (tag);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
any *makeAny(unsigned long size, char *data)
|
|
{
|
|
any *a = (any *) s_malloc((size_t) sizeof(any));
|
|
|
|
a->size = size;
|
|
a->bytes = data;
|
|
return (a);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
void freeAny(any *a)
|
|
/* destroy an any and its associated data. Assumes a->bytes was
|
|
allocated using the s_malloc family of libraries
|
|
*/
|
|
{
|
|
if (a != NULL) {
|
|
if (a->bytes != NULL)
|
|
s_free(a->bytes);
|
|
s_free(a);
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
any *duplicateAny(any *a)
|
|
{
|
|
any *copy = NULL;
|
|
|
|
if (a == NULL)
|
|
return (NULL);
|
|
|
|
copy = (any *) s_malloc((size_t) sizeof(any));
|
|
|
|
copy->size = a->size;
|
|
if (a->bytes == NULL)
|
|
copy->bytes = NULL;
|
|
else {
|
|
copy->bytes = (char *) s_malloc((size_t) copy->size);
|
|
MemCpy(copy->bytes, a->bytes, copy->size);
|
|
}
|
|
return (copy);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *writeAny(any *a, data_tag tag, char *buffer, long *len)
|
|
/* write an any + tag and size info */
|
|
{
|
|
char *buf = buffer;
|
|
|
|
if (a == NULL) /* handle unspecified optional args */
|
|
return (buf);
|
|
|
|
/* write the tags */
|
|
buf = writeTag(tag, buf, len);
|
|
buf = writeCompressedInteger(a->size, buf, len);
|
|
|
|
/* write the bytes */
|
|
CHECK_FOR_SPACE_LEFT(a->size, len);
|
|
MemCpy(buf, a->bytes, a->size);
|
|
|
|
return (buf + a->size);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *readAny(any **anAny, char *buffer)
|
|
/* read an any + tag and size info */
|
|
{
|
|
char *buf;
|
|
any *a;
|
|
data_tag tag;
|
|
|
|
a = (any *) s_malloc((size_t) sizeof(any));
|
|
|
|
buf = buffer;
|
|
|
|
buf = readTag(&tag, buf);
|
|
|
|
buf = readCompressedInteger(&a->size, buf);
|
|
|
|
/* now simply copy the bytes */
|
|
a->bytes = (char *) s_malloc((size_t) a->size);
|
|
MemCpy(a->bytes, buf, a->size);
|
|
*anAny = a;
|
|
|
|
return (buf + a->size);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
unsigned long writtenAnySize(data_tag tag, any *a)
|
|
{
|
|
unsigned long size;
|
|
|
|
if (a == NULL)
|
|
return (0);
|
|
|
|
size = writtenTagSize(tag);
|
|
size += writtenCompressedIntSize(a->size);
|
|
size += a->size;
|
|
return (size);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
any *stringToAny(char *s)
|
|
{
|
|
any *a = NULL;
|
|
|
|
if (s == NULL)
|
|
return (NULL);
|
|
|
|
a = (any *) s_malloc((size_t) sizeof(any));
|
|
|
|
a->size = strlen(s);
|
|
a->bytes = (char *) s_malloc((size_t) a->size);
|
|
MemCpy(a->bytes, s, a->size);
|
|
return (a);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *anyToString(any *a)
|
|
{
|
|
char *s = NULL;
|
|
|
|
if (a == NULL)
|
|
return (NULL);
|
|
|
|
s = s_malloc((size_t) (a->size + 1));
|
|
MemCpy(s, a->bytes, a->size);
|
|
s[a->size] = '\0';
|
|
return (s);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *writeString(char *s, data_tag tag, char *buffer, long *len)
|
|
/* Write a C style string. The terminating null is not written.
|
|
This function is not part of the Z39.50 spec. It is provided
|
|
for the convenience of those wishing to pass C strings in
|
|
the place of an any.
|
|
*/
|
|
{
|
|
char *buf = buffer;
|
|
any *data = NULL;
|
|
|
|
if (s == NULL)
|
|
return (buffer); /* handle unused optional item before making an any */
|
|
data = (any *) s_malloc((size_t) sizeof(any));
|
|
|
|
data->size = strlen(s);
|
|
data->bytes = s; /* save a copy here by not using stringToAny() */
|
|
buf = writeAny(data, tag, buf, len);
|
|
s_free(data); /* don't use freeAny() since it will free s too */
|
|
return (buf);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *readString(char **s, char *buffer)
|
|
/* Read an any and convert it into a C style string.
|
|
This function is not part of the Z39.50 spec. It is provided
|
|
for the convenience of those wishing to pass C strings in
|
|
the place of an any.
|
|
*/
|
|
{
|
|
any *data = NULL;
|
|
char *buf = readAny(&data, buffer);
|
|
|
|
*s = anyToString(data);
|
|
freeAny(data);
|
|
return (buf);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
unsigned long writtenStringSize(data_tag tag, char *s)
|
|
{
|
|
unsigned long size;
|
|
|
|
if (s == NULL)
|
|
return (0);
|
|
|
|
size = writtenTagSize(tag);
|
|
size += writtenCompressedIntSize(size);
|
|
size += strlen(s);
|
|
return (size);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
any *longToAny(long num)
|
|
/* a convenience function */
|
|
{
|
|
char s[40];
|
|
|
|
sprintf(s, "%ld", num);
|
|
|
|
return (stringToAny(s));
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
long anyToLong(any *a)
|
|
/* a convenience function */
|
|
{
|
|
long num;
|
|
char *str = NULL;
|
|
|
|
str = anyToString(a);
|
|
sscanf(str, "%ld", &num); /* could check the result and return
|
|
an error */
|
|
s_free(str);
|
|
return (num);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
#define bitsPerByte 8
|
|
|
|
bit_map *makeBitMap(unsigned long numBits, ...)
|
|
/* construct and return a bitmap with numBits elements */
|
|
{
|
|
va_list ap;
|
|
unsigned long i, j;
|
|
bit_map *bm = NULL;
|
|
|
|
LYva_start(ap, numBits);
|
|
|
|
bm = (bit_map *) s_malloc((size_t) sizeof(bit_map));
|
|
|
|
bm->size = (unsigned long) (ceil((double) numBits / bitsPerByte));
|
|
bm->bytes = (char *) s_malloc((size_t) bm->size);
|
|
|
|
/* fill up the bits */
|
|
for (i = 0; i < bm->size; i++) /* iterate over bytes */
|
|
{
|
|
char byte = 0;
|
|
|
|
for (j = 0; j < bitsPerByte; j++) /* iterate over bits */
|
|
{
|
|
if ((i * bitsPerByte + j) < numBits) {
|
|
boolean bit = false;
|
|
|
|
bit = (boolean) va_arg(ap, boolean);
|
|
|
|
if (bit) {
|
|
byte = byte | (1 << (bitsPerByte - j - 1));
|
|
}
|
|
}
|
|
}
|
|
bm->bytes[i] = byte;
|
|
}
|
|
|
|
va_end(ap);
|
|
return (bm);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
void freeBitMap(bit_map *bm)
|
|
/* destroy a bit map created by makeBitMap() */
|
|
{
|
|
s_free(bm->bytes);
|
|
s_free(bm);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
/* use this routine to interpret a bit map. pos specifies the bit
|
|
number. bit 0 is the Leftmost bit of the first byte.
|
|
Could do bounds checking.
|
|
*/
|
|
|
|
boolean bitAtPos(unsigned long pos, bit_map *bm)
|
|
{
|
|
if (pos > bm->size * bitsPerByte)
|
|
return false;
|
|
else
|
|
return ((bm->bytes[(pos / bitsPerByte)] &
|
|
(0x80 >> (pos % bitsPerByte))) ?
|
|
true : false);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *writeBitMap(bit_map *bm, data_tag tag, char *buffer, long *len)
|
|
/* write a bitmap + type and size info */
|
|
{
|
|
return (writeAny((any *) bm, tag, buffer, len));
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *readBitMap(bit_map **bm, char *buffer)
|
|
/* read a bitmap + type and size info */
|
|
{
|
|
char *c;
|
|
|
|
c = readAny((any **) bm, buffer);
|
|
return (c);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *writeByte(unsigned long byte, char *buf, long *len)
|
|
{
|
|
CHECK_FOR_SPACE_LEFT(1, len);
|
|
buf[0] = byte & 0xFF; /* we really only want the first byte */
|
|
return (buf + 1);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *readByte(unsigned char *byte, char *buf)
|
|
{
|
|
*byte = buf[0];
|
|
return (buf + 1);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *writeBoolean(boolean flag, char *buf, long *len)
|
|
{
|
|
return (writeByte(flag, buf, len));
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *readBoolean(boolean *flag, char *buffer)
|
|
{
|
|
unsigned char byte;
|
|
char *buf = readByte(&byte, buffer);
|
|
|
|
*flag = (byte == true) ? true : false;
|
|
return (buf);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *writePDUType(pdu_type pduType, char *buf, long *len)
|
|
/* PDUType is a single byte */
|
|
{
|
|
return (writeBinaryInteger((long) pduType, (unsigned long) 1, buf, len));
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *readPDUType(pdu_type *pduType, char *buf)
|
|
/* PDUType is a single byte */
|
|
{
|
|
return (readBinaryInteger((long *) pduType, (unsigned long) 1, buf));
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
pdu_type peekPDUType(char *buf)
|
|
/* read the next pdu without advancing the buffer, Note that this
|
|
function is to be used on a buffer that is known to contain an
|
|
APDU. The pdu_type is written HEADER_LEN bytes into the buffer
|
|
*/
|
|
{
|
|
pdu_type pdu;
|
|
|
|
readPDUType(&pdu, buf + HEADER_LEN);
|
|
return (pdu);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
#define BINARY_INTEGER_BYTES sizeof(long) /* the number of bytes used by
|
|
a "binary integer" */
|
|
char *writeBinaryInteger(long num, unsigned long size, char *buf, long *len)
|
|
/* write out first size bytes of num - no type info
|
|
XXX should this take unsigned longs instead ??? */
|
|
{
|
|
long i;
|
|
char byte;
|
|
|
|
if (size < 1 || size > BINARY_INTEGER_BYTES)
|
|
return (NULL); /* error */
|
|
|
|
CHECK_FOR_SPACE_LEFT(size, len);
|
|
|
|
for (i = size - 1; i >= 0; i--) {
|
|
byte = (char) (num & 255);
|
|
buf[i] = byte;
|
|
num = num >> bitsPerByte; /* don't and here */
|
|
}
|
|
|
|
return (buf + size);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *readBinaryInteger(long *num, unsigned long size, char *buf)
|
|
/* read in first size bytes of num - no type info
|
|
XXX this should take unsigned longs instead !!! */
|
|
{
|
|
unsigned long i;
|
|
unsigned char byte;
|
|
|
|
if (size < 1 || size > BINARY_INTEGER_BYTES)
|
|
return (buf); /* error */
|
|
*num = 0;
|
|
|
|
for (i = 0; i < size; i++) {
|
|
byte = buf[i];
|
|
*num = *num << bitsPerByte;
|
|
*num += byte;
|
|
}
|
|
|
|
return (buf + size);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
unsigned long writtenCompressedBinIntSize(long num)
|
|
/* return the number of bytes needed to represent the value num.
|
|
currently limited to max of 4 bytes
|
|
Only compresses for positive nums - negatives get whole 4 bytes
|
|
*/
|
|
{
|
|
if (num < 0L)
|
|
return (4);
|
|
else if (num < 256L) /* 2**8 */
|
|
return (1);
|
|
else if (num < 65536L) /* 2**16 */
|
|
return (2);
|
|
else if (num < 16777216L) /* 2**24 */
|
|
return (3);
|
|
else
|
|
return (4);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *writeNum(long num, data_tag tag, char *buffer, long *len)
|
|
/* write a binary integer + size and tag info */
|
|
{
|
|
char *buf = buffer;
|
|
long size = writtenCompressedBinIntSize(num);
|
|
|
|
if (num == UNUSED)
|
|
return (buffer);
|
|
|
|
buf = writeTag(tag, buf, len);
|
|
buf = writeCompressedInteger(size, buf, len);
|
|
buf = writeBinaryInteger(num, (unsigned long) size, buf, len);
|
|
return (buf);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *readNum(long *num, char *buffer)
|
|
/* read a binary integer + size and tag info */
|
|
{
|
|
char *buf = buffer;
|
|
data_tag tag;
|
|
unsigned long size;
|
|
unsigned long val;
|
|
|
|
buf = readTag(&tag, buf);
|
|
buf = readCompressedInteger(&val, buf);
|
|
size = (unsigned long) val;
|
|
buf = readBinaryInteger(num, size, buf);
|
|
return (buf);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
unsigned long writtenNumSize(data_tag tag, long num)
|
|
{
|
|
long dataSize = writtenCompressedBinIntSize(num);
|
|
long size;
|
|
|
|
size = writtenTagSize(tag); /* space for the tag */
|
|
size += writtenCompressedIntSize(dataSize); /* space for the size */
|
|
size += dataSize; /* space for the data */
|
|
|
|
return (size);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
typedef void (voidfunc) (void *);
|
|
|
|
void doList(void **list, voidfunc * func)
|
|
/* call func on each element of the NULL terminated list of pointers */
|
|
{
|
|
register long i;
|
|
register void *ptr = NULL;
|
|
|
|
if (list == NULL)
|
|
return;
|
|
for (i = 0, ptr = list[i]; ptr != NULL; ptr = list[++i])
|
|
(*func) (ptr);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *writeProtocolVersion(char *buf, long *len)
|
|
/* write a bitmap describing the protocols available */
|
|
{
|
|
static bit_map *version = NULL;
|
|
|
|
if (version == NULL) {
|
|
version = makeBitMap((unsigned long) 1, true); /* version 1! */
|
|
}
|
|
|
|
return (writeBitMap(version, DT_ProtocolVersion, buf, len));
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *defaultImplementationID(void)
|
|
{
|
|
static char ImplementationID[] = "TMC";
|
|
|
|
return (ImplementationID);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *defaultImplementationName(void)
|
|
{
|
|
static char ImplementationName[] = "Thinking Machines Corporation Z39.50";
|
|
|
|
return (ImplementationName);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *defaultImplementationVersion(void)
|
|
{
|
|
static char ImplementationVersion[] = "2.0A";
|
|
|
|
return (ImplementationVersion);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
/*
|
|
* Routines originally from ZType1.c -- FM
|
|
*
|
|
*----------------------------------------------------------------------*/
|
|
/* WIDE AREA INFORMATION SERVER SOFTWARE:
|
|
* No guarantees or restrictions. See the readme file for the full standard
|
|
* disclaimer.
|
|
*
|
|
* 3.26.90 Harry Morris, morris@think.com
|
|
* 4.11.90 HWM - generalized conditional includes (see c-dialect.h)
|
|
*/
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
query_term *makeAttributeTerm(char *use,
|
|
char *relation,
|
|
char *position,
|
|
char *structure,
|
|
char *truncation,
|
|
char *completeness,
|
|
any *term)
|
|
{
|
|
query_term *qt = (query_term *) s_malloc((size_t) sizeof(query_term));
|
|
|
|
qt->TermType = TT_Attribute;
|
|
|
|
/* copy in the attributes */
|
|
LYStrNCpy(qt->Use, use, ATTRIBUTE_SIZE);
|
|
LYStrNCpy(qt->Relation, relation, ATTRIBUTE_SIZE);
|
|
LYStrNCpy(qt->Position, position, ATTRIBUTE_SIZE);
|
|
LYStrNCpy(qt->Structure, structure, ATTRIBUTE_SIZE);
|
|
LYStrNCpy(qt->Truncation, truncation, ATTRIBUTE_SIZE);
|
|
LYStrNCpy(qt->Completeness, completeness, ATTRIBUTE_SIZE);
|
|
|
|
qt->Term = duplicateAny(term);
|
|
|
|
qt->ResultSetID = NULL;
|
|
|
|
return (qt);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
query_term *makeResultSetTerm(any *resultSet)
|
|
{
|
|
query_term *qt = (query_term *) s_malloc((size_t) sizeof(query_term));
|
|
|
|
qt->TermType = TT_ResultSetID;
|
|
|
|
qt->ResultSetID = duplicateAny(resultSet);
|
|
|
|
qt->Term = NULL;
|
|
|
|
return (qt);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
query_term *makeOperatorTerm(char *operatorCode)
|
|
{
|
|
query_term *qt = (query_term *) s_malloc((size_t) sizeof(query_term));
|
|
|
|
qt->TermType = TT_Operator;
|
|
|
|
LYStrNCpy(qt->Operator, operatorCode, OPERATOR_SIZE);
|
|
|
|
qt->Term = NULL;
|
|
qt->ResultSetID = NULL;
|
|
|
|
return (qt);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
void freeTerm(void *param)
|
|
{
|
|
query_term *qt = (query_term *) param;
|
|
|
|
switch (qt->TermType) {
|
|
case TT_Attribute:
|
|
freeAny(qt->Term);
|
|
break;
|
|
case TT_ResultSetID:
|
|
freeAny(qt->ResultSetID);
|
|
break;
|
|
case TT_Operator:
|
|
/* do nothing */
|
|
break;
|
|
default:
|
|
panic("Implementation error: Unknown term type %ld",
|
|
qt->TermType);
|
|
break;
|
|
}
|
|
s_free(qt);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
#define ATTRIBUTE_LIST_SIZE ATTRIBUTE_SIZE * 6
|
|
#define AT_DELIMITER " "
|
|
|
|
char *writeQueryTerm(query_term *qt, char *buffer, long *len)
|
|
{
|
|
char *buf = buffer;
|
|
char attributes[ATTRIBUTE_LIST_SIZE];
|
|
|
|
switch (qt->TermType) {
|
|
case TT_Attribute:
|
|
LYStrNCpy(attributes, qt->Use, ATTRIBUTE_LIST_SIZE);
|
|
s_strncat(attributes, AT_DELIMITER, sizeof(AT_DELIMITER) + 1, ATTRIBUTE_LIST_SIZE);
|
|
s_strncat(attributes, qt->Relation, ATTRIBUTE_SIZE, ATTRIBUTE_LIST_SIZE);
|
|
s_strncat(attributes, AT_DELIMITER, sizeof(AT_DELIMITER) + 1, ATTRIBUTE_LIST_SIZE);
|
|
s_strncat(attributes, qt->Position, ATTRIBUTE_SIZE, ATTRIBUTE_LIST_SIZE);
|
|
s_strncat(attributes, AT_DELIMITER, sizeof(AT_DELIMITER) + 1, ATTRIBUTE_LIST_SIZE);
|
|
s_strncat(attributes, qt->Structure, ATTRIBUTE_SIZE, ATTRIBUTE_LIST_SIZE);
|
|
s_strncat(attributes, AT_DELIMITER, sizeof(AT_DELIMITER) + 1, ATTRIBUTE_LIST_SIZE);
|
|
s_strncat(attributes, qt->Truncation, ATTRIBUTE_SIZE, ATTRIBUTE_LIST_SIZE);
|
|
s_strncat(attributes, AT_DELIMITER, sizeof(AT_DELIMITER) + 1, ATTRIBUTE_LIST_SIZE);
|
|
s_strncat(attributes, qt->Completeness, ATTRIBUTE_SIZE, ATTRIBUTE_LIST_SIZE);
|
|
buf = writeString(attributes, DT_AttributeList, buf, len);
|
|
buf = writeAny(qt->Term, DT_Term, buf, len);
|
|
break;
|
|
case TT_ResultSetID:
|
|
buf = writeAny(qt->ResultSetID, DT_ResultSetID, buf, len);
|
|
break;
|
|
case TT_Operator:
|
|
buf = writeString(qt->Operator, DT_Operator, buf, len);
|
|
break;
|
|
default:
|
|
panic("Implementation error: Unknown term type %ld",
|
|
qt->TermType);
|
|
break;
|
|
}
|
|
|
|
return (buf);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *readQueryTerm(query_term **qt, char *buffer)
|
|
{
|
|
char *buf = buffer;
|
|
char *attributeList = NULL;
|
|
char *operator = NULL;
|
|
any *term;
|
|
char *use = NULL;
|
|
char *relation = NULL;
|
|
char *position = NULL;
|
|
char *structure = NULL;
|
|
char *truncation = NULL;
|
|
char *completeness;
|
|
any *resultSetID = NULL;
|
|
data_tag tag;
|
|
|
|
tag = peekTag(buffer);
|
|
|
|
switch (tag) {
|
|
case DT_AttributeList:
|
|
buf = readString(&attributeList, buf);
|
|
buf = readAny(&term, buf);
|
|
use = strtok(attributeList, AT_DELIMITER);
|
|
relation = strtok(NULL, AT_DELIMITER);
|
|
position = strtok(NULL, AT_DELIMITER);
|
|
structure = strtok(NULL, AT_DELIMITER);
|
|
truncation = strtok(NULL, AT_DELIMITER);
|
|
completeness = strtok(NULL, AT_DELIMITER);
|
|
*qt = makeAttributeTerm(use, relation, position, structure,
|
|
truncation, completeness, term);
|
|
s_free(attributeList);
|
|
freeAny(term);
|
|
break;
|
|
case DT_ResultSetID:
|
|
buf = readAny(&resultSetID, buf);
|
|
*qt = makeResultSetTerm(resultSetID);
|
|
freeAny(resultSetID);
|
|
break;
|
|
case DT_Operator:
|
|
buf = readString(&operator, buf);
|
|
*qt = makeOperatorTerm(operator);
|
|
s_free(operator);
|
|
break;
|
|
default:
|
|
REPORT_READ_ERROR(buf);
|
|
break;
|
|
}
|
|
|
|
return (buf);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
static unsigned long getQueryTermSize(query_term *qt);
|
|
|
|
static unsigned long getQueryTermSize(query_term *qt)
|
|
/* figure out how many bytes it will take to write this query */
|
|
{
|
|
unsigned long size = 0;
|
|
static char attributes[] = "11 22 33 44 55 66"; /* we just need this to
|
|
|
|
calculate its written
|
|
size */
|
|
|
|
switch (qt->TermType) {
|
|
case TT_Attribute:
|
|
size = writtenStringSize(DT_AttributeList, attributes);
|
|
size += writtenAnySize(DT_Term, qt->Term);
|
|
break;
|
|
case TT_ResultSetID:
|
|
size = writtenAnySize(DT_ResultSetID, qt->ResultSetID);
|
|
break;
|
|
case TT_Operator:
|
|
size = writtenStringSize(DT_Operator, qt->Operator);
|
|
break;
|
|
default:
|
|
panic("Implementation error: Unknown term type %ld",
|
|
qt->TermType);
|
|
break;
|
|
}
|
|
|
|
return (size);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
/* A query is simply a null terminated list of query terms. For
|
|
transmission, a query is written into an any which is sent as
|
|
the user information field. */
|
|
|
|
any *writeQuery(query_term **terms)
|
|
{
|
|
any *info = NULL;
|
|
char *writePos = NULL;
|
|
char *data = NULL;
|
|
unsigned long size = 0;
|
|
long remaining = 0;
|
|
long i;
|
|
query_term *qt = NULL;
|
|
|
|
if (terms == NULL)
|
|
return (NULL);
|
|
|
|
/* calculate the size of write buffer */
|
|
for (i = 0, qt = terms[i]; qt != NULL; qt = terms[++i])
|
|
size += getQueryTermSize(qt);
|
|
|
|
data = (char *) s_malloc((size_t) size);
|
|
|
|
/* write the terms */
|
|
writePos = data;
|
|
remaining = size;
|
|
for (i = 0, qt = terms[i]; qt != NULL; qt = terms[++i])
|
|
writePos = writeQueryTerm(qt, writePos, &remaining);
|
|
|
|
info = makeAny(size, data);
|
|
|
|
return (info);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
query_term **readQuery(any *info)
|
|
{
|
|
char *readPos = info->bytes;
|
|
query_term **terms = NULL;
|
|
query_term *qt = NULL;
|
|
long numTerms = 0L;
|
|
char tmp[100];
|
|
|
|
sprintf(tmp, "readquery: bytes: %ld", info->size);
|
|
log_write(tmp);
|
|
|
|
while (readPos < info->bytes + info->size) {
|
|
readPos = readQueryTerm(&qt, readPos);
|
|
|
|
if (terms == NULL) {
|
|
terms = (query_term **) s_malloc((size_t) (sizeof(query_term *) * 2));
|
|
} else {
|
|
terms =
|
|
(query_term **) s_realloc((char *) terms,
|
|
(size_t) (sizeof(query_term *) *
|
|
(numTerms + 2)));
|
|
}
|
|
if (qt == NULL)
|
|
log_write("qt = null");
|
|
terms[numTerms++] = qt;
|
|
terms[numTerms] = NULL;
|
|
}
|
|
|
|
return (terms);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
/*
|
|
* Routines originally from panic.c -- FM
|
|
*
|
|
*----------------------------------------------------------------------*/
|
|
/* WIDE AREA INFORMATION SERVER SOFTWARE:
|
|
* No guarantees or restrictions. See the readme file for the full standard
|
|
* disclaimer.
|
|
*
|
|
* Morris@think.com
|
|
*/
|
|
|
|
/* panic is an error system interface. On the Mac, it will pop
|
|
* up a little window to explain the problem.
|
|
* On a unix box, it will print out the error and call perror()
|
|
*/
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
static void exitAction(long error);
|
|
|
|
static void exitAction(long error GCC_UNUSED)
|
|
{
|
|
exit_immediately(EXIT_SUCCESS);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
#define PANIC_HEADER "Fatal Error: "
|
|
|
|
void panic(char *format, ...)
|
|
{
|
|
va_list ap; /* the variable arguments */
|
|
|
|
fprintf(stderr, PANIC_HEADER);
|
|
LYva_start(ap, format); /* init ap */
|
|
vfprintf(stderr, format, ap); /* print the contents */
|
|
va_end(ap); /* free ap */
|
|
fflush(stderr);
|
|
|
|
exitAction(0);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
/*
|
|
* Routines originally from cutil.c -- FM
|
|
*
|
|
*----------------------------------------------------------------------*/
|
|
/* Wide AREA INFORMATION SERVER SOFTWARE
|
|
* No guarantees or restrictions. See the readme file for the full standard
|
|
* disclaimer.
|
|
*
|
|
* 3.26.90 Harry Morris, morris@think.com
|
|
* 4.11.90 HWM - generalized conditional includes (see c-dialect.h)
|
|
*/
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
void fs_checkPtr(void *ptr)
|
|
/* If the ptr is NULL, give an error */
|
|
{
|
|
if (ptr == NULL)
|
|
panic("checkPtr found a NULL pointer");
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
void *fs_malloc(size_t size)
|
|
/* does safety checks and optional accounting */
|
|
{
|
|
register void *ptr = NULL;
|
|
|
|
ptr = (void *) calloc((size_t) size, (size_t) 1);
|
|
s_checkPtr(ptr);
|
|
|
|
return (ptr);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
void *fs_realloc(void *ptr, size_t size)
|
|
/* does safety checks and optional accounting
|
|
note - we don't know how big ptr's memory is, so we can't ensure
|
|
that any new memory allocated is NULLed!
|
|
*/
|
|
{
|
|
register void *nptr = NULL;
|
|
|
|
if (ptr == NULL) /* this is really a malloc */
|
|
return (s_malloc(size));
|
|
|
|
nptr = (void *) realloc(ptr, size);
|
|
s_checkPtr(ptr);
|
|
|
|
return (nptr);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
void fs_free(void *ptr)
|
|
/* does safety checks and optional accounting */
|
|
{
|
|
if (ptr != NULL) /* some non-ansi compilers/os's can't handle freeing null */
|
|
{ /* if we knew the size of this block of memory, we could clear it - oh well */
|
|
free(ptr);
|
|
ptr = NULL;
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *s_strdup(char *s)
|
|
|
|
/* return a copy of s. This is identical to the standard library routine
|
|
strdup(), except that it is safe. If s == NULL or malloc fails,
|
|
appropriate action is taken.
|
|
*/
|
|
{
|
|
unsigned long len;
|
|
char *copy = NULL;
|
|
|
|
if (s == NULL) /* safety check to postpone stupid errors */
|
|
return (NULL);
|
|
|
|
len = strlen(s); /* length of string - terminator */
|
|
copy = (char *) s_malloc((size_t) (sizeof(char) * (len + 1)));
|
|
|
|
StrNCpy(copy, s, len + 1);
|
|
return (copy);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char *fs_strncat(char *dst, char *src, size_t maxToAdd, size_t maxTotal)
|
|
|
|
/* like strncat, except the fourth argument limits the maximum total
|
|
length of the resulting string
|
|
*/
|
|
{
|
|
size_t dstSize = strlen(dst);
|
|
size_t srcSize = strlen(src);
|
|
|
|
if (dstSize + srcSize < maxTotal) /* use regular old strncat */
|
|
return (StrNCat(dst, src, maxToAdd));
|
|
else {
|
|
size_t truncateTo = maxTotal - dstSize - 1;
|
|
char saveChar = src[truncateTo];
|
|
char *result = NULL;
|
|
|
|
src[truncateTo] = '\0';
|
|
result = StrNCat(dst, src, maxToAdd);
|
|
src[truncateTo] = saveChar;
|
|
return (result);
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
char char_downcase(unsigned long long_ch)
|
|
{
|
|
unsigned char ch = long_ch & 0xFF; /* just want one byte */
|
|
|
|
/* when ansi is the way of the world, this can be tolower */
|
|
return (((ch >= 'A') && (ch <= 'Z')) ? (ch + 'a' - 'A') : ch);
|
|
}
|
|
|
|
char *string_downcase(char *word)
|
|
{
|
|
long i = 0;
|
|
|
|
while (word[i] != '\0') {
|
|
word[i] = char_downcase((unsigned long) word[i]);
|
|
i++;
|
|
}
|
|
return (word);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
#endif /* VMS */
|