示例#1
0
int main
(  int               argc
,  const char*       argv[]
)
   {  int  a               =  0
   ;  mcxTing* argtxt      =  mcxTingEmpty(NULL, 10)

   ;  if (0)
      mclxIOsetQMode("MCLXIOVERBOSITY", MCL_APP_VB_NO)

   ;  opInitialize()       /* symtable etc */
   ;  globInitialize()     /* hdltable etc */

   ;  if (argc == 1)
      {  mcxTing* ops      =  mcxTingEmpty(NULL, 20)
      ;  mcxIO *xfin       =  mcxIOnew("-", "r")
      ;  mcxIOopen(xfin, EXIT_ON_FAIL)

      ;  fprintf
         (  stdout
         ,  "At your service: "
            "'[/<op>] help', '[/<str>] grep', 'ops', 'info', and 'quit'.\n"
         )

      ;  while (1)
         {  int ok
         ;  mcxTing* line  =  mcxTingEmpty(NULL, 30)

         ;  fprintf(stdout, "> ")
         ;  fflush(stdout)

         ;  if (STATUS_OK != mcxIOreadLine(xfin, line, MCX_READLINE_BSC))
            {  fprintf(stdout, "curtains!\n")
            ;  mcxTingFree(&line)
            ;  break
         ;  }
            mcxTingAppend(ops, line->str)
         ;  mcxTingFree(&line)

         ;  ok = zsDoSequence(ops->str)

         ;  if (ok && (v_g & V_STACK))
            zsList(0)

         ;  mcxTingEmpty(ops, 20)
      ;  }
      }
      else
      {  for (a=1;a<argc;a++)
         {  mcxTingWrite(argtxt, argv[a])
         ;  if (!zgUser(argtxt->str))
            mcxExit(1)
      ;  }
      }
      mcxTingFree(&argtxt)
   ;  return 0
;  }
示例#2
0
static mclx* process_queries
(  mcxIO* xq
,  mclx* mx
,  mclx* mxtp
,  mcxIO* xfmx
,  mclTab* tab
,  mcxIO* xfout
,  mcxIO* xfpath
,  mcxIO* xfstep
)
   {  mcxTing* line = mcxTingEmpty(NULL, 100)
   ;  mcxTing* sa = mcxTingEmpty(NULL, 100)
   ;  mcxTing* sb = mcxTingEmpty(NULL, 100)
   ;  SSPxy* sspo = mclgSSPxyNew(mx, mxtp)

   ;  mcxIOopen(xq, EXIT_ON_FAIL)

   ;  while (1)
      {  long a = -1, b = -2, ns = 0
      ;  mcxbool query = FALSE
      ;  if (isatty(fileno(xq->fp)))
         fprintf
         (  stdout
         ,  "\n(ready (expect two %s or : directive))\n"
         ,  tab ? "labels" : "graph indices"
         )
      ;  if
         (  STATUS_OK != mcxIOreadLine(xq, line, MCX_READLINE_CHOMP)
            || !strcmp(line->str, ".")
         )
         break

      ;  query = (u8) line->str[0] == ':'

      ;  if (query && (line->len == 1 || isspace((unsigned char) line->str[1])))
         {  fprintf(xfout->fp, "-->\n")
         ;  fprintf(xfout->fp, ":tf <tf-spec>\n")
         ;  fprintf(xfout->fp, ":top <num>\n")
         ;  fprintf(xfout->fp, ":list <node>\n")
         ;  fprintf(xfout->fp, ":clcf <node>\n")
         ;  fprintf(xfout->fp, ":reread>\n")
         ;  fprintf(xfout->fp, "<--\n")
         ;  continue
      ;  }

         mcxTingEnsure(sa, line->len)
      ;  mcxTingEnsure(sb, line->len)

      ;  ns = sscanf(line->str, "%s %s", sa->str, sb->str)
      ;  if (ns == 2)
            sa->len = strlen(sa->str)
         ,  sb->len = strlen(sb->str)
      ;  else
            sa->len = strlen(sa->str)
         ,  sb->len = 0
         ,  sb->str[0] = '\0'

      ;  if (!query && ns != 2)
         {  if (line->len)
            fprintf(stderr, "(error expect two nodes or : directive)\n")
         ;  continue
      ;  }

         if (query)
         {  mx = handle_query(mx, xfmx, sa, sb)
         ;  sspo->mx = mx                 /* fixme improve ownership handling */
         ;  sspo->mxtp = mx
         ;  fprintf(xfout->fp, "%s\n\n", line->str)
         ;  continue                      /* fixme improve flow */
      ;  }
         else if (tab)
         {  mcxKV* kv

         ;  if ((kv = mcxHashSearch(sa, hsh_g, MCX_DATUM_FIND)))
            a = VOID_TO_ULONG kv->val     /* fixme (> 2G labels) */
         ;  else
            {  label_not_found(sa)
            ;  continue
         ;  }

            if ((kv = mcxHashSearch(sb, hsh_g, MCX_DATUM_FIND)))
            b = VOID_TO_ULONG kv->val     /* fixme (> 2G labels) */
         ;  else
            {  label_not_found(sb)
            ;  continue
         ;  }
         }
         else if (mcxStrTol(sa->str, &a, NULL) || mcxStrTol(sb->str, &b, NULL))
         {  fprintf(stderr,  "(error failed-reading-number)\n")
         ;  continue
      ;  }

         if (check_bounds(mx, a))
         continue
      ;  if (check_bounds(mx, b))
         continue

      ;  fprintf
         (  xfout->fp
         ,  "\n(lattice\n"
            "   (anchors %s %s)\n"
         ,  sa->str
         ,  sb->str
         )

      ;  if (0 && a == b)
         {  fprintf
            (  xfout->fp
            ,  "  (path-length 0)\n"
               "(data\n"
            )
      ;  }
         else
         {  mcxstatus thestat = mclgSSPxyQuery(sspo, a, b)
         ;  dim t

         ;  if (thestat)
            fprintf(xfout->fp,  "   (path-length -2)\n(data\n")
         ;  else if (sspo->length < 0)       /* not in same component */
            fprintf(xfout->fp,  "   (path-length -1)\n(data\n")
         ;  else
            {  fprintf
               (  xfout->fp
               ,  "   (path-length %d)\n"
                  "(data\n"
               ,  (int) sspo->length
               )

            ;  if (sspo->length == 1)
               {  if (tab)
                  fprintf(xfout->fp, "((%s %s))\n", sa->str, sb->str)
               ;  else
                  fprintf(xfout->fp, "((%ld %ld))\n", (long) a, (long) b)
            ;  }
               else
               for (t=0; t< N_COLS(sspo->pathmx)-1; t++)
               erdos_link_together(xfout, mx, sspo->pathmx->cols+t, sspo->pathmx->cols+t+1)

            ;  fputs(")\n", xfout->fp)
            ;  fprintf(xfout->fp, "   (anchors %s %s)\n", sa->str, sb->str)
            ;  fprintf(xfout->fp, "   (considered %d)\n", (int) sspo->n_considered)
            ;  fprintf(xfout->fp, "   (participants %d)\n", (int) sspo->n_involved)
            ;  fprintf(xfout->fp, "   (path-length %d)\n", (int) sspo->length)
         ;  }
         }

         fprintf(xfout->fp, ")\n\n")

      ;  if (xfpath)
         mclxWrite(sspo->pathmx, xfpath, MCLXIO_VALUE_NONE, RETURN_ON_FAIL)
      ;  if (xfstep)
         mclxWrite(sspo->stepmx, xfstep, MCLXIO_VALUE_GETENV, RETURN_ON_FAIL)

      ;  mclgSSPxyReset(sspo)
   ;  }
      mcxTingFree(&sa)
   ;  mcxTingFree(&sb)
   ;  mcxTingFree(&line)
   ;  mclgSSPxyFree(&sspo)
   ;  return mx
;  }
示例#3
0
static mcxstatus read_123
(  mcxIO* xf
,  mcxTing* buf
,  stream_state* iface
,  mclxIOstreamer* streamer
,  double* value
,  mcxbits bits
)
   {  mcxstatus status = mcxIOreadLine(xf, buf, MCX_READLINE_CHOMP)
   ;  int cv = 0
   ;  const char* printable
   ;  const char* me = module
   ;  mcxbool strict = bits & MCLXIO_STREAM_STRICT
   ;  mcxbool warn   = bits & MCLXIO_STREAM_WARN
   ;  unsigned long x = 0, y = 0

   ;  while (1)
      {  if (status)
         break

      ;  status = STATUS_FAIL

      ;  printable = mcxStrChrAint(buf->str, isspace, buf->len)
      ;  if (printable && (unsigned char) printable[0] == '#')
         {  status = mcxIOreadLine(xf, buf, MCX_READLINE_CHOMP)
         ;  continue
      ;  }

         cv = sscanf(buf->str, "%lu%lu%lf", &x, &y, value)

      ;  if (x > LONG_MAX || y > LONG_MAX)
         {  mcxErr
            (me, "negative values in input stream? unsigned %lu %lu", x, y)
         ;  break
      ;  }

         if (cv == 2)
         *value = 1.0

      ;  else if (cv != 3)
         {  if (strict || warn)
            mcxErr
            (  module
            ,  "123-parser chokes at line %ld [%s]"
            ,  (long) xf->lc
            ,  buf->str
            )
         ;  if (strict)
            break

         ;  status = mcxIOreadLine(xf, buf, MCX_READLINE_CHOMP)
         ;  continue
      ;  }
         else if (!(*value < FLT_MAX))
         *value = 1.0

      ;  if
         (  (streamer->cmax_123 && x >= streamer->cmax_123)
         || (streamer->rmax_123 && y >= streamer->rmax_123)
         )
         {  status = STATUS_IGNORE
         ;  break
      ;  }

         status = STATUS_OK
      ;  break
   ;  }

      if (!status)
      {  iface->x = x
      ;  iface->y = y
      ;  if (iface->map_c->max_seen+1 < x+1)    /* note mixed-sign comparison */
         iface->map_c->max_seen = x
      ;  if (iface->map_r->max_seen+1 < y+1)    /* note mixed-sign comparison */
         iface->map_r->max_seen = y
   ;  }

      return status
;  }
示例#4
0
static mcxstatus read_abc
(  mcxIO* xf
,  mcxTing* buf
,  stream_state *iface
,  double* value
)
   {  mcxstatus status  =  mcxIOreadLine(xf, buf, MCX_READLINE_CHOMP)
   ;  mcxTing* xkey     =  mcxTingEmpty(NULL, buf->len)
   ;  mcxTing* ykey     =  mcxTingEmpty(NULL, buf->len)
   ;  mcxbits bits      =  iface->bits

   ;  mcxbool strict    =  bits & MCLXIO_STREAM_STRICT
   ;  mcxbool warn      =  bits & MCLXIO_STREAM_WARN

   ;  mcxbool label_cbits = bits & (MCLXIO_STREAM_CTAB_STRICT | MCLXIO_STREAM_CTAB_RESTRICT)
   ;  mcxbool label_rbits = bits & (MCLXIO_STREAM_RTAB_STRICT | MCLXIO_STREAM_RTAB_RESTRICT)
   ;  mcxbool label_dbits = bits & (MCLXIO_STREAM_WARN | MCLXIO_STREAM_DEBUG)

   ;  const char* printable
   ;  int cv = 0

   ;  iface->statusx =  STATUS_OK
   ;  iface->statusy =  STATUS_OK

   ;  do
      {  int xlen = 0
      ;  int ylen = 0

      ;  if (status)
         break

      ;  printable = mcxStrChrAint(buf->str, isspace, buf->len)
      ;  if (printable && (uchar) printable[0] == '#')
         {  status = mcxIOreadLine(xf, buf, MCX_READLINE_CHOMP)
         ;  continue
      ;  }

         mcxTingEnsure(xkey, buf->len)    /* fixme, bit wasteful */
      ;  mcxTingEnsure(ykey, buf->len)    /* fixme, bit wasteful */

      ;  cv =     strchr(buf->str, '\t')
               ?  sscanf(buf->str, "%[^\t]\t%[^\t]%lf", xkey->str, ykey->str, value)
               :  sscanf(buf->str, "%s%s%lf", xkey->str, ykey->str, value)

      /* WARNING: [xy]key->len have to be set.
       * we first check sscanf return value
      */

      ;  if (cv == 2)
         *value = 1.0

      ;  else if (cv != 3)
         {  if (warn || strict)
            mcxErr
            (  module
            ,  "abc-parser chokes at line %ld [%s]"
            ,  (long) xf->lc
            ,  buf->str
            )
         ;  if (strict)
            {  status = STATUS_FAIL
            ;  break
         ;  }
            status = mcxIOreadLine(xf, buf, MCX_READLINE_CHOMP)
         ;  continue
      ;  }
         else if (!(*value <= FLT_MAX))  /* should catch nan, inf */
         *value = 1.0

      ;  xlen = strlen(xkey->str)
      ;  ylen = strlen(ykey->str)

      ;  xkey->len = xlen
      ;  ykey->len = ylen

      ;     status
         =  iface->statusx
         =  handle_label(&xkey, &(iface->x), iface->map_c, label_cbits | label_dbits, "col")

      ;  if (status == STATUS_FAIL || status == STATUS_IGNORE)
         break

      ;     status
         =  iface->statusy
         =  handle_label(&ykey, &(iface->y), iface->map_r, label_rbits | label_dbits, "row")
      ;  if (status == STATUS_FAIL || status == STATUS_IGNORE)
         break

      ;  status = STATUS_OK     /* Note: status can never be STATUS_NEW */
      ;  break
   ;  }
      while (1)

;if(DEBUG2) fprintf(stderr, "read_abc status %s\n", MCXSTATUS(status))
   ;  if (status == STATUS_NEW)
      mcxErr(module, "read_abc panic, because status == STATUS_NEW")

               /* Below we remove the key from the map if it should be
                * ignored. It will be freed in the block following this one.
               */
   ;  if
      (   iface->statusx == STATUS_NEW
      && (iface->statusy == STATUS_FAIL || iface->statusy == STATUS_IGNORE)
      )
      {  mcxHashSearch(xkey, iface->map_c->map, MCX_DATUM_DELETE)
      ;  iface->map_c->max_seen--
      ;  iface->statusx = STATUS_IGNORE
   ;  }
      else if   /* Impossible (given that we break when iface->statusx) but defensive */
      (   iface->statusy == STATUS_NEW
      && (iface->statusx == STATUS_FAIL || iface->statusx == STATUS_IGNORE)
      )
      {  mcxHashSearch(ykey, iface->map_r->map, MCX_DATUM_DELETE)
      ;  iface->map_r->max_seen--
      ;  iface->statusy = STATUS_IGNORE
   ;  }

         /* NOTE handle_label might have set either to NULL but
          * that's OK.  This is needed because handle_label(&xkey)
          * might succeed and free xkey (because already present in
          * map_c->map); then when handle_label(&ykey) fails we need to
          * clean up.
         */
   ;  if (status)
      {  mcxTingFree(&xkey)   /* kv deleted if iface->statusx == STATUS_NEW */
      ;  mcxTingFree(&ykey)   /* kv deleted if iface->statusy == STATUS_NEW */
   ;  }

      return status
;  }
示例#5
0
   /* Purpose: read a single x/y combination. The x may be cached
    * due to the etc format, where a single line always refers to the same x
    * and that x is listed only at the start or line, or omitted with
    * the etc-ai and 235-ai formats.
    *
    * state->x_prev may be used by read_etc in order to obtain the
    * current x index.
   */
