From ba4f3460e48fc55eb90af6aec07400b1d3ab1a4f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 8 Aug 2020 19:39:46 -0700 Subject: [PATCH 1/5] quickfort xslx support --- internal/quickfort/command.lua | 12 ++-- internal/quickfort/list.lua | 100 +++++++++++++++++++++------------ internal/quickfort/parse.lua | 65 ++++++++++++++++----- quickfort.lua | 6 +- 4 files changed, 126 insertions(+), 57 deletions(-) diff --git a/internal/quickfort/command.lua b/internal/quickfort/command.lua index 2b1160863c..d26c71cd63 100644 --- a/internal/quickfort/command.lua +++ b/internal/quickfort/command.lua @@ -27,8 +27,8 @@ local valid_command_args = utils.invert({ '-quiet', 'v', '-verbose', - 's', - '-sheet', + 'n', + '-name', }) function do_command(in_args) @@ -42,14 +42,16 @@ function do_command(in_args) qerror("expected or parameter") end local list_num = tonumber(blueprint_name) + local sheet_name = nil if list_num then - blueprint_name = quickfort_list.get_blueprint_by_number(list_num) + blueprint_name, sheet_name = + quickfort_list.get_blueprint_by_number(list_num) end local args = utils.processArgs(in_args, valid_command_args) local quiet = args['q'] ~= nil or args['-quiet'] ~= nil local verbose = args['v'] ~= nil or args['-verbose'] ~= nil - local sheet = tonumber(args['s']) or tonumber(args['-sheet']) + sheet_name = sheet_name or tonumber(args['n']) or tonumber(args['-name']) local cursor = guidm.getCursorPos() if command ~= 'orders' and not cursor then @@ -60,7 +62,7 @@ function do_command(in_args) quickfort_common.verbose = verbose local filepath = quickfort_common.get_blueprint_filepath(blueprint_name) - local data = quickfort_parse.process_file(filepath, cursor) + local data = quickfort_parse.process_file(filepath, sheet_name, cursor) for zlevel, section_data_list in pairs(data) do for _, section_data in ipairs(section_data_list) do local modeline = section_data.modeline diff --git a/internal/quickfort/list.lua b/internal/quickfort/list.lua index 22be602706..3a79cd074a 100644 --- a/internal/quickfort/list.lua +++ b/internal/quickfort/list.lua @@ -20,64 +20,90 @@ end local blueprint_cache = {} -local function scan_blueprint(path) +local function scan_csv_blueprint(path) local filepath = quickfort_common.get_blueprint_filepath(path) local mtime = dfhack.filesystem.mtime(filepath) if not blueprint_cache[path] or blueprint_cache[path].mtime ~= mtime then blueprint_cache[path] = {modeline=get_modeline(filepath), mtime=mtime} end + if not blueprint_cache[path].modeline then + print(string.format('skipping "%s": no #mode marker detected', v.path)) + end return blueprint_cache[path].modeline end -local blueprint_files = {} +local function scan_xlsx_blueprint(path) + local filepath = quickfort_common.get_blueprint_filepath(path) + local mtime = dfhack.filesystem.mtime(filepath) + if blueprint_cache[path] and blueprint_cache[path].mtime == mtime then + return blueprint_cache[path].sheet_infos + end + local sheet_infos = {} + local xlsx_file = dfhack.xlsxreader.open_xlsx_file(filepath) + for _, sheet_name in ipairs(dfhack.xlsxreader.list_sheets(xlsx_file)) do + local xlsx_sheet = dfhack.xlsxreader.open_sheet(xlsx_file, sheet_name) + local row_cells = dfhack.xlsxreader.get_row(xlsx_sheet) + dfhack.xlsxreader.close_sheet(xlsx_sheet) + if not row_cells or #row_cells == 0 then goto continue end + local modeline = quickfort_parse.parse_modeline(row_cells[1]) + if not modeline then goto continue end + table.insert(sheet_infos, {name=sheet_name, modeline=modeline}) + ::continue:: + end + dfhack.xlsxreader.close_xlsx_file(xlsx_file) + if #sheet_infos == 0 then + print(string.format( + 'skipping "%s": no sheet with a #mode marker detected', v.path)) + end + blueprint_cache[path] = {sheet_infos=sheet_infos, mtime=mtime} + return sheet_infos +end + +local blueprints = {} local function scan_blueprints() local paths = dfhack.filesystem.listdir_recursive( quickfort_common.settings['blueprints_dir'].value, nil, false) - blueprint_files = {} - local library_files = {} + blueprints = {} + local library_blueprints = {} for _, v in ipairs(paths) do - if not v.isdir and - (string.find(v.path, '[.]csv$') or - string.find(v.path, '[.]xlsx$')) then - if string.find(v.path, '[.]xlsx$') then - print(string.format( - 'skipping "%s": .xlsx files not supported yet', v.path)) - goto skip + local is_library = string.find(v.path, '^library/') ~= nil + local target_list = blueprints + if is_library then target_list = library_blueprints end + if not v.isdir and string.find(v.path:lower(), '[.]csv$') then + local modeline = scan_csv_blueprint(v.path) + if modeline then + table.insert(target_list, + {path=v.path, modeline=modeline, is_library=is_library}) end - local modeline = scan_blueprint(v.path) - if not modeline then - print(string.format( - 'skipping "%s": no #mode marker detected', v.path)) - goto skip + elseif not v.isdir and string.find(v.path:lower(), '[.]xlsx$') then + local sheet_infos = scan_xlsx_blueprint(v.path) + if #sheet_infos > 0 then + for _, sheet_info in ipairs(sheet_infos) do + table.insert(target_list, + {path=v.path, + sheet_name=sheet_info.name, + modeline=sheet_info.modeline, + is_library=is_library}) + end end - if string.find(v.path, '^library/') ~= nil then - table.insert( - library_files, - {path=v.path, modeline=modeline, is_library=true}) - else - table.insert( - blueprint_files, - {path=v.path, modeline=modeline, is_library=false}) - end - ::skip:: end end -- tack library files on to the end so user files are contiguous - for i=1, #library_files do - blueprint_files[#blueprint_files + 1] = library_files[i] + for i=1, #library_blueprints do + blueprints[#blueprints + 1] = library_blueprints[i] end end function get_blueprint_by_number(list_num) - if #blueprint_files == 0 then + if #blueprints == 0 then scan_blueprints() end - local blueprint_file = blueprint_files[list_num] - if not blueprint_file then + local blueprint = blueprints[list_num] + if not blueprint then qerror(string.format('invalid list index: %d', list_num)) end - return blueprint_file.path + return blueprint.path, blueprint.sheet_name end local valid_list_args = utils.invert({ @@ -89,8 +115,12 @@ function do_list(in_args) local args = utils.processArgs(in_args, valid_list_args) local show_library = args['l'] ~= nil or args['-library'] ~= nil scan_blueprints() - for i, v in ipairs(blueprint_files) do + for i, v in ipairs(blueprints) do if show_library or not v.is_library then + local sheet_spec = '' + if v.sheet_name then + sheet_spec = string.format(' -n "%s"', v.sheet_name) + end local comment = ')' if #v.modeline.comment > 0 then comment = string.format(': %s)', v.modeline.comment) @@ -100,8 +130,8 @@ function do_list(in_args) start_comment = string.format('; cursor start: %s', v.modeline.start_comment) end - print(string.format('%d) "%s" (%s%s%s', - i, v.path, v.modeline.mode, comment, + print(string.format('%d) "%s"%s (%s%s%s', + i, v.path, sheet_spec, v.modeline.mode, comment, start_comment)) end end diff --git a/internal/quickfort/parse.lua b/internal/quickfort/parse.lua index 858d32a2a4..45098277e2 100644 --- a/internal/quickfort/parse.lua +++ b/internal/quickfort/parse.lua @@ -13,6 +13,7 @@ local function trim_and_insert(tokens, token) end -- adapted from example on http://lua-users.org/wiki/LuaCsv +-- returns a list of strings corresponding to the text in the cells in the row function tokenize_csv_line(line) line = string.gsub(line, '[\r\n]*$', '') local tokens = {} @@ -108,16 +109,56 @@ local function make_cell_label(col_num, row_num) return get_col_name(col_num) .. tostring(math.floor(row_num)) end +local function read_csv_line(ctx) + local line = ctx.csv_file:read() + if not line then return nil end + return tokenize_csv_line(line) +end + +local function cleanup_csv_ctx(ctx) + ctx.csv_file:close() +end + +local function read_xlsx_line(ctx) + return dfhack.xlsxreader.get_row(ctx.xlsx_sheet) +end + +local function cleanup_xslx_ctx(ctx) + dfhack.xlsxreader.close_sheet(ctx.xlsx_sheet) + dfhack.xlsxreader.close_xlsx_file(ctx.xlsx_file) +end + +local function init_reader_ctx(filepath, sheet_name) + local reader_ctx = {} + if string.find(filepath:lower(), '[.]csv$') then + local file = io.open(filepath) + if not file then + qerror(string.format('failed to open blueprint file: "%s"', + filepath)) + end + reader_ctx.csv_file = file + reader_ctx.get_row_tokens = read_csv_line + reader_ctx.cleanup = cleanup_csv_ctx + else + reader_ctx.xlsx_file = dfhack.xlsxreader.open_xlsx_file(filepath) + reader_ctx.xlsx_sheet = + dfhack.xlsxreader.open_sheet(reader_ctx.xlsx_file, sheet_name) + reader_ctx.get_row_tokens = read_xlsx_line + reader_ctx.cleanup = cleanup_xslx_ctx + end + return reader_ctx +end + -- returns a grid representation of the current section, the number of lines -- read from the input, and the next z-level modifier, if any. See process_file -- for grid format. -local function process_section(file, start_line_num, start_coord) +local function process_section(reader_ctx, start_line_num, start_coord) local grid = {} local y = start_coord.y while true do - local line = file:read() - if not line then return grid, y-start_coord.y end - for i, v in ipairs(tokenize_csv_line(line)) do + local row_tokens = reader_ctx.get_row_tokens(reader_ctx) + if not row_tokens then return grid, y-start_coord.y end + for i, v in ipairs(row_tokens) do if i == 1 then if v == '#<' then return grid, y-start_coord.y, 1 end if v == '#>' then return grid, y-start_coord.y, -1 end @@ -146,13 +187,10 @@ Where the structure of modeline is defined as per parse_modeline and grid is a: Map keys are numbers, and the keyspace is sparse -- only elements that have contents are non-nil. ]] -function process_file(filepath, start_cursor_coord) - local file = io.open(filepath) - if not file then - qerror(string.format('failed to open blueprint file: "%s"', filepath)) - end - local line = file:read() - local modeline = parse_modeline(tokenize_csv_line(line)[1]) +function process_file(filepath, sheet_name, start_cursor_coord) + local reader_ctx = init_reader_ctx(filepath, sheet_name) + local row_tokens = reader_ctx.get_row_tokens(reader_ctx) + local modeline = parse_modeline(row_tokens[1]) local cur_line_num = 2 local x = start_cursor_coord.x - modeline.startx + 1 local y = start_cursor_coord.y - modeline.starty + 1 @@ -160,7 +198,7 @@ function process_file(filepath, start_cursor_coord) local zlevels = {} while true do local grid, num_section_rows, zmod = - process_section(file, cur_line_num, xyz2pos(x, y, z)) + process_section(reader_ctx, cur_line_num, xyz2pos(x, y, z)) for _, _ in pairs(grid) do -- apparently, the only way to tell if a sparse array is not empty if not zlevels[z] then zlevels[z] = {} end @@ -171,7 +209,6 @@ function process_file(filepath, start_cursor_coord) cur_line_num = cur_line_num + num_section_rows + 1 z = z + zmod end - file:close() + reader_ctx.cleanup(reader_ctx) return zlevels end - diff --git a/quickfort.lua b/quickfort.lua index 17e41ae34f..a00ceb7291 100644 --- a/quickfort.lua +++ b/quickfort.lua @@ -39,10 +39,10 @@ Usage: subfolder are not shown. Specify ``-l`` to include library blueprints. **quickfort []** Applies the blueprint with the number from the list command. -**quickfort [-s|--sheet ] []** +**quickfort [-n|--name ] []** Applies the blueprint from the named file. If it is an ``.xlsx`` file, - the ``-s`` (or ``--sheet``) parameter is required to identify the sheet - number. The first sheet is ``-s 1``. + the ``-n`` (or ``--name``) parameter can identify the sheet name. If the + sheet name is not specified, the first sheet is used. **** can be one of: From 8112e776fa71b96c8dcf3ee7a344e8a9894bebea Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 9 Aug 2020 03:04:05 -0700 Subject: [PATCH 2/5] xlsx file reading error checking --- internal/quickfort/command.lua | 3 +-- internal/quickfort/parse.lua | 11 +++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/internal/quickfort/command.lua b/internal/quickfort/command.lua index d26c71cd63..7b866958c1 100644 --- a/internal/quickfort/command.lua +++ b/internal/quickfort/command.lua @@ -47,11 +47,10 @@ function do_command(in_args) blueprint_name, sheet_name = quickfort_list.get_blueprint_by_number(list_num) end - local args = utils.processArgs(in_args, valid_command_args) local quiet = args['q'] ~= nil or args['-quiet'] ~= nil local verbose = args['v'] ~= nil or args['-verbose'] ~= nil - sheet_name = sheet_name or tonumber(args['n']) or tonumber(args['-name']) + sheet_name = sheet_name or args['n'] or args['-name'] local cursor = guidm.getCursorPos() if command ~= 'orders' and not cursor then diff --git a/internal/quickfort/parse.lua b/internal/quickfort/parse.lua index 45098277e2..0bd3c8ed2f 100644 --- a/internal/quickfort/parse.lua +++ b/internal/quickfort/parse.lua @@ -141,6 +141,12 @@ local function init_reader_ctx(filepath, sheet_name) reader_ctx.cleanup = cleanup_csv_ctx else reader_ctx.xlsx_file = dfhack.xlsxreader.open_xlsx_file(filepath) + if not reader_ctx.xlsx_file then + qerror(string.format('failed to open blueprint file: "%s"', + filepath)) + end + -- open_sheet succeeds even if the sheet cannot be found; we need to + -- check that when we try to read reader_ctx.xlsx_sheet = dfhack.xlsxreader.open_sheet(reader_ctx.xlsx_file, sheet_name) reader_ctx.get_row_tokens = read_xlsx_line @@ -190,6 +196,11 @@ contents are non-nil. function process_file(filepath, sheet_name, start_cursor_coord) local reader_ctx = init_reader_ctx(filepath, sheet_name) local row_tokens = reader_ctx.get_row_tokens(reader_ctx) + if not row_tokens then + qerror(string.format( + 'sheet with name: "%s" in file "%s" empty or not found', + sheet_name, filepath)) + end local modeline = parse_modeline(row_tokens[1]) local cur_line_num = 2 local x = start_cursor_coord.x - modeline.startx + 1 From 58fe8a11140bd5a50e8ee4cde9631cb565fd8861 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 9 Aug 2020 09:51:54 -0700 Subject: [PATCH 3/5] use new xlsxreader plugin api --- internal/quickfort/list.lua | 13 +++++++------ internal/quickfort/parse.lua | 11 ++++++----- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/internal/quickfort/list.lua b/internal/quickfort/list.lua index 3a79cd074a..9449713b62 100644 --- a/internal/quickfort/list.lua +++ b/internal/quickfort/list.lua @@ -6,6 +6,7 @@ if not dfhack_flags.module then end local utils = require('utils') +local xlsxreader = require('plugins.xlsxreader') local quickfort_common = reqscript('internal/quickfort/common') local quickfort_parse = reqscript('internal/quickfort/parse') @@ -39,18 +40,18 @@ local function scan_xlsx_blueprint(path) return blueprint_cache[path].sheet_infos end local sheet_infos = {} - local xlsx_file = dfhack.xlsxreader.open_xlsx_file(filepath) - for _, sheet_name in ipairs(dfhack.xlsxreader.list_sheets(xlsx_file)) do - local xlsx_sheet = dfhack.xlsxreader.open_sheet(xlsx_file, sheet_name) - local row_cells = dfhack.xlsxreader.get_row(xlsx_sheet) - dfhack.xlsxreader.close_sheet(xlsx_sheet) + local xlsx_file = xlsxreader.open_xlsx_file(filepath) + for _, sheet_name in ipairs(xlsxreader.list_sheets(xlsx_file)) do + local xlsx_sheet = xlsxreader.open_sheet(xlsx_file, sheet_name) + local row_cells = xlsxreader.get_row(xlsx_sheet) + xlsxreader.close_sheet(xlsx_sheet) if not row_cells or #row_cells == 0 then goto continue end local modeline = quickfort_parse.parse_modeline(row_cells[1]) if not modeline then goto continue end table.insert(sheet_infos, {name=sheet_name, modeline=modeline}) ::continue:: end - dfhack.xlsxreader.close_xlsx_file(xlsx_file) + xlsxreader.close_xlsx_file(xlsx_file) if #sheet_infos == 0 then print(string.format( 'skipping "%s": no sheet with a #mode marker detected', v.path)) diff --git a/internal/quickfort/parse.lua b/internal/quickfort/parse.lua index 0bd3c8ed2f..14145aed53 100644 --- a/internal/quickfort/parse.lua +++ b/internal/quickfort/parse.lua @@ -5,6 +5,7 @@ if not dfhack_flags.module then qerror('this script cannot be called directly') end +local xlsxreader = require('plugins.xlsxreader') local quickfort_common = reqscript('internal/quickfort/common') local function trim_and_insert(tokens, token) @@ -120,12 +121,12 @@ local function cleanup_csv_ctx(ctx) end local function read_xlsx_line(ctx) - return dfhack.xlsxreader.get_row(ctx.xlsx_sheet) + return xlsxreader.get_row(ctx.xlsx_sheet) end local function cleanup_xslx_ctx(ctx) - dfhack.xlsxreader.close_sheet(ctx.xlsx_sheet) - dfhack.xlsxreader.close_xlsx_file(ctx.xlsx_file) + xlsxreader.close_sheet(ctx.xlsx_sheet) + xlsxreader.close_xlsx_file(ctx.xlsx_file) end local function init_reader_ctx(filepath, sheet_name) @@ -140,7 +141,7 @@ local function init_reader_ctx(filepath, sheet_name) reader_ctx.get_row_tokens = read_csv_line reader_ctx.cleanup = cleanup_csv_ctx else - reader_ctx.xlsx_file = dfhack.xlsxreader.open_xlsx_file(filepath) + reader_ctx.xlsx_file = xlsxreader.open_xlsx_file(filepath) if not reader_ctx.xlsx_file then qerror(string.format('failed to open blueprint file: "%s"', filepath)) @@ -148,7 +149,7 @@ local function init_reader_ctx(filepath, sheet_name) -- open_sheet succeeds even if the sheet cannot be found; we need to -- check that when we try to read reader_ctx.xlsx_sheet = - dfhack.xlsxreader.open_sheet(reader_ctx.xlsx_file, sheet_name) + xlsxreader.open_sheet(reader_ctx.xlsx_file, sheet_name) reader_ctx.get_row_tokens = read_xlsx_line reader_ctx.cleanup = cleanup_xslx_ctx end From 8b46dce5eb3cfaf31852ab4a31d17a77565c1187 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 10 Aug 2020 08:10:46 -0700 Subject: [PATCH 4/5] don't leak memory on errors --- internal/quickfort/list.lua | 44 +++++++++++++++++++++++++----------- internal/quickfort/parse.lua | 37 ++++++++++++++++++------------ 2 files changed, 54 insertions(+), 27 deletions(-) diff --git a/internal/quickfort/list.lua b/internal/quickfort/list.lua index 9449713b62..2f6846d4dc 100644 --- a/internal/quickfort/list.lua +++ b/internal/quickfort/list.lua @@ -33,25 +33,43 @@ local function scan_csv_blueprint(path) return blueprint_cache[path].modeline end +local function get_xlsx_sheet_modeline(xlsx_file, sheet_name) + local xlsx_sheet = xlsxreader.open_sheet(xlsx_file, sheet_name) + return dfhack.with_finalize( + function() xlsxreader.close_sheet(xlsx_sheet) end, + function() + local row_cells = xlsxreader.get_row(xlsx_sheet) + if not row_cells or #row_cells == 0 then return nil end + return quickfort_parse.parse_modeline(row_cells[1]) + end + ) +end + +local function get_xlsx_file_sheet_infos(filepath) + local xlsx_file = xlsxreader.open_xlsx_file(filepath) + return dfhack.with_finalize( + function() xlsxreader.close_xlsx_file(xlsx_file) end, + function() + local sheet_infos = {} + for _, sheet_name in ipairs(xlsxreader.list_sheets(xlsx_file)) do + local modeline = get_xlsx_sheet_modeline(xlsx_file, sheet_name) + if modeline then + table.insert(sheet_infos, + {name=sheet_name, modeline=modeline}) + end + end + return sheet_infos + end + ) +end + local function scan_xlsx_blueprint(path) local filepath = quickfort_common.get_blueprint_filepath(path) local mtime = dfhack.filesystem.mtime(filepath) if blueprint_cache[path] and blueprint_cache[path].mtime == mtime then return blueprint_cache[path].sheet_infos end - local sheet_infos = {} - local xlsx_file = xlsxreader.open_xlsx_file(filepath) - for _, sheet_name in ipairs(xlsxreader.list_sheets(xlsx_file)) do - local xlsx_sheet = xlsxreader.open_sheet(xlsx_file, sheet_name) - local row_cells = xlsxreader.get_row(xlsx_sheet) - xlsxreader.close_sheet(xlsx_sheet) - if not row_cells or #row_cells == 0 then goto continue end - local modeline = quickfort_parse.parse_modeline(row_cells[1]) - if not modeline then goto continue end - table.insert(sheet_infos, {name=sheet_name, modeline=modeline}) - ::continue:: - end - xlsxreader.close_xlsx_file(xlsx_file) + local sheet_infos = get_xlsx_file_sheet_infos(filepath) if #sheet_infos == 0 then print(string.format( 'skipping "%s": no sheet with a #mode marker detected', v.path)) diff --git a/internal/quickfort/parse.lua b/internal/quickfort/parse.lua index 14145aed53..ce428e812b 100644 --- a/internal/quickfort/parse.lua +++ b/internal/quickfort/parse.lua @@ -183,19 +183,7 @@ local function process_section(reader_ctx, start_line_num, start_coord) end end ---[[ -returns the following logical structure: - map of target map z coordinate -> - list of {modeline, grid} tables -Where the structure of modeline is defined as per parse_modeline and grid is a: - map of target y coordinate -> - map of target map x coordinate -> - {cell=spreadsheet cell, text=text from spreadsheet cell} -Map keys are numbers, and the keyspace is sparse -- only elements that have -contents are non-nil. -]] -function process_file(filepath, sheet_name, start_cursor_coord) - local reader_ctx = init_reader_ctx(filepath, sheet_name) +function process_sections(reader_ctx, filepath, sheet_name, start_cursor_coord) local row_tokens = reader_ctx.get_row_tokens(reader_ctx) if not row_tokens then qerror(string.format( @@ -221,6 +209,27 @@ function process_file(filepath, sheet_name, start_cursor_coord) cur_line_num = cur_line_num + num_section_rows + 1 z = z + zmod end - reader_ctx.cleanup(reader_ctx) return zlevels end + +--[[ +returns the following logical structure: + map of target map z coordinate -> + list of {modeline, grid} tables +Where the structure of modeline is defined as per parse_modeline and grid is a: + map of target y coordinate -> + map of target map x coordinate -> + {cell=spreadsheet cell, text=text from spreadsheet cell} +Map keys are numbers, and the keyspace is sparse -- only elements that have +contents are non-nil. +]] +function process_file(filepath, sheet_name, start_cursor_coord) + local reader_ctx = init_reader_ctx(filepath, sheet_name) + return dfhack.with_finalize( + function() reader_ctx.cleanup(reader_ctx) end, + function() + return process_sections(reader_ctx, filepath, sheet_name, + start_cursor_coord) + end + ) +end From 1843d7c40f86db9122733f34f0bbe98bd54267a6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 15 Aug 2020 23:43:05 -0700 Subject: [PATCH 5/5] don't use bad xlsx file handles --- internal/quickfort/list.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/quickfort/list.lua b/internal/quickfort/list.lua index 2f6846d4dc..cf61acb765 100644 --- a/internal/quickfort/list.lua +++ b/internal/quickfort/list.lua @@ -28,7 +28,7 @@ local function scan_csv_blueprint(path) blueprint_cache[path] = {modeline=get_modeline(filepath), mtime=mtime} end if not blueprint_cache[path].modeline then - print(string.format('skipping "%s": no #mode marker detected', v.path)) + print(string.format('skipping "%s": no #mode marker detected', path)) end return blueprint_cache[path].modeline end @@ -46,11 +46,12 @@ local function get_xlsx_sheet_modeline(xlsx_file, sheet_name) end local function get_xlsx_file_sheet_infos(filepath) + local sheet_infos = {} local xlsx_file = xlsxreader.open_xlsx_file(filepath) + if not xlsx_file then return sheet_infos end return dfhack.with_finalize( function() xlsxreader.close_xlsx_file(xlsx_file) end, function() - local sheet_infos = {} for _, sheet_name in ipairs(xlsxreader.list_sheets(xlsx_file)) do local modeline = get_xlsx_sheet_modeline(xlsx_file, sheet_name) if modeline then @@ -72,7 +73,7 @@ local function scan_xlsx_blueprint(path) local sheet_infos = get_xlsx_file_sheet_infos(filepath) if #sheet_infos == 0 then print(string.format( - 'skipping "%s": no sheet with a #mode marker detected', v.path)) + 'skipping "%s": no sheet with #mode markers detected', path)) end blueprint_cache[path] = {sheet_infos=sheet_infos, mtime=mtime} return sheet_infos