summaryrefslogtreecommitdiff
path: root/source/luametatex/source/luaoptional/lmtkpse.c
blob: e593f8b9bd16cf0bce72166047c42dea5ec4d235 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
/*
    See license.txt in the root of this project.
*/

# include "luametatex.h"
# include "lmtoptional.h"

/*tex

    As part of the lean and mean concept we have no \KPSE\ on board and as with \LUATEX\ the
    \CONTEXT\ macro package doesn't need it. However, because we might want to play with it being
    a runner for other engines (as we do with the \LUATEX\ binary in kpse mode), we have at least
    an interface for it. One problem is to locate the right version of the delayed loaded kpse
    library (but we can add some clever locating code for that if needed). We keep the interface
    mostly the same as \LUATEX.

    This is actually a left-over from an experiment, but it works okay, so I moved the code into
    the source tree and made a proper \CONTEXT\ library wrapper too. We are less clever than in
    \LUATEX, so there are no additional lookup functions. After all, it is just about locating
    files and not about writing a searcher in \LUA. So, there no subdir magic either, as one has
    \LUA\ for that kind of stuff. No \DPI\ related magic either. In \LUATEX\ there are some more
    functions in the \type {kpse} namespace but these don't really relate to locating files.

    We can actually omit the next two lists and pass numbers but then we need to store that list at
    the \LUA\ end so we don't save much (but I might do it some day nevertheless). The nect code is
    rather lightweight which is on purpose. Of course occationally we need to check the \API\ but
    \KPSE\ pretty stable and we don't need the extra stuff that it provides (keep in mind that it
    has to serve all kind of programs in the \TEX\ infrastructure so it's a complex beast).

*/

typedef enum kpselib_file_format_type {
    kpse_gf_format, kpse_pk_format, kpse_any_glyph_format, kpse_tfm_format, kpse_afm_format,
    kpse_base_format, kpse_bib_format, kpse_bst_format, kpse_cnf_format, kpse_db_format,
    kpse_fmt_format, kpse_fontmap_format, kpse_mem_format, kpse_mf_format, kpse_mfpool_format,
    kpse_mft_format, kpse_mp_format, kpse_mppool_format, kpse_mpsupport_format, kpse_ocp_format,
    kpse_ofm_format, kpse_opl_format, kpse_otp_format, kpse_ovf_format, kpse_ovp_format,
    kpse_pict_format, kpse_tex_format, kpse_texdoc_format, kpse_texpool_format,
    kpse_texsource_format, kpse_tex_ps_header_format, kpse_troff_font_format, kpse_type1_format,
    kpse_vf_format, kpse_dvips_config_format, kpse_ist_format, kpse_truetype_format,
    kpse_type42_format, kpse_web2c_format, kpse_program_text_format, kpse_program_binary_format,
    kpse_miscfonts_format, kpse_web_format, kpse_cweb_format, kpse_enc_format, kpse_cmap_format,
    kpse_sfd_format, kpse_opentype_format, kpse_pdftex_config_format, kpse_lig_format,
    kpse_texmfscripts_format, kpse_lua_format, kpse_fea_format, kpse_cid_format, kpse_mlbib_format,
    kpse_mlbst_format, kpse_clua_format, /* kpse_ris_format, */ /* kpse_bltxml_format, */
    kpse_last_format
} kpselib_file_format_type;

static const char *const kpselib_file_type_names[] = {
    "gf", "pk", "bitmap font", "tfm", "afm", "base", "bib", "bst", "cnf", "ls-R", "fmt", "map",
    "mem", "mf", "mfpool", "mft", "mp", "mppool", "MetaPost support", "ocp", "ofm", "opl", "otp",
    "ovf", "ovp", "graphic/figure", "tex", "TeX system documentation", "texpool",
    "TeX system sources", "PostScript header",  "Troff fonts", "type1 fonts", "vf", "dvips config",
    "ist", "truetype fonts", "type42 fonts", "web2c files", "other text files", "other binary files",
    "misc fonts", "web", "cweb", "enc files", "cmap files", "subfont definition files",
    "opentype fonts", "pdftex config", "lig files", "texmfscripts", "lua", "font feature files",
    "cid maps", "mlbib", "mlbst", "clua",
    NULL
};