static mcxstatus read_etc
(  mcxIO*         xf
,  stream_state  *iface
,  etc_state     *state
,  double*        value
)
   {  mcxbits bits      =  iface->bits
   ;  FILE* stdbug      =  stdout

   ;  mcxstatus status  =  STATUS_OK
   ;  mcxTing* ykey     =  NULL
   ;  mcxTing* xkey     =  NULL
   ;  const char* printable

   ;  mcxbool label_cbits = bits & (MCLXIO_STREAM_CTAB_STRICT | MCLXIO_STREAM_CTAB_RESTRICT)
   ;  mcxbool label_rbits = bits & (MCLXIO_STREAM_RTAB_STRICT | MCLXIO_STREAM_RTAB_RESTRICT)
   ;  mcxbool label_dbits = bits & (MCLXIO_STREAM_WARN | MCLXIO_STREAM_DEBUG)

   ;  iface->statusx    =  STATUS_OK
   ;  iface->statusy    =  STATUS_OK

   ;  iface->x =  state->x_prev
   ;  *value   =  1.0

;if(DEBUG)fprintf(stdbug, "read_etc initially set x to %d\n", (int) iface->x)

   ;  if (!state->etcbuf)
      state->etcbuf = mcxTingEmpty(NULL, 100)

   ;  do
      {  int n_char_read = 0
      ;  if (state->etcbuf->len != state->etcbuf_check)
         {  mcxErr
            (  module
            ,  "read_etc sanity check failed %ld %ld"
            ,  (long) state->etcbuf->len
            ,  (long) state->etcbuf_check
            )
         ;  status = STATUS_FAIL
         ;  break
      ;  }
                                             /* do we need to read a line ?   */
                                             /* -> then set iface->x          */
               /* fixmefixme: funcify this */
               /* iface->x can only be changed in this branch */
/* ************************************************************************** */
         if (state->etcbuf_ofs >= state->etcbuf->len)
         {  state->etcbuf_ofs  = 0
         ;  state->n_y = 0
         ;  if ((status = mcxIOreadLine(xf, state->etcbuf, MCX_READLINE_CHOMP)))
            break
         ;  state->etcbuf_check = state->etcbuf->len

         ;  if
            (  !(printable = mcxStrChrAint(state->etcbuf->str, isspace, -1))
            || (unsigned char) *printable == '#'
            )
            {  state->etcbuf_ofs = state->etcbuf->len
            ;  iface->statusy = STATUS_IGNORE
            ;  break    /* fixme: ^ statusx seems to work as well. cleanify design */
         ;  }

         ;  if (bits & (MCLXIO_STREAM_ETC_AI | MCLXIO_STREAM_235_AI))
            {
            }
                     /* In this branch we do not issue handle_label, so we take care of max_seen.
                     */
            else if (bits & MCLXIO_STREAM_235)
            {  if (1 != sscanf(state->etcbuf->str, "%lu%n", &(iface->x), &n_char_read))
               {  iface->statusx = STATUS_FAIL  
               ;  break
            ;  }
               state->etcbuf_ofs += n_char_read
            ;  if (iface->map_c->max_seen+1 < iface->x+1)      /* note mixed-sign comparison */
               iface->map_c->max_seen = iface->x
            ;  state->x_prev = iface->x
         ;  }
            else if (bits & MCLXIO_STREAM_ETC)
            {  xkey = mcxTingEmpty(NULL, state->etcbuf->len)
            ;  if (1 != sscanf(state->etcbuf->str, "%s%n", xkey->str, &n_char_read))
               break
            ;  state->etcbuf_ofs += n_char_read
            ;  xkey->len = strlen(xkey->str)
            ;  xkey->str[xkey->len] = '\0'
;if(DEBUG3)fprintf(stderr, "max %lu\n", (ulong) iface->map_c->max_seen)
            ;  iface->statusx
                = handle_label(&xkey, &(iface->x), iface->map_c, label_cbits | label_dbits, "col")
;if(DEBUG3)fprintf(stderr, "max %lu x %lu\n", (ulong) iface->map_c->max_seen, (ulong) iface->x)
            ;  if (iface->statusx == STATUS_IGNORE || iface->statusx == STATUS_FAIL)
               {  /* iface->x = 141414  recentlyadded */
;if(DEBUG3)fprintf(stderr, "max %lu\n", (ulong) iface->map_c->max_seen)
               ;  break
            ;  }
                     /* ^ Consider what happens when we break here (x label not
                      * accepted) with map_c->max_seen.  Basically x label is
                      * indepedent of y, so we never need to undo the
                      * handle_label action.
                     */
               state->x_prev = iface->x
         ;  }
            else
            mcxDie(1, module, "strange, really")
      ;  }
/* ************************************************************************** */

         if
         ( !(  printable
            =  mcxStrChrAint(state->etcbuf->str+state->etcbuf_ofs, isspace, -1)
            )
         || (uchar) *printable == '#'
         )
         {  state->etcbuf_ofs = state->etcbuf->len
         ;  /* iface->y = 141414 recentlyadded */
         ;  iface->statusy = STATUS_IGNORE
         ;  break
      ;  }

         if (bits & (MCLXIO_STREAM_235_AI | MCLXIO_STREAM_235))
         {  if (1 != sscanf(state->etcbuf->str+state->etcbuf_ofs, "%lu%n", &(iface->y), &n_char_read))
            {  char* s = state->etcbuf->str+state->etcbuf_ofs
            ;  while(isspace((uchar) s[0]))
               s++
            ;  mcxErr
               (  module
               ,  "unexpected string starting with <%c> on line %lu"
               ,  (int) ((uchar) s[0])
               ,  xf->lc
               )
            ;  iface->statusy = STATUS_FAIL
         ;  }
            else
            {
;if(DEBUG3)fprintf(stdbug, "hit at %d\n", (int) state->etcbuf_ofs);
               state->etcbuf_ofs += n_char_read
            ;  if (iface->map_r->max_seen+1 < iface->y+1)      /* note mixed-sign comparison */
               iface->map_r->max_seen = iface->y
         ;  }
         }
         else  /* ETCANY */
         {  ykey = mcxTingEmpty(NULL, state->etcbuf->len)
         ;  if (1 != sscanf(state->etcbuf->str+state->etcbuf_ofs, "%s%n", ykey->str, &n_char_read))
            break

         ;  ykey->len = strlen(ykey->str)
         ;  ykey->str[ykey->len] = '\0'
         ;  state->etcbuf_ofs += n_char_read

         ;  iface->statusy
            =  handle_label(&ykey, &(iface->y), iface->map_r, label_rbits | label_dbits, "row")
      ;  }

                  /* this won't scale well in terms of organisation if  and when
                   * tabs are allowed with 235 mode, because in that case,
                   * with 235-ai and restrict-tabr and extend-tabc we will
                   * need the stuff below duplicated in the 235 branch above.

                   * what happens here is that we only decide now whether
                   * the auto-increment is actually happening. It depends
                   * on there being at least one y that was not rejected.
                  */
      ;  if
         (  (bits & (MCLXIO_STREAM_ETC_AI | MCLXIO_STREAM_235_AI))
         && (iface->statusy == STATUS_OK || iface->statusy == STATUS_NEW)
         && !state->n_y
         )
         {  iface->x = state->x_prev + 1         /* works first time around */
         ;  iface->map_c->max_seen = state->x_prev + 1
         ;  state->n_y++
         ;  state->x_prev = iface->x
      ;  }

;if(DEBUG2)fprintf(stdbug, "etc handle label we have y %d status %s\n", (int) iface->y, MCXSTATUS(iface->statusy))
;     }
      while (0)

;if(DEBUG2)fprintf(stdbug, "status %s\n", MCXSTATUS(status))
   ;  do
      {  if (status)    /* e.g. STATUS_DONE (readline) [or STATUS_IGNORE (#)]*/
         break

                  /* below iface->statusy == STATUS_NEW should be impossible
                   * given this clause and the code sequence earlier.
                  */
      ;  if (iface->statusx == STATUS_FAIL || iface->statusx == STATUS_IGNORE)
         {  mcxTingFree(&xkey)
         ;  status = iface->statusx
         ;  break
      ;  }

                  /* case iface->statusx == STATUS_NEW is *always* honored
                  */
      ;  if (iface->statusy == STATUS_FAIL || iface->statusy == STATUS_IGNORE)
         {  mcxTingFree(&ykey)
         ;  status = iface->statusy
         ;  break
      ;  }
      }
      while (0)

   ;  if (status == STATUS_IGNORE || status == STATUS_FAIL)
      mcxTingFree(&ykey)

                  /* fixme, the action in this branch is done in other places too. cleanify design */
   ;  if
      (  iface->statusx == STATUS_IGNORE
      || !mcxStrChrAint(state->etcbuf->str+state->etcbuf_ofs, isspace, -1)
      )
      state->etcbuf_ofs = state->etcbuf->len

;if(DEBUG3)fprintf
( stdbug, "read_etc %s return x(%s -> %d stat=%s) y(%s -> %d stat=%s) status %s buf %d %d c_max_seen %lu\n"
,  MCXSTATUS(status)
, (xkey ? xkey->str : "-"), (int) iface->x, MCXSTATUS(iface->statusx)
, (ykey ? ykey->str : "-"), (int) iface->y, MCXSTATUS(iface->statusy)
, MCXSTATUS(status), (int) state->etcbuf->len, (int) state->etcbuf_ofs
,  (ulong) iface->map_c->max_seen
)
   ;  return status
;  }
示例#6
0
static dim do_a_file
(  aggr** collectpp
,  const char* fname
,  dim collect_n
)
   {  mcxIO* xf = mcxIOnew(fname, "r")
   ;  mcxTing* buf = mcxTingEmpty(NULL, 100)
   ;  mcxTing* lbl = mcxTingEmpty(NULL, 100)
   ;  mcxstatus status = STATUS_OK
   ;  aggr* collect = *collectpp
   ;  dim ct = 0, collect_alloc = 0

   ;  if (!collect_n)
         collect_alloc = 100
      ,  collect = mcxNAlloc(collect_alloc, sizeof collect[0], NULL, EXIT_ON_FAIL)

   ;  mcxIOopen(xf, EXIT_ON_FAIL)

   ;  while (STATUS_OK == (status = mcxIOreadLine(xf, buf, MCX_READLINE_CHOMP)))
      {  double val
      ;  const char* tabchar = NULL
      ;  mcxbool get_header = collect_g != 'p' && !ct ? TRUE : FALSE

      ;  mcxTingEnsure(lbl, buf->len)

               /* if header_g && !ct && !paste create/check label */
               /* body of this while loop does too many things, refactor */
      ;  if (collect_g == 'p' || get_header)
         {  if (!(tabchar = strchr(buf->str, '\t')))
            mcxDie(1, me, "paste error at line %d file %s (no tab)", (int) xf->lc, fname)
         ;  mcxTingNWrite(lbl, buf->str, tabchar - buf->str)
      ;  }
         else
         {  if (2 != sscanf(buf->str, "%s%lg", lbl->str, &val))
            mcxDie(1, me, "parse error at line %d file %s", (int) xf->lc, fname)
         ;  lbl->len = strlen(lbl->str)
      ;  }

         if (!collect_n)
         {  if (ct >= collect_alloc)
            {  dim collect_realloc = collect_alloc * 1.44
            ;  collect = mcxNRealloc(collect, collect_realloc, collect_alloc, sizeof collect[0], NULL, EXIT_ON_FAIL)
            ;  collect_alloc = collect_realloc
         ;  }
            collect[ct].label = mcxTingStr(lbl)
         ;  collect[ct].val = collect_g == 'p' || get_header ? 0.0 : val
         ;  collect[ct].columns
            =      collect_g == 'p' || get_header
                ?  mcxTingNew(tabchar + (get_header ? 1 : 0))
                :  NULL
      ;  }
         else
         {  if (ct >= collect_n)
            mcxDie(1, me, "additional lines in file %s", fname)
         ;  if (strcmp(collect[ct].label, lbl->str))
            mcxDie
            (  1
            ,  me
            ,  "label conflict %s/%s at line %d in file %s"
            ,  collect[ct].label
            ,  lbl->str
            ,  (int) xf->lc, fname
            )
         ;  if (get_header)            /* only need to check identity */
            {  if (strcmp(tabchar+1, collect[ct].columns->str))
               mcxDie(1, me, "different columns <%s> and <%s>", collect[ct].columns->str, tabchar+1)
         ;  }
            else if (collect_g == 'p')          /* tack it on */
            mcxTingNAppend(collect[ct].columns, tabchar, buf->len - lbl->len)
         ;  else
            collect[ct].val += val 
      ;  }
         ct++
   ;  }

      if (collect_n)
      {  if (ct != collect_n)
         mcxDie(1, me, "not enough lines in file %s", fname)
   ;  }
      else
      {  if (!ct)
         mcxDie(1, me, "empty file(s)")
      ;  *collectpp = collect
   ;  }

      mcxIOfree(&xf)
   ;  return ct
;  }