/* SV: Normally I use an list-class for all my lists. Therefore I haven't
** tested the changes very much (movelistnode2()).
*/

-> buildlist.e - Example which uses an application-specific Exec list

OPT MODULE, REG = 5

MODULE 'exec/lists', 'exec/nodes', 'amigalib/lists'

CONST DATASIZE = 512     /* Increased for our huge path strings */

OBJECT nameNode
  ln:ln               -> System Node structure
  data[DATASIZE]:ARRAY  -> Node-specific data
ENDOBJECT

CONST NAMENODE_ID=100  -> The type of 'nameNode'

-> Allocate a NameNode structure, copy the given name into the structure, then
-> add it the specified list.  This example does not provide an error return
-> for the out of memory condition.
-> E-Note: ...instead it raises an exception which is handled by the caller
EXPORT PROC addName(list:PTR TO lh, name, prednode = NIL:PTR TO ln)
  DEF namenode:PTR TO nameNode
  NEW namenode
  -> E-Note: copy *safely* to namenode.data
  AstrCopy(namenode.data, name, DATASIZE)
  namenode.ln.name := namenode.data
  namenode.ln.type := NAMENODE_ID
  namenode.ln.pri  := 0
  IF prednode
    Insert(list, namenode, prednode)
  ELSE
    AddTail(list, namenode)
  ENDIF
ENDPROC

/*
EXPORT PROC freeNode(list:PTR TO lh, num)
  DEF node:PTR TO ln, count, found

  IF list.tailpred <> list
    count := 0
    node  := list.head
    found := FALSE
    WHILE (found = FALSE) AND (node.succ)
      IF count = num
        found := TRUE
      ELSE
        node := node.succ
        INC count
      ENDIF
    ENDWHILE
  ENDIF
  IF found
    Remove(node)
    IF node.succ
      RETURN num
    ELSE
      RETURN num - 1
    ENDIF
  ENDIF
ENDPROC

** SV: this proc does not free an node!! It just removes it from the
** list but does not free the memory for the node (because it don't
** know the size).
**
** An working example (for removeNode()) is eq.:
**  DEF namednode:PTR TO nameNode
**    IF namednode:=removeNode(list,num)
**      END namednode
**    ENDIF
*/
/* removes the 'num'-th node from the execlist and returns it or NIL.
** you must free the node's memory
** 'num' should be 0..(countnodes(list)-1)
*/
EXPORT PROC removeNode(list:PTR TO lh,num)
  DEF node:PTR TO ln

  /* search the node
  */
  IF node:=getNode(list,num)
    /* Remove it from the list.
    ** Does *not* free the node's memory
    */
    Remove(node)
  ENDIF

ENDPROC num


-> Free the entire list, including the header.  The header is not updated as
-> the list is freed.  This function demonstrates how to avoid referencing
-> freed memory when deallocating nodes.
EXPORT PROC freeNameNodes(list:PTR TO lh)
  DEF worknode:PTR TO nameNode, nextnode
  worknode := list.head  -> First node
  WHILE nextnode := worknode.ln.succ
    END worknode
    worknode := nextnode
  ENDWHILE
  newList(list)
ENDPROC

EXPORT PROC findNodeNumber(list:PTR TO lh, str)
  DEF node:PTR TO ln, count, found

  IF list.tailpred <> list
    count := 0
    node  := list.head
    found := FALSE
    WHILE (found = FALSE) AND (node.succ)
      IF StrCmp(node.name, str)
        found := TRUE
      ELSE
        node := node.succ
        INC count
      ENDIF
    ENDWHILE
  ENDIF
ENDPROC IF found THEN count ELSE -1


/* SV: returns the name of the 'number'-th list entry or NIL.
** 'number' should be 0..(countnodes(list)-1)
*/
EXPORT PROC findNodeName(list:PTR TO lh, number)
  DEF node:PTR TO ln

  node := getNode(list,number)

ENDPROC IF node THEN node.name ELSE NIL


