/* -*- Mode: c; c-basic-offset: 2 -*-
 *
 * roqet.c - Rasqal RDF Query test program
 *
 * $Id: roqet.c 11500 2006-10-14 23:22:09Z dajobe $
 *
 * Copyright (C) 2004-2006, David Beckett http://purl.org/net/dajobe/
 * Copyright (C) 2004-2005, University of Bristol, UK http://www.bristol.ac.uk/
 * 
 * This package is Free Software and part of Redland http://librdf.org/
 * 
 * It is licensed under the following three licenses as alternatives:
 *   1. GNU Lesser General Public License (LGPL) V2.1 or any newer version
 *   2. GNU General Public License (GPL) V2 or any newer version
 *   3. Apache License, V2.0 or any newer version
 * 
 * You may not use this file except in compliance with at least one of
 * the above three licenses.
 * 
 * See LICENSE.html or LICENSE.txt at the top of this package for the
 * complete terms and further detail along with the license texts for
 * the licenses in COPYING.LIB, COPYING and LICENSE-2.0.txt respectively.
 * 
 * 
 */


#ifdef HAVE_CONFIG_H
#include <rasqal_config.h>
#endif

#ifdef WIN32
#include <win32_rasqal_config.h>
#endif

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
/* for access() and R_OK */
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#else
#include <rasqal_getopt.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

/* Rasqal includes */
#include <rasqal.h>

#ifdef NEED_OPTIND_DECLARATION
extern int optind;
extern char *optarg;
#endif

int rdql_parser_error(const char *msg);
int main(int argc, char *argv[]);


static char *program=NULL;


int
rdql_parser_error(const char *msg) 
{
  fprintf(stderr, "%s: Query parsing error: %s\n", program, msg);
  return (0);
}


#ifdef HAVE_GETOPT_LONG
#define HELP_TEXT(short, long, description) "  -" short ", --" long "  " description
#define HELP_ARG(short, long) "--" #long
#define HELP_PAD "\n                           "
#else
#define HELP_TEXT(short, long, description) "  -" short "  " description
#define HELP_ARG(short, long) "-" #short
#define HELP_PAD "\n      "
#endif


#define GETOPT_STRING "cd:e:f:hi:nr:qs:vw"

#ifdef HAVE_GETOPT_LONG
static struct option long_options[] =
{
  /* name, has_arg, flag, val */
  {"count", 0, 0, 'c'},
  {"dump-query", 1, 0, 'd'},
  {"dryrun", 0, 0, 'n'},
  {"exec", 1, 0, 'e'},
  {"feature", 1, 0, 'f'},
  {"help", 0, 0, 'h'},
  {"input", 1, 0, 'i'},
  {"quiet", 0, 0, 'q'},
  {"results", 1, 0, 'r'},
  {"source", 1, 0, 's'},
  {"version", 0, 0, 'v'},
  {"walk-query", 0, 0, 'w'},
  {NULL, 0, 0, 0}
};
#endif


static int error_count=0;

static const char *title_format_string="Rasqal RDF query utility %s\n";

#ifdef BUFSIZ
#define FILE_READ_BUF_SIZE BUFSIZ
#else
#define FILE_READ_BUF_SIZE 1024
#endif

#define MAX_QUERY_ERROR_REPORT_LEN 512


static void
roqet_error_handler(void *user_data, 
                    raptor_locator* locator, const char *message) 
{
  fprintf(stderr, "%s: Error - ", program);
  raptor_print_locator(stderr, locator);
  fprintf(stderr, " - %s\n", message);

  error_count++;
}

#define SPACES_LENGTH 80
static const char spaces[SPACES_LENGTH+1]="                                                                                ";

static void
roqet_write_indent(FILE *fh, int indent) 
{
  while(indent > 0) {
    int sp=(indent > SPACES_LENGTH) ? SPACES_LENGTH : indent;
    (void)fwrite(spaces, sizeof(char), sp, fh);
    indent -= sp;
  }
}

  

