[Tex/LaTex] Code listing spanning multiple pages with captions at top

captionsfloatslistingslongtablepage-breaking

Is it possible to span a lstlisting environment or lstlistinginput on multiple pages, such that the caption is repeated on each page (the listing counter stays the same), as well as being able to customize it on the following pages?

Something similar to what the longtable package provides.

Minimal example where the caption is displayed only once at the top, but should be displayed twice.

\documentclass[12pt,a4paper]{article}
\usepackage[utf8x]{inputenc}
\usepackage{listings}
\usepackage{caption}

\begin{document}

\lstset{
breaklines=true,
breakatwhitespace=false,
xleftmargin=1em,
frame=single,
numbers=left,
numbersep=5pt
}

\begin{lstlisting}[language=C, caption=Example listing of code, label=lst:c1]

struct safe_buffer {
struct list_head node;

/* original request */
void    *ptr;
size_t  size;
int direction;

/* safe buffer info */
struct dmabounce_pool *pool;
void    *safe;
dma_addr_t  safe_dma_addr;
};

struct dmabounce_pool {
unsigned long   size;
struct dma_pool *pool;
#ifdef STATS
unsigned long   allocs;
#endif
};

struct dmabounce_device_info {
struct device *dev;
struct list_head safe_buffers;
#ifdef STATS
unsigned long total_allocs;
unsigned long map_op_count;
unsigned long bounce_count;
int attr_res;
#endif
struct dmabounce_pool   small;
struct dmabounce_pool   large;

rwlock_t lock;

int (*needs_bounce)(struct device *, dma_addr_t, size_t);
};

#ifdef STATS
static ssize_t dmabounce_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct dmabounce_device_info *device_info = dev->archdata.dmabounce;
return sprintf(buf, "%lu %lu %lu %lu %lu %lu\n",
device_info->small.allocs,
device_info->large.allocs,
device_info->total_allocs - device_info->small.allocs -
device_info->large.allocs,
device_info->total_allocs,
device_info->map_op_count,
device_info->bounce_count);
}

static DEVICE_ATTR(dmabounce_stats, 0400, dmabounce_show, NULL);
#endif

\end{lstlisting}
\end{document}

Best Answer

One idea using the mdframed package and its middleextra, secondextra keys to add a continued caption:

\documentclass[12pt,a4paper]{article}
\usepackage[a5paper]{geometry}% just for the example
\usepackage[utf8x]{inputenc}
\usepackage{listings}
\usepackage{caption}
\usepackage[framemethod=tikz]{mdframed}

\lstset{
breaklines=true,
breakatwhitespace=false,
xleftmargin=1em,
frame=single,
numbers=left,
numbersep=5pt,
}

\newcommand\mylstcaption{}

\surroundwithmdframed[
hidealllines=true,
middleextra={
  \node[anchor=west] at (O|-P)
    {\lstlistingname~\thelstlisting\  (Cont.):~\mylstcaption};},
secondextra={
  \node[anchor=west] at (O|-P)
    {\lstlistingname~\thelstlisting\  (Cont.):~\mylstcaption};},
splittopskip=2\baselineskip
]{lstlisting}

\begin{document}

\renewcommand\mylstcaption{Example listing of code}
\begin{lstlisting}[language=C, caption=\mylstcaption, label=lst:c1]

struct safe_buffer {
struct list_head node;

/* original request */
void    *ptr;
size_t  size;
int direction;

/* safe buffer info */
struct dmabounce_pool *pool;
void    *safe;
dma_addr_t  safe_dma_addr;
};

struct dmabounce_pool {
unsigned long   size;
struct dma_pool *pool;
#ifdef STATS
unsigned long   allocs;
#endif
};

struct dmabounce_device_info {
struct device *dev;
struct list_head safe_buffers;
#ifdef STATS
unsigned long total_allocs;
unsigned long map_op_count;
unsigned long bounce_count;
int attr_res;
#endif
struct dmabounce_pool   small;
struct dmabounce_pool   large;

rwlock_t lock;

int (*needs_bounce)(struct device *, dma_addr_t, size_t);
};

#ifdef STATS
static ssize_t dmabounce_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct dmabounce_device_info *device_info = dev->archdata.dmabounce;
return sprintf(buf, "%lu %lu %lu %lu %lu %lu\n",
device_info->small.allocs,
device_info->large.allocs,
device_info->total_allocs - device_info->small.allocs -
device_info->large.allocs,
device_info->total_allocs,
device_info->map_op_count,
device_info->bounce_count);
}

static DEVICE_ATTR(dmabounce_stats, 0400, dmabounce_show, NULL);
#endif

\end{lstlisting}

\end{document}

enter image description here

Before each lstlisting environment you redefine \mylstcaption with the text of the caption.

For \lstinpulisting, the general idea is the same, but using this time a \newmdenv; to avoid unnecessary code duplication, a style was defined:

\documentclass[12pt,a4paper]{article}
\usepackage[a5paper]{geometry}% just for the example
\usepackage[utf8x]{inputenc}
\usepackage{listings}
\usepackage{caption}
\usepackage[framemethod=tikz]{mdframed}

