Module:Citation/CS1/Identifiers: Difference between revisions

    (sync from sandbox;)
    (sync from sandbox;)
    Line 87: Line 87:
    if options.encode == true or options.encode == nil then
    if options.encode == true or options.encode == nil then
    url_string = mw.uri.encode (url_string);
    url_string = mw.uri.encode (url_string, 'PATH');
    end
    end


    Line 242: Line 242:
    isxn_str = { isxn_str:byte(1, 13) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39
    isxn_str = { isxn_str:byte(1, 13) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39
    for i, v in ipairs (isxn_str) do
    for i, v in ipairs (isxn_str) do
    temp = temp + (3 - 2*(i % 2)) * tonumber (string.char (v) ); -- multiply odd index digits by 1, even index digits by 3 and sum; includes check digit
    temp = temp + (3 - 2*(i % 2)) * tonumber (string.char (v) ); -- multiply odd index digits by 1, even index digits by 3 and sum; includes check digit
    end
    end
    return temp % 10 == 0; -- sum modulo 10 is zero when ISBN-13/ISMN is correct
    return temp % 10 == 0; -- sum modulo 10 is zero when ISBN-13/ISMN is correct
    Line 351: Line 351:
    end
    end


    if err_cat then
    options.coins_list_t['ARXIV'] = nil; -- when error, unset so not included in COinS
    end
    err_cat = err_cat and table.concat ({' ', set_message ('err_bad_arxiv')}) or ''; -- set error message if flag is true
    err_cat = err_cat and table.concat ({' ', set_message ('err_bad_arxiv')}) or ''; -- set error message if flag is true
    Line 404: Line 408:
    err_type = cfg.err_msg_supl.value; -- so value error
    err_type = cfg.err_msg_supl.value; -- so value error
    else
    else
    local next_year = tonumber (os.date ('%Y')) + 1; -- get the current year as a number and add one for next year
    local next_year = tonumber (os.date ('%Y')) + 1; -- get the current year as a number and add one for next year
    year = tonumber (year); -- convert year portion of bibcode to a number
    year = tonumber (year); -- convert year portion of bibcode to a number
    if (1000 > year) or (year > next_year) then
    if (1000 > year) or (year > next_year) then
    Line 417: Line 421:
    if is_set (err_type) then -- if there was an error detected
    if is_set (err_type) then -- if there was an error detected
    text = text .. ' ' .. set_message ('err_bad_bibcode', {err_type});
    text = text .. ' ' .. set_message ('err_bad_bibcode', {err_type});
    options.coins_list_t['BIBCODE'] = nil; -- when error, unset so not included in COinS
    end
    end
    return text;
    return text;
    Line 462: Line 468:
    end -- err_cat remains set here when no match
    end -- err_cat remains set here when no match


    if err_cat then
    options.coins_list_t['BIORXIV'] = nil; -- when error, unset so not included in COinS
    end
    return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
    return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
    prefix = handler.prefix, id = id, separator = handler.separator,
    prefix = handler.prefix, id = id, separator = handler.separator,
    Line 487: Line 497:
    if not matched then
    if not matched then
    text = text .. ' ' .. set_message ('err_bad_citeseerx' );
    text = text .. ' ' .. set_message ('err_bad_citeseerx' );
    options.coins_list_t['CITESEERX'] = nil; -- when error, unset so not included in COinS
    end
    end
    return text;
    return text;
    Line 548: Line 559:
    local registrant_err_patterns = { -- these patterns are for code ranges that are not supported  
    local registrant_err_patterns = { -- these patterns are for code ranges that are not supported  
    '^[^1-3]%d%d%d%d%.%d%d*$', -- 5 digits with subcode (0xxxx, 40000+); accepts: 10000–39999
    '^[^1-3]%d%d%d%d%.%d%d*$', -- 5 digits with subcode (0xxxx, 40000+); accepts: 10000–39999
    '^[^1-4]%d%d%d%d$', -- 5 digits without subcode (0xxxx, 40000+); accepts: 10000–49999
    '^[^1-5]%d%d%d%d$', -- 5 digits without subcode (0xxxx, 60000+); accepts: 10000–59999
    '^[^1-9]%d%d%d%.%d%d*$', -- 4 digits with subcode (0xxx); accepts: 1000–9999
    '^[^1-9]%d%d%d%.%d%d*$', -- 4 digits with subcode (0xxx); accepts: 1000–9999
    '^[^1-9]%d%d%d$', -- 4 digits without subcode (0xxx); accepts: 1000–9999
    '^[^1-9]%d%d%d$', -- 4 digits without subcode (0xxx); accepts: 1000–9999
    Line 558: Line 569:


    if not ignore_invalid then
    if not ignore_invalid then
    if registrant then -- when DOI has proper form
    if registrant then -- when DOI has proper form
    for i, pattern in ipairs (registrant_err_patterns) do -- spin through error patterns
    for i, pattern in ipairs (registrant_err_patterns) do -- spin through error patterns
    if registrant:match (pattern) then -- to validate registrant codes
    if registrant:match (pattern) then -- to validate registrant codes
    err_cat = ' ' .. set_message ('err_bad_doi'); -- when found, mark this DOI as bad
    err_cat = ' ' .. set_message ('err_bad_doi'); -- when found, mark this DOI as bad
    break; -- and done
    break; -- and done
    end
    end
    end
    end
    else
    else
    err_cat = ' ' .. set_message ('err_bad_doi'); -- invalid directory or malformed
    err_cat = ' ' .. set_message ('err_bad_doi'); -- invalid directory or malformed
    end
    end
    else
    else
    Line 572: Line 583:
    end
    end


    if err_cat then
    options.coins_list_t['DOI'] = nil; -- when error, unset so not included in COinS
    end
    text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
    text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
    prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access,
    prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access,
    Line 641: Line 656:
    if nil == id:match("^[^%s–]-/[^%s–]-[^%.,]$") then -- HDL must contain a forward slash, must not contain spaces, endashes, and must not end with period or comma
    if nil == id:match("^[^%s–]-/[^%s–]-[^%.,]$") then -- HDL must contain a forward slash, must not contain spaces, endashes, and must not end with period or comma
    text = text .. ' ' .. set_message ('err_bad_hdl' );
    text = text .. ' ' .. set_message ('err_bad_hdl' );
    options.coins_list_t['HDL'] = nil; -- when error, unset so not included in COinS
    end
    end
    return text;
    return text;
    Line 664: Line 680:
    else -- here when not ignoring
    else -- here when not ignoring
    if not check then -- and there is an error
    if not check then -- and there is an error
    options.coins_list_t['ISBN'] = nil; -- when error, unset so not included in COinS
    return ISBN .. set_message ('err_bad_isbn', {err_type}, false, ' '); -- display an error message
    return ISBN .. set_message ('err_bad_isbn', {err_type}, false, ' '); -- display an error message
    end
    end
    Line 686: Line 703:
    return return_result (false, cfg.err_msg_supl.form);
    return return_result (false, cfg.err_msg_supl.form);
    end
    end
    return return_result (is_valid_isxn (id, 10), cfg.err_msg_supl.check);
    if not is_valid_isxn (id, 10) then -- test isbn-10 for numerical validity
    return return_result (false, cfg.err_msg_supl.check); -- fail if isbn-10 is not numerically valid
    end
    if id:find ('^63[01]') then -- 630xxxxxxx and 631xxxxxxx are (apparently) not valid isbn group ids but are used by amazon as numeric identifiers (asin)
    return return_result (false, cfg.err_msg_supl.group); -- fail if isbn-10 begins with 630/1
    end
    return return_result (true, cfg.err_msg_supl.check); -- pass if isbn-10 is numerically valid
    else
    else
    if id:match ('^%d+$') == nil then
    if id:match ('^%d+$') == nil then
    Line 709: Line 732:
    Error message if not 10 characters, if not ISBN-10, if mixed and first character is a digit.
    Error message if not 10 characters, if not ISBN-10, if mixed and first character is a digit.


    |asin=630....... is (apparently) not a legitimate ISBN though it checksums as one; these do not cause this
    |asin=630....... and |asin=631....... are (apparently) not a legitimate ISBN though it checksums as one; these
    function to emit the maint_asin message
    do not cause this function to emit the maint_asin message


    This function is positioned here because it calls isbn()
    This function is positioned here because it calls isbn()
    Line 720: Line 743:
    local domain = options.ASINTLD;
    local domain = options.ASINTLD;
    local err_cat = ""
    local err_cat = ''


    if not id:match("^[%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u]$") then
    if not id:match("^[%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u]$") then
    Line 726: Line 749:
    else
    else
    if id:match("^%d%d%d%d%d%d%d%d%d[%dX]$") then -- if 10-digit numeric (or 9 digits with terminal X)
    if id:match("^%d%d%d%d%d%d%d%d%d[%dX]$") then -- if 10-digit numeric (or 9 digits with terminal X)
    if isbn (options) then -- see if ASIN value is or validates as ISBN-10
    if is_valid_isxn (id, 10) then -- see if ASIN value is or validates as ISBN-10
    if not id:find ('^630') then -- 630xxxxxxx is (apparently) not a valid isbn prefix but is used by amazon as a numeric identifier
    if not id:find ('^63[01]') then -- 630xxxxxxx and 631xxxxxxx are (apparently) not a valid isbn prefixes but are used by amazon as a numeric identifier
    set_message ('maint_asin'); -- begins with something other than 630 so possibly an isbn
    err_cat = ' ' .. set_message ('err_bad_asin'); -- ASIN has ISBN-10 form but begins with something other than 630/1 so probably an isbn  
    end
    end
    elseif not is_set (err_cat) then
    elseif not is_set (err_cat) then
    Line 737: Line 760:
    end
    end
    end
    end
    if not is_set (domain) then  
    if (not is_set (domain)) or in_array (domain, {'us'}) then -- default: United States
    domain = "com";
    domain = "com";
    elseif in_array (domain, {'jp', 'uk'}) then -- Japan, United Kingdom
    elseif in_array (domain, {'jp', 'uk'}) then -- Japan, United Kingdom
    domain = "co." .. domain;
    domain = "co." .. domain;
    elseif in_array (domain, {'au', 'br', 'mx'}) then -- Australia, Brazil, Mexico
    elseif in_array (domain, {'z.cn'}) then -- China
    domain = "cn";
    elseif in_array (domain, {'au', 'br', 'mx', 'sg', 'tr'}) then -- Australia, Brazil, Mexico, Singapore, Turkey
    domain = "com." .. domain;
    domain = "com." .. domain;
    elseif not in_array (domain, {'ae', 'ca', 'cn', 'de', 'es', 'fr', 'in', 'it', 'nl', 'pl', 'sa', 'se', 'co.jp', 'co.uk', 'com', 'com.au', 'com.br', 'com.mx', 'com.sg', 'com.tr'}) then -- Arabic Emirates, Canada, China, Germany, Spain, France, Indonesia, Italy, Netherlands, Poland, Saudi Arabia, Sweden (as of 2021-03 Austria (.at), Liechtenstein (.li) and Switzerland (.ch) still redirect to the German site (.de) with special settings, so don't maintain local ASINs for them)
    err_cat = ' ' .. set_message ('err_bad_asin_tld'); -- unsupported asin-tld value
    end
    end
    local handler = options.handler;


    local handler = options.handler;
    if not is_set (err_cat) then
    options.coins_list_t['ASIN'] = handler.prefix .. domain .. "/dp/" .. id; -- experiment for asin coins
    else
    options.coins_list_t['ASIN'] = nil; -- when error, unset so not included in COinS
    end
    return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
    return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
    prefix = handler.prefix .. domain .. "/dp/",
    prefix = handler.prefix .. domain .. "/dp/",
    Line 757: Line 790:
    same check digit calculations.  See http://www.ismn-international.org/download/Web_ISMN_Users_Manual_2008-6.pdf
    same check digit calculations.  See http://www.ismn-international.org/download/Web_ISMN_Users_Manual_2008-6.pdf
    section 2, pages 9–12.
    section 2, pages 9–12.
    ismn value not made part of COinS metadata because we don't have a url or isn't a COinS-defined identifier (rft.xxx)
    or an identifier registered at info-uri.info (info:)


    ]]
    ]]
    Line 787: Line 823:


    if false == valid_ismn then
    if false == valid_ismn then
    options.coins_list_t['ISMN'] = nil; -- when error, unset so not included in COinS; not really necessary here because ismn not made part of COinS
    text = text .. ' ' .. set_message ('err_bad_ismn' ) -- add an error message if the ISMN is invalid
    text = text .. ' ' .. set_message ('err_bad_ismn' ) -- add an error message if the ISMN is invalid
    end  
    end  
    Line 839: Line 876:
    else
    else
    if false == valid_issn then
    if false == valid_issn then
    options.coins_list_t['ISSN'] = nil; -- when error, unset so not included in COinS
    text = text .. ' ' .. set_message ('err_bad_issn', (options.hkey == 'EISSN') and 'e' or ''); -- add an error message if the ISSN is invalid
    text = text .. ' ' .. set_message ('err_bad_issn', (options.hkey == 'EISSN') and 'e' or ''); -- add an error message if the ISSN is invalid
    end  
    end  
    Line 871: Line 909:
    else
    else
    err_cat = ' ' .. set_message ('err_bad_jfm' ); -- set an error message
    err_cat = ' ' .. set_message ('err_bad_jfm' ); -- set an error message
    options.coins_list_t['JFM'] = nil; -- when error, unset so not included in COinS
    end
    end
    Line 892: Line 931:
    if id:find ('[Jj][Ss][Tt][Oo][Rr]') or id:find ('^https?://') or id:find ('%s') then
    if id:find ('[Jj][Ss][Tt][Oo][Rr]') or id:find ('^https?://') or id:find ('%s') then
    err_msg = ' ' .. set_message ('err_bad_jstor'); -- set an error message
    err_msg = ' ' .. set_message ('err_bad_jstor'); -- set an error message
    options.coins_list_t['JSTOR'] = nil; -- when error, unset so not included in COinS
    end
    end
    Line 950: Line 990:
    if not is_set (err_cat) and nil ~= lccn:find ('%s') then
    if not is_set (err_cat) and nil ~= lccn:find ('%s') then
    err_cat = ' ' .. set_message ('err_bad_lccn'); -- lccn contains a space, set an error message
    err_cat = ' ' .. set_message ('err_bad_lccn'); -- lccn contains a space, set an error message
    end
    if is_set (err_cat) then
    options.coins_list_t['LCCN'] = nil; -- when error, unset so not included in COinS
    end
    end


    Line 983: Line 1,027:
    else
    else
    err_cat = ' ' .. set_message ('err_bad_mr'); -- set an error message
    err_cat = ' ' .. set_message ('err_bad_mr'); -- set an error message
    options.coins_list_t['MR'] = nil; -- when error, unset so not included in COinS
    end
    end
    Line 1,025: Line 1,070:
    else
    else
    err_msg = ' ' .. set_message ('err_bad_oclc') -- add an error message if the id is malformed
    err_msg = ' ' .. set_message ('err_bad_oclc') -- add an error message if the id is malformed
    options.coins_list_t['OCLC'] = nil; -- when error, unset so not included in COinS
    end
    end
    Line 1,057: Line 1,103:
    ident = id; -- copy id to ident so that we display the flawed identifier
    ident = id; -- copy id to ident so that we display the flawed identifier
    error_msg = ' ' .. set_message ('err_bad_ol');
    error_msg = ' ' .. set_message ('err_bad_ol');
    end
    if not is_set (error_msg) then
    options.coins_list_t['OL'] = handler.prefix .. prefix[code] .. ident; -- experiment for ol coins
    else
    options.coins_list_t['OL'] = nil; -- when error, unset so not included in COinS
    end
    end


    Line 1,084: Line 1,136:
    if id:match("[^%d]") then -- if OSTI has anything but digits
    if id:match("[^%d]") then -- if OSTI has anything but digits
    err_cat = ' ' .. set_message ('err_bad_osti'); -- set an error message
    err_cat = ' ' .. set_message ('err_bad_osti'); -- set an error message
    options.coins_list_t['OSTI'] = nil; -- when error, unset so not included in COinS
    else -- OSTI is only digits
    else -- OSTI is only digits
    local id_num = tonumber (id); -- convert id to a number for range testing
    local id_num = tonumber (id); -- convert id to a number for range testing
    if 1018 > id_num or handler.id_limit < id_num then -- if OSTI is outside test limit boundaries
    if 1018 > id_num or handler.id_limit < id_num then -- if OSTI is outside test limit boundaries
    err_cat = ' ' .. set_message ('err_bad_osti'); -- set an error message
    err_cat = ' ' .. set_message ('err_bad_osti'); -- set an error message
    options.coins_list_t['OSTI'] = nil; -- when error, unset so not included in COinS
    end
    end
    end
    end
    Line 1,151: Line 1,205:
    text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, -- no embargo date or embargo has expired, ok to link to article
    text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, -- no embargo date or embargo has expired, ok to link to article
    prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access,
    prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access,
    auto_link = not err_cat and 'pmc' or nil -- do not auto-link when PMC has error
    auto_link = not err_cat and 'pmc' or nil -- do not auto-link when PMC has error
    }) .. (err_cat and err_cat or ''); -- parentheses required
    }) .. (err_cat and err_cat or ''); -- parentheses required
    end
    end
    if err_cat then
    options.coins_list_t['PMC'] = nil; -- when error, unset so not included in COinS
    end
    return text;
    return text;
    end
    end
    Line 1,173: Line 1,232:
    if id:match("[^%d]") then -- if PMID has anything but digits
    if id:match("[^%d]") then -- if PMID has anything but digits
    err_cat = ' ' .. set_message ('err_bad_pmid'); -- set an error message
    err_cat = ' ' .. set_message ('err_bad_pmid'); -- set an error message
    options.coins_list_t['PMID'] = nil; -- when error, unset so not included in COinS
    else -- PMID is only digits
    else -- PMID is only digits
    local id_num = tonumber (id); -- convert id to a number for range testing
    local id_num = tonumber (id); -- convert id to a number for range testing
    if 1 > id_num or handler.id_limit < id_num then -- if PMID is outside test limit boundaries
    if 1 > id_num or handler.id_limit < id_num then -- if PMID is outside test limit boundaries
    err_cat = ' ' .. set_message ('err_bad_pmid'); -- set an error message
    err_cat = ' ' .. set_message ('err_bad_pmid'); -- set an error message
    options.coins_list_t['PMID'] = nil; -- when error, unset so not included in COinS
    end
    end
    end
    end
    Line 1,202: Line 1,263:
    if id:match("[^%d]") then -- if RFC has anything but digits
    if id:match("[^%d]") then -- if RFC has anything but digits
    err_cat = ' ' .. set_message ('err_bad_rfc'); -- set an error message
    err_cat = ' ' .. set_message ('err_bad_rfc'); -- set an error message
    options.coins_list_t['RFC'] = nil; -- when error, unset so not included in COinS
    else -- RFC is only digits
    else -- RFC is only digits
    local id_num = tonumber (id); -- convert id to a number for range testing
    local id_num = tonumber (id); -- convert id to a number for range testing
    if 1 > id_num or handler.id_limit < id_num then -- if RFC is outside test limit boundaries
    if 1 > id_num or handler.id_limit < id_num then -- if RFC is outside test limit boundaries
    err_cat = ' ' .. set_message ('err_bad_rfc'); -- set an error message
    err_cat = ' ' .. set_message ('err_bad_rfc'); -- set an error message
    options.coins_list_t['RFC'] = nil; -- when error, unset so not included in COinS
    end
    end
    end
    end
    Line 1,238: Line 1,301:
    if handler.id_limit < id_num then -- if S2CID is outside test limit boundaries
    if handler.id_limit < id_num then -- if S2CID is outside test limit boundaries
    err_cat = ' ' .. set_message ('err_bad_s2cid'); -- set an error message
    err_cat = ' ' .. set_message ('err_bad_s2cid'); -- set an error message
    options.coins_list_t['S2CID'] = nil; -- when error, unset so not included in COinS
    end
    end


    else -- when id format incorrect
    else -- when id format incorrect
    err_cat = ' ' .. set_message ('err_bad_s2cid'); -- set an error message
    err_cat = ' ' .. set_message ('err_bad_s2cid'); -- set an error message
    options.coins_list_t['S2CID'] = nil; -- when error, unset so not included in COinS
    end
    end


    Line 1,254: Line 1,319:


    9-digit form of ISBN-10; uses same check-digit validation when SBN is prefixed with an additional '0' to make 10 digits
    9-digit form of ISBN-10; uses same check-digit validation when SBN is prefixed with an additional '0' to make 10 digits
    sbn value not made part of COinS metadata because we don't have a url or isn't a COinS-defined identifier (rft.xxx)
    or an identifier registered at info-uri.info (info:)


    ]]
    ]]
    Line 1,266: Line 1,334:
    if not ignore_invalid then -- if not ignoring SBN errors
    if not ignore_invalid then -- if not ignoring SBN errors
    if not check then
    if not check then
    options.coins_list_t['SBN'] = nil; -- when error, unset so not included in COinS; not really necessary here because sbn not made part of COinS
    return SBN .. set_message ('err_bad_sbn', {err_type}, false, ' '); -- display an error message
    return SBN .. set_message ('err_bad_sbn', {err_type}, false, ' '); -- display an error message
    end
    end
    Line 1,278: Line 1,347:
    end
    end


    local ident = id:gsub ('[%s-]', ''); -- remove hyphens and whitespace; they interfere with the rest of the tests
    local ident = id:gsub ('[%s-]', ''); -- remove hyphens and whitespace; they interfere with the rest of the tests


    if  9 ~= ident:len() then
    if  9 ~= ident:len() then
    Line 1,315: Line 1,384:
    if 100 > id_num or handler.id_limit < id_num then -- if SSRN is outside test limit boundaries
    if 100 > id_num or handler.id_limit < id_num then -- if SSRN is outside test limit boundaries
    err_cat = ' ' .. set_message ('err_bad_ssrn'); -- set an error message
    err_cat = ' ' .. set_message ('err_bad_ssrn'); -- set an error message
    options.coins_list_t['SSRN'] = nil; -- when error, unset so not included in COinS
    end
    end
    else -- when id format incorrect
    else -- when id format incorrect
    err_cat = ' ' .. set_message ('err_bad_ssrn'); -- set an error message
    err_cat = ' ' .. set_message ('err_bad_ssrn'); -- set an error message
    options.coins_list_t['SSRN'] = nil; -- when error, unset so not included in COinS
    end
    end
    Line 1,343: Line 1,414:
    if not id:match('^.+@.+$') or not id:match('^[^<].*[^>]$') then -- doesn't have '@' or has one or first or last character is '< or '>'
    if not id:match('^.+@.+$') or not id:match('^[^<].*[^>]$') then -- doesn't have '@' or has one or first or last character is '< or '>'
    text = text .. ' ' .. set_message ('err_bad_usenet_id') -- add an error message if the message id is invalid
    text = text .. ' ' .. set_message ('err_bad_usenet_id') -- add an error message if the message id is invalid
    options.coins_list_t['USENETID'] = nil; -- when error, unset so not included in COinS
    end  
    end  
    Line 1,368: Line 1,440:
    elseif not id:match('^%d?%d?%d?%d%.%d%d%d%d%d$') then -- not temporary, is it normal format?
    elseif not id:match('^%d?%d?%d?%d%.%d%d%d%d%d$') then -- not temporary, is it normal format?
    err_cat = ' ' .. set_message ('err_bad_zbl'); -- no, set an error message
    err_cat = ' ' .. set_message ('err_bad_zbl'); -- no, set an error message
    options.coins_list_t['ZBL'] = nil; -- when error, unset so not included in COinS
    end
    end
    Line 1,414: Line 1,487:
    if is_set (access_level) then
    if is_set (access_level) then
    if not in_array (access_level, cfg.keywords_lists['id-access']) then -- exact match required
    if not in_array (access_level, cfg.keywords_lists['id-access']) then -- exact match required
    table.insert (z.message_tail, { set_message ('invalid_param_val', {access_param, access_level}, true) } );
    table.insert (z.message_tail, { set_message ('err_invalid_param_val', {access_param, access_level}, true) } );
    access_level = nil; -- invalid so unset
    access_level = nil; -- invalid so unset
    end
    end
    Line 1,432: Line 1,505:
    render the identifiers into a sorted sequence table
    render the identifiers into a sorted sequence table


    <id_list_t> is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value
    <ID_list_coins_t> is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value
    <options_t> is a table of is a table of various k/v option pairs provided in the call to new_build_id_list();
    <options_t> is a table of various k/v option pairs provided in the call to new_build_id_list();
    modified by this function and passed to all identifier rendering functions
    modified by this function and passed to all identifier rendering functions
    <access_levels_t> is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value (if valid)
    <access_levels_t> is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value (if valid)


    returns a sequence table of sorted (by hkey) rendered identifier strings
    returns a sequence table of sorted (by hkey - 'handler' key) rendered identifier strings


    ]]
    ]]


    local function build_id_list (id_list_t, options_t, access_levels_t)
    local function build_id_list (ID_list_coins_t, options_t, access_levels_t)
    local new_list_t = {};
    local ID_list_t = {};
    local accept;
    local accept;
    local func_map = { --function map points to functions associated with hkey identifier
    local func_map = { --function map points to functions associated with hkey identifier
    Line 1,473: Line 1,546:
    }
    }


    for hkey, v in pairs (id_list_t) do
    for hkey, v in pairs (ID_list_coins_t) do
    v, accept = has_accept_as_written (v); -- remove accept-as-written markup if present; accept is boolean true when markup removed; false else
    v, accept = has_accept_as_written (v); -- remove accept-as-written markup if present; accept is boolean true when markup removed; false else
    -- every function gets the options table with value v and accept boolean
    -- every function gets the options table with value v and accept boolean
    Line 1,481: Line 1,554:
    options_t.access = access_levels_t[hkey]; -- add the access level for those that have an |<identifier-access= parameter
    options_t.access = access_levels_t[hkey]; -- add the access level for those that have an |<identifier-access= parameter
    options_t.handler = cfg.id_handlers[hkey];
    options_t.handler = cfg.id_handlers[hkey];
    options_t.coins_list_t = ID_list_coins_t; -- pointer to ID_list_coins_t; for |asin= and |ol=; also to keep erroneous values out of the citation's metadata
    if func_map[hkey] then
    if func_map[hkey] then
    table.insert (new_list_t, {hkey, func_map[hkey] (options_t)}); -- call the function and add the results to the output sequence table
    table.insert (ID_list_t, {hkey, func_map[hkey] (options_t)}); -- call the function and add the results to the output sequence table
     
    -- TODO: also retrieve identifier validity status, OL A/M/W/X type and ASIN TLD info from the corresponding function call for improved metadata generation in COinS() in ~/COinS
     
    else
    else
    error (cfg.messages['unknown_ID_key'] .. ' ' .. hkey); -- here when func_map doesn't have a function for hkey
    error (cfg.messages['unknown_ID_key'] .. ' ' .. hkey); -- here when func_map doesn't have a function for hkey
    Line 1,495: Line 1,567:
    end
    end


    table.sort (new_list_t, comp); -- sequence table of tables sort
    table.sort (ID_list_t, comp); -- sequence table of tables sort
    for k, v in ipairs (new_list_t) do -- convert sequence table of tables to simple sequence table of strings
    for k, v in ipairs (ID_list_t) do -- convert sequence table of tables to simple sequence table of strings
    new_list_t[k] = v[2];
    ID_list_t[k] = v[2]; -- v[2] is the identifier rendering from the call to the various functions in func_map{}
    end
    end
    return new_list_t;
    return ID_list_t;
    end
     
     
    --[[--------------------------< O P T I O N S _ C H E C K >----------------------------------------------------
     
    check that certain option parameters have their associated identifier parameters with values
     
    <ID_list_coins_t> is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value
    <ID_support_t> is a sequence table of tables created in citation0() where each subtable has four elements:
    [1] is the support parameter's assigned value; empty string if not set
    [2] is a text string same as key in cfg.id_handlers
    [3] is cfg.error_conditions key used to create error message
    [4] is original ID support parameter name used to create error message
    returns nothing; on error emits an appropriate error message
     
    ]]
     
    local function options_check (ID_list_coins_t, ID_support_t)
    for _, v in ipairs (ID_support_t) do
    if is_set (v[1]) and not ID_list_coins_t[v[2]] then -- when support parameter has a value but matching identifier parameter is missing or empty
    table.insert (z.message_tail, {set_message (v[3], (v[4]))}); -- emit the appropriate error message
    end
    end
    end
    end


    Line 1,512: Line 1,608:
    ]]
    ]]


    local function identifier_lists_get (args, options_t)
    local function identifier_lists_get (args, options_t, ID_support_t)
    local ID_list_coins_t = extract_ids (args); -- get a table of identifiers and their values for use locally and for use in COinS
    local ID_list_coins_t = extract_ids (args); -- get a table of identifiers and their values for use locally and for use in COinS
    options_check (ID_list_coins_t, ID_support_t); -- ID support parameters must have matching identifier parameters
    local ID_access_levels_t = extract_id_access_levels (args, ID_list_coins_t); -- get a table of identifier access levels
    local ID_access_levels_t = extract_id_access_levels (args, ID_list_coins_t); -- get a table of identifier access levels
    local ID_list_t = build_id_list (ID_list_coins_t, options_t, ID_access_levels_t); -- get a sequence table of rendered identifier strings
    local ID_list_t = build_id_list (ID_list_coins_t, options_t, ID_access_levels_t); -- get a sequence table of rendered identifier strings
    -- TODO: add code to retrieve identifier validity status, OL A/M/W/X type and ASIN TLD info from ID_list_t and add this to ID_list_coins_t for improved metadata generation in COinS() in ~/COinS


    return ID_list_t, ID_list_coins_t; -- return the tables
    return ID_list_t, ID_list_coins_t; -- return the tables