Actually, since you're using numberfirstline=true
, this is simpler to accomplish than you might think. The listings
package provides a mechanism to escape out of the verbatim mode in the middle of a listing via escapeinside
to be able to insert additional macros. The trick here is that after returning from the escaped code, it considers the next line as the "first" line again, even though no counts are reset. Thus, all you have to do is escape out (and do nothing!) on the second to last line of the listing. If you do it on the very last, you're too late! The example below demonstrates using this to get what you desire.
\documentclass{article}
\usepackage{listings}
\lstset{
numbers=left,
stepnumber=5,
firstnumber=1,
numberfirstline=true
escapeinside={|(}{)|}
}
\begin{document}
\begin{lstlisting}
lorem ipsum dolor sit amet,
consectetur adipiscing elit,
sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua.
ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat.
duis aute irure dolor in reprehenderit
in voluptate velit esse cillum dolore
eu fugiat nulla pariatur.
excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia deserunt|()|
mollit anim id est laborum.
\end{lstlisting}
\end{document}
You should also check out this question about arbitrarily altering line numbering.
UPDATE
This requires modification to the listing source, and as such may be insufficient when using \lstinputlisting
. An alternative approach is to modify the logic of the listings package itself to number the last line when it is explicitly specified via the lastline
or linerange
keys, as is demonstrated below.
\documentclass{article}
\usepackage{listings}
\lstset{
numbers=left,
stepnumber=5,
firstnumber=1,
numberfirstline=true,
}
\makeatletter
\gdef\lst@SkipOrPrintLabel{%
\ifnum\lst@skipnumbers=\z@
\global\advance\lst@skipnumbers-\lst@stepnumber\relax
\lst@PlaceNumber
\lst@numberfirstlinefalse
\else
\ifnum\lst@lineno=\lst@lastline
\lst@PlaceNumber
\lst@numberfirstlinefalse
\fi
\lst@ifnumberfirstline
\lst@PlaceNumber
\lst@numberfirstlinefalse
\fi
\fi
\global\advance\lst@skipnumbers\@ne}
\makeatother
\begin{document}
\lstinputlisting[lastline=13]{input.tex}
\end{document}
This does require you to manually specify the last line number for each listing, but this seems like a reasonable sacrifice considering the difficulty of manipulating temporary files, which may potentially be the only real solution to dynamically discovering line count which only requires pure TeX. If a non-portable solution is acceptable, you could run a shell command such as wc -l
via \write18
to get the number of lines. This would of course require the chosen command to be present on all systems the source is used on, which may cause difficulties if those systems span across different operating systems. See this question for an explanation of \write18
and this question for how to capture it's output.
Worth noting is that when using linerange
, it will number the last line of each individual range, but not the first line for each range when using numberfirstline=true
. While this can similarly be changed, linerange
already results in enough such quirks that if this would be a problem, then it is most likely separate from the issue at hand here.
lastlinenumber=n
ability by doing something similar to the question I linked in my answer, and then to scan the input into a temporary file and count the lines to be able to get the last line number dynamically. It may also be possible to process the input without creating a temporary file while still being able to count lines, but I'm not immediately sure if and how that could be done. – Hiko Haieto