typedef struct dllist {
struct dllist *flink;
struct dllist *blink;
Jval val;
} *Dllist;
Here are the operations supported by dllist.o:
#define dll_traverse(ptr, list) \ for (ptr = (list)->flink; ptr != (list); ptr = ptr->flink) #define dll_rtraverse(ptr, list) \ for (ptr = (list)->blink; ptr != (list); ptr = ptr->blink)
The typedef for a dllist node is:
typedef struct dllist {
struct dllist *flink;
struct dllist *blink;
Jval val;
} *Dllist;
Note that each node has two pointers -- a forward link (flink) to
the next node on the list, and a backward link (blink) to the
previous node on the list. A Dllist is a pointer to the sentinel
node.
The list is circular in both directions -- the sentinel's flink points to the first node on the list, and its blink points to the last node on the list. The first node's blink points to the sentinel, as does the last node's flink.
Some ascii art: Here's an empty list l:
l -------------+--> |-----------|
| | flink ---------\
| | blink -------\ |
| | val = ? | | |
| |-----------| | |
| | |
\-------------------+-/
And here's that list after calling dll_append(l, new_jval_i(3));:
(or dll_prepend(l, new_jval_i(3)) for that matter).
l -------------+--> |-----------| /-+->|-----------|
| | flink -------/ | | flink ---------\
| | blink ---------/ | blink -------\ |
| | val = ? | | val.i = 3 | | |
| |-----------| |-----------| | |
| | |
\---------------------------------------+-/
Actually, it makes the drawing cleaner to have the back links go backwards:
l ----------------->|-----------| |-----------|
/--->| flink ----------->| flink ---------\
| /------ blink |<----------- blink |<-\ |
| | | val = ? | | val.i = 3 | | |
| | |-----------| |-----------| | |
| | | |
| \------------------------------------/ |
| |
\-----------------------------------------/
Here's that list after calling dll_append(l, new_jval_i(5));:
l ---------->|-----------| |-----------| |-----------|
/--->| flink ----------->| flink ----------->| flink ---------\
| /------ blink |<----------- blink |<----------- blink |<-\ |
| | | val = ? | | val.i = 3 | | val.i = 5 | | |
| | |-----------| |-----------| |-----------| | |
| | | |
| \--------------------------------------------------------/ |
| |
\-------------------------------------------------------------/
I won't go over more examples with ascii art. You should be getting
the hang of this by now.
Many of the procedure implementations are trivial procedures or macros:
The only subtle pieces of code are dll_insert_b() and dll_delete_node. With dll_insert_b(n, v) we malloc() a new node, set its value to v, and then link it into the list right before n. This means that we set the new node's flink field to n, and its blink field to n->blink. Then we set n->blink to the new node, and the old n->blink's flink field to the new node. Here's the code:
Dllist new_dllist() { Dllist d; d = (Dllist) malloc (sizeof(struct dllist)); d->flink = d; d->blink = d; return d; }
dll_empty(Dllist l) { return (l->flink == l); }
free_dllist(Dllist l) { while (!dll_empty(l)) { dll_delete_node(dll_first(l)); } free(l); }
#define dll_first(d) ((d)->flink) #define dll_next(d) ((d)->flink) #define dll_last(d) ((d)->blink) #define dll_prev(d) ((d)->blink) #define dll_nil(d) (d)
dll_insert_b(Dllist node, Jval v) /* Inserts before a given node */
{
Dllist new;
new = (Dllist) malloc (sizeof(struct dllist));
new->val = v;
new->flink = node;
new->blink = node->blink;
new->flink->blink = new;
new->blink->flink = new;
}
Once we have dll_insert_b() the other three list insertion
routines are simply calls to dll_insert_b():
dll_insert_a(Dllist n, Jval val) /* Inserts after a given node */
{
dll_insert_b(n->flink, val);
}
dll_append(Dllist l, Jval val) /* Inserts at the end of the list */
{
dll_insert_b(l, val);
}
dll_prepend(Dllist l, Jval val) /* Inserts at the beginning of the list */
{
dll_insert_b(l->flink, val);
}
Deletion is pretty easy too. First you must remove the node n's
from the list by setting n->flink->blink to n->blink
and by setting n->blink->flink to n->flink. Then
you free n:
dll_delete_node(Dllist node) /* Deletes an arbitrary iterm */
{
node->flink->blink = node->blink;
node->blink->flink = node->flink;
free(node);
}
#include < stdio.h >
#include < string.h >
#include "fields.h"
#include "dllist.h"
main()
{
IS is;
Dllist l;
Dllist tmp;
is = new_inputstruct(NULL);
l = new_dllist();
while (get_line(is) >= 0) {
dll_append(l, new_jval_s(strdup(is->text1)));
}
dll_rtraverse(tmp, l) printf("%s", jval_s(tmp->val));
}
The second example is another standard: printing the last
n lines of standard input. We do this by reading
standard input into a Dllist, and making sure that
the Dllist always has at most n nodes. Then
we print it out:
The code is in dlltail.c:
#include < stdio.h >
#include < string.h >
#include "fields.h"
#include "dllist.h"
main(int argc, char **argv)
{
IS is;
int n;
Dllist l;
Dllist tmp;
if (argc != 2) {
fprintf(stderr, "usage: dlltail n\n");
exit(1);
}
n = atoi(argv[1]);
if (n < 0) {
fprintf(stderr, "usage: dlltail n -- n must be >= 0\n");
exit(1);
}
is = new_inputstruct(NULL);
l = new_dllist();
while (get_line(is) >= 0) {
dll_append(l, new_jval_s(strdup(is->text1)));
if (is->line > n) {
tmp = dll_first(l);
free(jval_s(dll_val(tmp)));
dll_delete_node(tmp);
}
}
dll_traverse(tmp, l) printf("%s", jval_s(tmp->val));
}