Merge branch 'master' of git://github.com/angavrilov/dfhack

develop
Robert Heinrich 2012-04-10 10:18:38 +02:00
commit 7d0cfb7e80
9 changed files with 182 additions and 55 deletions

@ -68,6 +68,11 @@ All typed objects have the following built-in features:
and values. Fields are enumerated in memory order. Methods and and values. Fields are enumerated in memory order. Methods and
lua wrapper properties are not included in the iteration. lua wrapper properties are not included in the iteration.
**WARNING**: a few of the data structures (like ui_look_list)
contain unions with pointers to different types with vtables.
Using pairs on such structs is an almost sure way to crash with
an access violation.
* ``ref._kind`` * ``ref._kind``
Returns one of: ``primitive``, ``struct``, ``container``, Returns one of: ``primitive``, ``struct``, ``container``,
@ -629,10 +634,18 @@ Job module
Prints info about the job. Prints info about the job.
* ``dfhack.job.getJobHolder(job)`` * ``dfhack.job.printItemDetails(jobitem,idx)``
Prints info about the job item.
* ``dfhack.job.getHolder(job)``
Returns the building holding the job. Returns the building holding the job.
* ``dfhack.job.getWorker(job)``
Returns the unit performing the job.
* ``dfhack.job.is_equal(job1,job2)`` * ``dfhack.job.is_equal(job1,job2)``
Compares important fields in the job and nested item structures. Compares important fields in the job and nested item structures.
@ -641,6 +654,12 @@ Job module
Compares important fields in the job item structures. Compares important fields in the job item structures.
* ``dfhack.job.listNewlyCreated(first_id)``
Returns the current value of ``df.global.job_next_id``, and
if there are any jobs with ``first_id <= id < job_next_id``,
a lua list containing them.
Units module Units module
------------ ------------

@ -395,6 +395,10 @@ Every structured field access produces a new userdata instance.</p>
<p>Returns an iterator for the sequence of actual C++ field names <p>Returns an iterator for the sequence of actual C++ field names
and values. Fields are enumerated in memory order. Methods and and values. Fields are enumerated in memory order. Methods and
lua wrapper properties are not included in the iteration.</p> lua wrapper properties are not included in the iteration.</p>
<p><strong>WARNING</strong>: a few of the data structures (like ui_look_list)
contain unions with pointers to different types with vtables.
Using pairs on such structs is an almost sure way to crash with
an access violation.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">ref._kind</tt></p> <li><p class="first"><tt class="docutils literal">ref._kind</tt></p>
<p>Returns one of: <tt class="docutils literal">primitive</tt>, <tt class="docutils literal">struct</tt>, <tt class="docutils literal">container</tt>, <p>Returns one of: <tt class="docutils literal">primitive</tt>, <tt class="docutils literal">struct</tt>, <tt class="docutils literal">container</tt>,
@ -869,15 +873,26 @@ The is_bright boolean actually seems to invert the brightness.</p>
<li><p class="first"><tt class="docutils literal">dfhack.job.printJobDetails(job)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.job.printJobDetails(job)</tt></p>
<p>Prints info about the job.</p> <p>Prints info about the job.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.job.getJobHolder(job)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.job.printItemDetails(jobitem,idx)</tt></p>
<p>Prints info about the job item.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.job.getHolder(job)</tt></p>
<p>Returns the building holding the job.</p> <p>Returns the building holding the job.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.job.getWorker(job)</tt></p>
<p>Returns the unit performing the job.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.job.is_equal(job1,job2)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.job.is_equal(job1,job2)</tt></p>
<p>Compares important fields in the job and nested item structures.</p> <p>Compares important fields in the job and nested item structures.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.job.is_item_equal(job_item1,job_item2)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.job.is_item_equal(job_item1,job_item2)</tt></p>
<p>Compares important fields in the job item structures.</p> <p>Compares important fields in the job item structures.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.job.listNewlyCreated(first_id)</tt></p>
<p>Returns the current value of <tt class="docutils literal">df.global.job_next_id</tt>, and
if there are any jobs with <tt class="docutils literal">first_id &lt;= id &lt; job_next_id</tt>,
a lua list containing them.</p>
</li>
</ul> </ul>
</div> </div>
<div class="section" id="units-module"> <div class="section" id="units-module">