/*EXPORT PROC movelistnode(l1:PTR TO lh, pos1, l2:PTR TO lh, pos2)
  DEF pn:PTR TO ln, copynode = NIL:PTR TO lh, i
  DEF forwards

  IF (pos2 < 0) OR (pos2 >= countnodes(l2)) THEN RETURN pos1

  IF     (pos1 > pos2); forwards := FALSE
  ELSEIF (pos1 < pos2); forwards := TRUE
  ENDIF

  IF l1 = l2
    IF (pos1 = pos2) THEN RETURN pos1
    IF forwards = FALSE THEN INC pos2
    IF forwards         THEN INC pos1
  ENDIF

  pn := l1
  i := 0
  WHILE (i < pos1) AND pn.succ
   pn := pn.succ
   INC i
  ENDWHILE
  copynode := pn
  Remove(pn)

  pn := l2
  i := 0
  WHILE (i < pos2) AND pn.succ
   pn := pn.succ
   INC i
  ENDWHILE
  Insert(l2, copynode, pn)

  IF l1 = l2
    IF forwards
      RETURN i
    ELSE
      RETURN i - 1
    ENDIF
  ELSE
    RETURN i
  ENDIF
ENDPROC

** SV: I don't know what you expect as return value of this proc,
** so I could not write an equivalent function.
** The main problem is that you may pass illegal values for
** 'pos1' and 'pos2' which results in invalid nodes after the
** WHILE-loops (remember pn.succ must be <>NIL otherwise the node
** is not valid and therefore may not be Remove()d or Insert()ed).
** Also 'pn:=l1' should be pn:=l1.head ?!
*/
/* moves 'pos1'-th entry from list 'l1' to list 'l2' at 'pos2'-th
** position.
** Returns TRUE on sucess.
*/
EXPORT PROC movelistnode2(l1:PTR TO lh, pos1, l2:PTR TO lh, pos2)
  DEF newnode:PTR TO ln, prednode:PTR TO ln

  /* get first node
  */
  IF newnode := getNode(l1, pos1)

    /* correct inserting offset if both lists are equal and we move
    ** an entry from front to back.
    */
    IF (l1 = l2) AND (pos1 < pos2) THEN INC pos2

    IF pos2 < 1

      /* remove the node from first list
      */
      Remove(newnode)

      /* insert newnode at list start
      */
      AddHead(l2, newnode)

      RETURN 0
    ELSEIF prednode := getNode(l2, pos2 - 1)
      /* The entry should be inserted at 'pos2'-th position.
      ** As we could only insert nodes behind an entry, decrease
      ** 'pos2'
      */

      /* if the nodes are equal, do nothing
      ** (inserting an node after itself doesn't make sense)
      */
      IF newnode <> prednode

        /* remove the node from first list
        */
        Remove(newnode)

        /* insert newnode behind prednode
        */
        Insert(l2, newnode, prednode)
        IF (pos1 < pos2)
          RETURN (pos2 - 1)
        ELSE
          RETURN (pos2)
        ENDIF
      ENDIF
    ELSE
      RETURN (pos2 - 2)
    ENDIF

  ENDIF

ENDPROC


EXPORT PROC replacenode(list:PTR TO lh, str, num)
  DEF node:PTR TO ln

  IF countnodes(list) >= num
    node := getNode(list, num)
    addName(list, str, node)
    Remove(node)
  ELSE
    addName(list, str)
  ENDIF
ENDPROC

EXPORT PROC countnodes(list:PTR TO lh)
  DEF node:PTR TO ln, count

  count := 0

  IF list.tailpred <> list
    node  := list.head
    WHILE (node.succ)
      node := node.succ
      INC count
    ENDWHILE
  ENDIF
ENDPROC count


/* returns the 'num'-th node of execlist 'list' or NIL.
** 'num' should be 0..(countnodes(list)-1)
*/
EXPORT PROC getNode(list:PTR TO lh, num)
DEF node:PTR TO ln

  /* safety checks for empty lists and illegial positions.
  */
  IF (list.tailpred <> list) AND (num >= 0)

    node := list.head

    /* run down the list.
    ** check node.succ for end of list
    */
    WHILE (num-- >= 0) AND (node.succ <> NIL)
      node := node.succ
    ENDWHILE

    /* return the node if it's valid
    */
    IF node.succ THEN RETURN node

  ENDIF

  /* error, return NIL.
  */
ENDPROC NIL