static void
roqet_graph_pattern_walk(rasqal_graph_pattern *gp, int gp_index,
                         FILE *fh, int indent) {
  int triple_index=0;
  rasqal_graph_pattern_operator op;
  int seen;
  raptor_sequence *seq;
  int idx;
  
  op=rasqal_graph_pattern_get_operator(gp);
  
  roqet_write_indent(fh, indent);
  fprintf(fh, "%s graph pattern", 
          rasqal_graph_pattern_operator_as_string(op));
  idx=rasqal_graph_pattern_get_index(gp);
  if(idx >= 0)
    fprintf(fh, "[%d]", idx);
  if(gp_index >= 0)
    fprintf(fh, " #%d", gp_index);
  fputs(" {\n", fh);
  
  indent+= 2;

  /* look for triples */
  seen=0;
  while(1) {
    rasqal_triple* t=rasqal_graph_pattern_get_triple(gp, triple_index);
    if(!t)
      break;
    
    if(!seen) {
      roqet_write_indent(fh, indent);
      fputs("triples {\n", fh);
      seen=1;
    }
    roqet_write_indent(fh, indent+2);
    fprintf(fh, "triple #%d { ", triple_index);
    rasqal_triple_print(t, fh);
    fputs(" }\n", fh);

    triple_index++;
  }
  if(seen) {
    roqet_write_indent(fh, indent);
    fputs("}\n", fh);
  }


  /* look for sub-graph patterns */
  seq=rasqal_graph_pattern_get_sub_graph_pattern_sequence(gp);
  if(seq && raptor_sequence_size(seq) > 0) {
    roqet_write_indent(fh, indent);
    fprintf(fh, "sub-graph patterns (%d) {\n", raptor_sequence_size(seq));

    gp_index=0;
    while(1) {
      rasqal_graph_pattern* sgp=rasqal_graph_pattern_get_sub_graph_pattern(gp, gp_index);
      if(!sgp)
        break;
      
      roqet_graph_pattern_walk(sgp, gp_index, fh, indent+2);
      gp_index++;
    }

    roqet_write_indent(fh, indent);
    fputs("}\n", fh);
  }
  

  /* look for constraints */
  seq=rasqal_graph_pattern_get_constraint_sequence(gp);
  if(seq && raptor_sequence_size(seq) > 0) {
    roqet_write_indent(fh, indent);
    fprintf(fh, "constraints (%d) {\n", raptor_sequence_size(seq));

    gp_index=0;
    while(1) {
      rasqal_expression* expr=rasqal_graph_pattern_get_constraint(gp, gp_index);
      if(!expr)
        break;
      
      roqet_write_indent(fh, indent+2);
      fprintf(fh, "constraint #%d { ", gp_index);
      rasqal_expression_print(expr, fh);
      fputs("}\n", fh);
      
      gp_index++;
    }

    roqet_write_indent(fh, indent);
    fputs("}\n", fh);
  }
  

  indent-=2;
  
  roqet_write_indent(fh, indent);
  fputs("}\n", fh);
}


    
static void
roqet_query_walk(rasqal_query *rq, FILE *fh, int indent) {
  rasqal_query_verb verb;
  int i;
  rasqal_graph_pattern* gp;
  raptor_sequence *seq;

  verb=rasqal_query_get_verb(rq);
  roqet_write_indent(fh, indent);
  fprintf(fh, "query verb: %s\n", rasqal_query_verb_as_string(verb));

  i=rasqal_query_get_distinct(rq);
  if(i != 0) {
    roqet_write_indent(fh, indent);
    fprintf(fh, "query asks for distinct results\n");
  }
  
  i=rasqal_query_get_limit(rq);
  if(i >= 0) {
    roqet_write_indent(fh, indent);
    fprintf(fh, "query asks for result limits %d\n", i);
  }
  
  i=rasqal_query_get_offset(rq);
  if(i >= 0) {
    roqet_write_indent(fh, indent);
    fprintf(fh, "query asks for result offset %d\n", i);
  }
  
  seq=rasqal_query_get_bound_variable_sequence(rq);
  if(seq && raptor_sequence_size(seq) > 0) {
    fprintf(fh, "query bound variables (%d): ", 
            raptor_sequence_size(seq));
    i=0;
    while(1) {
      rasqal_variable* v=(rasqal_variable*)raptor_sequence_get_at(seq, i);
      if(!v)
        break;

      if(i > 0)
        fputs(", ", fh);
      fputs((const char*)v->name, fh);

      i++;
    }
    fputc('\n', fh);
  }

  gp=rasqal_query_get_query_graph_pattern(rq);
  if(!gp)
    return;


  seq=rasqal_query_get_construct_triples_sequence(rq);
  if(seq && raptor_sequence_size(seq) > 0) {
    roqet_write_indent(fh, indent);
    fprintf(fh, "query construct triples (%d) {\n", 
            raptor_sequence_size(seq));
    i=0;
    while(1) {
      rasqal_triple* t=rasqal_query_get_construct_triple(rq, i);
      if(!t)
        break;
    
      roqet_write_indent(fh, indent+2);
      fprintf(fh, "triple #%d { ", i);
      rasqal_triple_print(t, fh);
      fputs(" }\n", fh);

      i++;
    }
    roqet_write_indent(fh, indent);
    fputs("}\n", fh);
  }

  fputs("query ", fh);
  roqet_graph_pattern_walk(gp, -1, fh, indent);
}


