Merge remote branch 'origin/master' into import-gui
authorJohn Darrington <john@darrington.wattle.id.au>
Fri, 29 Mar 2013 11:27:59 +0000 (12:27 +0100)
committerJohn Darrington <john@darrington.wattle.id.au>
Fri, 29 Mar 2013 11:27:59 +0000 (12:27 +0100)
Conflicts:
src/ui/gui/text-data-import-dialog.c

1  2 
src/data/casereader.c
src/data/ods-reader.c
src/ui/gui/text-data-import-dialog.c

diff --combined src/data/casereader.c
index c0968669a23532e05d2195d3d521d6f451a19fbc,c8050c6cde4c7b2a35d6d2b45aa752c3ecbee1e8..e50096f5e8cc10980ec93b5b682042072466d2c9
@@@ -91,10 -91,10 +91,10 @@@ casereader_destroy (struct casereader *
    bool ok = true;
    if (reader != NULL)
      {
--      reader->class->destroy (reader, reader->aux);
--      ok = taint_destroy (reader->taint);
--      caseproto_unref (reader->proto);
--      free (reader);
++      //      reader->class->destroy (reader, reader->aux);
++      // ok = taint_destroy (reader->taint);
++      // caseproto_unref (reader->proto);
++      //  free (reader);
      }
    return ok;
  }
@@@ -239,12 -239,13 +239,12 @@@ static casenumbe
  casereader_count_cases__ (const struct casereader *reader,
                            casenumber max_cases)
  {
 -  struct casereader *clone;
 -  casenumber n_cases;
 -
 -  clone = casereader_clone (reader);
 -  n_cases = casereader_advance (clone, max_cases);
 -  casereader_destroy (clone);
 -
 +  struct casereader *clone = casereader_clone (reader);
 +  casenumber n_cases = casereader_advance (clone, max_cases);
 +#if (__GNUC__ == 4 ) && (__GNUC_MINOR__ == 4)
-   volatile int x = 1;
++  // volatile int x = 1;
 +#endif
-   casereader_destroy (clone);
++  //  casereader_destroy (clone);
    return n_cases;
  }
  
diff --combined src/data/ods-reader.c
index cbd7feaa170d8f51d4245f879c9b526a4594be20,b3586b82ec8b7817d77f3f5dffcaf0bde6fd87f5..dd9331a777b4a2ba7295972d933232d8dcfe5277
@@@ -100,35 -100,31 +100,35 @@@ enum reader_stat
      STATE_CELL_CONTENT     /* Found a the text within a cell */
    };
  
 -struct ods_reader
 +struct state_data
  {
 -  struct spreadsheet spreadsheet;
 -  struct zip_reader *zreader;
    xmlTextReaderPtr xtr;
 -  int ref_cnt;
 -
 +  int node_type;
    enum reader_state state;
    int row;
    int col;
 -  int node_type;
    int current_sheet;
    xmlChar *current_sheet_name;
  
 -  xmlChar *target_sheet_name;
 -  int target_sheet_index;
 +  int col_span;
 +};
  
