[OS X TeX] Bibliography Citations (was: New TeXShop macro: "Insert reference")

Matthew Hills hills_tex at tina.stanford.edu
Tue Aug 24 00:03:03 EDT 2004


Interesting to see all of these approaches to citations.
Here's my rundown on the recent contributions:

Thomas' applescript:
     - clean parsing of BibTeX files (leverages David Kotz's scripts, 
which runs BibTeX with custom style files)
     - grabs all *.bib files in project directory
     - puts a few extra files into /tmp
     - no filtering of output

Joachim's TCLSH script:
     - REGEX search for key definitions in *.bib files

Claus recommended BibDesk's "Complete Citation" service:
     - this seems promising if you are already using BibDesk
     - very fast to lookup entries
     - slow to cut out entries that you don't want
     - lookup is performed by title (and not author,
        although there is another service to lookup by cite key)
     - would also be nice to have some sort of browser pop up instead...

In the recent spirit of the group, I'm appending an applescript that I 
wrote this
evening while thinking about some of these things:
     - searches the project files to find the included bibliography files
     - the current text selection is used to filter the suggested BibTeX 
entries
        (this filtering is case sensitive, but compares against all 
fields)

Any suggestions on the proper search path for the bibtex files?  (where 
does
BibTeX look for files other than the current directory?)

Thanks,
Matt

PS--anyone following the "software for writing manuals" over on 
scitech at lists.apple.com?



-- Applescript
-- ---------------------------------------------------------------
--     Cite Bibliography
-- ---------------------------------------------------------------
--  This script is designed to run from TeXShop
--  and will try to open the bibliography file and
--  show the user a list of possible items to cite.

-- ----------------------------------------
--     Properties
-- ----------------------------------------
--   Search Options:   (list of strings)
--         a list of strings, with possible entries:
--        "sort" -- will sort the output 
--        "title" -- will show both the reference and the title
property search_options : {"sort", "title"}

--   cite_cmd:   (string)
--        the command that is used when placing text back into TeXShop
--        using "\\cite" will produce "\cite{tag}" in the editor
property cite_cmd : "\\cite"

--   limit_by_selection:   (true/false)
--        If this is true, the current selection in TeXShop will
--        be used to filter the BibTeX entries that are displayed in 
the dialog.
--        This filtering is case-sensitive, but can match any field in 
the BibTeX 
--        entry (title, keywords, etc).
property limit_by_selection : true

--   front_app:    (string)
--        this is used in a number of places to make sure the dialogs 
come up in TeXShop
property front_app : "TeXShop"

--   title_delim:   (string)
--        if the "title" option is enabled, this string is used to 
separate the
--        reference from the title displayed in the dialog box.
property title_delim : "   --  "

-- ----------------------------------------
--     Dev Notes
-- ----------------------------------------
--  Original Version:  8/23/2004 - mdh
--  To-do:
--    *  can improve O() by searching for other tags
--        while collecting project files
--    *  Should check TeX's search path when searching for files
--        (currently only looking in project directory)

on run
	try
		set search_pattern to ""
		tell application "TeXShop"
			set f to ((path of front document as string) as POSIX file)
			if limit_by_selection then
				set search_pattern to the content of the selection of the front 
document
			end if
		end tell
		tell application "Finder" to set current_folder to the container of 
(f as alias)
		set rootfile to get_project_root(f)
		set project_files to get_project_files(rootfile, current_folder)
		set bib_files to get_bib_file(project_files, current_folder)
		
		set cite_ref to query_for_citation(bib_files, search_pattern, 
search_options)
		if (cite_ref ­ "") then
			tell application "TeXShop" -- not sure why name is needed here 
explicitly
				set the selection of the front document to (cite_cmd & "{" & 
cite_ref & "}") as string
			end tell
		end if
	on error e
		tell application front_app
			display dialog ("Error: " & e) buttons {"Okay"}
		end tell
	end try
end run

-- --------------------------------
-- get_project_root
-- --------------------------------
--  given an alias to the current TeX file,
--  will check if their is a valid *.texshop file
--  and will return that, otherwise will return the
--  current file
on get_project_root(f)
	try
		set root_texshop to (f as string) & "shop"
		tell application "Finder" to set root_texshop_alias to (root_texshop 
as alias)
		set fnum to (open for access root_texshop_alias)
		set texshop_data to (read fnum as text)
		close access fnum
		tell application "Finder" to set current_folder to the container of 
