tracker.c 3.04 KB
#include <stdlib.h>
#include <errno.h>
#include <mailutils/types.h>
#include <mailutils/locus.h>

struct mu_locus_track
{
  char const *file_name;     /* Name of the source file */
  size_t max_lines;          /* Max. number of lines history kept by tracker */
  size_t head;               /* Bottom of stack */
  size_t level;              /* Number of elements on stack */
  unsigned hline;            /* Number of line corresponding to cols[head] */
  unsigned *cols;            /* Cyclic stack */
};

int
mu_locus_track_create (mu_locus_track_t *ret,
		       char const *file_name, size_t max_lines)
{
  int rc;
  struct mu_locus_track *trk;
  
  trk = malloc (sizeof *trk);
  if (!trk)
    return errno;
  trk->cols = calloc (max_lines, sizeof (trk->cols[0]));
  if (!trk->cols)
    {
      rc = errno;
      free (trk);
      return rc;
    }
  rc = mu_ident_ref (file_name, &trk->file_name);
  if (rc)
    {
      free (trk->cols);
      free (trk);
      return rc;
    }

  trk->max_lines = max_lines;
  trk->head = 0;
  trk->level = 0;
  trk->hline = 1;
  trk->cols[0] = 0;
  
  *ret = trk;
  return 0;
}

void
mu_locus_track_free (mu_locus_track_t trk)
{
  if (trk)
    {
      mu_ident_deref (trk->file_name);
      free (trk->cols);
      free (trk);
    }
}

void
mu_locus_track_destroy (mu_locus_track_t *trk)
{
  if (trk)
    {
      mu_locus_track_free (*trk);
      *trk = NULL;
    }
}   

size_t
mu_locus_track_level (mu_locus_track_t trk)
{
  return trk->level;
}

static inline unsigned *
cols_tos_ptr (mu_locus_track_t trk)
{
  return &trk->cols[(trk->head + trk->level) % trk->max_lines];
}

static inline unsigned
cols_peek (mu_locus_track_t trk, size_t n)
{
  return trk->cols[(trk->head + n) % trk->max_lines];
}

static inline unsigned *
push (mu_locus_track_t trk)
{
  unsigned *ptr;
  if (trk->level == trk->max_lines)
    {
      trk->head++;
      trk->hline++;
    }
  else
    trk->level++;
  *(ptr = cols_tos_ptr (trk)) = 0;
  return ptr;
}

static inline unsigned *
pop (mu_locus_track_t trk)
{
  if (trk->level == 0)
    {
      *cols_tos_ptr (trk) = 0;
      return NULL; //FIXME
    }
  trk->level--;
  return cols_tos_ptr (trk);
}

void
mu_locus_tracker_advance (struct mu_locus_track *trk,
			  struct mu_locus_range *loc,
			  char const *text, size_t leng)
{
  unsigned *ptr;

  if (text == NULL || leng == 0)
    return;
  
  loc->beg.mu_file = loc->end.mu_file = trk->file_name;
  loc->beg.mu_line = trk->hline + trk->level;
  ptr = cols_tos_ptr (trk);
  loc->beg.mu_col = *ptr + 1;
  while (leng--)
    {
      (*ptr)++;
      if (*text == '\n')
	ptr = push (trk);
      text++;
    }
  if (*ptr)
    {
      loc->end.mu_line = trk->hline + trk->level;
      loc->end.mu_col = *ptr;
    }
  else
    {
      loc->end.mu_line = trk->hline + trk->level - 1;
      loc->end.mu_col = cols_peek (trk, trk->level - 1) - 1;
    }
}

void
mu_locus_tracker_retreat (struct mu_locus_track *trk, size_t n)
{
  unsigned *ptr;

  ptr = cols_tos_ptr (trk);
  while (n--)
    {
      if (*ptr == 0)
	{
	  ptr = pop (trk);
	  if (!ptr)
	    break;
	}
      --*ptr;
    }
}