buy() reads the lot, prompts for input, then writes back the lot,
triggering a generation oops. Any updates made by other threads while
buy() waits for input are wiped out, triggering a seqno mismatch oops.
Since commodities are taken from the seller when he puts them on the
market, and given to the buyer when the trade executes, the wiped out
lot's seller loses his goods without compensation, the other seller
gets to keep his goods, and the buyer receives their duplicates.
This can be abused by two conspiring countries to duplicate
commodities. The seller puts them on the market (say 100 gold bars).
The buyer starts a buy command, and waits at its last prompt for the
lot to be replaced. The seller takes them off the market (possible,
since there's no bid, yet), and sells something else (say one food)
quickly enough to get the same lot number assigned. The buyer then
completes the buy command. The seller loses one food, the buyer gains
100 gold bars.
Replaces a partial fix from v4.0.1, which only caught lots gone away,
not lots replaced by new ones.
Fighters, SAMs, ABMs and anti-sats could intercept, and tactical
missiles could interdict ships or land units.
Missed when the other missions were fixed in v4.2.7.
setsector() reads the sector, prompts for input, then writes back the
sector, triggering a generation oops. Any updates made by other
threads while setsector() waits for input are wiped out, triggering a
seqno mismatch oops.
Same for setres().
They can still get split by output arriving between two reads from
input, but that's unavoidable, because the client is designed to read
and write big chunks, not lines.
give() reads the sector, prompts for input, updates the sector and
writes it back, triggering a generation oops. Any updates made by
other threads during the yield are wiped out, triggering a seqno
mismatch oops.
getstarg(), snxtitem() and snxtsct() can yield the processor, because
they call getstring(). But only for null or empty arguments. For
other arguments, we should call ef_make_stale(), to catch errors.
Problem: if a caller never passes null or empty arguments, it may rely
on these functions not yielding. We'd get false positives. In
general, we can't know whether that's the case. But we do know in the
common special case of player arguments. Call ef_make_stale() for
those.
Without ARG1, display_region_map() formats a rectangular area around
CURX,CURY, so it can use do_map(). Ugly, and duplicates some
unit_map() functionality.
Factor snxtsct_around() out of unit_map(). Inline do_map() into
display_region_map(), so we can use snxtsct_around().
Don't mess with player->condarg. Leftover from when we abused map()
here.
Split with parse() and pass first two arguments instead of the raw
tail to the map() callback. Advantages:
* Consistent with do_unit_move().
* Does the right thing when the tail is just spaces. Before, the
spaces got passed to the map() callback, which complained about
syntax. Now, they are ignored. This is what the commit I just
reverted tried to fix.
* Works better when the tail splits into more than two arguments.
Except for explore_map(), which ignores the argument(s), the map()
callbacks use display_region_map(), which split the tail at the
first space, and complained about any spaces in the second part.
Now, display_region_map() takes two argument strings instead of a
single, unsplit argument string, and extra arguments get silently
ignored, as usual.
parse_map_flags() silently truncates map flags after the first 't' or
'r'. This makes it accept arguments "true" and "revert". However, it
also breaks the perfectly sensible argument "ts", which should show
ships just like "st", but doesn't.
info bmap & friends document arguments "true" and "revert", and also
suggest flags 't' and 'r'. What a mess.
Make argument "revert" a special case. Deprecate flag 'r', and clean
up truncation there.
Don't truncate after flag 't'. If any bad flags follow, ignore
everything after 't', but deprecate that usage.
Drop .SY for map commands other than the one documented by the info
page.
Use the page's command to discuss arguments "revert" and "true", not
"bmap".
Clarify flags argument.
player->aborted gets set when we get an interrupt or EOF cookie from
the player, when update or shutdown abort commands, and when we abort
an attack (not relevant here).
The checks are useless: player interrupt and EOF are checked
elsewhere, and update/shutdown can run only when we yield the
processor, which we never do (output doesn't yield because C_MOD is
set).
buil() complains about the argument when snxtsct() fails. Misleading
when the argument is fine, but snxtsct() fails due to bad conditional
argument.
Same for radar() with snxtitem().
It misuses snxtsct() and snxtitem() to find out whether the first
argument looks like sectors or like ships, which doesn't work with a
bad conditional argument.
Not worth fixing now; it's been disabled since 4.0.1, and broken at
least since commit 2fc1e74a (v4.3.0) broke its sector/ship
disambiguation via third argument.
It assumes snxtsct() fails only when the argument can't be parsed. It
can also fail when the condition argument has errors. `map # ?xxx'
first complains about xxx, then maps around ship#0. Broken since
Chainsaw 2 introduced smap, pmap and lmap.
Use sarg_type() to recognize sectors vs. unit argument. `map # ?xxx'
now fails as it should.
Subtle side effect: do_map() no longer prompts for argument "",
because snxtsct() is now guarded by sarg_type(). Impact on callers:
* display_region_map() is not affected, because it never passes "".
* map() passes on "" arguments. Change it to prompt in that case.
Consistent with how other commands behave. No functional change.
* do_unit_move() passes on "" arguments. Keep it that way. This
changes navigate and march sub-commands 'M' and 'B' not to prompt
for "" arguments, which is consistent with sub-command 'm' of move,
test and transport.