The file contains the following routines:
It also contains some notes on sorting that might be useful.
/************************************************************************/ /* */ /* CHECK_MONTH - Parse a month name */ /* */ /* Calling Format: */ /* */ /* status = CHECK_MONTH (inp_ptr, num_ptr, name_ptr_ptr); */ /* */ /* Where: */ /* */ /* status = PSP_FALSE if month was valid. */ /* = PSP_TRUE otherwise. */ /* inp_ptr = Pointer to month abbreviation name to validate */ /* num_ptr = Pointer to int to hold month number (1-12) */ /* name_ptr_ptr = Pointer to char* pointer to point to month name */ /* */ /* This routine checks that the first three characters in the */ /* specified buffer represent a valid "Locus month" abbreviation. */ /* It also optionally returns the 'number' of the month (1-12 for */ /* January to December, and 0 otherwise), and/or a pointer to the */ /* full month name. Either num_ptr or name_ptr_ptr may be null. */ /* */ /************************************************************************/
/************************************************************************/ /* */ /* CHK_SERIES - Check series name for conflict */ /* */ /* Calling Format: */ /* */ /* status = CHK_SERIES (series_ptr, inpaut_ptr, ficflag, */ /* val_level, prtfil_ptr); */ /* */ /* Where: */ /* */ /* status = PSP_FALSE if series was invalid. */ /* = PSP_TRUE otherwise. */ /* series_ptr = Pointer to series name to check */ /* inpaut_ptr = Pointer to name of author(s) using series */ /* valflag = TRUE if the item is "fiction" */ /* = FALSE otherwise */ /* val_level = Validation level: */ /* -1 - Just count the usage(s) */ /* 1 - Suppress unmatched series */ /* >1 - Don't suppress any messages */ /* prtfil_ptr = Diagnostics file pointer. */ /* */ /* This routine checks to see if the series name(s) are defined */ /* for the specified author(s). */ /* */ /************************************************************************/
/************************************************************************/ /* */ /* CONVERT_SERIES - Convert a series name from external to */ /* internal format */ /* */ /* Calling Format: */ /* */ /* CONVERT_SERIES (inpbuf_ptr, outbuf_ptr, buflen); */ /* */ /* Where: */ /* */ /* inpbuf_ptr = Pointer to input buffer */ /* outbuf_ptr = Pointer to output buffer */ /* buflen = Length of output buffer */ /* */ /************************************************************************/
/************************************************************************/ /* */ /* COUNT_SERIES - Output list of series with zero use count */ /* */ /* Calling Format: */ /* */ /* COUNT_SERIES (prtfil_ptr); */ /* */ /* Where: */ /* */ /* prtfil_ptr = Diagnostics file pointer. */ /* */ /* This routine checks to see if any series has a zero usage count */ /* and, if so, outputs a diagnostic. */ /* */ /************************************************************************/
/************************************************************************/ /* */ /* FIX_SERIES - See if series name is obsolete or inverted and, */ /* if so, fix it. */ /* */ /* Calling Format: */ /* */ /* status = FIX_SERIES (series_ptr, outbuf_ptr, buflen, */ /* prtfil_ptr); */ /* */ /* Where: */ /* */ /* status = PSP_TRUE if series was replaced. */ /* = PSP_FALSE otherwise. */ /* series_ptr = Pointer to series name(s) to check */ /* outbuf_ptr = Pointer to buffer to hold replacement series */ /* buflen = length of buffer at outbuf_ptr */ /* prtfil_ptr = Diagnostics file pointer. */ /* */ /* This routine checks to see if the series name is defined by */ /* a "use" replacement and, if so, replaces it. */ /* */ /************************************************************************/
/************************************************************************/ /* */ /* FREE_SERIES - Free memory allocated to series names */ /* */ /* Calling Format: */ /* */ /* FREE_SERIES (); */ /* */ /* This routine simply checks the series name structures, freeing */ /* up any allocated memory. */ /* */ /************************************************************************/
/************************************************************************/ /* */ /* PARSE_AUTHDATE - Parse date fields in an author record. */ /* */ /* Calling Format: */ /* */ /* status = PARSE_AUTHDATE (dates_ptr, sdate_ptr, edate_ptr, */ /* flflag_ptr, prtfil_ptr); */ /* */ /* Where: */ /* */ /* status = PSP_TRUE if dates parsed OK */ /* = PSP_FALSE otherwise */ /* dates_ptr = Pointer to dates to be parsed */ /* sdate_ptr = Pointer to buffer to hold start date */ /* = NULL if date not required */ /* edate_ptr = Pointer to buffer to hold end date */ /* = NULL if date not required */ /* flflag_ptr = Pointer to buffer to hold "fl." flag */ /* = NULL if flag not required */ /* prtfil_ptr = Diagnostics file pointer. */ /* */ /* This routine checks to see if the dates field in a PSEUD record */ /* contains a date range and, if so, parses it. */ /* */ /************************************************************************/
/************************************************************************/ /* */ /* PARSE_BOOKTYP - Parse fields containing book type/classification */ /* and construct a description from it. */ /* */ /* Calling Format: */ /* */ /* status = PARSE_BOOKTYP (bktype_ptr, bkclass_ptr, buff_ptr, */ /* bufsize, prtfil_ptr); */ /* */ /* Where: */ /* */ /* status = PSP_TRUE if type/classification parsed OK */ /* = PSP_FALSE otherwise */ /* bktype_ptr = Pointer to null-terminated book type */ /* bkclass_ptr = Pointer to null-terminated book type */ /* buff_ptr = Pointer to buffer to append description to */ /* bufsize = Size of output buffer */ /* prtfil_ptr = Diagnostics file pointer. */ /* */ /* This routine checks the book type and classification to see if */ /* each is valid, and constructs a formatted description if asked */ /* to do so. */ /* */ /************************************************************************/
/************************************************************************/ /* */ /* PARSE_ITMTYP - Parse a "Locus" item type */ /* */ /* Calling Format: */ /* */ /* status = PARSE_ITMTYP (type_ptr, str_ptr_ptr, flags_ptr, */ /* category_ptr); */ /* */ /* Where: */ /* */ /* status = PSP_TRUE if item type is valid */ /* = PSP_FALSE otherwise */ /* type_ptr = Pointer to item type string */ /* str_ptr_ptr = Pointer to char* pointer for expanded string */ /* flags_ptr = Pointer to int to hold item type flags: */ /* PRSFLG_VALBOK = Valid for a Book */ /* PRSFLG_VALSTY = Valid for a "story" */ /* category_ptr = Pointer to int to hold category: */ /* PRSCAT_ARTICLE = Item is an article */ /* PRSCAT_BOOK = Item is a book */ /* PRSCAT_FICBOOK = Item is a book of fiction */ /* PRSCAT_FICTION = Item is other fiction */ /* PRSCAT_INTERVIEW = Item is an interview */ /* PRSCAT_MEDIA = Item is media adaptation */ /* PRSCAT_MISC = Item is miscellaneous */ /* PRSCAT_POEM = Item is poem, play, etc. */ /* PRSCAT_REVIEW = Item is a review */ /* */ /* This routine checks the item type to see if it is valid, setting */ /* up any specified return fields as appropriate. */ /* */ /* Note that no error messages are output by this routine. */ /* */ /************************************************************************/
/************************************************************************/ /* */ /* PARSE_SERIES - Parse fields on a DS record and construct a */ /* description from them. */ /* */ /* Calling Format: */ /* */ /* status = PARSE_SERIES (fld_ptr, buff_ptr, bufsize, prtfil_ptr); */ /* */ /* Where: */ /* */ /* status = PSP_TRUE if DS record parsed OK */ /* = PSP_FALSE otherwise */ /* fld_ptr = Array of Pointers to fields on DS record */ /* buff_ptr = Pointer to buffer to append description to */ /* bufsize = Size of output buffer */ /* prtfil_ptr = Diagnostics file pointer. */ /* */ /* This routine checks the fields on a DS record to see if each is */ /* valid, and constructs a formatted description if asked to do so. */ /* */ /************************************************************************/
/************************************************************************/ /* */ /* VAL_BINDING - Validate a Binding field */ /* */ /* Calling Format: */ /* */ /* status = VAL_BINDING (binding_ptr, prtfil_ptr); */ /* */ /* Where: */ /* */ /* status = PSP_FALSE if an "at or near" diagnostic required */ /* = PSP_TRUE otherwise. */ /* binding_ptr = Pointer to binding to be validated */ /* prtfil_ptr = Diagnostics file pointer. */ /* */ /* This routine validates the contents of a binding field. This */ /* may be one of hc = hardback; pb = rack-size paperback; */ /* tp = trade paperback; lp = large paperback (UK only); */ /* ph = pamphlet; bx = boxed set. If an "odd" size then it can be */ /* (US) quarto, octavo, digest, A4, A5 or is in centimetres in the */ /* form {n.m x p.q}, or inches in the form {n.m" x p.q"}. */ /* */ /************************************************************************/
/************************************************************************/ /* */ /* VAL_SERIES - Validate series files */ /* */ /* Calling Format: */ /* */ /* VAL_SERIES (prtfil_ptr); */ /* */ /* Where: */ /* */ /* prtfil_ptr = Diagnostics file pointer. */ /* */ /* This routine validates the contents of the two series files. */ /* */ /************************************************************************/
One of the problems I keep banging my head against is how to use qsort correctly in different sorts of environments, so here are a few pointers:
qsort is passed the address of an array, the number of items in the array, the size of each item in the array, and the name of a routine to compare two items, as in:
qsort (record_idx, record_cnt, sizeof(int), FSORT_COMP_RTN);
where record_idx is an array of int. This is typically used where we have an array of "things" we want to sort but we don't want to sort the things themselves (e.g. long strings) as this is inefficient and hence sort indexes into the arrays. Thus, in this case (taken from Asort) we have:
static char *record_ptr[MAXREC]; static int record_idx[MAXREC];
Records are added to record_ptr and, as we do so, we set each element of record_idx to the value of its subscript (i.e. the 0th element is 0; the 1st is 1 and so on). We then dereference these in the local routine to do the actual comparison, as in:
static int FSORT_COMP_RTN (const void *rec1_ptr,
const void *rec2_ptr)
{
/* Local Data */
int rtnsts; /* Routine status */
char *tmp1_ptr; /* Pointer to first name */
char *tmp2_ptr; /* Pointer to second name */
int index1; /* First array index */
int index2; /* Second array index */
index1 = *(int *)rec1_ptr; /* Dereference 1st pointer */
index2 = *(int *)rec2_ptr; /* and 2nd pointer */
tmp1_ptr = record_ptr[index1]; /* and hence pointer to string */
tmp2_ptr = record_ptr[index2]; /* and hence pointer to string */
rtnsts = strcmp (tmp1_ptr, tmp2_ptr);
/* Compare main fields */
return rtnsts; /* Return status */
}
Note that the routine is specific to the routine/program that uses it because it needs to know how to dereference the subscript (i.e. the record_ptr[index1] above). After the sort, any reference to the main table must now be done via the index table, as in:
for (isub = 0; isub < record_cnt; isub++)
{
tmp1_ptr = record_ptr[record_idx[isub]];
/* Initialize the pointer */ }
If we know that we have relatively few entries in an array containing strings to be sorted "normally" then we can take a shortcut and use the predefined routines CPTR_COMP_RTN and CPTR_COMP_NOCASE in LIB_RTN.C (the former does a case-sensitive sort and the latter a case-insensitive sort) as in:
qsort (entry_ptr, entry_cnt, sizeof(char *), CPTR_COMP_RTN);
from Chron.
As a quick and dirty we can use the same approach for sorting an array of structures where the first element of the structure is the string we want to sort by, as in:
struct abbstr {
char abbrec[ABBSIZ+1]; /* Room for abbreviation */
char lowabb[ABBSIZ+1]; /* and a lower-case version */
} static struct abbstr *abbstr_ptr[MAXABB];
qsort (abbstr_ptr, abbr_cnt, sizeof(char *), CPTR_COMP_RTN);
However, more generally, if we want to sort by some other field in the structure we need to dereference it, as in:
qsort (abbstr_ptr, abbr_cnt, sizeof(char *), LOWABB_COMP_RTN);
static int LOWABB_COMP_RTN (const void *rec1_ptr,
const void *rec2_ptr)
{
/* Local Data */
int rtnsts; /* Routine status */
struct abbstr *abb1_ptr; /* Pointer to first abbreviation */
struct abbstr *abb2_ptr; /* Pointer to second abbreviation */
abb1_ptr = *(struct abbstr **) rec1_ptr; /* Dereference 1st pointer */
abb2_ptr = *(struct abbstr **) rec2_ptr; /* and 2nd pointer */
rtnsts = strcmp (abb1_ptr->lowabb, abb2_ptr->lowabb);
/* Compare names */
return rtnsts; /* Return status */
}
Note that we should be able to combine the above approaches (though I don't seem to have done so anywhere) so that we can sort the same array of structures by multiple fields simultaneously simply by using separate lookup arrays (as in the Asort example above).