(f as alias) as string
		set new_root to current_folder & texshop_data
		return (new_root as alias)
	on error e
		return f
	end try
end get_project_root

-- --------------------------------
-- get_bib_file
-- --------------------------------
--  given a list of .tex files, will
--  search for \bibliography statement(s)
--  and return a list of aliases to bib files
on get_bib_file(f_list, project_folder)
	set bib_filenames to {}
	set bib_files to {}
	repeat with tex_file in f_list
		set new_bib_arg to extract_tags(tex_file, "\\\\bibliography")
		-- Note that there can be multiple bib files declared: 
\bibliography{file1,file2}
		-- so we need to split these up:
		set old_delim to AppleScript's text item delimiters
		set AppleScript's text item delimiters to ","
		set new_bib_filenames to (every text item of (new_bib_arg as text))
		set AppleScript's text item delimiters to old_delim
		set bib_filenames to bib_filenames & new_bib_filenames
	end repeat
	repeat with bib_filename in bib_filenames
		try
			set new_file to ((project_folder as string) & bib_filename & ".bib") 
as alias
			if bib_files does not contain new_file then
				set bib_files to bib_files & new_file
			end if
		end try
	end repeat
	return bib_files
end get_bib_file

-- --------------------------------
-- get_project_files
-- --------------------------------
--  given the head tex file, will return
--  a list of \input  files, descending into
--  those files as needed
on get_project_files(root_file, project_folder)
	set file_list to {root_file}
	set file_indx to 1
	repeat while file_indx ² (count (file_list))
		set input_files to extract_tags((item file_indx of file_list), 
"\\\\input")
		set k to 1
		repeat while k ² (count (input_files))
			try
				set new_file to ((project_folder as string) & ((item k of 
input_files) as string) & ".tex") as alias
				if file_list does not contain new_file then
					set file_list to file_list & new_file
				end if
			end try
			set k to k + 1
		end repeat
		set file_indx to file_indx + 1
	end repeat
	return file_list
end get_project_files

-- --------------------------------
-- extract_tags
-- --------------------------------
--  given a file and a tag to search for,
--  will return a list of the arguments to
--  the tag.
--  Notes:
--     (1)  will not return multi-line argument
--     (2)  does not match optional arguments e.g., 
\mytag[optional]{arg1}
--     (3) tag string should be escaped: eg,  set tag to "\\\\input"
-- Explanation of search_cmd:
--    (1) remove comments
--    (2) convert internal {}:  TAG{ ...{..}...}  ==> TAG{ 
...(%...)%...}
--    (3) convert TAG{...} to "[space]%%"..."[space]%%%"
--    (4) remove all residual {} tokens
--    (5) restore %% and %%% to { and }, respectively
--    (6) remove characters outside of {...} pairs
--    (7) print out, with one set of contents per line
on extract_tags(f, tag)
	set esc_newline to "\\
	"
	set search_cmd to " | sed -n -e '/^%/d;s/\\([^\\]\\)%.*/\\1/;/" & tag 