@ -893,26 +893,31 @@ int Core::Update()
if (new_wdata != last_world_data_ptr) if (new_wdata != last_world_data_ptr)
{ {
// we check for map change too // we check for map change too
bool mapchange = new_mapdata != last_local_map_ptr; bool had_map = isMapLoaded();
last_world_data_ptr = new_wdata; last_world_data_ptr = new_wdata;
last_local_map_ptr = new_mapdata; last_local_map_ptr = new_mapdata;
getWorld()->ClearPersistentCache(); getWorld()->ClearPersistentCache();
// and if the world is going away, we report the map change first // and if the world is going away, we report the map change first
if(!new_wdata && mapchange) if(had_map)
plug_mgr->OnStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED); plug_mgr->OnStateChange(out, SC_MAP_UNLOADED);
// and if the world is appearing, we report map change after that // and if the world is appearing, we report map change after that
plug_mgr->OnStateChange(out, new_wdata ? SC_WORLD_LOADED : SC_WORLD_UNLOADED); plug_mgr->OnStateChange(out, new_wdata ? SC_WORLD_LOADED : SC_WORLD_UNLOADED);
if(new_wdata && mapchange) if(isMapLoaded())
plug_mgr->OnStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED); plug_mgr->OnStateChange(out, SC_MAP_LOADED);
} }
// otherwise just check for map change... // otherwise just check for map change...
else if (new_mapdata != last_local_map_ptr) else if (new_mapdata != last_local_map_ptr)
{ {
bool had_map = isMapLoaded();
last_local_map_ptr = new_mapdata; last_local_map_ptr = new_mapdata;
getWorld()->ClearPersistentCache();
plug_mgr->OnStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED); if (isMapLoaded() != had_map)
{
getWorld()->ClearPersistentCache();
plug_mgr->OnStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED);
}
} }
// detect if the viewscreen changed // detect if the viewscreen changed

@ -517,10 +517,13 @@ static void OpenMatinfo(lua_State *state)
* Wrappers for C++ API * * Wrappers for C++ API *
************************/ ************************/
static void OpenModule(lua_State *state, const char *mname, const LuaWrapper::FunctionReg *reg) static void OpenModule(lua_State *state, const char *mname,
const LuaWrapper::FunctionReg *reg, const luaL_Reg *reg2 = NULL)
{ {
luaL_getsubtable(state, lua_gettop(state), mname); luaL_getsubtable(state, lua_gettop(state), mname);
LuaWrapper::SetFunctionWrappers(state, reg); LuaWrapper::SetFunctionWrappers(state, reg);
if (reg2)
luaL_setfuncs(state, reg2, 0);
lua_pop(state, 1); lua_pop(state, 1);
} }
@ -547,14 +550,46 @@ static bool jobEqual(df::job *job1, df::job *job2) { return *job1 == *job2; }
static bool jobItemEqual(df::job_item *job1, df::job_item *job2) { return *job1 == *job2; } static bool jobItemEqual(df::job_item *job1, df::job_item *job2) { return *job1 == *job2; }
static const LuaWrapper::FunctionReg dfhack_job_module[] = { static const LuaWrapper::FunctionReg dfhack_job_module[] = {
WRAP(cloneJobStruct), WRAPM(Job,cloneJobStruct),
WRAP(printJobDetails), WRAPM(Job,printItemDetails),
WRAP(getJobHolder), WRAPM(Job,printJobDetails),
WRAPM(Job,getHolder),
WRAPM(Job,getWorker),
WRAPN(is_equal, jobEqual), WRAPN(is_equal, jobEqual),
WRAPN(is_item_equal, jobItemEqual), WRAPN(is_item_equal, jobItemEqual),
{ NULL, NULL } { NULL, NULL }
}; };
static int job_listNewlyCreated(lua_State *state)
{
int nxid = luaL_checkint(state, 1);
lua_settop(state, 1);
std::vector<df::job*> pvec;
if (Job::listNewlyCreated(&pvec, &nxid))
{
lua_pushinteger(state, nxid);
lua_newtable(state);
for (size_t i = 0; i < pvec.size(); i++)
{
Lua::PushDFObject(state, pvec[i]);
lua_rawseti(state, -2, i+1);
}
return 2;
}
else
return 1;
}
static const luaL_Reg dfhack_job_funcs[] = {
{ "listNewlyCreated", job_listNewlyCreated },
{ NULL, NULL }
};
static const LuaWrapper::FunctionReg dfhack_units_module[] = { static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, setNickname), WRAPM(Units, setNickname),
WRAPM(Units, getVisibleName), WRAPM(Units, getVisibleName),
@ -576,6 +611,6 @@ void OpenDFHackApi(lua_State *state)
LuaWrapper::SetFunctionWrappers(state, dfhack_module); LuaWrapper::SetFunctionWrappers(state, dfhack_module);
OpenModule(state, "gui", dfhack_gui_module); OpenModule(state, "gui", dfhack_gui_module);
OpenModule(state, "job", dfhack_job_module); OpenModule(state, "job", dfhack_job_module, dfhack_job_funcs);
OpenModule(state, "units", dfhack_units_module); OpenModule(state, "units", dfhack_units_module);
} }

