I have/use many versions of the following doubly linked list code. It uses macros to do templating.
This is the most compact/standalone version.
I created it as a part of a previous SO answer: Implementing input/output redirection in a Linux shell using C
The templating macros are DLH* and DLK*
// shpipe/shpipe.h -- shell pipe control
#ifndef _shpipe_shpipe_h_
#define _shpipe_shpipe_h_
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#define SUPER_INLINE __attribute__((always_inline)) static inline
#ifdef _SHPIPE_GLO_
#define EXTRN_SHPIPE /**/
#else
#define EXTRN_SHPIPE extern
#endif
typedef unsigned char byte;
typedef unsigned int u32;
#define CAST(_typ,_val) \
((_typ) (_val))
#define FWD(_typ) \
struct _##_typ; \
typedef struct _##_typ _typ##_t; \
typedef _typ##_t *_typ##_p
FWD(xstr);
FWD(xpipe);
FWD(redir);
FWD(xcmd);
FWD(xlst);
#define SHANYMAX 300 // maximum for anything
// bit mask operations
#define BTVOFF(_bitno) ((_bitno) >> 3)
#define BTVMSK(_bitno) (1u << ((_bitno) % 0x07))
#define BTVLEN(_bitcnt) ((((_bitcnt) + 7) >> 3) << 3)
#define BTVSET(_vec,_bitno) _vec[BTVOFF(_bitno)] |= BTVMSK(_bitno)
#define BTVCLR(_vec,_bitno) _vec[BTVOFF(_bitno)] &= ~BTVMSK(_bitno)
#ifndef DLHCNT
#define DLHCNT 1
#endif
#ifndef XMEMCHK
#define XMEMCHK 1
#endif
#if XMEMCHK
#define syscalloc(_cnt,_len,_mag) _syscalloc(_cnt,_len,_mag,__FILE__,__LINE__)
#define sysfree(_vp) _sysfree(_vp)
#define sysmchk() _sysmchk()
#else
#define syscalloc(_cnt,_len,_mag) calloc(_cnt,_len)
#define sysfree(_vp) free(_vp)
#define sysmchk() /**/
#endif
#ifndef XFDCHK
#define XFDCHK 1
#endif
#define XFDMAX 1024
#if XFDCHK
#define xfdget(_vec) _xfdget(_vec)
#define xfdchk(_vec) _xfdchk(_vec)
#else
#define xfdget(_vec) /**/
#define xfdchk(_vec) /**/
#endif
pid_t pidtop; // top/master process id
FILE *xfintop; // top input stream
EXTRN_SHPIPE byte xfdchk_std[BTVLEN(XFDMAX)];
// magic numbers
typedef enum {
SYSMAGIC_XSTR = 0xDEADBE01,
SYSMAGIC_XCMD = 0xDEADBE02,
SYSMAGIC_XLST = 0xDEADBE03,
SYSMAGIC_ARGV = 0xDEADBE04,
} sysmagic_t;
// allocation control
FWD(xmem);
struct _xmem {
sysmagic_t xmem_magic; // magic number
xmem_p xmem_next; // linkage
const char *xmem_file; // file
int xmem_lno; // line number
//long xmem_rsvp;
};
EXTRN_SHPIPE xmem_p xmemall;
EXTRN_SHPIPE int opt_debug;
#define sysfault(_fmt...) \
do { \
fprintf(stderr,_fmt); \
exit(1); \
} while (0)
#define dbgprt(_lvl,_fmt...) \
do { \
if (opt_debug) \
_dbgprt(_fmt); \
} while (0)
#define _dbgprt(_fmt...) \
fprintf(stderr,_fmt)
#define dbgexec(_lvl,_expr) \
do { \
if (opt_debug) \
_expr; \
} while (0)
#define OPTALL(_cmd) \
_cmd(TOKSEP,"special separater") \
_cmd(TOKQUO1,"single quoted string") \
_cmd(TOKQUO2,"double quoted string") \
_cmd(TOKWORD,"ordinary word") \
_cmd(OPTNOGO,"error occurred") \
_cmd(OPTDETACH,"detach job") \
_cmd(OPTLOCK,"struct locked") \
_cmd(OPTIPIPE,"input pipe") \
_cmd(OPTOPIPE,"output pipe") \
_cmd(OPTDONE,"command done")
#define _OPTDEF(_sym,_reason) \
_##_sym,
enum {
OPTALL(_OPTDEF)
};
#define _OPTMSK(_sym,_reason) \
_sym = (1 << _##_sym),
enum {
OPTALL(_OPTMSK)
};
// option/symbol display control
FWD(tgb);
struct _tgb {
const char *tgb_tag; // symbol name
u32 tgb_val; // symbol value
const char *tgb_reason; // symbol explanation
};
#define TGBEOT \
{ .tgb_tag = NULL }
#define TGBMORE(_tgb) \
_tgb != NULL
#define _OPTTGB(_sym,_reason) \
{ .tgb_tag = #_sym, .tgb_val = _sym, .tgb_reason = _reason },
#ifdef _SHPIPE_GLO_
tgb_t opt_tgb[] = {
OPTALL(_OPTTGB)
TGBEOT
};
#else
extern tgb_t opt_tgb[];
#endif
FWD(dlh);
FWD(dlk);
FWD(dlv);
#define DLKDEF(_typ) \
sysmagic_t _typ##_magic; /* magic number */ \
u32 _typ##_opt; /* options */ \
_typ##_p _typ##_prev; /* pointer to previous item in list */ \
_typ##_p _typ##_next /* pointer to next item in list */
#define DLKOF(_ptr) \
CAST(dlk_p,_ptr)
#define DLHDEF(_pre,_typ) \
_typ##_p _pre##head; /* head of list */ \
_typ##_p _pre##tail; /* tail of list */ \
dlv_p _pre##dlv; /* virtual function table pointer */ \
int _pre##cnt; /* number of items in list */
#define DLHOF(_ptr,_pre) \
CAST(dlh_p,&_ptr->_pre##head)
// doubly linked list virtual function table
struct _dlv {
void (*dlv_free)(dlk_p dlk); // free the item
void (*dlv_show)(dlk_p dlk,const char *reason); // show the item
};
// doubly linked list item
struct _dlk {
DLKDEF(dlk);
};
// doubly linked list header
struct _dlh {
DLHDEF(dlh_,dlk);
};
// string buffer
struct _xstr {
DLKDEF(xstr); // linkage
u32 xstr_type; // string type
size_t xstr_maxlen; // maximum space in string buffer
char *xstr_lhs; // pointer to start of string
char *xstr_rhs; // pointer to current string append
char *xstr_file; // input file
long xstr_fpos; // input file position
int xstr_lno; // input file line number
char *xstr_bufptr; // pointer to buffer
long xstr_bufoff; // beginning buffer offset
};
// pipe control
struct _xpipe {
int xpipe_fd[2]; // pipe units
};
#define XPIPE_READ 0 // read/input side of pipe
#define XPIPE_WRITE 1 // write/output side of pipe
// redirection control
struct _redir {
xstr_p red_sep; // separater
xstr_p red_file; // filename
int red_fd; // open file unit
};
// close file descriptor
#define CLOSEME(_fd) \
_fd = closeme(_fd,__FILE__,__LINE__)
#define XSTRFREE(_xstr) \
_xstr = xstrfree(_xstr)
// command control
struct _xcmd {
DLKDEF(xcmd); // linkage
DLHDEF(xcmd_,xstr); // list of strings
redir_t xcmd_redir[2]; // redirection control
xpipe_t xcmd_xpipe; // output side pipe
pid_t xcmd_pid; // process id
int xcmd_status; // completion status
};
#define XCMD_DLHOF(_xcmd) \
DLHOF(_xcmd,xcmd_)
// command list
struct _xlst {
DLKDEF(xlst); // linkage
DLHDEF(xlst_,xcmd); // list of commands
};
#define XLST_DLHOF(_xlst) \
DLHOF(_xlst,xlst_)
EXTRN_SHPIPE xstr_t parseinfo; // information
EXTRN_SHPIPE char *parsebuf; // current buffer
EXTRN_SHPIPE char *parsecur; // current offset
EXTRN_SHPIPE int errstop; // errors
#include <shpipe.proto>
// closeme -- close a unit
SUPER_INLINE int
closeme(int fd,const char *file,int lno)
{
#if XFDCHK
fd = _closeme(fd,file,lno);
#else
do {
if (fd < 0)
break;
dbgprt(1,"closeme: CLOSE fd=%d file='%s' lno=%d\n",
fd,file,lno);
close(fd);
_fd = -1;
} while (0)
#endif
return fd;
}
// btvget -- fetch bit vector value
SUPER_INLINE byte
btvget(const byte *vec,int bitno)
{
vec += BTVROFF(bitno);
return *vec & BTVMSK(bitno);
}
// btvset -- set bit vector value
SUPER_INLINE void
btvset(byte *vec,int bitno,int val)
{
byte msk;
vec += BTVROFF(bitno);
msk = BTVMSK(bitno);
if (val)
*vec |= msk;
else
*vec &= ~msk;
}
// dlhinc -- increment/decrement list count
SUPER_INLINE void
dlhinc(dlh_p dlh,int inc)
{
#if DLHCNT
dlh->dlh_cnt += inc;
#endif
}
// xstrcstr -- get the "c string" value
SUPER_INLINE char *
xstrcstr(xstr_p xstr)
{
return xstr->xstr_lhs;
}
// xcmd virtual function table
#ifdef _SHPIPE_GLO_
dlv_t xcmd_dlv = {
.dlv_free = xstrfree_dlv,
.dlv_show = xstrshow_dlv,
};
#else
extern dlv_t xcmd_dlv;
#endif
// xlst virtual function table
#ifdef _SHPIPE_GLO_
dlv_t xlst_dlv = {
.dlv_free = xcmdfree_dlv,
.dlv_show = xcmdshow_dlv,
};
#else
extern dlv_t xlst_dlv;
#endif
#endif
// shpipe/dlk.c -- "smart" list "class" for C
#include <shpipe.h>
// dlhfree -- free list
dlh_p
dlhfree(dlh_p dlh)
{
_dlhfree(dlh);
sysfree(dlh);
dlh = NULL;
return dlh;
}
// _dlhfree -- clean list
dlh_p
_dlhfree(dlh_p dlh)
{
dlv_p dlv;
dlk_p dlk;
dlk_p next;
dlv = dlh->dlh_dlv;
for (dlk = dlh->dlh_head; dlk != NULL; dlk = next) {
next = dlk->dlk_next;
dlv->dlv_free(dlk);
}
dlh = NULL;
return dlh;
}
// dlkpush -- append string to end of list
dlk_p
dlkpush(dlh_p dlh,dlk_p dlk)
{
dlk_p tail;
tail = dlh->dlh_tail;
dlk->dlk_next = NULL;
dlk->dlk_prev = tail;
if (tail != NULL)
tail->dlk_next = dlk;
dlh->dlh_tail = dlk;
if (dlh->dlh_head == NULL)
dlh->dlh_head = dlk;
dlhinc(dlh,1);
return dlk;
}
// dlhcut -- cut out entry and split list
void
dlhcut(dlh_p dlhlhs,dlh_p dlhrhs,dlk_p dlk)
{
dlk_p prev;
dlk_p next;
dbgprt(1,"dlhcut: ENTER\n");
dbgexec(1,dlhrhs->dlh_dlv->dlv_show(dlk,"dlhcut"));
dbgexec(1,dlhshow(dlhrhs,"dlhrhs/BEF"));
prev = dlk->dlk_prev;
next = dlk->dlk_next;
// set left side list
dlhlhs->dlh_tail = prev;
if (dlk == dlhrhs->dlh_head)
dlhlhs->dlh_head = NULL;
else
dlhlhs->dlh_head = dlhrhs->dlh_head;
// set right side list
dlhrhs->dlh_head = next;
if (dlk == dlhrhs->dlh_tail)
dlhlhs->dlh_tail = NULL;
// break sibling links
if (next != NULL)
next->dlk_prev = NULL;
if (prev != NULL)
prev->dlk_next = NULL;
// recount the nodes in both lists
#if DLHCNT
dlhlhs->dlh_cnt = 0;
dlhinc(dlhrhs,-1);
for (dlk_p cur = dlhlhs->dlh_head; cur != NULL; cur = cur->dlk_next) {
dlhinc(dlhlhs,1);
dlhinc(dlhrhs,-1);
}
#endif
dlk->dlk_prev = NULL;
dlk->dlk_next = NULL;
dbgexec(1,dlhshow(dlhrhs,"dlhrhs/AFT"));
dbgexec(1,dlhshow(dlhlhs,"dlhlhs/AFT"));
dbgprt(1,"dlhcut: EXIT\n");
}
// dlkunlink -- remove item from list
dlk_p
dlkunlink(dlh_p dlh,dlk_p dlk)
{
dlk_p prev;
dlk_p next;
prev = dlk->dlk_prev;
next = dlk->dlk_next;
dlhinc(dlh,-1);
if (prev != NULL)
prev->dlk_next = next;
else
dlh->dlh_head = next;
if (next != NULL)
next->dlk_prev = prev;
else
dlh->dlh_tail = prev;
dlk->dlk_prev = NULL;
dlk->dlk_next = NULL;
return dlk;
}
// dlhcnt -- calculate item count
int
dlhcnt(dlh_p dlh)
{
int cnt;
#if DLHCNT
cnt = dlh->dlh_cnt;
#else
cnt = 0;
for (dlk_p dlk = dlh->dlh_head; dlk != NULL; dlk = dlk->dlk_next)
cnt += 1;
#endif
return cnt;
}
// dlhshow -- output a list
void
dlhshow(dlh_p dlh,const char *reason)
{
dlk_p dlk;
dlv_p dlv;
_dbgprt("dlhshow: ENTER (from %s)\n",reason);
dlv = dlh->dlh_dlv;
for (dlk = dlh->dlh_head; dlk != NULL; dlk = dlk->dlk_next) {
_dbgprt("dlhshow: OPT dlk_opt=%s\n",strtgb(opt_tgb,dlk->dlk_opt));
dlv->dlv_show(dlk,reason);
}
_dbgprt("dlhshow: EXIT\n");
}
As mentioned in my other answer, the full code for the shell is too large to fit in an answer, so it's here: http://pastebin.com/Ny1w6pUh