From 09da414d5ff0230b79f8a5f7edcaa05f81e83adb Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Mon, 12 Mar 2012 23:40:01 +0000 Subject: new lua script checkcites (12mar12) git-svn-id: svn://tug.org/texlive/trunk@25623 c570f23f-e606-0410-a88d-b1316a301751 --- .../texmf-dist/scripts/checkcites/checkcites.lua | 673 +++++++++++++++++++++ 1 file changed, 673 insertions(+) create mode 100755 Master/texmf-dist/scripts/checkcites/checkcites.lua (limited to 'Master/texmf-dist/scripts/checkcites') diff --git a/Master/texmf-dist/scripts/checkcites/checkcites.lua b/Master/texmf-dist/scripts/checkcites/checkcites.lua new file mode 100755 index 00000000000..c2271f28cc4 --- /dev/null +++ b/Master/texmf-dist/scripts/checkcites/checkcites.lua @@ -0,0 +1,673 @@ +#!/usr/bin/env texlua +-- ****************************************************************** +-- checkcites.lua +-- Copyright 2012 Enrico Gregorio, Paulo Roberto Massa Cereda +-- +-- This work may be distributed and/or modified under the +-- conditions of the LaTeX Project Public License, either version 1.3 +-- of this license or (at your option) any later version. +-- +-- The latest version of this license is in +-- http://www.latex-project.org/lppl.txt +-- and version 1.3 or later is part of all distributions of LaTeX +-- version 2005/12/01 or later. +-- +-- This work has the LPPL maintenance status `maintained'. +-- +-- The Current Maintainers of this work are the original authors. +-- +-- This work consists of the file checkcites.lua. +-- +-- Project page: http://github.com/cereda/checkcites +-- ****************************************************************** + +-- version and date, to be updated on each release/commit +VERSION = "1.0g" +DATE = "March 7, 2012" + +-- The following code adds a 'split' function to the string type, +-- thanks to the codebase available here: +-- http://lua-users.org/wiki/SplitJoin +string.split = function(str, pattern) + + pattern = pattern or "[^%s]+" + + if pattern:len() == 0 then + + pattern = "[^%s]+" + + end + + local parts = {__index = table.insert} + + setmetatable(parts, parts) + + str:gsub(pattern, parts) + + setmetatable(parts, nil) + + parts.__index = nil + + return parts + +end + +-- In order to make our lives easier, we borrowed the following +-- codes for implementing common set operations, available here: +-- http://www.phailed.me/2011/02/common-set-operations-in-lua/ +function setFind(a, tbl) + + for _,a_ in ipairs(tbl) do + + if a_==a then + + return true + end + + end + +end + +-- This code returns a table containing the difference between +-- the two other tables, also available here: +-- http://www.phailed.me/2011/02/common-set-operations-in-lua/ +function setDifference(a, b) + + local ret = {} + + for _,a_ in ipairs(a) do + + if not setFind(a_,b) then + + table.insert(ret, a_) + + end + + end + + return ret + +end + +-- Code to remove duplicates from a table array. +function removeDuplicates(tb) + + -- new table + local ret = {} + + -- flag to spot new insertions + local flag + + -- set local variables + local i + local k + local j + local l + + -- iterate through the original table + for i, k in pairs(tb) do + + -- at first, insert element + flag = true + + -- iterate through the new table + for j, l in pairs(ret) do + + -- if the element already exists + if k == l then + + -- set flag to false, so the + -- new element won't be inserted + flag = false + + end + + end + + -- if it's ok to insert + if flag then + + -- insert new element + table.insert(ret, k) + + end + + end + + -- return new table + return ret +end + +-- This function opens and gets all data from the provided +-- aux file. +-- Return: +-- * list of citations +-- * list of bibfiles +function getDataFromAuxFile(theAuxFile) + + -- open a file handler + local fileHandler = io.open(theAuxFile,"r") + + -- check if the handler is valid + if fileHandler then + + -- create a local table for citations + local theCitations = {} + + -- and a local reference to the bib file + local theBibFiles = {} + + -- define local variables + local currentLine + local citation + local index + local theCitation + local theBibFile + local entry + + -- now let's iterate through the lines + for currentLine in fileHandler:lines() do + + -- if the citation matches, extract it + for citation in string.gmatch(currentLine, '\\citation{(.+)}') do + + -- sanity check, in case it's an '*' + if citation ~= "*" then + + -- break the citations list, in case of multiple + -- citations in the same \citation{} + local parts = string.split(citation, "[^,%s]+" ) + + -- for every entry in the citations list + for index, theCitation in pairs(parts) do + + -- insert the reference + table.insert(theCitations, theCitation) + + end + + -- found a '*' + else + + -- show message + print("I found a '*' citation, which means that all references will be used.\nI'm afraid I have nothing to do now.") + + -- and abort the script + os.exit() + + end + + -- end of citation in current line + end + + -- in the same current line, check if there's the + -- bibdata entry and extract it + for entry in string.gmatch(currentLine, '\\bibdata{(.+)}') do + + -- break the bib files list, in case of multiple + -- files in the same \bibdata{} + local parts = string.split(entry, "[^,%s]+" ) + + -- for every entry in the bib files list + for index, theBibFile in pairs(parts) do + + -- insert the file + table.insert(theBibFiles, theBibFile) + + end + + -- end of bib files in the current line + end + + -- end of current line + end + + -- close the file handler + fileHandler:close() + + -- remove duplicated citations + theCitations = removeDuplicates(theCitations) + + -- print a message about the citations + print("I found " .. #theCitations .. " citation(s).") + + -- remove possible duplicated files + theBibFiles = removeDuplicates(theBibFiles) + + -- if there are no bib files + if #theBibFiles == 0 then + + -- show message + print("I couldn't find any bibliography files.\nI'm afraid I have nothing to do now.") + + -- and abort the script + os.exit() + + -- if there is only one bib file + elseif #theBibFiles == 1 then + + -- show message + print("Great, there's only one 'bib' file. Let me check it.") + + -- there are more bib files + else + + -- show message + print("Oh no, I have to check more than one 'bib' file. Please wait.") + + end + + -- return both citations and bib files + return theCitations, theBibFiles + + -- the file handler is invalid + else + + -- print an error message + print("File '" .. theAuxFile .. "' does not exist or is unavailable. Aborting script.") + + -- and abort the script + os.exit() + + end + +-- end of function +end + +-- This function opens and gets all data from all the available +-- bib files. +function getDataFromBibFiles(theBibFiles) + + -- create a table to store the citations + local theReferences = {} + + -- set local variables + local index + local theBibFile + local currentLine + local reference + + -- iterate through all bib files + for index, theBibFile in pairs(theBibFiles) do + + -- open the bib file + local fileHandler = io.open(theBibFile .. ".bib","r") + + -- check if the handler is valid + if fileHandler then + + -- iterate through every line of the bib file + for currentLine in fileHandler:lines() do + + -- if a reference is found + for reference in string.gmatch(currentLine, '@%w+{(.+),') do + + -- insert the reference + table.insert(theReferences, reference) + + end + + -- end current line + end + + -- close the file handler + fileHandler:close() + + -- bib file does not exist + else + + -- error message + print("File '" .. theBibFile .. ".bib' does not exist. Aborting.") + + -- abort script + os.exit() + + end + + -- end iteration through the bib files + end + + -- remove duplicated references + theReferences = removeDuplicates(theReferences) + + -- print message + print("I found " .. #theReferences .. " reference(s).") + + -- return references + return theReferences + +-- end of function +end + +-- This function show all the undefined references. It's very +-- simple, it's a difference between two sets. +function showUndefinedReferences(citations, references) + + -- get all undefined references + local undefined = setDifference(citations, references) + + -- print message + print("\nUndefined reference(s) in your TeX file: " .. #undefined) + + -- if there are undefined references + if #undefined ~= 0 then + + -- local variables + local index + local reference + + -- iterate + for index, reference in pairs(undefined) do + + -- and print + print("- " .. reference) + + end + + end + +-- end of function +end + +-- This function show all the unused references. It's very +-- simple, it's a difference between two sets. +function showUnusedReferences(citations, references) + +-- get all undefined references + local unused = setDifference(references, citations) + + -- print message + print("\nUnused reference(s) in your bibliography file(s): " .. #unused) + + -- if there are unused references + if #unused ~= 0 then + + -- local variables + local index + local reference + + -- iterate + for index, reference in pairs(unused) do + + -- and print + print("- " .. reference) + + end + + end + +-- end of function +end + +-- This function parses the command line arguments and returns a +-- bunch of info for us to use. +-- Return: +-- * an argument code +-- * the filename +-- * an action code +function parseArguments(theArgs) + + -- check if there are no arguments + if #theArgs == 0 then + + -- return usage code + return 0, nil, nil + + -- there is one argument + elseif #theArgs == 1 then + + -- check if it's help + if theArgs[1] == "--help" then + + -- return help code + return 1, nil, nil + + -- check if it's version + elseif theArgs[1] == "--version" then + + -- return version code + return 2, nil, nil + + -- check if it's invalid + elseif string.sub(theArgs[1], 1, 1) == "-" then + + -- return invalid code + return 3, nil, nil + + -- it seems a clean argument + else + + -- return it as a valid argument + return 4, theArgs[1], nil + + -- end for one parameter + end + + -- there are two arguments + elseif #theArgs == 2 then + + -- check if both are valid + if ((theArgs[1] == "--all" or theArgs[1] == "--unused" or theArgs[1] == "--undefined") and string.sub(theArgs[2], 1, 1) ~= "-") or ((theArgs[2] == "--all" or theArgs[2] == "--unused" or theArgs[2] == "--undefined") and string.sub(theArgs[1], 1, 1) ~= "-") then + + -- create an action code + local actionCode + + -- check which one is the file name + if string.sub(theArgs[1], 1, 1) ~= "-" then + + -- check for --all + if theArgs[2] == "--all" then + + -- set the action code + actionCode = 0 + + -- check for --unused + elseif theArgs[2] == "--unused" then + + -- set the action code + actionCode = 1 + + -- it's --undefined + else + + -- set the action code + actionCode = 2 + + end + + -- it's the first + return 4, theArgs[1], actionCode + + else + + -- check for --all + if theArgs[1] == "--all" then + + -- set the action code + actionCode = 0 + + -- check for --unused + elseif theArgs[1] == "--unused" then + + -- set the action code + actionCode = 1 + + else + + -- it's --undefined + actionCode = 2 + + end + + -- it's the second + return 4, theArgs[2], actionCode + + end + + else + + -- return invalid code + return 3, nil, nil + + end + + else + + -- more than two arguments, return usage code + return 0, nil, nil + + end + +-- end of function +end + +-- This function prints the script header. +function printHeader() + + -- print message + print("checkcites.lua -- a reference checker script (v" .. VERSION .. ")") + print("Copyright (c) 2012 Enrico Gregorio, Paulo Roberto Massa Cereda\n") + +-- end of function +end + +-- This function prints the script usage +function printUsage() + + -- show message + print("Usage: " .. arg[0] .. " [--all | --unused | --undefined] file.aux\n") + print("--all Lists all unused and undefined references.") + print("--unused Lists only unused references in your 'bib' file.") + print("--undefined Lists only undefined references in your 'tex' file.\n") + print("If no flag is provided, '--all' is set by default.") + print("Be sure to have all your 'bib' files in the same directory.") + +-- end of function +end + +function printHelp() + + -- show message + print("checkcites.lua is a Lua script written for the sole purpose of") + print("detecting undefined/unused references from LaTeX auxiliary or") + print("bibliography files. It's very easy to use!\n") + + -- print usage + printUsage() + +-- end of function +end + +-- This function prints the script version. +function printVersion() + + -- print message + print("checkcites.lua version " .. VERSION .. " (dated " .. DATE .. ")") + print("You can find more information about this script in the official") + print("source code repository:\n") + print("http://github.com/cereda/checkcites\n") + print("checkcites.lua is licensed under the LaTeX Project Public License.") + +-- end of function +end + +-- This function prints a message for invalid parameters. +function printInvalid() + + -- print message + print("Oh no, it seems you used an invalid argument.\n") + + -- print usage + printUsage() + +-- end of function +end + +-- This is our main function. +function runMain(theArgs) + + -- print the script header + printHeader() + + -- set local variables + local argCode + local fileName + local actionCode + + -- parse arguments and get the result + argCode, fileName, actionCode = parseArguments(theArgs) + + -- check for usage + if argCode == 0 then + + -- print usage + printUsage() + + -- it's help + elseif argCode == 1 then + + -- print help + printHelp() + + -- it's version + elseif argCode == 2 then + + -- print version + printVersion() + + -- it's an invalid parameter + elseif argCode == 3 then + + -- print invalid + printInvalid() + + -- it's a valid operation + else + + -- get data from aux + a, b = getDataFromAuxFile(fileName) + + -- get data from bib + c = getDataFromBibFiles(b) + + -- if there is an action code + if actionCode ~= nil then + + -- it's --all + if actionCode == 0 then + + -- do everything + showUnusedReferences(a,c) + showUndefinedReferences(a,c) + + -- it's --unused + elseif actionCode == 1 then + + -- only show unused + showUnusedReferences(a,c) + + -- it's --undefined + else + + -- only show undefined + showUndefinedReferences(a,c) + + end + + -- there's only one parameter, the file name, + -- so we set --all + else + + -- show everything + showUnusedReferences(a,c) + showUndefinedReferences(a,c) + + end + + end + +-- end of function +end + +-- run the main function +runMain(arg) -- cgit v1.2.3