texlive[70398] branches/branch2023.final: checkcites (branch)

commits+karl at tug.org commits+karl at tug.org
Mon Mar 4 22:13:03 CET 2024


Revision: 70398
          https://tug.org/svn/texlive?view=revision&revision=70398
Author:   karl
Date:     2024-03-04 22:13:03 +0100 (Mon, 04 Mar 2024)
Log Message:
-----------
checkcites (branch) (4mar24)

Modified Paths:
--------------
    branches/branch2023.final/Build/source/texk/texlive/linked_scripts/checkcites/checkcites.lua
    branches/branch2023.final/Master/texmf-dist/doc/support/checkcites/README
    branches/branch2023.final/Master/texmf-dist/doc/support/checkcites/checkcites-doc.pdf
    branches/branch2023.final/Master/texmf-dist/doc/support/checkcites/checkcites-doc.tex
    branches/branch2023.final/Master/texmf-dist/scripts/checkcites/checkcites.lua

Modified: branches/branch2023.final/Build/source/texk/texlive/linked_scripts/checkcites/checkcites.lua
===================================================================
--- branches/branch2023.final/Build/source/texk/texlive/linked_scripts/checkcites/checkcites.lua	2024-03-04 21:11:36 UTC (rev 70397)
+++ branches/branch2023.final/Build/source/texk/texlive/linked_scripts/checkcites/checkcites.lua	2024-03-04 21:13:03 UTC (rev 70398)
@@ -2,7 +2,7 @@
 -- -----------------------------------------------------------------
 -- checkcites.lua
 -- Copyright 2012, 2019, Enrico Gregorio, Paulo Cereda
--- Copyright 2022, Enrico Gregorio, Island of TeX
+-- Copyright 2024, Enrico Gregorio, Island of TeX
 --
 -- This work may be distributed and/or modified under the conditions
 -- of the LaTeX  Project Public License, either version  1.3 of this
@@ -486,9 +486,9 @@
 print("|___|_|_|___|___|_,_|___|_|_| |___|___|")
 print()
   print(wrap('checkcites.lua -- a reference ' ..
-             'checker script (v2.6)', 74))
+             'checker script (v2.7)', 74))
   print(wrap('Copyright (c) 2012, 2019, Enrico Gregorio, Paulo Cereda', 74))
-  print(wrap('Copyright (c) 2022, Enrico Gregorio, Island of TeX', 74))
+  print(wrap('Copyright (c) 2024, Enrico Gregorio, Island of TeX', 74))
 end
 
 -- Operation namespace
@@ -498,6 +498,7 @@
 -- @param citations Citations.
 -- @param references References.
 -- @return Integer representing the status.
+-- @return Table of unused references.
 operations.unused = function(citations, references, crossrefs)
   print()
   print(pad('-', 74))
@@ -521,16 +522,18 @@
   end
 
   local r = difference(references, citations)