\lstset{
breaklines=true,
breakatwhitespace=false,
xleftmargin=1em,
frame=single,
numbers=left,
numbersep=5pt,
}

\newcommand\mylstcaption{}

\mdfdefinestyle{mymdstyle}{
hidealllines=true,
middleextra={
  \node[anchor=west] at (O|-P)
    {\lstlistingname~\thelstlisting\  (Cont.):~\mylstcaption};},
secondextra={
  \node[anchor=west] at (O|-P)
    {\lstlistingname~\thelstlisting\  (Cont.):~\mylstcaption};},
splittopskip=2\baselineskip
}

\surroundwithmdframed[style=mymdstyle]{lstlisting}
\newmdenv[style=mymdstyle]{mdlisting}

\begin{document}

\renewcommand\mylstcaption{Example listing of code}
\begin{mdlisting}
\lstinputlisting[language=C, caption=\mylstcaption, label=lst:c1]{file}
\end{mdlisting}

\end{document}

Update

Here's now the same idea but using now tcolorbox:

\documentclass[12pt,a4paper]{article}
\usepackage[utf8x]{inputenc}
\usepackage[many]{tcolorbox}
\tcbuselibrary{listings}

\lstset{
  breaklines=true,
  breakatwhitespace=false,
  xleftmargin=1em,
  frame=single,
  numbers=left,
  numbersep=5pt,
}

\newtcblisting{TCBlisting}[1][]{
  breakable,
  enhanced,
  arc=0pt,
  outer arc=0pt,
  boxrule=0pt,
  colback=white,
  listing only,
  listing remove caption=false,
  listing options={#1},
  overlay middle and last={
  \node[anchor=south west,text width=\columnwidth] at (frame.north west)
    {\lstlistingname~\thelstlisting\  (Cont.):~\mylstcaption};
  }
}

\newcommand\mylstcaption{}

\begin{document}

\renewcommand\mylstcaption{Example listing of code with a long caption spanning several lines in a two-column document. this is just a test for the example.}
\begin{TCBlisting}[language=C,caption={\mylstcaption}]
struct safe_buffer {
struct list_head node;

/* original request */
void    *ptr;
size_t  size;
int direction;

/* safe buffer info */
struct dmabounce_pool *pool;
void    *safe;
dma_addr_t  safe_dma_addr;
};

struct dmabounce_pool {
unsigned long   size;
struct dma_pool *pool;
#ifdef STATS
unsigned long   allocs;
#endif
};

struct dmabounce_device_info {
struct device *dev;
struct list_head safe_buffers;
#ifdef STATS
unsigned long total_allocs;
unsigned long map_op_count;
unsigned long bounce_count;
int attr_res;
#endif
struct dmabounce_pool   small;
struct dmabounce_pool   large;

rwlock_t lock;

int (*needs_bounce)(struct device *, dma_addr_t, size_t);
};

#ifdef STATS
static ssize_t dmabounce_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct dmabounce_device_info *device_info = dev->archdata.dmabounce;
return sprintf(buf, "%lu %lu %lu %lu %lu %lu\n",
device_info->small.allocs,
device_info->large.allocs,
device_info->total_allocs - device_info->small.allocs -
device_info->large.allocs,
device_info->total_allocs,
device_info->map_op_count,
device_info->bounce_count);
}

static DEVICE_ATTR(dmabounce_stats, 0400, dmabounce_show, NULL);
#endif

\end{TCBlisting}
\end{document}
\begin{lstlisting}[language=C, caption=\mylstcaption, label=lst:c1]

struct safe_buffer {
struct list_head node;

/* original request */
void    *ptr;
size_t  size;
int direction;

/* safe buffer info */
struct dmabounce_pool *pool;
void    *safe;
dma_addr_t  safe_dma_addr;
};

struct dmabounce_pool {
unsigned long   size;
struct dma_pool *pool;
#ifdef STATS
unsigned long   allocs;
#endif
};

struct dmabounce_device_info {
struct device *dev;
struct list_head safe_buffers;
#ifdef STATS
unsigned long total_allocs;
unsigned long map_op_count;
unsigned long bounce_count;
int attr_res;
#endif
struct dmabounce_pool   small;
struct dmabounce_pool   large;

rwlock_t lock;

int (*needs_bounce)(struct device *, dma_addr_t, size_t);
};

#ifdef STATS
static ssize_t dmabounce_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct dmabounce_device_info *device_info = dev->archdata.dmabounce;
return sprintf(buf, "%lu %lu %lu %lu %lu %lu\n",
device_info->small.allocs,
device_info->large.allocs,
device_info->total_allocs - device_info->small.allocs -
device_info->large.allocs,
device_info->total_allocs,
device_info->map_op_count,
device_info->bounce_count);
}

static DEVICE_ATTR(dmabounce_stats, 0400, dmabounce_show, NULL);
#endif

\end{lstlisting}

\end{document}

enter image description here