typedef struct kpselib_state_info {

    int initialized;
    int prognameset;

    void   (*lib_kpse_set_program_name)   ( const char *prog, const char *name );
    void   (*lib_kpse_reset_program_name) ( const char *name );
    char * (*lib_kpse_path_expand)        ( const char *name );
    char * (*lib_kpse_brace_expand)       ( const char *name );
    char * (*lib_kpse_var_expand)         ( const char *name );
    char * (*lib_kpse_var_value)          ( const char *name );
    char * (*lib_kpse_readable_file)      ( const char *name );
    char * (*lib_kpse_find_file)          ( const char *name, int filetype, int mustexist );
    char **(*lib_kpse_all_path_search)    ( const char *path, const char *name );

} kpselib_state_info;

static kpselib_state_info kpselib_state = {

    .initialized                 = 0,
    .prognameset                 = 0,

    .lib_kpse_set_program_name   = NULL,
    .lib_kpse_reset_program_name = NULL,
    .lib_kpse_path_expand        = NULL,
    .lib_kpse_brace_expand       = NULL,
    .lib_kpse_var_expand         = NULL,
    .lib_kpse_var_value          = NULL,
    .lib_kpse_readable_file      = NULL,
    .lib_kpse_find_file          = NULL,
    .lib_kpse_all_path_search    = NULL,

};

static int kpselib_aux_valid_progname(lua_State *L)
{
    (void) L;
    if (kpselib_state.prognameset) {
        return 1;
    } else if (! kpselib_state.initialized) {
        tex_normal_warning("kpse", "not yet initialized");
        return 0;
    } else {
        tex_normal_warning("kpse", "no program name set");
        return 0;
    }
}

static int kpselib_set_program_name(lua_State *L)
{
    (void) L;
    if (kpselib_state.initialized) {
        const char *exe_name = luaL_checkstring(L, 1);
        const char *prog_name = luaL_optstring(L, 2, exe_name);
        if (kpselib_state.prognameset) {
            kpselib_state.lib_kpse_reset_program_name(prog_name);
        } else {
            kpselib_state.lib_kpse_set_program_name(exe_name, prog_name);
            kpselib_state.prognameset = 1;
        }
    }
    return 0;
}

static int kpselib_find_file(lua_State *L)
{
    if (kpselib_aux_valid_progname(L)) {
        unsigned filetype = kpse_tex_format;
        int mustexist = 0;
        const char *filename = luaL_checkstring(L, 1);
        int top = lua_gettop(L);
        for (int i = 2; i <= top; i++) {
            switch (lua_type(L, i)) {
                case LUA_TBOOLEAN:
                    mustexist = lua_toboolean(L, i);
                    break;
                case LUA_TNUMBER:
                    /*tex This is different from \LUATEX: we accept a filetype number. */
                    filetype = (unsigned) lua_tointeger(L, i);
                    break;
                case LUA_TSTRING:
                    filetype = luaL_checkoption(L, i, NULL, kpselib_file_type_names);
                    break;
            }
            if (filetype >= kpse_last_format) {
                filetype = kpse_tex_format;
            }
        }
        lua_pushstring(L, kpselib_state.lib_kpse_find_file(filename, filetype, mustexist));
        return 1;
    } else {
        return 0;
    }
}

/*
    I'll ask Taco about the free. For now it will do. Currently I only need to do some lookups for
    checking clashes with other installations (issue reported on context ml).
*/

static int kpselib_find_files(lua_State *L)
{
    if (kpselib_aux_valid_progname(L)) {
        const char *userpath = luaL_checkstring(L, 1);
        const char *filename = luaL_checkstring(L, 2);
        char *filepath = kpselib_state.lib_kpse_path_expand(userpath);
        if (filepath) {
            char **result = kpselib_state.lib_kpse_all_path_search(filepath, filename);
         /* free(filepath); */ /* crashes, so it looks like def kpse keeps it */
            if (result) {
                lua_Integer r = 0;
                lua_newtable(L);
                while (result[r]) {
                    lua_pushstring(L, result[r]);
                    lua_rawseti(L, -2, ++r);
                }
             /* free(result); */ /* idem */
                return 1;
            }
        } else {
         /* free(filepath); */ /* idem */
        }
    }
    return 0;
}