& "{.*}/{' -e ':A' -e 's/\\(" & tag & 
"{[^{}]*\\){\\([^{}]*\\)}/\\1(%\\2)%/g ;t A' -e 's/" & tag & 
"{\\([^}]*\\)}/ %%\\1 %%%/g;s/[}{]//g;s/ %%%/}/g;s/ 
%%/{/g;s/^[^{]*{//g;s/}[^{]*$//g;s/}[^{]*{/" & esc_newline & 
"/g;s/(%/{/g;s/)%/}/g;p;}'"
	--	\\([^%]*\\)%)/{\\1}/g;p;}'"
	set fpath to quoted form of (POSIX path of f)
	set search_script to "tr '\\r' '\\n' <  " & fpath & search_cmd
	set results to (do shell script search_script)
	if results = "" then
		return {}
	else
		return every paragraph of results
	end if
end extract_tags


-- --------------------------------
-- query_for_citation
-- --------------------------------
--  given a list of bibfile aliases, will
--  put  up a dialog with a list of citations
--  and  ask the user to select an item.
--  (see notes in parse_bibfile() for
--  comments on search_pattern and options)
on query_for_citation(f, search_pattern, options)
	set bib_data to parse_bibfile(f, search_pattern, options)
	
	set citation to ""
	set bib_entry_count to (length of bib_data)
	if (bib_entry_count = 0) then
		tell application "Finder" to set bib_filename to the name of (the 
first item of f)
		tell application front_app
			if search_pattern = "" then
				display dialog ("Sorry, no BibTeX entries were found in the file 
\"" & bib_filename & "\".") buttons {"Okay"}
			else
				display dialog ("Sorry, no BibTeX entries were found in the file 
\"" & bib_filename & "\" that contain the pattern \"" & search_pattern 
& "\".") buttons {"Okay"}
			end if
		end tell
	else
		if search_pattern = "" then
			set my_prompt to "Choose from among the following " & 
bib_entry_count & " BibTeX entries that were found:"
		else
			set my_prompt to "Choose from among the following " & 
bib_entry_count & " BibTeX entries that were found that contain the 
pattern \"" & search_pattern & "\":"
		end if
		tell application front_app
			set user_selection to choose from list bib_data with prompt 
my_prompt with multiple selections allowed
		end tell
		
		if user_selection ­ false then
			set AppleScript's text item delimiters to title_delim
			set output_tags to {}
			repeat with selected_item in user_selection
				set output_tags to output_tags & (the first text item of 
(selected_item as text) as string)
			end repeat
			set AppleScript's text item delimiters to ","
			set citation to output_tags as string
		end if
	end if
	return citation
end query_for_citation

-- --------------------------------
-- parse_bibfile
-- --------------------------------
--  given an alias to a bibfile, will return bibliography entries
-- 
--   If "search_pattern" is not "", will look for that pattern in
--   entry fields and will only return matching items.  This is
--   case sensitive, so:  parse_bibfile(f,"Smith",{}) will
--   match an entry with 'Author="J. Smith"', but would
--   not match against 'Author="J. smith"'
--
--  Options is a list of text items:
--      "sort"   - to sort the output
--      "title"   - to show the title
--
--   BUGS:
--     (1)  not robust against all bibtex definitions like 
"@string{...}"
on parse_bibfile(f, search_pattern, options)
	if (class of f is list) then
		set fpath to ""
		repeat with f_item in f
			set fpath to fpath & " " & (quoted form of (POSIX path of f_item))
		end repeat
	else
		set fpath to quoted form of (POSIX path of f)
	end if
	
	if options contains "sort" then
		set sort_cmd to " | sort -df"
	else
		set sort_cmd to ""
	end if
	
	if options contains "title" then
		set set_title_action to "{split($0,line,\"=\");t=\"" & title_delim & 
"\" line[2]}"
		set title_cmd to "/title.*=.*/" & set_title_action & "/Title.*=.*/ " 
& set_title_action & " /TITLE.*=.*/" & set_title_action
	else
		set title_cmd to ""
	end if
	
	if search_pattern ­ "" then
		-- Note that we handle two cases--when the search_pattern occurs 
inside the citation's first line and when it doesn't
		set limit_cmd to "/.*=.*" & search_pattern & "/{if (ref!=\"\"){f=1;}}"
		set def_filt_cmd to "/^[ ]*@.*{.*,/ {if(f==1){print ref 
t};split($0,line,\"{\");split(line[2],r2,\",\");ref=r2[1];f=0}/^[ 
]*@.*{.*" & search_pattern & ".*,/ {f=1}"
	else
		set limit_cmd to ""
		set def_filt_cmd to "/^[ ]*@.*{.*,/ {if(f==1){print ref 
t};split($0,line,\"{\");split(line[2],r2,\",\");ref=r2[1];f=1}"
	end if
	
	set parse_cmd to " | awk 'BEGIN { t=\"\"; ref=\"\"; f=0;}" & 
def_filt_cmd & title_cmd & limit_cmd & "END{if(f==1){print ref  t}}'"
	
	set search_script to "cat " & fpath & " | tr '\\r' '\\n' " & parse_cmd 
& sort_cmd
	set results to (do shell script search_script)
	if results = "" then return {}
	return every paragraph of results
end parse_bibfile

--------------------- Info ---------------------
Mac-TeX Website: http://www.esm.psu.edu/mac-tex/
           & FAQ: http://latex.yauh.de/faq/
TeX FAQ: http://www.tex.ac.uk/faq
List Post: <mailto:MacOSX-TeX at email.esm.psu.edu>





More information about the MacOSX-TeX mailing list