/*
 * sid_list.h: Part of GNU CSSC.
 * 
 * 
 *    Copyright (C) 1997,2001 Free Software Foundation, Inc. 
 * 
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 * 
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 * 
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
 * 
 * CSSC was originally Based on MySC, by Ross Ridge, which was 
 * placed in the Public Domain.
 *
 *
 * Defines the template range_list.
 *
 * $Id: sid_list.h,v 1.16 2001/09/29 19:39:41 james_youngman Exp $
 * @(#) CSSC sid_list.h 1.1 93/11/09 17:17:51
 *
 */

#ifndef CSSC__SID_LIST_H__
#define CSSC__SID_LIST_H__

#ifdef __GNUC__
// #pragma this does not work with templates interface
#endif

template <class TYPE>
struct range
{
  TYPE from;
  TYPE to;
  range<TYPE> *next;
};

template <class TYPE>
class range_list
{
public:
  // constructors & destructors.
  ~range_list();
  range_list();
  range_list(const char *list);
  range_list(range_list const &list);
  range_list &operator =(range_list const &list);

  // query functions.
  int valid() const;
  int empty() const;
  int member(TYPE id) const;
  
  // manipulation.
  void invalidate();
  range_list &merge  (range_list const &list);
  range_list &remove (range_list const &list);

  // output.
  int print(FILE *out) const;

private:  
  // Data members.
  range<TYPE> *head;
  int valid_flag;

  // Implementation.
  void destroy();
  static range<TYPE> *do_copy_list(range<TYPE> *);

  int clean(); // clean the range list eliminating overlaps etc.
};


template <class TYPE> range_list<TYPE>::range_list(const char *list)
    : head(NULL), valid_flag(1)
{
  const char *s = list;
  if (s == NULL || *s == '\0')
    {
      return;
    }
  
  do
    {
      const char *comma = strchr(s, ',');
      
      if (comma == NULL)
        {
          comma = s + strlen(s);
        }
      
      char buf[64];
      size_t len = comma - s;
      if (len > sizeof(buf) - 1)
        {
          ctor_fail(-1, "Range in list too long: '%s'", list);
        }
      else if (len)
        {
          /* SourceForge bug number #438857:
           * ranges like "1.1.1.2," cause an assertion 
           * failure while SCCS just ignores the empty list item.
           * Hence we introduce the conditional surrounding this 
           * block.
           */
          memcpy(buf, s, len);
          buf[len] = '\0';
          
          char *dash = strchr(buf, '-');
          range<TYPE> *p = new range<TYPE>;
          
          if (dash == NULL)
            {
              p->to = p->from = TYPE(buf);
            }
          else
            {
              *dash++ = '\0';
              p->from = TYPE(buf);
              p->to = TYPE(dash);
            }
          
          p->next = head;
          head = p;
        }
      s = comma;
    } while(*s++ != '\0');
  
  if (clean())                  // returns invalid flag.
    {
      destroy();
      head = NULL;
      invalidate();
    }
  else
    {
      ASSERT(valid());
    }
}

template <class TYPE>
int
range_list<TYPE>::clean()
{
  if (!valid())
    return 1;
  
  range<TYPE> *sp = head;
  range<TYPE> *new_head = NULL;
  while (sp != NULL)
    {
      range<TYPE> *next_sp = sp->next;

      if (sp->from <= sp->to)
        {
          range<TYPE> *dp = new_head;
          range<TYPE> *pdp = NULL;
          
          TYPE sp_to_1 = sp->to;
          TYPE sp_from_1 = sp->from;
          ++sp_to_1;
          --sp_from_1;

          while (dp != NULL && dp->to < sp_from_1)
            {
              pdp = dp;
              dp = dp->next;
            }

          while (dp != NULL && dp->from <= sp_to_1)
            {
              /* While sp overlaps dp, merge dp into sp. */
              if (dp->to > sp->to)
                {
                  sp_to_1 = sp->to = dp->to;
                  ++sp_to_1;
                }
              if (dp->from < sp->from)
                {
                  sp->from = dp->from;
                }

              range<TYPE> *next_dp = dp->next;
              delete dp;
              dp = next_dp;
              if (pdp == NULL)
                {
                  new_head = dp;
                }
              else
                {
                  pdp->next = dp;
                }
            }
          if (pdp == NULL)
            {
              sp->next = new_head; 
              new_head = sp;
            }
          else
            {
              sp->next = pdp->next;
              pdp->next = sp;
            }
        }
      else
        {
          invalidate();
          delete sp;
        }
      sp = next_sp;
    }
  head = new_head;
  return !valid_flag;
}               
                                
