use crate::{
auxi::AuxData,
bibs::{BibCommand, BibData},
buffer::{BufTy, GlobalBuffer},
char_info::LexClass,
cite::CiteInfo,
exec::{bst_ex_warn_print, bst_ln_num_print, ExecCtx},
hash::{BstFn, HashData},
other::OtherData,
peekable::input_ln,
pool::{StrNumber, StringPool},
scan::{Scan, ScanRes},
ASCIICode, Bibtex, BibtexError, CiteNumber, FieldLoc, HashPointer,
};
use std::{ffi::CStr, io::Write, slice};
use tectonic_io_base::OutputHandle;
pub trait AsBytes {
fn as_bytes(&self) -> &[u8];
}
impl AsBytes for str {
fn as_bytes(&self) -> &[u8] {
self.as_bytes()
}
}
impl AsBytes for String {
fn as_bytes(&self) -> &[u8] {
self.as_bytes()
}
}
impl AsBytes for [u8] {
fn as_bytes(&self) -> &[u8] {
self
}
}
pub(crate) fn bib_close_log(ctx: &mut Bibtex<'_, '_>) {
if let Some(log) = ctx.logs.file.take() {
ctx.engine.output_close(log);
}
}
pub fn print_overflow(ctx: &mut Bibtex<'_, '_>) {
ctx.write_logs("Sorry---you've exceeded BibTeX's ");
ctx.mark_fatal();
}
pub fn print_confusion(ctx: &mut Bibtex<'_, '_>) {
ctx.write_logs("---this can't happen\n*Please notify the Tectonic maintainer*\n");
ctx.mark_fatal();
}
pub(crate) fn out_token(handle: &mut OutputHandle, buffers: &GlobalBuffer) {
let bytes = buffers.buffer(BufTy::Base);
let start = buffers.offset(BufTy::Base, 1);
let end = buffers.offset(BufTy::Base, 2);
handle.write_all(&bytes[start..end]).unwrap();
}
pub(crate) fn print_a_token(ctx: &mut Bibtex<'_, '_>, buffers: &GlobalBuffer) {
out_token(ctx.engine.get_output(ctx.logs.stdout.unwrap()), buffers);
out_token(ctx.engine.get_output(ctx.logs.file.unwrap()), buffers);
}
pub(crate) fn print_bad_input_line(ctx: &mut Bibtex<'_, '_>, buffers: &GlobalBuffer) {
ctx.write_logs(" : ");
let offset2 = buffers.offset(BufTy::Base, 2);
let slice = &buffers.buffer(BufTy::Base)[0..offset2];
for code in slice {
if LexClass::of(*code) == LexClass::Whitespace {
ctx.write_logs(" ");
} else {
ctx.write_logs(slice::from_ref(code))
}
}
ctx.write_logs("\n : ");
let str = (0..offset2).map(|_| ' ').collect::<String>();
ctx.write_logs(&str);
let last = buffers.init(BufTy::Base);
if offset2 < last {
let slice = &buffers.buffer(BufTy::Base)[offset2..last];
for code in slice {
if LexClass::of(*code) == LexClass::Whitespace {
ctx.write_logs(" ");
} else {
ctx.write_logs(slice::from_ref(code));
}
}
}
ctx.write_logs("\n");
if !slice
.iter()
.any(|c| LexClass::of(*c) != LexClass::Whitespace)
{
ctx.write_logs("(Error may have been on previous line)\n");
}
ctx.mark_error();
}
pub(crate) fn print_skipping_whatever_remains(ctx: &mut Bibtex<'_, '_>) {
ctx.write_logs("I'm skipping whatever remains of this ");
}
pub(crate) fn out_pool_str(
ctx: &mut Bibtex<'_, '_>,
pool: &StringPool,
s: StrNumber,
) -> Result<(), BibtexError> {
let str = pool.try_get_str(s);
if let Some(str) = str {
ctx.write_log_file(str);
Ok(())
} else {
ctx.write_logs(&format!("Illegal string number: {s}"));
print_confusion(ctx);
Err(BibtexError::Fatal)
}
}
pub(crate) fn print_a_pool_str(
ctx: &mut Bibtex<'_, '_>,
s: StrNumber,
pool: &StringPool,
) -> Result<(), BibtexError> {
let str = pool.try_get_str(s);
if let Some(str) = str {
ctx.write_logs(str);
Ok(())
} else {
ctx.write_logs(&format!("Illegal string number: {s}"));
print_confusion(ctx);
Err(BibtexError::Fatal)
}
}
pub fn sam_wrong_file_name_print(ctx: &mut Bibtex<'_, '_>, file: &CStr) {
ctx.write_stdout(&format!(
"I couldn't open file name `{}`",
file.to_str().unwrap()
));
}
pub(crate) fn print_aux_name(
ctx: &mut Bibtex<'_, '_>,
pool: &StringPool,
name: StrNumber,
) -> Result<(), BibtexError> {
print_a_pool_str(ctx, name, pool)?;
ctx.write_logs("\n");
Ok(())
}
pub(crate) fn log_pr_aux_name(
ctx: &mut Bibtex<'_, '_>,
aux: &AuxData,
pool: &StringPool,
) -> Result<(), BibtexError> {
out_pool_str(ctx, pool, aux.top_file().name)?;
ctx.write_log_file("\n");
Ok(())
}
pub(crate) fn aux_err_print(
ctx: &mut Bibtex<'_, '_>,
buffers: &GlobalBuffer,
aux: &AuxData,
pool: &StringPool,
) -> Result<(), BibtexError> {
ctx.write_logs(&format!("---line {} of file ", aux.top_file().line));
print_aux_name(ctx, pool, aux.top_file().name)?;
print_bad_input_line(ctx, buffers);
print_skipping_whatever_remains(ctx);
ctx.write_logs("command\n");
Ok(())
}
pub(crate) enum AuxTy {
Data,
Style,
}
pub(crate) fn aux_err_illegal_another_print(
ctx: &mut Bibtex<'_, '_>,
cmd: AuxTy,
) -> Result<(), BibtexError> {
ctx.write_logs("Illegal, another \\bib");
match cmd {
AuxTy::Data => ctx.write_logs("data"),
AuxTy::Style => ctx.write_logs("style"),
}
ctx.write_logs(" command");
Ok(())
}
pub fn aux_err_no_right_brace_print(ctx: &mut Bibtex<'_, '_>) {
ctx.write_logs("No \"}\"");
}
pub fn aux_err_stuff_after_right_brace_print(ctx: &mut Bibtex<'_, '_>) {
ctx.write_logs("Stuff after \"}\"");
}
pub fn aux_err_white_space_in_argument_print(ctx: &mut Bibtex<'_, '_>) {
ctx.write_logs("White space in argument");
}
pub fn aux_end1_err_print(ctx: &mut Bibtex<'_, '_>) {
ctx.write_logs("I found no ");
}
pub(crate) fn aux_end2_err_print(
ctx: &mut Bibtex<'_, '_>,
pool: &StringPool,
name: StrNumber,
) -> Result<(), BibtexError> {
ctx.write_logs("---while reading file ");
print_aux_name(ctx, pool, name)?;
ctx.mark_error();
Ok(())
}
pub(crate) fn print_bib_name(
ctx: &mut Bibtex<'_, '_>,
pool: &StringPool,
name: StrNumber,
) -> Result<(), BibtexError> {
print_a_pool_str(ctx, name, pool)?;
let res = pool
.try_get_str(name)
.ok_or(BibtexError::Fatal)
.map(|str| str.ends_with(b".bib"))?;
if !res {
ctx.write_logs(".bib");
}
ctx.write_logs("\n");
Ok(())
}
pub(crate) fn log_pr_bib_name(
ctx: &mut Bibtex<'_, '_>,
pool: &StringPool,
name: StrNumber,
) -> Result<(), BibtexError> {
out_pool_str(ctx, pool, name)?;
let res = pool
.try_get_str(name)
.map(|str| str.ends_with(b".bib"))
.ok_or(BibtexError::Fatal)?;
if !res {
ctx.write_log_file(".bib");
}
ctx.write_log_file("\n");
Ok(())
}
pub(crate) fn log_pr_bst_name(
ctx: &mut Bibtex<'_, '_>,
pool: &StringPool,
) -> Result<(), BibtexError> {
out_pool_str(ctx, pool, ctx.bst.as_ref().unwrap().name)?;
ctx.write_log_file(".bst\n");
Ok(())
}
pub(crate) fn hash_cite_confusion(ctx: &mut Bibtex<'_, '_>) {
ctx.write_logs("Cite hash error");
print_confusion(ctx);
}
pub(crate) fn bst_warn_print(
ctx: &mut Bibtex<'_, '_>,
pool: &StringPool,
) -> Result<(), BibtexError> {
bst_ln_num_print(ctx, pool)?;
ctx.mark_warning();
Ok(())
}
pub fn eat_bst_print(ctx: &mut Bibtex<'_, '_>) {
ctx.write_logs("Illegal end of style file in command: ");
}
pub fn id_scanning_confusion(ctx: &mut Bibtex<'_, '_>) {
ctx.write_logs("Identifier scanning error");
print_confusion(ctx);
}
pub(crate) fn bst_id_print(
ctx: &mut Bibtex<'_, '_>,
buffers: &GlobalBuffer,
scan_result: ScanRes,
) -> Result<(), BibtexError> {
let char = buffers.at_offset(BufTy::Base, 2) as char;
match scan_result {
ScanRes::IdNull => {
ctx.write_logs(&format!("\"{char}\" begins identifier, command: "));
Ok(())
}
ScanRes::OtherCharAdjacent => {
ctx.write_logs(&format!(
"\"{char}\" immediately follows identifier, command: ",
));
Ok(())
}
_ => {
id_scanning_confusion(ctx);
Err(BibtexError::Fatal)
}
}
}
pub fn bst_left_brace_print(ctx: &mut Bibtex<'_, '_>) {
ctx.write_logs("\"{\" is missing in command: ");
}
pub fn bst_right_brace_print(ctx: &mut Bibtex<'_, '_>) {
ctx.write_logs("\"}\" is missing in command: ");
}
pub(crate) fn bib_ln_num_print(
ctx: &mut Bibtex<'_, '_>,
pool: &StringPool,
bibs: &BibData,
) -> Result<(), BibtexError> {
ctx.write_logs(&format!("--line {} of file ", bibs.top_file().line));
print_bib_name(ctx, pool, bibs.top_file().name)
}
pub(crate) fn bib_err_print(
ctx: &mut Bibtex<'_, '_>,
buffers: &GlobalBuffer,
pool: &StringPool,
bibs: &BibData,
bib_command: Option<BibCommand>,
) -> Result<(), BibtexError> {
ctx.write_logs("-");
bib_ln_num_print(ctx, pool, bibs)?;
print_bad_input_line(ctx, buffers);
print_skipping_whatever_remains(ctx);
if bib_command.is_some() {
ctx.write_logs("command\n");
} else {
ctx.write_logs("entry\n");
}
Ok(())
}
pub(crate) fn bib_warn_print(
ctx: &mut Bibtex<'_, '_>,
pool: &StringPool,
bibs: &BibData,
) -> Result<(), BibtexError> {
bib_ln_num_print(ctx, pool, bibs)?;
ctx.mark_warning();
Ok(())
}
pub(crate) fn eat_bib_print(
ctx: &mut Bibtex<'_, '_>,
buffers: &GlobalBuffer,
pool: &StringPool,
bibs: &BibData,
bib_command: Option<BibCommand>,
) -> Result<(), BibtexError> {
ctx.write_logs("Illegal end of database file");
bib_err_print(ctx, buffers, pool, bibs, bib_command)
}
pub(crate) fn bib_one_of_two_print(
ctx: &mut Bibtex<'_, '_>,
buffers: &GlobalBuffer,
pool: &StringPool,
bibs: &BibData,
char1: ASCIICode,
char2: ASCIICode,
bib_command: Option<BibCommand>,
) -> Result<(), BibtexError> {
ctx.write_logs(&format!(
"I was expecting a `{}' or a `{}'",
char1 as char, char2 as char
));
bib_err_print(ctx, buffers, pool, bibs, bib_command)
}
pub(crate) fn bib_equals_sign_print(
ctx: &mut Bibtex<'_, '_>,
buffers: &GlobalBuffer,
pool: &StringPool,
bibs: &BibData,
bib_command: Option<BibCommand>,
) -> Result<(), BibtexError> {
ctx.write_logs("I was expecting an \"=\"");
bib_err_print(ctx, buffers, pool, bibs, bib_command)
}
pub(crate) fn bib_unbalanced_braces_print(
ctx: &mut Bibtex<'_, '_>,
buffers: &GlobalBuffer,
pool: &StringPool,
bibs: &BibData,
bib_command: Option<BibCommand>,
) -> Result<(), BibtexError> {
ctx.write_logs("Unbalanced braces");
bib_err_print(ctx, buffers, pool, bibs, bib_command)
}
pub(crate) fn macro_warn_print(ctx: &mut Bibtex<'_, '_>, buffers: &GlobalBuffer) {
ctx.write_logs("Warning--string name \"");
print_a_token(ctx, buffers);
ctx.write_logs("\" is ");
}
pub(crate) fn bib_id_print(
ctx: &mut Bibtex<'_, '_>,
buffers: &GlobalBuffer,
scan_res: ScanRes,
) -> Result<(), BibtexError> {
match scan_res {
ScanRes::IdNull => {
ctx.write_logs("You're missing ");
Ok(())
}
ScanRes::OtherCharAdjacent => {
let char = buffers.at_offset(BufTy::Base, 2);
ctx.write_logs(&format!("\"{char}\" immediately follows "));
Ok(())
}
_ => {
id_scanning_confusion(ctx);
Err(BibtexError::Fatal)
}
}
}
pub fn cite_key_disappeared_confusion(ctx: &mut Bibtex<'_, '_>) {
ctx.write_logs("A cite key disappeared");
print_confusion(ctx);
}
pub(crate) fn bad_cross_reference_print(
ctx: &mut Bibtex<'_, '_>,
pool: &StringPool,
cites: &CiteInfo,
cite_ptr: CiteNumber,
s: StrNumber,
) -> Result<(), BibtexError> {
ctx.write_logs("--entry \"");
print_a_pool_str(ctx, cites.get_cite(cite_ptr), pool)?;
ctx.write_logs("\"\nrefers to entry \"");
print_a_pool_str(ctx, s, pool)?;
ctx.write_logs("\"");
Ok(())
}
pub(crate) fn print_missing_entry(
ctx: &mut Bibtex<'_, '_>,
pool: &StringPool,
s: StrNumber,
) -> Result<(), BibtexError> {
ctx.write_logs("Warning--I didn't find a database entry for \"");
print_a_pool_str(ctx, s, pool)?;
ctx.write_logs("\"\n");
ctx.mark_warning();
Ok(())
}
pub(crate) fn bst_mild_ex_warn_print(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &StringPool,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
if ctx.mess_with_entries {
ctx.write_logs(" for entry ");
print_a_pool_str(ctx, cites.get_cite(cites.ptr()), pool)?;
}
ctx.write_logs("\nwhile executing");
bst_warn_print(ctx, pool)
}
pub(crate) fn bst_cant_mess_with_entries_print(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &StringPool,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
ctx.write_logs("You can't mess with entries here");
bst_ex_warn_print(ctx, pool, cites)
}
pub fn bst_1print_string_size_exceeded(ctx: &mut Bibtex<'_, '_>) {
ctx.write_logs("Warning--you've exceeded ");
}
pub(crate) fn bst_2print_string_size_exceeded(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &StringPool,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
ctx.write_logs("-string-size,");
bst_mild_ex_warn_print(ctx, pool, cites)?;
ctx.write_logs("*Please notify the bibstyle designer*\n");
Ok(())
}
pub(crate) fn braces_unbalanced_complaint(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &StringPool,
cites: &CiteInfo,
pop_lit_var: StrNumber,
) -> Result<(), BibtexError> {
ctx.write_logs("Warning--\"");
print_a_pool_str(ctx, pop_lit_var, pool)?;
ctx.write_logs("\" isn't a brace-balanced string");
bst_mild_ex_warn_print(ctx, pool, cites)
}
pub(crate) fn print_fn_class(
ctx: &mut Bibtex<'_, '_>,
hash: &HashData,
fn_loc: HashPointer<BstFn>,
) {
match hash.get(fn_loc).extra() {
BstFn::Builtin(_) => ctx.write_logs("built-in"),
BstFn::Wizard(_) => ctx.write_logs("wizard-defined"),
BstFn::Field(_) => ctx.write_logs("field"),
BstFn::IntEntry(_) => ctx.write_logs("integer-entry-variable"),
BstFn::StrEntry(_) => ctx.write_logs("string-entry-variable"),
BstFn::IntGlbl(_) => ctx.write_logs("integer-global-variable"),
BstFn::StrGlbl(_) => ctx.write_logs("string-global-variable"),
}
}
pub(crate) fn bst_err_print_and_look_for_blank_line(
ctx: &mut Bibtex<'_, '_>,
buffers: &mut GlobalBuffer,
pool: &StringPool,
) -> Result<(), BibtexError> {
ctx.write_logs("-");
bst_ln_num_print(ctx, pool)?;
print_bad_input_line(ctx, buffers);
while buffers.init(BufTy::Base) != 0 {
if !input_ln(ctx.engine, &mut ctx.bst.as_mut().unwrap().file, buffers) {
return Err(BibtexError::Recover);
} else {
ctx.bst.as_mut().unwrap().line += 1;
}
}
buffers.set_offset(BufTy::Base, 2, buffers.init(BufTy::Base));
Ok(())
}
pub(crate) fn already_seen_function_print(
ctx: &mut Bibtex<'_, '_>,
buffers: &mut GlobalBuffer,
pool: &StringPool,
hash: &HashData,
seen_fn_loc: HashPointer<BstFn>,
) -> Result<(), BibtexError> {
print_a_pool_str(ctx, hash.get(seen_fn_loc).text(), pool)?;
ctx.write_logs(" is already a type \"");
print_fn_class(ctx, hash, seen_fn_loc);
ctx.write_logs("\" function name\n");
bst_err_print_and_look_for_blank_line(ctx, buffers, pool)
}
pub(crate) fn nonexistent_cross_reference_error(
ctx: &mut Bibtex<'_, '_>,
pool: &StringPool,
cites: &CiteInfo,
other: &OtherData,
cite_ptr: CiteNumber,
field_ptr: FieldLoc,
) -> Result<(), BibtexError> {
ctx.write_logs("A bad cross reference-");
bad_cross_reference_print(ctx, pool, cites, cite_ptr, other.field(field_ptr))?;
ctx.write_logs(", which doesn't exist\n");
ctx.mark_error();
Ok(())
}
pub(crate) fn output_bbl_line(ctx: &mut Bibtex<'_, '_>, buffers: &mut GlobalBuffer) {
if buffers.init(BufTy::Out) != 0 {
let mut init = buffers.init(BufTy::Out);
while init > 0 {
if LexClass::of(buffers.at(BufTy::Out, init - 1)) == LexClass::Whitespace {
init -= 1;
} else {
break;
}
}
buffers.set_init(BufTy::Out, init);
if init == 0 {
return;
}
let slice = &buffers.buffer(BufTy::Out)[..init];
ctx.engine
.get_output(ctx.bbl_file.unwrap())
.write_all(slice)
.unwrap();
}
writeln!(ctx.engine.get_output(ctx.bbl_file.unwrap())).unwrap();
ctx.bbl_line_num += 1;
buffers.set_init(BufTy::Out, 0);
}
pub(crate) fn skip_token_print(
ctx: &mut Bibtex<'_, '_>,
buffers: &mut GlobalBuffer,
pool: &StringPool,
) -> Result<(), BibtexError> {
ctx.write_logs("-");
bst_ln_num_print(ctx, pool)?;
ctx.mark_error();
Scan::new()
.chars(b"}%")
.class(LexClass::Whitespace)
.scan_till(buffers, buffers.init(BufTy::Base));
Ok(())
}
pub(crate) fn print_recursion_illegal(
ctx: &mut Bibtex<'_, '_>,
buffers: &mut GlobalBuffer,
pool: &StringPool,
) -> Result<(), BibtexError> {
ctx.write_logs("Curse you, wizard, before you recurse me:\nfunction ");
print_a_token(ctx, buffers);
ctx.write_logs(" is illegal in its own definition\n");
skip_token_print(ctx, buffers, pool)
}
pub(crate) fn skip_token_unknown_function_print(
ctx: &mut Bibtex<'_, '_>,
buffers: &mut GlobalBuffer,
pool: &StringPool,
) -> Result<(), BibtexError> {
print_a_token(ctx, buffers);
ctx.write_logs(" is an unknown function");
skip_token_print(ctx, buffers, pool)
}
pub(crate) fn skip_illegal_stuff_after_token_print(
ctx: &mut Bibtex<'_, '_>,
buffers: &mut GlobalBuffer,
pool: &StringPool,
) -> Result<(), BibtexError> {
ctx.write_logs(&format!(
"\"{}\" can't follow a literal",
buffers.at_offset(BufTy::Base, 2) as char
));
skip_token_print(ctx, buffers, pool)
}
pub(crate) fn brace_lvl_one_letters_complaint(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &StringPool,
cites: &CiteInfo,
str: StrNumber,
) -> Result<(), BibtexError> {
ctx.write_logs("The format string \"");
print_a_pool_str(ctx, str, pool)?;
ctx.write_logs("\" has an illegal brace-level-1 letter");
bst_ex_warn_print(ctx, pool, cites)?;
Ok(())
}