typedef enum {
  QUERY_OUTPUT_UNKNOWN,
  QUERY_OUTPUT_DEBUG,
  QUERY_OUTPUT_STRUCTURE,
  QUERY_OUTPUT_SPARQL,
  QUERY_OUTPUT_LAST= QUERY_OUTPUT_SPARQL
} query_output_format;

const char* query_output_format_labels[QUERY_OUTPUT_LAST+1][2]={
  { NULL, NULL },
  { "debug", "Debug query dump (output format may change)" },
  { "structure", "Query structure walk (output format may change)" },
  { "sparql", "SPARQL" }
};



int
main(int argc, char *argv[]) 
{ 
  int query_from_string=0;
  void *query_string=NULL;
  unsigned char *uri_string=NULL;
  int free_uri_string=0;
  unsigned char *base_uri_string=NULL;
  rasqal_query *rq;
  rasqal_query_results *results;
  const char *ql_name="sparql";
  char *ql_uri=NULL;
  int rc=0;
  raptor_uri *uri=NULL;
  raptor_uri *base_uri=NULL;
  char *filename=NULL;
  char *p;
  int usage=0;
  int help=0;
  int quiet=0;
  int count=0;
  int dryrun=0;
  raptor_sequence* source_uris=NULL;
  raptor_serializer* serializer=NULL;
  const char *serializer_syntax_name="ntriples";
  query_output_format output_format= QUERY_OUTPUT_UNKNOWN;
  rasqal_query_results_formatter* results_formatter=NULL;
  rasqal_feature query_feature=(rasqal_feature)-1;
  int query_feature_value= -1;
  unsigned char* query_feature_string_value=NULL;
  
  program=argv[0];
  if((p=strrchr(program, '/')))
    program=p+1;
  else if((p=strrchr(program, '\\')))
    program=p+1;
  argv[0]=program;
  

  rasqal_init();

  while (!usage && !help)
  {
    int c;
    raptor_uri *source_uri;
    
#ifdef HAVE_GETOPT_LONG
    int option_index = 0;

    c = getopt_long (argc, argv, GETOPT_STRING, long_options, &option_index);
#else
    c = getopt (argc, argv, GETOPT_STRING);
#endif
    if (c == -1)
      break;

    switch (c) {
      case 0:
      case '?': /* getopt() - unknown option */
        usage=1;
        break;
        
      case 'c':
        count=1;
        break;

      case 'd':
        output_format= QUERY_OUTPUT_UNKNOWN;
        if(optarg) {
          int i;

          for(i=1; i <= QUERY_OUTPUT_LAST; i++)
            if(!strcmp(optarg, query_output_format_labels[i][0])) {
              output_format=i;
              break;
            }
            
        }
        if(output_format == QUERY_OUTPUT_UNKNOWN) {
          int i;
          fprintf(stderr,
                  "%s: invalid argument `%s' for `" HELP_ARG(d, dump-query) "'\n",
                  program, optarg);
          for(i=1; i <= QUERY_OUTPUT_LAST; i++)
            printf("  %-12s for %s\n", query_output_format_labels[i][0],
                   query_output_format_labels[i][1]);
          usage=1;
        }
        break;
        

      case 'e':
	if(optarg) {
          query_string=optarg;
          query_from_string=1;
        }
        break;

      case 'f':
        if(optarg) {
          if(!strcmp(optarg, "help")) {
            int i;
            
            fprintf(stderr, "%s: Valid query features are:\n", program);
            for(i=0; i < (int)rasqal_get_feature_count(); i++) {
              const char *feature_name;
              const char *feature_label;
              if(!rasqal_features_enumerate((rasqal_feature)i, &feature_name, NULL, &feature_label)) {
                const char *feature_type=(rasqal_feature_value_type((rasqal_feature)i) == 0) ? "" : " (string)";
                printf("  %-20s  %s%s\n", feature_name, feature_label, 
                       feature_type);
              }
            }
            fputs("Features are set with `" HELP_ARG(f, feature) " FEATURE=VALUE or `-f FEATURE'\nand take a decimal integer VALUE except where noted, defaulting to 1 if omitted.\n", stderr);

            rasqal_finish();
            exit(0);
          } else {
            int i;
            size_t arg_len=strlen(optarg);
            
            for(i=0; i < (int)rasqal_get_feature_count(); i++) {
              const char *feature_name;
              size_t len;
              
              if(rasqal_features_enumerate((rasqal_feature)i, &feature_name, NULL, NULL))
                continue;
              len=strlen(feature_name);
              if(!strncmp(optarg, feature_name, len)) {
                query_feature=(rasqal_feature)i;
                if(rasqal_feature_value_type(query_feature) == 0) {
                  if(len < arg_len && optarg[len] == '=')
                    query_feature_value=atoi(&optarg[len+1]);
                  else if(len == arg_len)
                    query_feature_value=1;
                } else {
                  if(len < arg_len && optarg[len] == '=')
                    query_feature_string_value=(unsigned char*)&optarg[len+1];
                  else if(len == arg_len)
                    query_feature_string_value=(unsigned char*)"";
                }
                break;
              }
            }
            
            if(query_feature_value < 0 && !query_feature_string_value) {
              fprintf(stderr, "%s: invalid argument `%s' for `" HELP_ARG(f, feature) "'\nTry '%s " HELP_ARG(f, feature) " help' for a list of valid features\n",
                      program, optarg, program);
              usage=1;
            }
          }
        }
        break;

      case 'h':
        help=1;
        break;

      case 'n':
        dryrun=1;
        break;

      case 'r':
        if(optarg) {
          if(results_formatter)
            rasqal_free_query_results_formatter(results_formatter);

          if(!strcmp(optarg, "simple"))
            results_formatter=NULL;
          else {
            results_formatter=rasqal_new_query_results_formatter(optarg, NULL);
            if(!results_formatter) {
              fprintf(stderr, 
                      "%s: invalid argument `%s' for `" HELP_ARG(r, results) "'\n",
                      program, optarg);
              usage=1;
              break;
            }
          }
        }
        break;

      case 'i':
	if(rasqal_language_name_check(optarg))
          ql_name=optarg;
	else {
          int i;
          fprintf(stderr,
                  "%s: invalid argument `%s' for `" HELP_ARG(i, input) "'\n",
                  program, optarg);
          fprintf(stderr, "Valid arguments are:\n");
          for(i=0; 1; i++) {
            const char *help_name;
            const char *help_label;
            if(rasqal_languages_enumerate(i, &help_name, &help_label, NULL))
              break;
            printf("  %-12s for %s\n", help_name, help_label);
          }
          usage=1;
	}
        break;

      case 'q':
        quiet=1;
        break;

      case 's':
        if(!source_uris) {
          source_uris=raptor_new_sequence((raptor_sequence_free_handler*)raptor_free_uri,
                                          (raptor_sequence_print_handler*)raptor_sequence_print_uri);
          if(!source_uris) {
            fprintf(stderr, "%s: Failed to create source sequence\n", program);
          return(1);
          }
        }
        
        if(!access((const char*)optarg, R_OK)) {
          unsigned char* source_uri_string=raptor_uri_filename_to_uri_string((const char*)optarg);
          source_uri=raptor_new_uri(source_uri_string);
          raptor_free_memory(source_uri_string);
        } else
          source_uri=raptor_new_uri((const unsigned char*)optarg);

        if(!source_uri) {
          fprintf(stderr, "%s: Failed to create source URI for %s\n",
                  program, optarg);
          return(1);
        }
        raptor_sequence_push(source_uris, source_uri);
        break;

      case 'v':
        fputs(rasqal_version_string, stdout);
        fputc('\n', stdout);
        exit(0);

      case 'w':
        fprintf(stderr,
                "%s: WARNING: `-w' is deprecated.  Please use `" HELP_ARG(d, dump-query) " structure' instead.\n",
                program);
        output_format=QUERY_OUTPUT_STRUCTURE;
        break;

    }
    
  }
  
  if(!help && !usage) {
    if(query_string) {
      if(optind != argc && optind != argc-1)
        usage=2; /* Title and usage */
    } else {
      if(optind != argc-1 && optind != argc-2)
        usage=2; /* Title and usage */
    }
  }

  
  if(usage) {
    if(usage>1) {
      fprintf(stderr, title_format_string, rasqal_version_string);
      fputs(rasqal_short_copyright_string, stderr);
      fputc('\n', stderr);
    }
    fprintf(stderr, "Try `%s " HELP_ARG(h, help) "' for more information.\n",
                    program);
    exit(1);
  }

  if(help) {
    int i;
    
    printf("Usage: %s [OPTIONS] <query URI> [base URI]\n", program);
    printf("       %s [OPTIONS] -e <query string> [base URI]\n", program);
    printf(title_format_string, rasqal_version_string);
    puts(rasqal_short_copyright_string);
    puts("Run an RDF query giving variable bindings or RDF triples.");
    puts("Normal operation is to execute the query retrieved from URI <query URI>");
    puts("and print the results in a simple text format.");
    puts("\nMain options:");
    puts(HELP_TEXT("e", "exec QUERY      ", "Execute QUERY string instead of <query URI>"));
    puts(HELP_TEXT("i", "input LANGUAGE  ", "Set query language name to one of:"));
    for(i=0; 1; i++) {
      const char *help_name;
      const char *help_label;
      if(rasqal_languages_enumerate(i, &help_name, &help_label, NULL))
        break;
      printf("    %-15s         %s", help_name, help_label);
      if(!i)
        puts(" (default)");
      else
        putchar('\n');
    }
    puts(HELP_TEXT("r", "results FORMAT  ", "Set query results format to one of:"));
    puts("    For variable bindings and boolean results:");
    puts("      simple                A simple text format (default)");
    for(i=0; i < (int)raptor_get_feature_count(); i++) {
      const char *name;
      const char *label;
      if(!rasqal_query_results_formats_enumerate(i, &name, &label, 
                                                 NULL))
        printf("      %-10s            %s\n", name, label);
    }
    puts("    For RDF graph results:");
    for(i=0; 1; i++) {
      const char *help_name;
      const char *help_label;
      if(raptor_serializers_enumerate(i, &help_name, &help_label, NULL, NULL))
        break;
      printf("      %-15s       %s", help_name, help_label);
      if(!i)
        puts(" (default)");
      else
        putchar('\n');
    }
    puts("\nAdditional options:");
    puts(HELP_TEXT("c", "count            ", "Count triples - no output"));
    puts(HELP_TEXT("d", "dump-query FORMAT", "Print the parsed query out in FORMAT:"));
    for(i=1; i <= QUERY_OUTPUT_LAST; i++)
      printf("      %-15s        %s\n", query_output_format_labels[i][0],
             query_output_format_labels[i][1]);
    puts(HELP_TEXT("f FEATURE(=VALUE)", "feature FEATURE(=VALUE)", HELP_PAD "Set query features" HELP_PAD "Use `-f help' for a list of valid features"));
    puts(HELP_TEXT("h", "help             ", "Print this help, then exit"));
    puts(HELP_TEXT("n", "dryrun           ", "Prepare but do not run the query"));
    puts(HELP_TEXT("q", "quiet            ", "No extra information messages"));
    puts(HELP_TEXT("s", "source URI       ", "Query against RDF data at source URI"));
    puts(HELP_TEXT("v", "version          ", "Print the Rasqal version"));
    puts(HELP_TEXT("w", "walk-query       ", "Print query.  Same as '-d structure'"));
    puts("\nReport bugs to http://bugs.librdf.org/");
    puts("Rasqal home page: http://librdf.org/rasqal/");
    exit(0);
  }


  if(query_string) {
    if(optind == argc-1)
      base_uri_string=(unsigned char*)argv[optind];
  } else {
    if(optind == argc-1)
      uri_string=(unsigned char*)argv[optind];
    else {
      uri_string=(unsigned char*)argv[optind++];
      base_uri_string=(unsigned char*)argv[optind];
    }
    
    /* If uri_string is "path-to-file", turn it into a file: URI */
    if(!strcmp((const char*)uri_string, "-")) {
      if(!base_uri_string) {
        fprintf(stderr, "%s: A Base URI is required when reading from standard input.\n",
                program);
        return(1);
      }
      uri_string=NULL;
    } else if(!access((const char*)uri_string, R_OK)) {
      filename=(char*)uri_string;
      uri_string=raptor_uri_filename_to_uri_string(filename);
      free_uri_string=1;
    }
    
    if(uri_string) {
      uri=raptor_new_uri(uri_string);
      if(!uri) {
        fprintf(stderr, "%s: Failed to create URI for %s\n",
                program, uri_string);
        return(1);
      }
    } else
      uri=NULL; /* stdin */
  }
  

  if(!base_uri_string) {
    if(uri)
      base_uri=raptor_uri_copy(uri);
  } else {
    base_uri=raptor_new_uri(base_uri_string);
    if(!base_uri) {
      fprintf(stderr, "%s: Failed to create URI for %s\n",
              program, base_uri_string);
      return(1);
    }
  }

  if(query_string) {
    /* NOP - already got it */
  } else if(!uri_string) {
    query_string=calloc(FILE_READ_BUF_SIZE, 1);
    rc=fread(query_string, FILE_READ_BUF_SIZE, 1, stdin);
    if(ferror(stdin)) {
      fprintf(stderr, "%s: query string stdin read failed - %s\n",
              program, strerror(errno));
      return(1);
    }
    query_from_string=0;
  } else if(filename) {
    raptor_stringbuffer *sb=raptor_new_stringbuffer();
    size_t len;
    FILE *fh;

    fh=fopen(filename, "r");
    if(!fh) {
      fprintf(stderr, "%s: file '%s' open failed - %s", 
              program, filename, strerror(errno));
      rc=1;
      goto tidy_setup;
    }
    
    while(!feof(fh)) {
      unsigned char buffer[FILE_READ_BUF_SIZE];
      size_t read_len;
      read_len=fread((char*)buffer, 1, FILE_READ_BUF_SIZE, fh);
      if(read_len > 0)
        raptor_stringbuffer_append_counted_string(sb, buffer, read_len, 1);
      if(read_len < FILE_READ_BUF_SIZE) {
        if(ferror(fh)) {
          fprintf(stderr, "%s: file '%s' read failed - %s\n",
                  program, filename, strerror(errno));
          fclose(fh);
          return(1);
        }
        break;
      }
    }
    fclose(fh);

    len=raptor_stringbuffer_length(sb);
    query_string=malloc(len+1);
    raptor_stringbuffer_copy_to_string(sb, (unsigned char*)query_string, len);
    raptor_free_stringbuffer(sb);
    query_from_string=0;
  } else {
    raptor_www *www=raptor_www_new();
    if(www) {
      raptor_www_set_error_handler(www, roqet_error_handler, NULL);
      raptor_www_fetch_to_string(www, uri, &query_string, NULL, malloc);
      raptor_www_free(www);
    }
    if(!query_string || error_count) {
      fprintf(stderr, "%s: Retrieving query at URI '%s' failed\n", 
              program, uri_string);
      rc=1;
      goto tidy_setup;
    }
    query_from_string=0;
  }


  if(!quiet) {
    if(query_from_string) {
      if(base_uri_string)
        fprintf(stderr, "%s: Running query '%s' with base URI %s\n", program,
                (char*)query_string, base_uri_string);
      else
        fprintf(stderr, "%s: Running query '%s'\n", program,
                (char*)query_string);
    } else if(filename) {
      if(base_uri_string)
        fprintf(stderr, "%s: Querying from file %s with base URI %s\n", program,
                filename, base_uri_string);
      else
        fprintf(stderr, "%s: Querying from file %s\n", program, filename);
    } else if(uri_string) {
      if(base_uri_string)
        fprintf(stderr, "%s: Querying URI %s with base URI %s\n", program,
                uri_string, base_uri_string);
      else
        fprintf(stderr, "%s: Querying URI %s\n", program, uri_string);
    }
  }
  
  rq=rasqal_new_query((const char*)ql_name, (const unsigned char*)ql_uri);
  rasqal_query_set_error_handler(rq, NULL, roqet_error_handler);
  rasqal_query_set_fatal_error_handler(rq, NULL, roqet_error_handler);

  if(query_feature_value >= 0)
    rasqal_query_set_feature(rq, query_feature, query_feature_value);
  if(query_feature_string_value)
    rasqal_query_set_feature_string(rq, query_feature,
                                    query_feature_string_value);
  
  if(source_uris) {
    while(raptor_sequence_size(source_uris)) {
      raptor_uri* source_uri=(raptor_uri*)raptor_sequence_pop(source_uris);
      if(rasqal_query_add_data_graph(rq, source_uri, source_uri, 
                                     RASQAL_DATA_GRAPH_NAMED)) {
        fprintf(stderr, "%s: Failed to add named graph %s\n", program, 
                raptor_uri_as_string(source_uri));
      }
      raptor_free_uri(source_uri);
    }
  }

  if(rasqal_query_prepare(rq, (const unsigned char*)query_string, base_uri)) {
    size_t len=strlen((const char*)query_string);
    
    fprintf(stderr, "%s: Parsing query '", program);
    if(len > MAX_QUERY_ERROR_REPORT_LEN) {
      (void)fwrite(query_string, MAX_QUERY_ERROR_REPORT_LEN, sizeof(char), stderr);
      fprintf(stderr, "...' (%d bytes) failed\n", (int)len);
    } else {
      (void)fwrite(query_string, len, sizeof(char), stderr);
      fputs("' failed\n", stderr);
    }
    rc=1;
    goto tidy_query;
  }

  if(output_format != QUERY_OUTPUT_UNKNOWN) {
    if(!quiet)
      fprintf(stderr, "Query:\n");
    switch(output_format) {
    case QUERY_OUTPUT_DEBUG:
      rasqal_query_print(rq, stdout);
      break;
  
    case QUERY_OUTPUT_STRUCTURE:
      roqet_query_walk(rq, stdout, 0);
      break;
      
    case QUERY_OUTPUT_SPARQL:
      if(1) {
        raptor_iostream* iostr=raptor_new_iostream_to_file_handle(stdout);
        rasqal_query_write(iostr, rq, NULL, base_uri);
        raptor_free_iostream(iostr);
      }
      break;
      
    case QUERY_OUTPUT_UNKNOWN:
    default:
      fprintf(stderr, "%s: Unknown query output format %d\n", program,
              output_format);
      abort();
    }
  }

  if(dryrun)
    goto tidy_query;

  if(!(results=rasqal_query_execute(rq))) {
    fprintf(stderr, "%s: Query execution failed\n", program);
    rc=1;
    goto tidy_query;
  }

  if(results_formatter != NULL) {
    raptor_iostream *iostr;
  
    iostr=raptor_new_iostream_to_file_handle(stdout);
    if(!iostr) {
      fprintf(stderr, "%s: Creating output iostream failed\n", program);
      rc=1;
      goto tidy_query;
    }

    rasqal_query_results_formatter_write(iostr, results_formatter,
                                         results, base_uri);
    raptor_free_iostream(iostr);
  } else {
    if(rasqal_query_results_is_bindings(results)) {
      if(!quiet)
        fprintf(stdout, "%s: Query has a variable bindings result\n", program);

      while(!rasqal_query_results_finished(results)) {
        if(!count) {
          int i;
          
          fputs("result: [", stdout);
          for(i=0; i<rasqal_query_results_get_bindings_count(results); i++) {
            const unsigned char *name=rasqal_query_results_get_binding_name(results, i);
            rasqal_literal *value=rasqal_query_results_get_binding_value(results, i);
            
            if(i>0)
              fputs(", ", stdout);
            fprintf(stdout, "%s=", name);
            if(value)
              rasqal_literal_print(value, stdout);
            else
              fputs("NULL", stdout);
          }
          fputs("]\n", stdout);
        }
        
        rasqal_query_results_next(results);
      }

      if(!quiet)
        fprintf(stdout, "%s: Query returned %d results\n", program, 
                rasqal_query_results_get_count(results));

    } else if (rasqal_query_results_is_boolean(results)) {
      fprintf(stdout, "%s: Query has a boolean result: %s\n",
              program,
              rasqal_query_results_get_boolean(results) ? "true" : "false");
    }
    else if (rasqal_query_results_is_graph(results)) {
      int triple_count=0;
      
      if(!quiet)
        fprintf(stdout, "%s: Query has a graph result:\n", program);

      serializer=raptor_new_serializer(serializer_syntax_name);
      if(!serializer) {
        fprintf(stderr, 
                "%s: Failed to create raptor serializer type %s\n", program,
                serializer_syntax_name);
        return(1);
      }

      raptor_serialize_start_to_file_handle(serializer, base_uri, stdout);

      while(1) {
        raptor_statement *rs=rasqal_query_results_get_triple(results);
        if(!rs)
          break;
        raptor_serialize_statement(serializer, rs);
        triple_count++;

        if(rasqal_query_results_next_triple(results))
          break;
      }

      raptor_serialize_end(serializer);
      raptor_free_serializer(serializer);

      if(!quiet)
        fprintf(stdout, "%s: Total %d triples\n", program, triple_count);
    } else {
      fprintf(stdout, "%s: Query returned unknown result format\n", program);
      rc=1;
    }
  }

  rasqal_free_query_results(results);
  
 tidy_query:  
  if(results_formatter)
    rasqal_free_query_results_formatter(results_formatter);

  rasqal_free_query(rq);

  if(!query_from_string)
    free(query_string);

 tidy_setup:

  if(source_uris)
    raptor_free_sequence(source_uris);
  if(base_uri)
    raptor_free_uri(base_uri);
  if(uri)
    raptor_free_uri(uri);
  if(free_uri_string)
    raptor_free_memory(uri_string);

  rasqal_finish();
  
  return (rc);
}