+  local forJson = {}
   print()
   print(wrap('Unused references in your TeX document: ' ..
              tostring(#r), 74))
   if #r == 0 then
-    return 0
+    return 0, forJson
   else
     for _, v in ipairs(r) do
       print('=> ' .. v)
+      table.insert(forJson, v)
     end
-    return 1
+    return 1, forJson
   end
 end
 
@@ -538,6 +541,7 @@
 -- @param citations Citations.
 -- @param references References.
 -- @return Integer value indicating the status.
+-- @return Table of undefined references.
 operations.undefined = function(citations, references, crossrefs)
   print()
   print(pad('-', 74))
@@ -561,16 +565,18 @@
   end
 
   local r = difference(citations, references)
+  local forJson = {}
   print()
   print(wrap('Undefined references in your TeX document: ' ..
         tostring(#r), 74))
   if #r == 0 then
-    return 0
+    return 0, forJson
   else
     for _, v in ipairs(r) do
       print('=> ' .. v)
+      table.insert(forJson, v)
     end
-    return 1
+    return 1, forJson
   end
 end
 
@@ -578,14 +584,16 @@
 -- @param citations Citations.
 -- @param references References.
 -- @return Integer value indicating the status.
+-- @return Table containing both unused and undefined references.
 operations.all = function(citations, references, crossrefs)
   local x, y
-  x = operations.unused(citations, references, crossrefs)
-  y = operations.undefined(citations, references, crossrefs)
+  local forJson = {}
+  x, forJson['unused'] = operations.unused(citations, references, crossrefs)
+  y, forJson['undefined'] = operations.undefined(citations, references, crossrefs)
   if x + y > 0 then
-    return 1
+    return 1, forJson
   else
-    return 0
+    return 0, forJson
   end
 end
 
@@ -612,6 +620,74 @@
   return bad, good
 end
 
+-- Converts a table of elements into a valid JSON array in
+-- a string format, where each item is enclosed by quotes.
+-- @param elements Table to be converted.
+-- @return A JSON array in a string format.
+local function toArray(elements)
+  if #elements ~=0 then
+    return '[ "' .. table.concat(elements, '", "') .. '" ]'
+  else
+    return '[]'
+  end
+end
+
+-- Gets a description of the operation being performed.
+-- @param check The operation name.
+-- @return The corresponding description.
+local function getOperation(check)
+  if check == 'unused' then
+    return 'list only unused references'
+  elseif check == 'undefined' then
+    return 'list only undefined references'
+  else
+    return 'list all unused and undefined references'
+  end
+end
+
+-- Writes the text to the file.
+-- @param file The file to be written into.
+-- @param text The text to be written.
+local function write(file, text)
+  local handler = io.open(file, 'w')
+  if handler then
+    handler:write(text)
+    handler:close()
+  end
+end
+
+-- Exports the report to a JSON file.
+-- @param file JSON file to be written.
+-- @param schema Table containing the report.
+local function toJson(file, schema)
+  local string = '{\n'
+  string = string .. '  "settings" : {\n'
+  string = string .. '    "backend" : "' .. schema['backend'] .. '",\n'
+  string = string .. '    "operation" : "' .. getOperation(schema['check']) .. '",\n'
+  string = string .. '    "crossrefs" : ' .. ((schema['crossrefs'] and 'true') or 'false') .. '\n'
+  string = string .. '  },\n'
+  string = string .. '  "project" : {\n'
+  string = string .. '    "forcibly_cite_all" : ' .. schema['asterisk'] .. ',\n'
+  string = string .. '    "bibliographies" : ' .. toArray(schema['bibliographies']) .. ',\n'
+  string = string .. '    "citations" : ' .. toArray(schema['citations']) .. ',\n'
+  string = string .. '    "crossrefs" : ' .. toArray(schema['crossrefs'] or {}) .. '\n'
+  string = string .. '  },\n'
+  string = string .. '  "results" : {\n'
+  string = string .. '    "unused" : {\n'
+  string = string .. '      "active" : ' .. (exists({'unused', 'all'}, schema['check'])
+                                             and 'true' or 'false') .. ',\n'
+  string = string .. '      "occurrences" : ' .. toArray(schema['unused']) .. '\n'
+  string = string .. '    },\n'
+  string = string .. '    "undefined" : {\n'
+  string = string .. '      "active" : ' .. (exists({'undefined', 'all'}, schema['check'])
+                                             and 'true' or 'false') .. ',\n'
+  string = string .. '      "occurrences" : ' .. toArray(schema['undefined']) .. '\n'
+  string = string .. '    }\n'
+  string = string .. '  }\n'
+  string = string .. '}'
+  write(file, string)
+end
+
 -- Main function.
 -- @param args Command line arguments.
 -- @return Integer value indicating the status
@@ -629,12 +705,15 @@
     { short = 'v', long = 'version', argument = false },
     { short = 'h', long = 'help', argument = false },
     { short = 'c', long = 'crossrefs', argument = false },
-    { short = 'b', long = 'backend', argument = true }
+    { short = 'b', long = 'backend', argument = true },
+    { short = 'j', long = 'json', argument = true }
   }
 
   local keys, err = parse(parameters, args)
   local check, backend = 'all', 'bibtex'
 
+  local json = {}
+
   if #err ~= 0 then
     print()
     print(pad('-', 74))
@@ -670,8 +749,8 @@
   if keys['version'] or keys['help'] then
     if keys['version'] then
       print()
-      print(wrap('checkcites.lua, version 2.6 (dated August ' ..
-                 '20, 2022)', 74))
+      print(wrap('checkcites.lua, version 2.7 (dated March ' ..
+                 '3, 2024)', 74))
 
       print(pad('-', 74))
       print(wrap('You can find more details about this ' ..
@@ -689,8 +768,8 @@
       print()
       print(wrap('Usage: ' .. args[0] .. ' [ [ --all | --unused | ' ..
                  '--undefined ] [ --backend <arg> ] <file> [ ' ..
-                 '<file 2> ... <file n> ] | --help | --version ' ..
-                 ']', 74))
+                 '<file 2> ... <file n> ] | --json <file> | ' ..
+                 '--help | --version ]', 74))
 
       print()
       print('-a,--all           list all unused and undefined references')
@@ -698,6 +777,7 @@
       print('-U,--undefined     list only undefined references in your TeX source file')
       print('-c,--crossrefs     enable cross-reference checks (disabled by default)')
       print('-b,--backend <arg> set the backend-based file lookup policy')
+      print('-j,--json <file>   export the generated report as a JSON file')
       print('-h,--help          print the help message')
       print('-v,--version       print the script version')
 
@@ -752,6 +832,9 @@
     end
   end
 
+  json['backend'] = backend
+  json['check'] = check
+
   local auxiliary = apply(keys['unpaired'], function(a)
                     return sanitize(a, (backend == 'bibtex'
                     and 'aux') or 'bcf') end)
@@ -794,6 +877,10 @@
   local lines = flatten(apply(auxiliary, read))
   local asterisk, citations, bibliography = backends[backend](lines, true)
 
+  json['citations'] = citations
+  json['bibliographies'] = bibliography
+  json['asterisk'] = (asterisk and 'true') or 'false'
+
   print()
   print(wrap('Great, I found ' .. tostring(#citations) .. ' ' ..
              plural(#citations, 'citation', 'citations') .. ' in ' ..
@@ -850,6 +937,12 @@
   local crossrefs = (keys['crossrefs'] and organize(apply(bibliography,
                     function(a) return crossref(read(a)) end))) or {}
 
+  json['references'] = references
+
+  if (keys['crossrefs']) then
+    json['crossrefs'] = crossrefs
+  end
+
   print()
   print(wrap('Fantastic, I found ' .. tostring(#references) ..
              ' ' .. plural(#references, 'reference',
@@ -859,7 +952,16 @@
              plural(((check == 'all' and 2) or 1), 'report is',
              'reports are') .. ' generated.', 74))
 
-  return operations[check](citations, references, crossrefs)
+  local status, result = operations[check](citations, references, crossrefs)
+
+  json['unused'] = result['unused'] or (check == 'unused') and result or {}
+  json['undefined'] = result['undefined'] or (check == 'undefined') and result or {}
+
+  if keys['json'] then
+    toJson(keys['json'][1], json)
+  end
+
+  return status
 end
 
 -- Call and exit

Modified: branches/branch2023.final/Master/texmf-dist/doc/support/checkcites/README
===================================================================
--- branches/branch2023.final/Master/texmf-dist/doc/support/checkcites/README	2024-03-04 21:11:36 UTC (rev 70397)
+++ branches/branch2023.final/Master/texmf-dist/doc/support/checkcites/README	2024-03-04 21:13:03 UTC (rev 70398)
@@ -1,11 +1,11 @@
-checkcites.lua -- Version 2.6 from August 20, 2022
-==================================================
+checkcites.lua -- Version 2.7 from March 3, 2024
+================================================
 
 License
 -------
 
 Copyright (c) 2012, 2019, Enrico Gregorio, Paulo Cereda
-Copyright (c) 2022, Enrico Gregorio, Island of TeX
+Copyright (c) 2024, Enrico Gregorio, Island of TeX
 
 - Enrico dot Gregorio at univr dot it
 - https://gitlab.com/islandoftex

Modified: branches/branch2023.final/Master/texmf-dist/doc/support/checkcites/checkcites-doc.pdf
===================================================================
(Binary files differ)

Modified: branches/branch2023.final/Master/texmf-dist/doc/support/checkcites/checkcites-doc.tex
===================================================================
--- branches/branch2023.final/Master/texmf-dist/doc/support/checkcites/checkcites-doc.tex	2024-03-04 21:11:36 UTC (rev 70397)
+++ branches/branch2023.final/Master/texmf-dist/doc/support/checkcites/checkcites-doc.tex	2024-03-04 21:13:03 UTC (rev 70398)
@@ -20,7 +20,7 @@
 
 \newcommand{\checkcites}{\texttt{checkcites}}
 \newcommand{\email}[1]{\small\texttt{#1}}
-\newcommand{\version}{Version 2.6 from August 20, 2022.}
+\newcommand{\version}{Version 2.7 from March 3, 2024.}
 
 \newenvironment{infoblock}[1]
   {\par\addvspace{\medskipamount}
@@ -66,10 +66,11 @@
 modern \TeX\ distribution.
 
 \begin{infoblock}{Attention!}
-From version 2.1 on, \checkcites\ relies on specific libraries available
-in the \verb|texlua| ecosystem and thus is not be supported in vanilla
-\verb|lua| interpreters. Please make sure to use this script with an
-updated \verb|texlua| interpreter in order to ensure the correct behaviour.
+From  version  2.1  on,  \checkcites\  relies  on  specific  libraries
+available in the \verb|texlua| ecosystem  and thus is not be supported
+in  vanilla \verb|lua|  interpreters.  Please make  sure  to use  this
+script with  an updated \verb|texlua|  interpreter in order  to ensure
+the correct behaviour.
 \end{infoblock}
 
 \section{How the script works}
@@ -228,9 +229,9 @@
 |  _|   | -_|  _| '_|  _| |  _| -_|_ -|
 |___|_|_|___|___|_,_|___|_|_| |___|___|
 
-checkcites.lua -- a reference checker script (v2.6)
+checkcites.lua -- a reference checker script (v2.7)
 Copyright (c) 2012, 2019, Enrico Gregorio, Paulo Cereda
-Copyright (c) 2022, Enrico Gregorio, Island of TeX
+Copyright (c) 2024, Enrico Gregorio, Island of TeX
 
 --------------------------------------------------------------------------
 I am sorry, but you have not provided any command line argument, including
@@ -265,9 +266,9 @@
 |  _|   | -_|  _| '_|  _| |  _| -_|_ -|
 |___|_|_|___|___|_,_|___|_|_| |___|___|
 
-checkcites.lua -- a reference checker script (v2.6)
+checkcites.lua -- a reference checker script (v2.7)
 Copyright (c) 2012, 2019, Enrico Gregorio, Paulo Cereda
-Copyright (c) 2022, Enrico Gregorio, Island of TeX
+Copyright (c) 2024, Enrico Gregorio, Island of TeX
 
 Usage: checkcites.lua [ [ --all | --unused | --undefined ] [ --backend
 <arg> ] <file> [ <file 2> ... <file n> ] | --help | --version ]
@@ -277,6 +278,7 @@
 -U,--undefined     list only undefined references in your TeX source file
 -c,--crossrefs     enable cross-reference checks (disabled by default)
 -b,--backend <arg> set the backend-based file lookup policy
+-j,--json <file>   export the generated report as a JSON file
 -h,--help          print the help message
 -v,--version       print the script version
 
@@ -327,9 +329,9 @@
 |  _|   | -_|  _| '_|  _| |  _| -_|_ -|
 |___|_|_|___|___|_,_|___|_|_| |___|___|
 
-checkcites.lua -- a reference checker script (v2.6)
+checkcites.lua -- a reference checker script (v2.7)
 Copyright (c) 2012, 2019, Enrico Gregorio, Paulo Cereda
-Copyright (c) 2022, Enrico Gregorio, Island of TeX
+Copyright (c) 2024, Enrico Gregorio, Island of TeX
 
 Great, I found 4 citations in 1 file. I also found 1 bibliography file. Let
 me check this file and extract the references. Please wait a moment.
@@ -469,9 +471,9 @@
 |  _|   | -_|  _| '_|  _| |  _| -_|_ -|
 |___|_|_|___|___|_,_|___|_|_| |___|___|
 
-checkcites.lua -- a reference checker script (v2.6)
+checkcites.lua -- a reference checker script (v2.7)
 Copyright (c) 2012, 2019, Enrico Gregorio, Paulo Cereda
-Copyright (c) 2022, Enrico Gregorio, Island of TeX
+Copyright (c) 2024, Enrico Gregorio, Island of TeX
 
 Great, I found 4 citations in 1 file. I also found 1 bibliography file. Let
 me check this file and extract the references. Please wait a moment.
@@ -498,9 +500,11 @@
 \end{verbatim}
 \end{terminal}
 
-If you rely on cross-references in your bibliography file, \checkcites\ might complain about
-unused entries. We can try the experimental feature available from version 2.3 on that attempts
-to check cross-references through the \verb|--crossrefs| command line flag:
+If   you  rely   on  cross-references   in  your   bibliography  file,
+\checkcites\  might complain  about  unused entries.  We  can try  the
+experimental feature  available from version  2.3 on that  attempts to
+check  cross-references through  the  \verb|--crossrefs| command  line
+flag:
 
 \begin{terminal}
 \begin{verbatim}
@@ -508,9 +512,62 @@
 \end{verbatim}
 \end{terminal}
 
-This feature is disabled by default and it is known to work with both \verb|bibtex| and \verb|biber|
-backends. Please report if you find an issue. That is all, folks!
+This feature is disabled by default and  it is known to work with both
+\verb|bibtex| and \verb|biber| backends. Please  report if you find an
+issue.
 
+From version 2.7 on, \checkcites\ can export the reference report to a
+JSON file through the \verb|--json| command line flag:
+
+\begin{terminal}
+\begin{verbatim}
+$ checkcites document.aux --json report.json
+\end{verbatim}
+\end{terminal}
+
+The  script will  generate a  file named  \verb|report.json| with  the
+following structure and content:
+
+\begin{tcblisting}{colframe=DarkTurquoise,coltitle=black,listing only,
+  title=JSON file, fonttitle=\bfseries, breakable,
+  listing options={columns=fullflexible,basicstyle=\ttfamily}}
+{
+  "settings" : {
+    "backend" : "bibtex",
+    "operation" : "list all unused and undefined references",
+    "crossrefs" : false
+  },
+  "project" : {
+    "forcibly_cite_all" : false,
+    "bibliographies" : [ "example" ],
+    "citations" : [ "foo:2012a", "foo:2012c",
+                    "foo:2012f", "foo:2012d" ],
+    "crossrefs" : []
+  },
+  "results" : {
+    "unused" : {
+      "active" : true,
+      "occurrences" : [ "foo:2012b", "foo:2012e" ]
+    },
+    "undefined" : {
+      "active" : true,
+      "occurrences" : [ "foo:2012f" ]
+    }
+  }
+}  
+\end{tcblisting}
+
+Note  that   the  JSON   file  has  three   main  groups.   The  first
+group  contains  the execution  settings  and  has the  backend  used,
+a  description   of  the   operation  being  performed,   and  whether
+cross-references  checks  were  enabled.  The  second  group  contains
+relevant information  about the  project itself,  such as  whether all
+references will  be cited (when  \verb|\nocite{*}| is found),  and the
+list of bibliographies, citations and cross-references found. Finally,
+the  third  group  contains  the  analysis  results,  with  a  special
+\verb|active|  key that  indicates whether  that particular  check has
+been performed, and a list of occurrences. That is all, folks!
+
 \section{License}
 \label{sec:license}
 

Modified: branches/branch2023.final/Master/texmf-dist/scripts/checkcites/checkcites.lua
===================================================================
--- branches/branch2023.final/Master/texmf-dist/scripts/checkcites/checkcites.lua	2024-03-04 21:11:36 UTC (rev 70397)
+++ branches/branch2023.final/Master/texmf-dist/scripts/checkcites/checkcites.lua	2024-03-04 21:13:03 UTC (rev 70398)
@@ -2,7 +2,7 @@
 -- -----------------------------------------------------------------
 -- checkcites.lua
 -- Copyright 2012, 2019, Enrico Gregorio, Paulo Cereda
--- Copyright 2022, Enrico Gregorio, Island of TeX
+-- Copyright 2024, Enrico Gregorio, Island of TeX
 --
 -- This work may be distributed and/or modified under the conditions
 -- of the LaTeX  Project Public License, either version  1.3 of this
@@ -486,9 +486,9 @@
 print("|___|_|_|___|___|_,_|___|_|_| |___|___|")
 print()
   print(wrap('checkcites.lua -- a reference ' ..
-             'checker script (v2.6)', 74))
+             'checker script (v2.7)', 74))
   print(wrap('Copyright (c) 2012, 2019, Enrico Gregorio, Paulo Cereda', 74))
-  print(wrap('Copyright (c) 2022, Enrico Gregorio, Island of TeX', 74))
+  print(wrap('Copyright (c) 2024, Enrico Gregorio, Island of TeX', 74))
 end
 
 -- Operation namespace
@@ -498,6 +498,7 @@
 -- @param citations Citations.
 -- @param references References.
 -- @return Integer representing the status.
+-- @return Table of unused references.
 operations.unused = function(citations, references, crossrefs)
   print()
   print(pad('-', 74))
@@ -521,16 +522,18 @@
   end
 
   local r = difference(references, citations)
+  local forJson = {}
   print()
   print(wrap('Unused references in your TeX document: ' ..
              tostring(#r), 74))
   if #r == 0 then
-    return 0
+    return 0, forJson
   else
     for _, v in ipairs(r) do
       print('=> ' .. v)
+      table.insert(forJson, v)
     end
-    return 1
+    return 1, forJson
   end
 end
 
@@ -538,6 +541,7 @@
 -- @param citations Citations.
 -- @param references References.
 -- @return Integer value indicating the status.
+-- @return Table of undefined references.
 operations.undefined = function(citations, references, crossrefs)
   print()
   print(pad('-', 74))
@@ -561,16 +565,18 @@
   end
 
   local r = difference(citations, references)
+  local forJson = {}
   print()
   print(wrap('Undefined references in your TeX document: ' ..
         tostring(#r), 74))
   if #r == 0 then
-    return 0
+    return 0, forJson
   else
     for _, v in ipairs(r) do
       print('=> ' .. v)
+      table.insert(forJson, v)
     end
-    return 1
+    return 1, forJson
   end
 end
 
@@ -578,14 +584,16 @@
 -- @param citations Citations.
 -- @param references References.
 -- @return Integer value indicating the status.
+-- @return Table containing both unused and undefined references.
 operations.all = function(citations, references, crossrefs)
   local x, y
-  x = operations.unused(citations, references, crossrefs)
-  y = operations.undefined(citations, references, crossrefs)
+  local forJson = {}
+  x, forJson['unused'] = operations.unused(citations, references, crossrefs)
+  y, forJson['undefined'] = operations.undefined(citations, references, crossrefs)
   if x + y > 0 then
-    return 1
+    return 1, forJson
   else
-    return 0
+    return 0, forJson
   end
 end
 
@@ -612,6 +620,74 @@
   return bad, good
 end
 
+-- Converts a table of elements into a valid JSON array in
+-- a string format, where each item is enclosed by quotes.
+-- @param elements Table to be converted.
+-- @return A JSON array in a string format.
+local function toArray(elements)
+  if #elements ~=0 then
+    return '[ "' .. table.concat(elements, '", "') .. '" ]'
+  else
+    return '[]'
+  end
+end
+
+-- Gets a description of the operation being performed.
+-- @param check The operation name.
+-- @return The corresponding description.
+local function getOperation(check)
+  if check == 'unused' then
+    return 'list only unused references'
+  elseif check == 'undefined' then
+    return 'list only undefined references'
+  else
+    return 'list all unused and undefined references'
+  end
+end
+
+-- Writes the text to the file.
+-- @param file The file to be written into.
+-- @param text The text to be written.
+local function write(file, text)
+  local handler = io.open(file, 'w')
+  if handler then
+    handler:write(text)
+    handler:close()
+  end
+end
+
+-- Exports the report to a JSON file.
+-- @param file JSON file to be written.
+-- @param schema Table containing the report.
+local function toJson(file, schema)
+  local string = '{\n'
+  string = string .. '  "settings" : {\n'
+  string = string .. '    "backend" : "' .. schema['backend'] .. '",\n'
+  string = string .. '    "operation" : "' .. getOperation(schema['check']) .. '",\n'
+  string = string .. '    "crossrefs" : ' .. ((schema['crossrefs'] and 'true') or 'false') .. '\n'
+  string = string .. '  },\n'
+  string = string .. '  "project" : {\n'
+  string = string .. '    "forcibly_cite_all" : ' .. schema['asterisk'] .. ',\n'
+  string = string .. '    "bibliographies" : ' .. toArray(schema['bibliographies']) .. ',\n'
+  string = string .. '    "citations" : ' .. toArray(schema['citations']) .. ',\n'
+  string = string .. '    "crossrefs" : ' .. toArray(schema['crossrefs'] or {}) .. '\n'
+  string = string .. '  },\n'
+  string = string .. '  "results" : {\n'
+  string = string .. '    "unused" : {\n'
+  string = string .. '      "active" : ' .. (exists({'unused', 'all'}, schema['check'])
+                                             and 'true' or 'false') .. ',\n'
+  string = string .. '      "occurrences" : ' .. toArray(schema['unused']) .. '\n'
+  string = string .. '    },\n'
+  string = string .. '    "undefined" : {\n'
+  string = string .. '      "active" : ' .. (exists({'undefined', 'all'}, schema['check'])
+                                             and 'true' or 'false') .. ',\n'
+  string = string .. '      "occurrences" : ' .. toArray(schema['undefined']) .. '\n'
+  string = string .. '    }\n'
+  string = string .. '  }\n'
+  string = string .. '}'
+  write(file, string)
+end
+
 -- Main function.
 -- @param args Command line arguments.
 -- @return Integer value indicating the status
@@ -629,12 +705,15 @@
     { short = 'v', long = 'version', argument = false },
     { short = 'h', long = 'help', argument = false },
     { short = 'c', long = 'crossrefs', argument = false },
-    { short = 'b', long = 'backend', argument = true }
+    { short = 'b', long = 'backend', argument = true },
+    { short = 'j', long = 'json', argument = true }
   }
 
   local keys, err = parse(parameters, args)
   local check, backend = 'all', 'bibtex'
 
+  local json = {}
+
   if #err ~= 0 then
     print()
     print(pad('-', 74))
@@ -670,8 +749,8 @@
   if keys['version'] or keys['help'] then
     if keys['version'] then
       print()
-      print(wrap('checkcites.lua, version 2.6 (dated August ' ..
-                 '20, 2022)', 74))
+      print(wrap('checkcites.lua, version 2.7 (dated March ' ..
+                 '3, 2024)', 74))
 
       print(pad('-', 74))
       print(wrap('You can find more details about this ' ..
@@ -689,8 +768,8 @@
       print()
       print(wrap('Usage: ' .. args[0] .. ' [ [ --all | --unused | ' ..
                  '--undefined ] [ --backend <arg> ] <file> [ ' ..
-                 '<file 2> ... <file n> ] | --help | --version ' ..
-                 ']', 74))
+                 '<file 2> ... <file n> ] | --json <file> | ' ..
+                 '--help | --version ]', 74))
 
       print()
       print('-a,--all           list all unused and undefined references')
@@ -698,6 +777,7 @@
       print('-U,--undefined     list only undefined references in your TeX source file')
       print('-c,--crossrefs     enable cross-reference checks (disabled by default)')
       print('-b,--backend <arg> set the backend-based file lookup policy')
+      print('-j,--json <file>   export the generated report as a JSON file')
       print('-h,--help          print the help message')
       print('-v,--version       print the script version')
 
@@ -752,6 +832,9 @@
     end
   end
 
+  json['backend'] = backend
+  json['check'] = check
+
   local auxiliary = apply(keys['unpaired'], function(a)
                     return sanitize(a, (backend == 'bibtex'
                     and 'aux') or 'bcf') end)
@@ -794,6 +877,10 @@
   local lines = flatten(apply(auxiliary, read))
   local asterisk, citations, bibliography = backends[backend](lines, true)
 
+  json['citations'] = citations
+  json['bibliographies'] = bibliography
+  json['asterisk'] = (asterisk and 'true') or 'false'
+
   print()
   print(wrap('Great, I found ' .. tostring(#citations) .. ' ' ..
              plural(#citations, 'citation', 'citations') .. ' in ' ..
@@ -850,6 +937,12 @@
   local crossrefs = (keys['crossrefs'] and organize(apply(bibliography,
                     function(a) return crossref(read(a)) end))) or {}
 
+  json['references'] = references
+
+  if (keys['crossrefs']) then
+    json['crossrefs'] = crossrefs
+  end
+
   print()
   print(wrap('Fantastic, I found ' .. tostring(#references) ..
              ' ' .. plural(#references, 'reference',
@@ -859,7 +952,16 @@
              plural(((check == 'all' and 2) or 1), 'report is',
              'reports are') .. ' generated.', 74))
 
-  return operations[check](citations, references, crossrefs)
+  local status, result = operations[check](citations, references, crossrefs)
+
+  json['unused'] = result['unused'] or (check == 'unused') and result or {}
+  json['undefined'] = result['undefined'] or (check == 'undefined') and result or {}
+
+  if keys['json'] then
+    toJson(keys['json'][1], json)
+  end
+
+  return status
 end
 
 -- Call and exit



More information about the tex-live-commits mailing list.