# What is the correct way to delineate directories in Lua?

I have been experimenting with some Lua code to list directories and provide them to the TeX engine.

I have been using the Lua File System (lfs) module.

The first part list the current directory (code line 4) and it is printed using \, as C:\Users\Admin\...

In the second part I iterate over the directory which I am providing as a variable,

local z="C:/test"


This also works if I provide it as: local z="C:\test"

The library accepts this as a valid directory. I am curious to find out how well this will work on other operating systems (I have tested on Windows) and what is the best practice in this regard.

Full MWE listing follows. (Warning it can print 100s of pages if you test on C: alone, create a small temporary directory to test).

\documentclass{tufte-book}
\usepackage[listings]{tcolorbox}
\lstloadlanguages{[LaTeX]TeX, [primitive]TeX}
\usepackage{luacode} % loads luatexbase as well
% Emphasis
%\renewcommand{\ttdefault}{cmtt}            % prefer old tt font
\newcommand\emphasis[2][blue]{\lstset{emph={exec,if,then,else,do,end,while,for,print,sprint,directlua,#2},
emphstyle={\ttfamily\textcolor{#1}}}}%

\lstset{language={[LaTeX]TeX},
escapeinside={{(*@}{@*)}},
numbers=left,
gobble=0,
stepnumber=1,numbersep=5pt,
numberstyle={\footnotesize\color{gray}},%firstnumber=last,
breaklines=true,
framesep=5pt,
basicstyle=\small\ttfamily,
showstringspaces=false,
keywordstyle=\ttfamily\textcolor{blue},
stringstyle=\color{orange},
commentstyle=\color{black},
rulecolor=\color{gray!10},
breakatwhitespace=true,
showspaces=false,  % shows spacing symbol
xleftmargin=0pt,
xrightmargin=5pt,
aboveskip=3pt, % compact the code looks ugly in type
belowskip=7pt,  % user responsible to insert any skips
backgroundcolor=\color{gray!15}}
\begin{document}

%\begin{tcblisting}{} uncomment if you have the latest version of tcolorbox
\begin{luacode}
require "lfs"
local temp=lfs.currentdir()
tex.sprint(-2, temp)
tex.sprint("\\par")
function attrdir (path)
for file in lfs.dir(path) do
if file ~= "." and file ~= ".." then
local f = path..'/'..file
tex.sprint (-1, f.."\\par     ")
local attr = lfs.attributes (f)
assert (type(attr) == "table")
if attr.mode == "directory" then
attrdir (f)
else
-- for name, value in pairs(attr) do
--tex.sprint (-2,name, value)
-- end
end
end
end
end
local z="C:/test"
attrdir (z)
\end{luacode}
%\end{tcblisting}
\end{document}

-
Great question! I've searched about path separator and found an old thread about it in the lua-l mailing list. An entry in the Lua FAQ reference tells us: "package.config is a string where the first 'character' is the directory separator; so package.config:sub(1,1) is either a slash or a backslash. As a general rule, try to use this when building paths." –  Paulo Cereda Mar 15 '12 at 21:05
@PauloCereda Thanks, so what you saying lfs has package.config:sub(1,1) already incorporated? –  Yiannis Lazarides Mar 15 '12 at 21:17
I'm really not sure if lfs gets the path separator from package.config. :( Maybe it relies on /, since it seems to work in most of the cases (at least for both Linux and Windows). That would be my guess. package.config is a string and the first character contains the path separator. I think it would be more reliable to create a local variable, get the path separator and use it instead of hardcoding it, but / might just work fine. :) –  Paulo Cereda Mar 15 '12 at 21:28
add comment

## 3 Answers

You can use / for path separators on both unix and windows. "google" tells me that windows software also accepts / as a separator, but you can't use it on the command line as it denotes an option / command line argument.

Instead of writing tex.sprint(-1, f.."\\par ") you should write tex.tprint({-2 , f},{"\\par "}) and you can use lfs.isdir() to check for a directory. Otherwise a good solution.

-
Thanks. Oops! didn't know about the tex.tprint is that in the reference? –  Yiannis Lazarides Mar 15 '12 at 21:46
@YiannisLazarides yes, it was added recently (a year ago) ;) - It lets you insert strings with different catcodes into the buffer, very handy for this kind of mixture of pure string and TeX command string. –  topskip Mar 15 '12 at 21:48
add comment

I decided to write an answer. :)

In general, portability is very challenging. Usually the best situation is to write code that does not depend on the underlying system. Sadly, there are cases where we need to know about the bowels of our host. And now comes the tricky part.

Patrick's answer is the way to go. :) IMHO using / as path separator is generally portable - of course, there are extreme cases where some devices don't even have a hierarchical filesystem, but thankfully it's not our situation here. :) If we stick with the Windows/Unix/Linux/BSD/Mac worlds, I think we are safe with /.

A potential problem that depends on the underlying system is when we talk about absolute paths. Now it's tricky. I'd say to avoid assigning drive letters or filesystem protocols to paths. I'd go with relative paths and try to get stuff done from it.

I've been asking myself if Lua actually has a path separator. It does, as the Lua Unofficial FAQ tell us:

package.config is a string where the first 'character' is the directory separator; so package.config:sub(1,1) is either a slash or a backslash. As a general rule, try to use this when building paths.

I'd stick with / for the fun of it. :) Still, if we want to rely on the system's path separator, I'd write a function to build paths instead of hardcoding them:

function getPath(...)
local pathseparator = package.config:sub(1,1);
local elements = {...}
return table.concat(elements, pathseparator)
end


If I want to access foo ► bar ► baz.txt, you can use getPath("foo","bar","baz.txt") which will give you:

foo\bar\baz.txt


in Windows, or:

foo/bar/baz.txt


in Unix and friends. :)

-
add comment

I just tried to play with your code on my new shiny desktop

$uname -a OpenBSD oko.bagdala2.net 5.1 GENERIC.MP#188 i386  As you can see I am using version of OpenBSD which will be released 1st of May. My version of Lua is $ lua
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
>


My version of TeXLive on this machine is

texlive_texmf-full-2011p0


which is roughly a stable release from the July of the last year.

I was not sure what you were expecting me to do with the code but

$luatex mama.tex This is LuaTeX, Version beta-0.70.1-2012030705 (rev 4277) restricted \write18 enabled. (./mama.tex ! Undefined control sequence. l.1 \documentclass {tufte-book}  The Lua code itself $ lua
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> require "lfs"
stdin:1: module 'lfs' not found:
no field package.preload['lfs']
no file './lfs.lua'
no file '/usr/local/share/lua/5.1/lfs.lua'
no file '/usr/local/share/lua/5.1/lfs/init.lua'
no file '/usr/local/lib/lua/5.1/lfs.lua'
no file '/usr/local/lib/lua/5.1/lfs/init.lua'
no file './lfs.so'
no file '/usr/local/lib/lua/5.1/lfs.so'
no file '/usr/local/lib/lua/5.1/loadall.so'
stack traceback:
[C]: in function 'require'
stdin:1: in main chunk
[C]: ?
>


By the way your

z="C:/test"


is totally unportable. I made quick fix but you will have to have some kind of autoconfig to be able to run on various operating systems.

Edit:

Thanks for quick inputs! Running lualatex with produces output after screaming about

! LaTeX Error: File tcolorbox.sty' not found.

Type X to quit or <RETURN> to proceed,
or enter new name. (Default extension: sty)

! LaTeX Error: Unknown option listings' for package tcolorbox'.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H <return>  for immediate help.
...

l.3 \lstloadlanguages
{[LaTeX]TeX, [primitive]TeX}
?


but sure enough there is an output. I have not changed tufte-book class. The same problem with tcolorbox.sty (which is expected) even if I use article class. Do you want me to put somewhere on the server log file or pdf file?

Also using texlua instead of lua just with the portion of lua script gives

\$ texlua mama.lua
mama.lua:3: attempt to call field 'sprint' (a nil value)


I am guessing that this is now more useful?

New Edit

After adding tcolorbox.sty into the local directory there was another problem

(/usr/local/share/texmf-dist/tex/latex/tools/verbatim.sty)
! I can't find file tcblistings.code.tex'.
<to be read again>
\futurelet
l.299 \tcb@temp

Please type another input file name:
! I can't find file tcblistings.code'.
<to be read again>
\futurelet
l.299 \tcb@temp


I downloaded from the Internet and installed tcblistings.code.tex

After that lualatex went without a glitch. The pdf file has only one line

tcolorbox /home/predrag


Which happens not to be the directory from the file (your C:/test) which I edited to

  local z="/home/predrag/Desktop/downloads"


However that is the directory in which I had your tex source code, style file and the other required tex file.

I am still not sure what you were trying to do but sure enough after two iterations it "works" on OpenBSD. At this point I could guess that it should work on NetBSD and DragonFly BSD (Note TeXLive is not ported to FreeBSD).

-
Thanks for trying it out. I am sorry, you need the tufte-class or change to article, sorry I left it there from a document I am writing. You must also check you have lualibs which should load lfs. –  Yiannis Lazarides Mar 15 '12 at 21:26
You should run this through lualatex, not luatex, as it's a LaTeX document. And: lua does not have lfs installed. Use texlua. Other than that, I am not sure if your answer is really an answer?!? –  topskip Mar 15 '12 at 21:28
You will need the latest version of tcolorbox (that is why I had a note in the code). Worth having a look it is a good package. –  Yiannis Lazarides Mar 15 '12 at 21:45
With your latest edit: you can't call functions from the tex library in texlua. –  topskip Mar 15 '12 at 21:53
@Patrick Gundlach Thanks Patrick. As you could see I am completely ignorant about Lua. Maybe some time in the future I will find some time and motivation to learn something about it. Right now I do not see how I could use for my day work. –  Predrag Punosevac Mar 16 '12 at 1:49
show 1 more comment