- struct ods_reader
++struct ods_reader             /*  */
 +{
 +  struct spreadsheet spreadsheet;
 +  struct zip_reader *zreader;
 +  int ref_cnt;
 +  int target_sheet_index;
 +  xmlChar *target_sheet_name;
 +  
 +  struct state_data foo;
 +  struct state_data rsd;
  
    int start_row;
    int start_col;
    int stop_row;
    int stop_col;
  
 -  int col_span;
 -
    struct sheet_detail *sheets;
    int n_allocated_sheets;
  
  void
  ods_destroy (struct spreadsheet *s)
  {
++  printf ("%s:%d\n", __FILE__, __LINE__);
 +#if 0
    struct ods_reader *r = (struct ods_reader *) s;
  
    if (--r->ref_cnt == 0)
        free (r->sheets);
        free (r);
      }
 +#endif
  }
  
  
  
  static bool
 -reading_target_sheet (const struct ods_reader *r)
 +reading_target_sheet (const struct ods_reader *r, const struct state_data *foo)
  {
    if (r->target_sheet_name != NULL)
      {
 -      if ( 0 == xmlStrcmp (r->target_sheet_name, r->current_sheet_name))
 +      if ( 0 == xmlStrcmp (r->target_sheet_name, foo->current_sheet_name))
        return true;
      }
    
 -  if (r->target_sheet_index == r->current_sheet + 1)
 +  if (r->target_sheet_index == foo->current_sheet + 1)
      return true;
  
    return false;
  }
  
  
 -static void process_node (struct ods_reader *r);
 +static void process_node (struct ods_reader *or, struct state_data *r);
  
  
  const char *
  ods_get_sheet_name (struct spreadsheet *s, int n)
  {
 -  struct ods_reader *or = (struct ods_reader *) s;
 -  
 +  struct ods_reader *r = (struct ods_reader *) s;
 +  struct state_data *or = &r->foo;
 +
    assert (n < s->n_sheets);
  
    while ( 
 -        (or->n_allocated_sheets <= n)
 +        (r->n_allocated_sheets <= n)
          || or->state != STATE_SPREADSHEET
          )
      {
        if ( ret != 1)
        break;
  
 -      process_node (or);
 +      process_node (r, or);
      }
  
 -  return or->sheets[n].name;
 +  return r->sheets[n].name;
  }
  
  char *
  ods_get_sheet_range (struct spreadsheet *s, int n)
  {
 -  struct ods_reader *or = (struct ods_reader *) s;
 +  struct ods_reader *r = (struct ods_reader *) s;
 +  struct state_data *or = &r->foo;
    
    assert (n < s->n_sheets);
  
    while ( 
 -        (or->n_allocated_sheets <= n)
 -        || (or->sheets[n].stop_row == -1) 
 +        (r->n_allocated_sheets <= n)
 +        || (r->sheets[n].stop_row == -1) 
          || or->state != STATE_SPREADSHEET
          )
      {
        if ( ret != 1)
        break;
  
 -      process_node (or);
 +      process_node (r, or);
      }
  
    return create_cell_ref (
 -                        or->sheets[n].start_col,
 -                        or->sheets[n].start_row,
 -                        or->sheets[n].stop_col,
 -                        or->sheets[n].stop_row);
 +                        r->sheets[n].start_col,
 +                        r->sheets[n].start_row,
 +                        r->sheets[n].stop_col,
 +                        r->sheets[n].stop_row);
  }
  
  
  static void
  ods_file_casereader_destroy (struct casereader *reader UNUSED, void *r_)
  {
++  printf ("%s:%d %p\n", __FILE__, __LINE__, r_);
 +#if 0
    struct ods_reader *r = r_;
    if ( r == NULL)
      return ;
    xmlFree (r->target_sheet_name);
  
    ods_destroy (&r->spreadsheet);
 +#endif
  }
  
  
  
  
  static void
 -process_node (struct ods_reader *r)
 +process_node (struct ods_reader *or, struct state_data *r)
  {
    xmlChar *name = xmlTextReaderName (r->xtr);
    if (name == NULL)
  
          ++r->current_sheet;
  
 -        if (r->current_sheet >= r->n_allocated_sheets)
 +        if (r->current_sheet >= or->n_allocated_sheets)
            {
 -            assert (r->current_sheet == r->n_allocated_sheets);
 -            r->sheets = xrealloc (r->sheets, sizeof (*r->sheets) * ++r->n_allocated_sheets);
 -            r->sheets[r->n_allocated_sheets - 1].start_col = -1;
 -            r->sheets[r->n_allocated_sheets - 1].stop_col = -1;
 -            r->sheets[r->n_allocated_sheets - 1].start_row = -1;
 -            r->sheets[r->n_allocated_sheets - 1].stop_row = -1;
 -            r->sheets[r->n_allocated_sheets - 1].name = CHAR_CAST (char *, xmlStrdup (r->current_sheet_name));
 +            assert (r->current_sheet == or->n_allocated_sheets);
 +            or->sheets = xrealloc (or->sheets, sizeof (*or->sheets) * ++or->n_allocated_sheets);
 +            or->sheets[or->n_allocated_sheets - 1].start_col = -1;
 +            or->sheets[or->n_allocated_sheets - 1].stop_col = -1;
 +            or->sheets[or->n_allocated_sheets - 1].start_row = -1;
 +            or->sheets[or->n_allocated_sheets - 1].stop_row = -1;
 +            or->sheets[or->n_allocated_sheets - 1].name = CHAR_CAST (char *, xmlStrdup (r->current_sheet_name));
            }
  
          r->col = 0;
        break;
      case STATE_CELL_CONTENT:
        assert (r->current_sheet >= 0);
 -      assert (r->current_sheet < r->n_allocated_sheets);
 +      assert (r->current_sheet < or->n_allocated_sheets);
  
 -      if (r->sheets[r->current_sheet].start_row == -1)
 -      r->sheets[r->current_sheet].start_row = r->row - 1;
 +      if (or->sheets[r->current_sheet].start_row == -1)
 +      or->sheets[r->current_sheet].start_row = r->row - 1;
  
        if ( 
 -        (r->sheets[r->current_sheet].start_col == -1)
 +        (or->sheets[r->current_sheet].start_col == -1)
          ||
 -        (r->sheets[r->current_sheet].start_col >= r->col - 1)
 +        (or->sheets[r->current_sheet].start_col >= r->col - 1)
           )
 -      r->sheets[r->current_sheet].start_col = r->col - 1;
 +      or->sheets[r->current_sheet].start_col = r->col - 1;
  
 -      r->sheets[r->current_sheet].stop_row = r->row - 1;
 +      or->sheets[r->current_sheet].stop_row = r->row - 1;
  
 -      if ( r->sheets[r->current_sheet].stop_col <  r->col - 1)
 -      r->sheets[r->current_sheet].stop_col = r->col - 1;
 +      if ( or->sheets[r->current_sheet].stop_col <  r->col - 1)
 +      or->sheets[r->current_sheet].stop_col = r->col - 1;
  
        if (XML_READER_TYPE_END_ELEMENT  == r->node_type)
        r->state = STATE_CELL;
@@@ -490,11 -480,11 +492,11 @@@ convert_xml_to_value (struct ccase *c, 
            CHAR_CAST (const char *, xmv->value) : CHAR_CAST (const char *, xmv->text);
  
  
--        free (data_in (ss_cstr (text), "UTF-8",
++        data_in (ss_cstr (text), "UTF-8",
                         fmt->type,
                         v,
                         var_get_width (var),
--                       "UTF-8"));
++                       "UTF-8");
        }
      }
  }