@ -1072,10 +1072,10 @@ void LuaWrapper::SetFunctionWrappers(lua_State *state, const FunctionReg *reg)
/** /**
* Add fields in the array to the UPVAL_FIELDTABLE candidates on the stack. * Add fields in the array to the UPVAL_FIELDTABLE candidates on the stack.
*/ */
static void IndexFields(lua_State *state, int base, struct_identity *pstruct) static void IndexFields(lua_State *state, int base, struct_identity *pstruct, bool globals)
{ {
if (pstruct->getParent()) if (pstruct->getParent())
IndexFields(state, base, pstruct->getParent()); IndexFields(state, base, pstruct->getParent(), globals);
auto fields = pstruct->getFields(); auto fields = pstruct->getFields();
if (!fields) if (!fields)
@ -1105,7 +1105,10 @@ static void IndexFields(lua_State *state, int base, struct_identity *pstruct)
break; break;
default: default:
AssociateId(state, base+3, ++cnt, name.c_str()); // Do not add invalid globals to the enumeration order
if (!globals || *(void**)fields[i].offset)
AssociateId(state, base+3, ++cnt, name.c_str());
lua_pushlightuserdata(state, (void*)&fields[i]); lua_pushlightuserdata(state, (void*)&fields[i]);
lua_setfield(state, base+2, name.c_str()); lua_setfield(state, base+2, name.c_str());
break; break;
@ -1143,7 +1146,7 @@ void LuaWrapper::IndexStatics(lua_State *state, int meta_idx, int ftable_idx, st
* Make a struct-style object metatable. * Make a struct-style object metatable.
*/ */
static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct, static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct,
lua_CFunction reader, lua_CFunction writer) lua_CFunction reader, lua_CFunction writer, bool globals = false)
{ {
int base = lua_gettop(state); int base = lua_gettop(state);
@ -1152,7 +1155,7 @@ static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct,
// Index the fields // Index the fields
lua_newtable(state); lua_newtable(state);
IndexFields(state, base, pstruct); IndexFields(state, base, pstruct, globals);
// Add the iteration metamethods // Add the iteration metamethods
PushStructMethod(state, base+1, base+3, meta_struct_next); PushStructMethod(state, base+1, base+3, meta_struct_next);
@ -1304,7 +1307,7 @@ void struct_identity::build_metatable(lua_State *state)
void global_identity::build_metatable(lua_State *state) void global_identity::build_metatable(lua_State *state)
{ {
MakeFieldMetatable(state, this, meta_global_index, meta_global_newindex); MakeFieldMetatable(state, this, meta_global_index, meta_global_newindex, true);
} }
/** /**

@ -36,24 +36,32 @@ namespace df
struct job_item; struct job_item;
struct job_item_filter; struct job_item_filter;
struct building; struct building;
struct unit;
} }
namespace DFHack namespace DFHack
{ {
// Duplicate the job structure. It is not linked into any DF lists. namespace Job {
DFHACK_EXPORT df::job *cloneJobStruct(df::job *job); // Duplicate the job structure. It is not linked into any DF lists.
DFHACK_EXPORT df::job *cloneJobStruct(df::job *job);
// Delete a cloned structure. // Delete a cloned structure.
DFHACK_EXPORT void deleteJobStruct(df::job *job); DFHACK_EXPORT void deleteJobStruct(df::job *job);
DFHACK_EXPORT bool operator== (const df::job_item &a, const df::job_item &b); DFHACK_EXPORT void printItemDetails(color_ostream &out, df::job_item *item, int idx);
DFHACK_EXPORT bool operator== (const df::job &a, const df::job &b); DFHACK_EXPORT void printJobDetails(color_ostream &out, df::job *job);
DFHACK_EXPORT df::building *getHolder(df::job *job);
DFHACK_EXPORT df::unit *getWorker(df::job *job);
DFHACK_EXPORT void printJobDetails(color_ostream &out, df::job *job); DFHACK_EXPORT bool linkIntoWorld(df::job *job, bool new_id = true);
DFHACK_EXPORT df::building *getJobHolder(df::job *job); // lists jobs with ids >= *id_var, and sets *id_var = *job_next_id;
DFHACK_EXPORT bool listNewlyCreated(std::vector<df::job*> *pvec, int *id_var);
}
DFHACK_EXPORT bool linkJobIntoWorld(df::job *job, bool new_id = true); DFHACK_EXPORT bool operator== (const df::job_item &a, const df::job_item &b);
DFHACK_EXPORT bool operator== (const df::job &a, const df::job &b);
} }
#endif #endif

@ -53,7 +53,7 @@ using namespace std;
using namespace DFHack; using namespace DFHack;
using namespace df::enums; using namespace df::enums;
df::job *DFHack::cloneJobStruct(df::job *job) df::job *DFHack::Job::cloneJobStruct(df::job *job)
{ {
CHECK_NULL_POINTER(job); CHECK_NULL_POINTER(job);
@ -87,7 +87,7 @@ df::job *DFHack::cloneJobStruct(df::job *job)
return pnew; return pnew;
} }
void DFHack::deleteJobStruct(df::job *job) void DFHack::Job::deleteJobStruct(df::job *job)
{ {
if (!job) if (!job)
return; return;
@ -148,7 +148,7 @@ bool DFHack::operator== (const df::job &a, const df::job &b)
return true; return true;
} }
static void print_job_item_details(color_ostream &out, df::job *job, unsigned idx, df::job_item *item) void DFHack::Job::printItemDetails(color_ostream &out, df::job_item *item, int idx)
{ {
CHECK_NULL_POINTER(item); CHECK_NULL_POINTER(item);
@ -184,7 +184,7 @@ static void print_job_item_details(color_ostream &out, df::job *job, unsigned id
out << " tool use: " << ENUM_KEY_STR(tool_uses, item->has_tool_use) << endl; out << " tool use: " << ENUM_KEY_STR(tool_uses, item->has_tool_use) << endl;
} }
void DFHack::printJobDetails(color_ostream &out, df::job *job) void DFHack::Job::printJobDetails(color_ostream &out, df::job *job)
{ {
CHECK_NULL_POINTER(job); CHECK_NULL_POINTER(job);
@ -224,10 +224,10 @@ void DFHack::printJobDetails(color_ostream &out, df::job *job)
out << " reaction: " << job->reaction_name << endl; out << " reaction: " << job->reaction_name << endl;
for (size_t i = 0; i < job->job_items.size(); i++) for (size_t i = 0; i < job->job_items.size(); i++)
print_job_item_details(out, job, i, job->job_items[i]); printItemDetails(out, job->job_items[i], i);
} }
df::building *DFHack::getJobHolder(df::job *job) df::building *DFHack::Job::getHolder(df::job *job)
{ {
CHECK_NULL_POINTER(job); CHECK_NULL_POINTER(job);
@ -241,7 +241,21 @@ df::building *DFHack::getJobHolder(df::job *job)
return NULL; return NULL;
} }
bool DFHack::linkJobIntoWorld(df::job *job, bool new_id) df::unit *DFHack::Job::getWorker(df::job *job)
{
CHECK_NULL_POINTER(job);
for (size_t i = 0; i < job->references.size(); i++)
{
VIRTUAL_CAST_VAR(ref, df::general_ref_unit_workerst, job->references[i]);
if (ref)
return ref->getUnit();
}
return NULL;
}
bool DFHack::Job::linkIntoWorld(df::job *job, bool new_id)
{ {
using df::global::world; using df::global::world;
using df::global::job_next_id; using df::global::job_next_id;
@ -269,3 +283,31 @@ bool DFHack::linkJobIntoWorld(df::job *job, bool new_id)
return true; return true;
} }
} }
bool DFHack::Job::listNewlyCreated(std::vector<df::job*> *pvec, int *id_var)
{
using df::global::world;
using df::global::job_next_id;
pvec->clear();
if (!job_next_id || *job_next_id <= *id_var)
return false;
int old_id = *id_var;
int cur_id = *job_next_id;
*id_var = cur_id;
pvec->reserve(std::min(20,cur_id - old_id));
df::job_list_link *link = world->job_list.next;
for (; link; link = link->next)
{
int id = link->item->id;
if (id >= old_id)
pvec->push_back(link->item);
}
return true;
}

@ -289,9 +289,9 @@ static command_result job_duplicate(color_ostream &out, vector <string> & parame
} }
// Actually clone // Actually clone
df::job *pnew = cloneJobStruct(job); df::job *pnew = Job::cloneJobStruct(job);
linkJobIntoWorld(pnew); Job::linkIntoWorld(pnew);
vector_insert_at(building->jobs, ++*ui_workshop_job_cursor, pnew); vector_insert_at(building->jobs, ++*ui_workshop_job_cursor, pnew);
return CR_OK; return CR_OK;
@ -325,14 +325,14 @@ static command_result job_cmd(color_ostream &out, vector <string> & parameters)
return CR_WRONG_USAGE; return CR_WRONG_USAGE;
if (cmd == "query") { if (cmd == "query") {
printJobDetails(out, job); Job::printJobDetails(out, job);
} else { } else {
if (!Gui::workshop_job_hotkey(Core::getTopViewscreen())) if (!Gui::workshop_job_hotkey(Core::getTopViewscreen()))
return CR_WRONG_USAGE; return CR_WRONG_USAGE;
df::building *selected = world->selected_building; df::building *selected = world->selected_building;
for (size_t i = 0; i < selected->jobs.size(); i++) for (size_t i = 0; i < selected->jobs.size(); i++)
printJobDetails(out, selected->jobs[i]); Job::printJobDetails(out, selected->jobs[i]);
} }
} }
else if (cmd == "item-material") else if (cmd == "item-material")
@ -355,7 +355,7 @@ static command_result job_cmd(color_ostream &out, vector <string> & parameters)
if (minfo.isValid() && !iinfo.matches(*item, &minfo)) { if (minfo.isValid() && !iinfo.matches(*item, &minfo)) {
out.printerr("Material does not match the requirements.\n"); out.printerr("Material does not match the requirements.\n");
printJobDetails(out, job); Job::printJobDetails(out, job);
return CR_FAILURE; return CR_FAILURE;
} }
@ -376,7 +376,7 @@ static command_result job_cmd(color_ostream &out, vector <string> & parameters)
out.printerr("WARNING: Due to a probable bug, creature & plant material subtype\n" out.printerr("WARNING: Due to a probable bug, creature & plant material subtype\n"
" is ignored unless the item type is also specified.\n"); " is ignored unless the item type is also specified.\n");
printJobDetails(out, job); Job::printJobDetails(out, job);
return CR_OK; return CR_OK;
} }
else if (cmd == "item-type") else if (cmd == "item-type")
@ -399,7 +399,7 @@ static command_result job_cmd(color_ostream &out, vector <string> & parameters)
if (iinfo.isValid() && !iinfo.matches(*item, &minfo)) { if (iinfo.isValid() && !iinfo.matches(*item, &minfo)) {
out.printerr("Item type does not match the requirements.\n"); out.printerr("Item type does not match the requirements.\n");
printJobDetails(out, job); Job::printJobDetails(out, job);
return CR_FAILURE; return CR_FAILURE;
} }
@ -407,7 +407,7 @@ static command_result job_cmd(color_ostream &out, vector <string> & parameters)
item->item_subtype = iinfo.subtype; item->item_subtype = iinfo.subtype;
out << "Job item updated." << endl; out << "Job item updated." << endl;
printJobDetails(out, job); Job::printJobDetails(out, job);
return CR_OK; return CR_OK;
} }
else else

@ -184,9 +184,9 @@ public:
ProtectedJob(df::job *job) : id(job->id) ProtectedJob(df::job *job) : id(job->id)
{ {
tick_idx = cur_tick_idx; tick_idx = cur_tick_idx;
holder = getJobHolder(job); holder = Job::getHolder(job);
building_id = holder ? holder->id : -1; building_id = holder ? holder->id : -1;
job_copy = cloneJobStruct(job); job_copy = Job::cloneJobStruct(job);
actual_job = job; actual_job = job;
reaction_id = -1; reaction_id = -1;
@ -196,7 +196,7 @@ public:
~ProtectedJob() ~ProtectedJob()
{ {
deleteJobStruct(job_copy); Job::deleteJobStruct(job_copy);
} }
bool isActuallyResumed() { bool isActuallyResumed() {
@ -214,8 +214,8 @@ public:
return; return;
reaction_id = -1; reaction_id = -1;
deleteJobStruct(job_copy); Job::deleteJobStruct(job_copy);
job_copy = cloneJobStruct(job); job_copy = Job::cloneJobStruct(job);
} }
void tick_job(df::job *job, int ticks) void tick_job(df::job *job, int ticks)
@ -365,7 +365,7 @@ static ProtectedJob *get_known(int id)
static bool isSupportedJob(df::job *job) static bool isSupportedJob(df::job *job)
{ {
return job->misc_links.empty() && return job->misc_links.empty() &&
getJobHolder(job) && Job::getHolder(job) &&
(!job->job_items.empty() || (!job->job_items.empty() ||
job->job_type == job_type::CollectClay || job->job_type == job_type::CollectClay ||
job->job_type == job_type::CollectSand); job->job_type == job_type::CollectSand);
@ -526,11 +526,11 @@ static bool recover_job(color_ostream &out, ProtectedJob *pj)
} }
// Create and link in the actual job structure // Create and link in the actual job structure
df::job *recovered = cloneJobStruct(pj->job_copy); df::job *recovered = Job::cloneJobStruct(pj->job_copy);
if (!linkJobIntoWorld(recovered, false)) // reuse same id if (!Job::linkIntoWorld(recovered, false)) // reuse same id
{ {
deleteJobStruct(recovered); Job::deleteJobStruct(recovered);
out.printerr("Inconsistency: job %d (%s) already in list.\n", out.printerr("Inconsistency: job %d (%s) already in list.\n",
pj->id, ENUM_KEY_STR(job_type, pj->job_copy->job_type).c_str()); pj->id, ENUM_KEY_STR(job_type, pj->job_copy->job_type).c_str());
@ -1435,7 +1435,7 @@ static void print_job(color_ostream &out, ProtectedJob *pj)
df::job *job = pj->isLive() ? pj->actual_job : pj->job_copy; df::job *job = pj->isLive() ? pj->actual_job : pj->job_copy;
printJobDetails(out, job); Job::printJobDetails(out, job);
if (job->job_type == job_type::MeltMetalObject && if (job->job_type == job_type::MeltMetalObject &&
isOptionEnabled(CF_AUTOMELT)) isOptionEnabled(CF_AUTOMELT))
@ -1560,7 +1560,7 @@ static command_result workflow_cmd(color_ostream &out, vector <string> & paramet
pending = true; pending = true;
} }
printJobDetails(out, pending_recover[i]->job_copy); Job::printJobDetails(out, pending_recover[i]->job_copy);
} }
} }