/*
**  Copyright 2000-2004 University of Illinois Board of Trustees
**  Copyright 2000-2004 Mark D. Roth
**  All rights reserved.
**
**  query.c - libphclient query code
**
**  Mark D. Roth <roth@feep.net>
*/

#include <internal.h>

#include <errno.h>
#include <ctype.h>

#ifdef STDC_HEADERS
# include <string.h>
# include <stdlib.h>
#endif


/*
** ph_query() - send a query to the PH server and return matching entries.
** returns:
**	number of entries returned	success
**	-1				error (sets errno)
**	PH_ERR_TOOMANY			too many entries matched
**	PH_ERR_NOMATCH			no matching entries
**	PH_ERR_DATAERR			invalid query
*/
int
ph_query(PH *ph, struct ph_fieldselector query[], char *retfields[],
	 ph_entry **entries)
{
	int i;
	size_t sz, buflen = 0;
	char buf[PH_BUF_SIZE];
	int code, entrynum;
	int numresponses = 0, numfields = 0;
	char *field, *value;
	void *ptr;
	ph_entry entry;

#ifdef DEBUG
	puts("==> ph_query()");
	for (i = 0; query[i].pfs_field; i++)
		printf("   query[%d]: %s%c%s\n",
		       i, query[i].pfs_field, query[i].pfs_operation,
		       query[i].pfs_value);
#endif

	/* important initialization */
	*entries = NULL;

	/* construct the query command */
	buflen = sprintf(buf, "query");
	buflen += ph_decode_selectors(query, buf + buflen,
				      sizeof(buf) - buflen);
	if (retfields != NULL)
	{
		buflen = strlcat(buf, " return", sizeof(buf));
		for (i = 0; retfields[i] != NULL; i++)
			buflen += snprintf(buf + buflen, sizeof(buf) - buflen,
					   " %s", retfields[i]);
	}

	/* send the query command */
#ifdef DEBUG
	printf("ph_query(): sending query: \"%s\"\n", buf);
#endif
	if (ph_send_command(ph, "%s", buf) == -1)
		return -1;

	/* read the server response */
	while (ph_get_response(ph, &code, buf, sizeof(buf)) == 0)
	{
#ifdef DEBUG
		printf("top of ph_get_response() loop: code=%d, buf=\"%s\"\n",
		       code, buf);
#endif

		/* check response code */
		if ((code < LR_OK && code > 99)
		    || (code < 0 && !isdigit((int)buf[0])))
			continue;
		if (code == LR_OK || code == LR_NOMATCH)
			return (numresponses > 0
				? numresponses
				: PH_ERR_NOMATCH);
		if (code == LR_TOOMANY)
			return PH_ERR_TOOMANY;
		if (code > 0)
			return PH_ERR_DATAERR;

		/* parse the response text */
		if (ph_parse_response(buf, &entrynum, &field, &value) == -1)
			return -1;

		sz = strspn(field, " ");

		/* first field of a new entry */
		if (entrynum > numresponses)
		{
			/*
			** field name can't be all spaces on first line
			** of a new entry
			*/
			if (sz == strlen(field))
			{
				errno = EINVAL;
				return -1;
			}

			ptr = realloc(*entries,
				      (entrynum + 1) * sizeof(ph_entry));
			if (ptr == NULL)
				return -1;
			*entries = (ph_entry *)ptr;
			memset(&((*entries)[entrynum - 1]), 0,
			       (entrynum - numresponses + 1) * sizeof(ph_entry));
			numresponses = entrynum;
			numfields = 0;
		}

		value += strspn(value, " ");

		/*
		** if field name is all spaces, value should be appended
		** to last field
		*/
		if (sz == strlen(field))
		{
#ifdef DEBUG
			printf("old value: (length = %d) \"%s\"\n",
			       strlen(entry[numfields - 1].pfv_value),
			       entry[numfields - 1].pfv_value);
			printf("new value: (length = %d) \"%s\"\n",
			       strlen(value), value);
#endif
			sz = strlen(entry[numfields - 1].pfv_value) + strlen(value) + 2;
			ptr = realloc(entry[numfields - 1].pfv_value, sz);
			if (ptr == NULL)
				return -1;
			entry[numfields - 1].pfv_value = (char *)ptr;
			strlcat(entry[numfields - 1].pfv_value, "\n", sz);
			if (value[0] != '\0')
				strlcat(entry[numfields - 1].pfv_value,
					value, sz);
#ifdef DEBUG
			printf("combined value: (length = %d) \"%s\"\n",
			       strlen(entry[numfields - 1].pfv_value),
			       entry[numfields - 1].pfv_value);
#endif
		}
		else
		{
			/* new field */
			ptr = realloc((*entries)[entrynum - 1],
				      (numfields + 2) * sizeof(struct ph_fieldvalue));
			if (ptr == NULL)
				return -1;
			(*entries)[entrynum - 1] = (struct ph_fieldvalue *)ptr;
			entry = (*entries)[entrynum - 1];
			memset(entry + numfields, 0,
			       2 * sizeof(struct ph_fieldvalue));
			entry[numfields].pfv_field = strdup(field + sz);
			entry[numfields].pfv_value = strdup(value);
			if (entry[numfields].pfv_field == NULL
			    || entry[numfields].pfv_value == NULL)
				return -1;
			entry[numfields].pfv_code = code;
			numfields++;
		}
	}

#ifdef DEBUG
	puts("ph_query(): reached end of ph_get_response() loop!");
#endif
	return -1;
}


/*
** ph_free_entries() - free memory returned by ph_query().
*/
void
ph_free_entries(ph_entry *entries)
{
	int i, j;

	if (entries == NULL)
		return;

	for (i = 0; entries[i] != NULL; i++)
	{
		for (j = 0; entries[i][j].pfv_field != NULL; j++)
		{
			free(entries[i][j].pfv_field);
			free(entries[i][j].pfv_value);
		}
		free(entries[i]);
	}
	free(entries);
}




syntax highlighted by Code2HTML, v. 0.9.1