@@@ -553,31 -543,38 +555,31 @@@ ods_error_handler (void *ctx, const cha
  }
  
  
 -static bool
 +static xmlTextReaderPtr
  init_reader (struct ods_reader *r, bool report_errors)
  {
    struct zip_member *content = zip_member_open (r->zreader, "content.xml");
    xmlTextReaderPtr xtr;
  
    if ( content == NULL)
 -    return false;
 -
 -  if (r->xtr)
 -    xmlFreeTextReader (r->xtr);
 +    return NULL;
  
    zip_member_ref (content);
    xtr = xmlReaderForIO ((xmlInputReadCallback) zip_member_read,
--                      (xmlInputCloseCallback) zip_member_finish,
++                      (xmlInputCloseCallback) NULL,
                        content,   NULL, NULL,
                        report_errors ? 0 : (XML_PARSE_NOERROR | XML_PARSE_NOWARNING) );
  
    if ( xtr == NULL)
      return false;
  
 -  r->xtr = xtr;
 +
    r->spreadsheet.type = SPREADSHEET_ODS;
 -  r->row = 0;
 -  r->col = 0;
 -  r->current_sheet = 0;
 -  r->state = STATE_INIT;
  
    if (report_errors) 
      xmlTextReaderSetErrorHandler (xtr, ods_error_handler, r);
  
 -  return true;
 +  return xtr;
  }
  
  
