unit = make_per(fields, ucode, utype, symbol)
end
if not unit then
-- Do not define an alias in terms of another alias.
quit('m_als_undef', symbol)
end
insert_unique_unit(data, unit, units_index)
return
elseif prefix == '!' then
-- ucode may be incorrectly entered as a unit code.
-- symbol is a message saying what unit code should be used.
local unit = { unitcode = ucode, shouldbe = symbol }
insert_unique_unit(data, unit, nil)
return
end
-- Make the unit.
local unit = { utype = utype }
for i, name in ipairs(fieldnames) do
if not empty(fields[i]) then
unit[name] = fields[i]
end
end
-- Remove redundancy from unit.
if unit.sym_us == symbol then
unit.sym_us = nil
end
local prefixes = unit.prefixes
local name1, name2 = unit.name1, unit.name2
if name1 then
if name1 == symbol and not prefixes then
-- A unit which takes an SI prefix must not have a nil name because,
-- for example, the name for "kW" = "kilo" .. "watt" (name for "W").
-- The "not prefixes" test is needed for bnwiki where the
-- watt unit has the same name and symbol.
unit.name1 = nil
end
else
name1 = symbol
end
if name2 then
if name2 == name1 .. plural_suffix then
unit.name2 = nil
end
else
name2 = name1 .. plural_suffix
end
local name1_us, name2_us = unit.name1_us, unit.name2_us
if name1_us then
if name1_us == name1 then
unit.name1_us = nil
end
end
if name2_us then
if unit.name1_us then
if name2_us == unit.name1_us .. plural_suffix then
unit.name2_us = nil
end
elseif name2_us == name2 then
unit.name2_us = nil
end
end
-- Other changes to unit.
unit.scale = clean_scale(unit.scale)
local extra = unit.extra
if not empty(extra) then
-- Set appropriate fields for a unit that needs more than a simple
-- multiplication by a ratio of unit scales to convert values.
unit.iscomplex = true
if extra == 'volume/length' then
unit.invert = 1
elseif extra == 'length/volume' then
unit.invert = -1
elseif specials.utype[utype] == 'type_temperature' then
unit.offset = extra
elseif extra == 'invert' then
unit.invert = -1
else
unit.builtin = extra
end
end
if prefix == '~' then
-- Magic code for units like "acre" where the symbol is not really a
-- symbol, and output should use the singular or plural name instead.
unit.usename = 1
elseif prefix == '*' then
-- Magic code for units like "pitch" which have a symbol that is the same as
-- another unit with entries defined in the default or link exceptions tables.
unit.defkey = ucode -- key for default exceptions
unit.linkey = ucode -- key for link exceptions
end
local name_for_link
if prefixes then
if prefixes == 'SI' then
unit.prefixes = 1
elseif prefixes == 'SI2' then
unit.prefixes = 2
elseif prefixes == 'SI3' then
unit.prefixes = 3
else
quit('m_pfx_bad', prefixes)
end
else
-- Only units which do not accept SI prefixes have name_for_link set.
-- That is because, for example, if set name_for_link = name1 for unit g,
-- then the link is "kilogram" for kg, and "yottagram" for Yg, and so on
-- for all prefixes. That might be desirable for some units, but not all.
name_for_link = name1
end
unit.link, unit.customary = clean_link(unit.link, name_for_link)
if prefixes then
-- The SI prefix is always at the start (position = 1) for symbol and sym_us.
-- However, each name (name1, name2, name1_us, name2_us) can have the SI prefix
-- at any position, and that position can be different for each name.
-- For enwiki, the only units with names where the prefix is not at the start
-- are "square metre" and "cubic metre" ("square meter" and "cubic meter" for sp=us).
-- Some other wikis want the flexibility that the prefix position can be different
-- so the position is stored as nil (if always 1), or N (an integer, if always N),
-- or a string of four comma-separated numbers such as "5,7,9,11" which means the
-- prefix position for (name1, name2, name1_us, name2_us) is (5, 7, 9, 11)
-- respectively.
local name1, name1_us = unit.name1, unit.name1_us -- after redundancy removed
if not name1 then
quit('m_pfx_name')
end
local positions = collection
for i, k in ipairs({ 'name1', 'name2', 'name1_us', 'name2_us' }) do
local name = unit[k]
local pos
if name then
pos = name:find('%s', 1, true)
if pos then
unit[k] = name:sub(1, pos - 1) .. name:sub(pos + 2)
end
elseif i == 2 or i == 3 then
pos = positions[1]
elseif i == 4 then
pos = positions[unit.name1_us and 3 or 2]
end
positions:add(pos or 1)
end
local pos = positions[1]
for i = 2, positions.n do
if pos ~= positions[i] then
pos = '"' .. positions:join(',') .. '"'
break
end
end
if pos ~= 1 then
unit.prefix_position = pos
end
for _, name in ipairs({ 'symbol', 'sym_us', 'name1', 'name1_us', 'name2', 'name2_us' }) do
unit['_' .. name] = unit[name]
unit[name] = nil -- force call to __index metamethod so any SI prefix can be handled
end
end
for name, v in pairs(unit) do
-- Reject if a string field includes "%s" (should not occur after above).
if type(v) == 'string' and v:find('%s', 1, true) then
quit('m_percent_s', name)
end
end
insert_unique_unit(data, unit, units_index)
end
end  
local function make_combination(cfg, data)
-- Return a function which, when called, stores a table that defines a
-- single combination unit. The table is stored in data (also a table).
return function (utype, fields)
-- Store a table defining a unit.
-- This is for a combination unit that specifies more than one output.
-- The target units must be defined first.
-- Throw an error if a problem occurs.
local unit = { utype = utype, combination = {} }
for i, v in ipairs(fields) do
if i == 1 then -- unitcode
if v == '' then
quit('m_cmb_miss')
end
unit.unitcode = v
elseif v == '' then
-- Ignore empty fields.
else
local target = get_unit(v)
if not target then
quit('m_cmb_undef', v, unit.unitcode)
end
if target.utype ~= utype then
quit('m_cmb_type', v, unit.unitcode)
end
table.insert(unit.combination, v)
end
end
if #unit.combination < 2 then
quit(#unit.combination == 0 and 'm_cmb_none' or 'm_cmb_one', unit.unitcode)
end
insert_unique_unit(data, unit, units_index)
end
end  
local function make_perunit(cfg, data)
-- Return a function which, when called, stores a table that defines a
-- fixup for an automatic per unit. The table is stored in data (also a table).
local pertype_index = {} -- to detect attempts to define a fixup twice
return function (utype, fields)
-- Store a table to define a fixup.
-- Typos or other errors in the input are not detected!
-- Parameter utype is ignored (it is nil).
-- Throw an error if a problem occurs.
local lhs, rhs, link, multiplier
for i, v in ipairs(fields) do
if v == '' then
-- Ignore empty fields.
elseif i == 1 then
lhs = v -- like "length/time"
elseif i == 2 then
rhs = v -- like "speed"
elseif i == 3 then
link = v
elseif i == 4 then
if not tonumber(v) then
quit('m_per_inv')
end
multiplier = v
else
quit('m_per_inv')
end
end
if lhs and (rhs or link or multiplier) then
if link or multiplier then
local parts = collection
if rhs then
parts:add('utype = "' .. rhs .. '"')
end
if link then
parts:add('link = "' .. link .. '"')
end
if multiplier then
parts:add('multiplier = ' .. multiplier)
end
rhs = '{ ' .. parts:join(', ') .. ' }'
else
rhs = '"' .. rhs .. '"'
end
if pertype_index[lhs] then
quit('m_per_dup', lhs)
end
pertype_index[lhs] = rhs
table.insert(data, { lhs = lhs, rhs = rhs })
else
quit('m_per_inv')
end
end
end  
local function make_varname(cfg, data)
-- Return a function which, when called, stores a table that defines a
-- variable name for a unit. The table is stored in data (also a table).
return function (utype, fields)
-- Set or update an entry in the data table to record that a unit has a variable name.
-- This is for slwiki where a unit name depends on the value.
-- The target units must be defined first.
-- Parameter utype is ignored (it is nil).
-- Throw an error if a problem occurs.
local count = #fields
if count ~= cfg.varcolumns then
quit('m_var_cnt')
end
local ucode
local names = {}
for i = 1, count do
local v = fields[i]
if empty(v) then
quit('m_var_miss')
end
if i == 1 then -- unitcode
ucode = v
if not get_unit(v) then