static int kpselib_expand_path(lua_State *L)
{
    if (kpselib_aux_valid_progname(L)) {
        lua_pushstring(L, kpselib_state.lib_kpse_path_expand(luaL_checkstring(L, 1)));
        return 1;
    } else {
        return 0;
    }
}

static int kpselib_expand_braces(lua_State *L)
{
    if (kpselib_aux_valid_progname(L)) {
        lua_pushstring(L, kpselib_state.lib_kpse_brace_expand(luaL_checkstring(L, 1)));
    return 1;
    } else {
        return 0;
    }
}

static int kpselib_expand_var(lua_State *L)
{
    if (kpselib_aux_valid_progname(L)) {
        lua_pushstring(L, kpselib_state.lib_kpse_var_expand(luaL_checkstring(L, 1)));
        return 1;
    } else {
        return 0;
    }
}

static int kpselib_var_value(lua_State *L)
{
    if (kpselib_aux_valid_progname(L)) {
        lua_pushstring(L, kpselib_state.lib_kpse_var_value(luaL_checkstring(L, 1)));
        return 1;
    } else {
        return 0;
    }
}

static int kpselib_readable_file(lua_State *L)
{
    if (kpselib_aux_valid_progname(L)) {
        /* Why the dup? */
        char *name = strdup(luaL_checkstring(L, 1));
        lua_pushstring(L, kpselib_state.lib_kpse_readable_file(name));
        free(name);
        return 1;
    } else {
        return 0;
    }
}

static int kpselib_get_file_types(lua_State *L)
{
    if (kpselib_aux_valid_progname(L)) {
        lua_createtable(L, kpse_last_format, 0);
        for (lua_Integer i = 0; i < kpse_last_format; i++) {
            if (kpselib_file_type_names[i]) {
                lua_pushstring(L, kpselib_file_type_names[i]);
                lua_rawseti(L, -2, i + 1);
            } else {
                break;
            }
        }
        return 1;
    } else {
        return 0;
    }
}

static int kpselib_initialize(lua_State *L)
{
    if (! kpselib_state.initialized) {
        const char *filename = lua_tostring(L, 1);
        if (filename) {

            lmt_library lib = lmt_library_load(filename);

            kpselib_state.lib_kpse_set_program_name   = lmt_library_find(lib, "kpse_set_program_name");
            kpselib_state.lib_kpse_reset_program_name = lmt_library_find(lib, "kpse_reset_program_name");
            kpselib_state.lib_kpse_all_path_search    = lmt_library_find(lib, "kpse_all_path_search");
            kpselib_state.lib_kpse_find_file          = lmt_library_find(lib, "kpse_find_file");
            kpselib_state.lib_kpse_path_expand        = lmt_library_find(lib, "kpse_path_expand");
            kpselib_state.lib_kpse_brace_expand       = lmt_library_find(lib, "kpse_brace_expand");
            kpselib_state.lib_kpse_var_expand         = lmt_library_find(lib, "kpse_var_expand");
            kpselib_state.lib_kpse_var_value          = lmt_library_find(lib, "kpse_var_value");
            kpselib_state.lib_kpse_readable_file      = lmt_library_find(lib, "kpse_readable_file");

            kpselib_state.initialized = lmt_library_okay(lib);
        }
    }
    lua_pushboolean(L, kpselib_state.initialized);
    return 1;
}

/*tex We use the official names here, with underscores. */

/* init_prog           : no need         */
/* show_path           : maybe           */
/* lookup              : maybe           */
/* default_texmfcnf    : not that useful */
/* record_output_file  : makes no sense  */
/* record_input_file   : makes no sense  */
/* check_permissions   : luatex extra    */

static struct luaL_Reg kpselib_function_list[] = {
    { "initialize",       kpselib_initialize         },
    { "set_program_name", kpselib_set_program_name   },
    { "find_file",        kpselib_find_file          },
    { "find_files",       kpselib_find_files         },
    { "expand_path",      kpselib_expand_path        },
    { "expand_var",       kpselib_expand_var         },
    { "expand_braces",    kpselib_expand_braces      },
    { "var_value",        kpselib_var_value          },
    { "readable_file",    kpselib_readable_file      },
    { "get_file_types",   kpselib_get_file_types     },
    { NULL,               NULL                       },
};

int luaopen_kpse(lua_State * L)
{
    lmt_library_register(L, "kpse", kpselib_function_list);
    return 0;
}