@@@ -588,7 -585,6 +590,7 @@@ ods_probe (const char *filename, bool r
    struct string errs = DS_EMPTY_INITIALIZER;
    int sheet_count;
    struct zip_reader *zr = zip_reader_create (filename, &errs);
 +  xmlTextReaderPtr xtr;
  
    if (zr == NULL)
      {
    r->zreader = zr;
    r->ref_cnt = 1;
  
 -  if (! init_reader (r, report_errors))
 +  xtr = init_reader (r, report_errors);
 +  if (xtr == NULL)
      {
        goto error;
      }
 +  r->foo.xtr = xtr;
 +  r->foo.row = 0;
 +  r->foo.col = 0;
 +  r->foo.current_sheet = 0;
 +  r->foo.state = STATE_INIT;
 +
  
    r->spreadsheet.n_sheets = sheet_count;
    r->n_allocated_sheets = 0;
@@@ -645,7 -634,6 +647,7 @@@ ods_make_reader (struct spreadsheet *sp
    int i;
    struct var_spec *var_spec = NULL;
    int n_var_specs = 0;
 +  xmlTextReaderPtr xtr;
  
    struct ods_reader *r = (struct ods_reader *) spreadsheet;
    xmlChar *val_string = NULL;
    ds_init_empty (&r->ods_errs);
    ++r->ref_cnt;
  
 -  if ( !init_reader (r, true))
 +  xtr = init_reader (r, true);
 +  if ( xtr == NULL)
      goto error;
  
 +  r->rsd.xtr = xtr;
 +  r->rsd.row = 0;
 +  r->rsd.col = 0;
 +  r->rsd.current_sheet = 0;
 +  r->rsd.state = STATE_INIT;
 +
 +
    if (opts->cell_range)
      {
        if ( ! convert_cell_ref (opts->cell_range,
        r->stop_row = -1;
      }
  
 -  r->state = STATE_INIT;
    r->target_sheet_name = xmlStrdup (BAD_CAST opts->sheet_name);
    r->target_sheet_index = opts->sheet_index;
 -  r->row = r->col = 0;
 -
  
    /* Advance to the start of the cells for the target sheet */
 -  while ( ! reading_target_sheet (r)  
 -        || r->state != STATE_ROW || r->row <= r->start_row )
 +  while ( ! reading_target_sheet (r, &r->rsd)  
 +        || r->rsd.state != STATE_ROW || r->rsd.row <= r->start_row )
      {
 -      if (1 != (ret = xmlTextReaderRead (r->xtr)))
 +      if (1 != (ret = xmlTextReaderRead (r->rsd.xtr)))
           break;
  
 -      process_node (r);
 +      process_node (r, &r->rsd);
      }
  
    if (ret < 1)
  
    if ( opts->read_names)
      {
 -      while (1 == (ret = xmlTextReaderRead (r->xtr)))
 +      while (1 == (ret = xmlTextReaderRead (r->rsd.xtr)))
        {
          int idx;
  
 -        process_node (r);
 +        process_node (r, &r->rsd);
  
          /* If the row is finished then stop for now */
 -        if (r->state == STATE_TABLE && r->row > r->start_row)
 +        if (r->rsd.state == STATE_TABLE && r->rsd.row > r->start_row)
            break;
  
 -        idx = r->col - r->start_col -1 ;
 +        idx = r->rsd.col - r->start_col -1 ;
  
          if ( idx < 0)
            continue;
          if (r->stop_col != -1 && idx > r->stop_col - r->start_col)
            continue;
  
 -        if (r->state == STATE_CELL_CONTENT 
 +        if (r->rsd.state == STATE_CELL_CONTENT 
              &&
 -            XML_READER_TYPE_TEXT  == r->node_type)
 +            XML_READER_TYPE_TEXT  == r->rsd.node_type)
            {
 -            xmlChar *value = xmlTextReaderValue (r->xtr);
 +            xmlChar *value = xmlTextReaderValue (r->rsd.xtr);
  
              if ( idx >= n_var_specs)
                {
      }
  
    /* Read in the first row of data */
 -  while (1 == xmlTextReaderRead (r->xtr))
 +  while (1 == xmlTextReaderRead (r->rsd.xtr))
      {
        int idx;
 -      process_node (r);
 +      process_node (r, &r->rsd);
  
 -      if ( ! reading_target_sheet (r) )
 +      if ( ! reading_target_sheet (r, &r->rsd) )
        break;
  
        /* If the row is finished then stop for now */
 -      if (r->state == STATE_TABLE &&
 -        r->row > r->start_row + (opts->read_names ? 1 : 0))
 +      if (r->rsd.state == STATE_TABLE &&
 +        r->rsd.row > r->start_row + (opts->read_names ? 1 : 0))
        break;
  
 -      idx = r->col - r->start_col - 1;
 +      idx = r->rsd.col - r->start_col - 1;
        if (idx < 0)
        continue;
  
        if (r->stop_col != -1 && idx > r->stop_col - r->start_col)
        continue;
  
 -      if ( r->state == STATE_CELL &&
 -         XML_READER_TYPE_ELEMENT  == r->node_type)
 +      if ( r->rsd.state == STATE_CELL &&
 +         XML_READER_TYPE_ELEMENT  == r->rsd.node_type)
        {
 -        type = xmlTextReaderGetAttribute (r->xtr, _xml ("office:value-type"));
 -        val_string = xmlTextReaderGetAttribute (r->xtr, _xml ("office:value"));
 +        type = xmlTextReaderGetAttribute (r->rsd.xtr, _xml ("office:value-type"));
 +        val_string = xmlTextReaderGetAttribute (r->rsd.xtr, _xml ("office:value"));
        }
  
 -      if ( r->state == STATE_CELL_CONTENT &&
 -         XML_READER_TYPE_TEXT  == r->node_type)
 +      if ( r->rsd.state == STATE_CELL_CONTENT &&
 +         XML_READER_TYPE_TEXT  == r->rsd.node_type)
        {
          if (idx >= n_var_specs)
            {
            }
  
          var_spec [idx].firstval.type = type;
 -        var_spec [idx].firstval.text = xmlTextReaderValue (r->xtr);
 +        var_spec [idx].firstval.text = xmlTextReaderValue (r->rsd.xtr);
          var_spec [idx].firstval.value = val_string;
  
          val_string = NULL;
  
    /* Create the dictionary and populate it */
    r->spreadsheet.dict = r->dict = dict_create (
 -    CHAR_CAST (const char *, xmlTextReaderConstEncoding (r->xtr)));
 +    CHAR_CAST (const char *, xmlTextReaderConstEncoding (r->rsd.xtr)));
  
    for (i = 0; i < n_var_specs ; ++i )
      {
      }
  
    /* Read in the first row of data */
 -  while (1 == xmlTextReaderRead (r->xtr))
 +  while (1 == xmlTextReaderRead (r->rsd.xtr))
      {
 -      process_node (r);
 +      process_node (r, &r->rsd);
  
 -      if (r->state == STATE_ROW)
 +      if (r->rsd.state == STATE_ROW)
        break;
      }
  
@@@ -905,65 -888,65 +907,70 @@@ static struct ccase 
  ods_file_casereader_read (struct casereader *reader UNUSED, void *r_)
  {
    struct ccase *c = NULL;
--  xmlChar *val_string = NULL;
--  xmlChar *type = NULL;
    struct ods_reader *r = r_;
  
    if (!r->used_first_case)
      {
++      printf ("%s:%d\n", __FILE__, __LINE__);
        r->used_first_case = true;
        return r->first_case;
      }
  
  
    /* Advance to the start of a row. (If there is one) */
 -  while (r->state != STATE_ROW 
 -       && 1 == xmlTextReaderRead (r->xtr)
 +  while (r->rsd.state != STATE_ROW 
 +       && 1 == xmlTextReaderRead (r->rsd.xtr)
         )
      {
 -      process_node (r);
 +      process_node (r, &r->rsd);
      }
  
  
 -  if ( ! reading_target_sheet (r)  
 -       ||  r->state < STATE_TABLE
 -       ||  (r->stop_row != -1 && r->row > r->stop_row + 1)
 +  if ( ! reading_target_sheet (r, &r->rsd)  
 +       ||  r->rsd.state < STATE_TABLE
 +       ||  (r->stop_row != -1 && r->rsd.row > r->stop_row + 1)
         )
      {
++      printf ("%s:%d\n", __FILE__, __LINE__);
        return NULL;
      }
++  printf ("%s:%d\n", __FILE__, __LINE__);
  
    c = case_create (r->proto);
    case_set_missing (c);
    
 -  while (1 == xmlTextReaderRead (r->xtr))
++#if 1
++  xmlChar *val_string = NULL;
++  xmlChar *type = NULL;
++
 +  while (1 == xmlTextReaderRead (r->rsd.xtr))
      {
 -      process_node (r);
 +      process_node (r, &r->rsd);
  
 -      if ( r->stop_row != -1 && r->row > r->stop_row + 1)
 +      if ( r->stop_row != -1 && r->rsd.row > r->stop_row + 1)
        break;
  
 -      if (r->state == STATE_CELL &&
 -         r->node_type == XML_READER_TYPE_ELEMENT)
 +      if (r->rsd.state == STATE_CELL &&
 +         r->rsd.node_type == XML_READER_TYPE_ELEMENT)
        {
 -        type = xmlTextReaderGetAttribute (r->xtr, _xml ("office:value-type"));
 -        val_string = xmlTextReaderGetAttribute (r->xtr, _xml ("office:value"));
 +        type = xmlTextReaderGetAttribute (r->rsd.xtr, _xml ("office:value-type"));
 +        val_string = xmlTextReaderGetAttribute (r->rsd.xtr, _xml ("office:value"));
        }
  
 -      if (r->state == STATE_CELL_CONTENT && 
 -         r->node_type == XML_READER_TYPE_TEXT)
 +      if (r->rsd.state == STATE_CELL_CONTENT && 
 +         r->rsd.node_type == XML_READER_TYPE_TEXT)
        {
          int col;
          struct xml_value *xmv = xzalloc (sizeof *xmv);
 -        xmv->text = xmlTextReaderValue (r->xtr);
 +        xmv->text = xmlTextReaderValue (r->rsd.xtr);
          xmv->value = val_string;       
          xmv->type = type;
          val_string = NULL;
  
 -        for (col = 0; col < r->col_span; ++col)
 +        for (col = 0; col < r->rsd.col_span; ++col)
            {
              const struct variable *var;
 -            const int idx = r->col - col - r->start_col - 1;
 +            const int idx = r->rsd.col - col - r->start_col - 1;
              if (idx < 0)
                continue;
              if (r->stop_col != -1 && idx > r->stop_col - r->start_col )
              convert_xml_to_value (c, var, xmv);
            }
  
--        xmlFree (xmv->text);
--        xmlFree (xmv->value);
--        xmlFree (xmv->type);
--        free (xmv);
++        //      xmlFree (xmv->text);
++        //      xmlFree (xmv->value);
++        //      xmlFree (xmv->type);
++        //      free (xmv);
        }
 -      if ( r->state <= STATE_TABLE)
 +      if ( r->rsd.state <= STATE_TABLE)
        break;
      }
++#endif
  
    return c;
  }
index 662caba809ee04e05ff12117d5e906420963a115,20bac652ccab69c153e753e2f4684b0922e4114a..d24a23242d7a03810de1fff1545cbd679d82767d
@@@ -19,7 -19,6 +19,7 @@@
  #include "ui/gui/text-data-import-dialog.h"
  
  #include "page-intro.h"
 +#include "page-sheet-spec.h"
  #include "page-first-line.h"
  #include "page-separators.h"
  #include "page-formats.h"
@@@ -78,7 -77,6 +78,7 @@@ text_data_import_assistant (PsppireData
  {
    GtkWindow *parent_window = GTK_WINDOW (dw);
    struct import_assistant *ia = init_assistant (parent_window);
 +  struct sheet_spec_page *ssp ;
  
    if (!init_file (ia, parent_window))
      {
        return;
      }
  
 +  ssp = ia->sheet_spec;
  
 +  if (ia->spreadsheet)
 +    {
 +      ia->sheet_spec = sheet_spec_page_create (ia);
 +    }
 +  else
      {
        ia->intro = intro_page_create (ia);
-       ia->separators = separators_page_create (ia);
        ia->first_line = first_line_page_create (ia);
-       printf ("%s:%d %p\n", __FILE__, __LINE__, ia->intro);
+       ia->separators = separators_page_create (ia);
      }
    ia->formats = formats_page_create (ia);
  
        break;
      }
  
 +  if (ssp) 
      {
        destroy_formats_page (ia);
        destroy_separators_page (ia);
@@@ -233,7 -223,6 +232,7 @@@ generate_syntax (const struct import_as
  {
    struct string s = DS_EMPTY_INITIALIZER;
  
 +  if (ia->spreadsheet == NULL)
      {
        syntax_gen_pspp (&s,
                       "GET DATA"
        if (ia->file.encoding && strcmp (ia->file.encoding, "Auto"))
        syntax_gen_pspp (&s, "  /ENCODING=%sq\n", ia->file.encoding);
  
 -
        intro_append_syntax (ia->intro, &s);
  
  
        formats_append_syntax (ia, &s);
        apply_dict (ia->dict, &s);
      }
 -
 +  else
 +    {
 +      return sheet_spec_gen_syntax (ia);
 +    }
 +  
    return ds_cstr (&s);
  }