template <class TYPE>
int
range_list<TYPE>::member(TYPE id) const
{
  const range<TYPE> *p = head;

  while (p)
    {
      if (p->from <= id && id <= p->to)
        {
          return 1;             // yes
        }
      p = p->next;
    }
  return 0;                     // no
}

template <class TYPE>
void
range_list<TYPE>::destroy()
{
  if (!valid())
      return;

  range<TYPE> *p = head;

  while(p != NULL)
    {
      range<TYPE> *np = p->next;
      delete p;
      p = np;
    }
  head = NULL;
}

template <class TYPE> 
range<TYPE> *
range_list<TYPE>::do_copy_list(range<TYPE> *p) // static member.
{
  if (p == NULL)
    {
      return NULL;
    }
  range<TYPE> *copy_head = new range<TYPE>;
  range<TYPE> *np = copy_head;
  
  while(1)
    {
      np->from = p->from;
      np->to = p->to;
      
      p = p->next;
      if (p == NULL)
        {
          break;
        }

      np = np->next = new range<TYPE>;
    }

  np->next = NULL;
  return copy_head;
}               

template <class TYPE>
range_list<TYPE>::range_list(range_list const &list)
{
  valid_flag = 1;
  ASSERT(list.valid());
  head = do_copy_list(list.head);
  ASSERT(valid());
}       

template <class TYPE>
range_list<TYPE> &
range_list<TYPE>::operator =(range_list<TYPE> const &list)
{
  ASSERT(valid());
  ASSERT(list.valid());

  range<TYPE> *p = do_copy_list(list.head);
  destroy();
  head = p;

  ASSERT(valid());
  return *this;
}       


template <class TYPE>
range_list<TYPE>::range_list(): head(0), valid_flag(1) 
{
}

template <class TYPE>
int
range_list<TYPE>::valid() const
{
  return valid_flag;
}

template <class TYPE>
int
range_list<TYPE>::empty() const
{
  return head ? 0 : 1;
}

template <class TYPE>
void
range_list<TYPE>::invalidate()
{
  valid_flag = 0;
}

template <class TYPE>
range_list<TYPE>::~range_list()
{
  destroy();
  invalidate();
}

template <class TYPE>
int
range_list<TYPE>::print(FILE *out) const
{
  if (empty() || !valid())
    {
      return 0;
    }


  range<TYPE> *p = head;
  while (1)
    {
      if (p->from.print(out))
        {
          return 1;
        }
      if (p->to != p->from
          && (putc('-', out) == EOF
              || p->to.print(out)))
        {
          return 1;
        }

      p = p->next;
      if (p == NULL)
        {
          return 0;
        }

      if (putc(',', out) == EOF)
        {
          return 1;
        }
    }
}

#ifdef TEST

extern "C" int isatty(int);

void usage()
{
}

void prompt()
{
  fputs("\n> ", stdout);
  fflush(stdout);
}

int
main()
{
  cssc_linebuf linebuf;

  // create & destroy a sid_list first.
  if (1)
    {
      sid_list x;
    }
  
  // Other constructors...
  if (1)
    {
      sid_list a;
      sid_list b(a);
      sid_list c = a;
    }
  
  if (!isatty(0))
    {
      printf("No interactive test -- stdin is not a tty.\n");
      return 0;
    }

  for (prompt(); !linebuf.read_line(stdin); prompt())
    {
      linebuf[strlen(linebuf) - 1] = '\0';
      printf("\"%s\"\n -> ", (const char *) linebuf);
      
      sid_list test(linebuf);
      
      fputs("\"", stdout);
      test.print(stdout);
      fputs("\"", stdout);
    }
  return 0;
}

// Explicit template instantiations -- only when TEST is defined.
//template class range_list<sid>;
//template class list<sid>;
//template class list<mystring>;

//#include "stack.h"
//template class stack<unsigned short>;

//#include "sid_list.h"
//template class range_list<release>;

#endif
                        

                
/* Local variables: */
/* mode: c++ */
/* End: */

#endif /* __SID_LIST_H__ */
        
/* Local variables: */
/* mode: c++ */
/* End: */