use crate::{
bibs::{compress_bib_white, eat_bib_white_space, BibCommand, BibData},
buffer::{BufTy, GlobalBuffer},
char_info::{IdClass, LexClass},
cite::{add_database_cite, CiteInfo},
exec::{ControlSeq, ExecCtx},
hash,
hash::{BstFn, HashData},
log::{
bib_err_print, bib_id_print, bib_unbalanced_braces_print, bib_warn_print,
braces_unbalanced_complaint, bst_err_print_and_look_for_blank_line, eat_bib_print,
eat_bst_print, hash_cite_confusion, macro_warn_print, print_a_pool_str, print_confusion,
print_recursion_illegal, skip_illegal_stuff_after_token_print, skip_token_print,
skip_token_unknown_function_print,
},
other::WizOp,
peekable::input_ln,
pool::{StrNumber, StringPool},
ASCIICode, Bibtex, BibtexError, BufPointer, GlobalItems, HashPointer,
};
pub(crate) enum ScanRes {
IdNull,
SpecifiedCharAdjacent,
OtherCharAdjacent,
WhitespaceAdjacent,
}
#[derive(Default)]
pub(crate) struct Scan<'a> {
chars: &'a [ASCIICode],
not_class: Option<LexClass>,
class: Option<LexClass>,
}
impl<'a> Scan<'a> {
pub fn new() -> Scan<'a> {
Scan {
chars: &[],
not_class: None,
class: None,
}
}
pub fn chars(mut self, chars: &'a [ASCIICode]) -> Scan<'a> {
self.chars = chars;
self
}
pub fn not_class(mut self, class: LexClass) -> Self {
self.not_class = Some(class);
self
}
pub fn class(mut self, class: LexClass) -> Self {
self.class = Some(class);
self
}
fn match_char(&self, char: ASCIICode) -> bool {
self.not_class
.is_some_and(|class| LexClass::of(char) != class)
|| self.class.is_some_and(|class| LexClass::of(char) == class)
|| self.chars.contains(&char)
}
pub fn scan_till(&self, buffers: &mut GlobalBuffer, last: BufPointer) -> bool {
buffers.set_offset(BufTy::Base, 1, buffers.offset(BufTy::Base, 2));
let mut idx = buffers.offset(BufTy::Base, 2);
while idx < last && !self.match_char(buffers.at(BufTy::Base, idx)) {
idx += 1;
}
buffers.set_offset(BufTy::Base, 2, idx);
idx < last
}
pub fn scan_till_nonempty(&self, buffers: &mut GlobalBuffer, last: BufPointer) -> bool {
let start = buffers.offset(BufTy::Base, 2);
buffers.set_offset(BufTy::Base, 1, start);
let mut idx = start;
while idx < last && !self.match_char(buffers.at(BufTy::Base, idx)) {
idx += 1;
}
buffers.set_offset(BufTy::Base, 2, idx);
idx - start != 0
}
}
pub(crate) fn scan_identifier(
buffers: &mut GlobalBuffer,
char1: ASCIICode,
char2: ASCIICode,
char3: ASCIICode,
) -> ScanRes {
let last = buffers.init(BufTy::Base);
let start = buffers.offset(BufTy::Base, 2);
buffers.set_offset(BufTy::Base, 1, start);
let mut idx = start;
let char = buffers.at(BufTy::Base, idx);
if LexClass::of(char) != LexClass::Numeric {
while idx < last && IdClass::of(buffers.at(BufTy::Base, idx)) == IdClass::LegalIdChar {
idx += 1;
}
buffers.set_offset(BufTy::Base, 2, idx);
}
let char = buffers.at(BufTy::Base, idx);
if idx - start == 0 {
ScanRes::IdNull
} else if LexClass::of(char) == LexClass::Whitespace || idx == last {
ScanRes::WhitespaceAdjacent
} else if char == char1 || char == char2 || char == char3 {
ScanRes::SpecifiedCharAdjacent
} else {
ScanRes::OtherCharAdjacent
}
}
fn scan_integer(buffers: &mut GlobalBuffer, token_value: &mut i64) -> bool {
let last = buffers.init(BufTy::Base);
let start = buffers.offset(BufTy::Base, 2);
buffers.set_offset(BufTy::Base, 1, start);
let mut idx = start;
let sign = if buffers.at(BufTy::Base, idx) == b'-' {
idx += 1;
true
} else {
false
};
*token_value = 0;
let mut char = buffers.at(BufTy::Base, idx);
while idx < last && LexClass::of(char) == LexClass::Numeric {
*token_value = *token_value * 10 + (char - 48) as i64;
idx += 1;
char = buffers.at(BufTy::Base, idx);
}
buffers.set_offset(BufTy::Base, 2, idx);
if sign {
*token_value *= -1;
}
idx - start != if sign { 1 } else { 0 }
}
pub(crate) fn eat_bst_white_space(ctx: &mut Bibtex<'_, '_>, buffers: &mut GlobalBuffer) -> bool {
loop {
let init = buffers.init(BufTy::Base);
if Scan::new()
.not_class(LexClass::Whitespace)
.scan_till(buffers, init)
&& buffers.at_offset(BufTy::Base, 2) != b'%'
{
return true;
}
if !input_ln(ctx.engine, &mut ctx.bst.as_mut().unwrap().file, buffers) {
return false;
}
ctx.bst.as_mut().unwrap().line += 1;
buffers.set_offset(BufTy::Base, 2, 0);
}
}
fn handle_char(
ctx: &mut Bibtex<'_, '_>,
globals: &mut GlobalItems<'_>,
single_function: &mut Vec<WizOp>,
wiz_loc: HashPointer<BstFn>,
char: ASCIICode,
) -> Result<(), BibtexError> {
match char {
b'#' => {
let mut token_value = 0;
globals
.buffers
.set_offset(BufTy::Base, 2, globals.buffers.offset(BufTy::Base, 2) + 1);
if !scan_integer(globals.buffers, &mut token_value) {
ctx.write_logs("Illegal integer in integer literal");
return skip_token_print(ctx, globals.buffers, globals.pool);
}
let str = &globals.buffers.buffer(BufTy::Base)
[globals.buffers.offset(BufTy::Base, 1)..globals.buffers.offset(BufTy::Base, 2)];
let res =
globals
.hash
.lookup_str_insert::<hash::Integer>(globals.pool, str, token_value);
let char = globals.buffers.at_offset(BufTy::Base, 2);
if globals.buffers.offset(BufTy::Base, 2) < globals.buffers.init(BufTy::Base)
&& LexClass::of(char) != LexClass::Whitespace
&& char != b'}'
&& char != b'%'
{
return skip_illegal_stuff_after_token_print(ctx, globals.buffers, globals.pool);
}
single_function.push(WizOp::Int(res.loc));
}
b'"' => {
globals
.buffers
.set_offset(BufTy::Base, 2, globals.buffers.offset(BufTy::Base, 2) + 1);
let init = globals.buffers.init(BufTy::Base);
if !Scan::new().chars(b"\"").scan_till(globals.buffers, init) {
ctx.write_logs("No `\"` to end string literal");
return skip_token_print(ctx, globals.buffers, globals.pool);
}
let str = &globals.buffers.buffer(BufTy::Base)
[globals.buffers.offset(BufTy::Base, 1)..globals.buffers.offset(BufTy::Base, 2)];
let res = globals
.hash
.lookup_str_insert::<hash::Text>(globals.pool, str, ());
globals
.buffers
.set_offset(BufTy::Base, 2, globals.buffers.offset(BufTy::Base, 2) + 1);
let char = globals.buffers.at_offset(BufTy::Base, 2);
if globals.buffers.offset(BufTy::Base, 2) < globals.buffers.init(BufTy::Base)
&& LexClass::of(char) != LexClass::Whitespace
&& char != b'}'
&& char != b'%'
{
return skip_illegal_stuff_after_token_print(ctx, globals.buffers, globals.pool);
}
single_function.push(WizOp::Text(res.loc));
}
b'\'' => {
globals
.buffers
.set_offset(BufTy::Base, 2, globals.buffers.offset(BufTy::Base, 2) + 1);
let init = globals.buffers.init(BufTy::Base);
Scan::new()
.chars(b"}%")
.class(LexClass::Whitespace)
.scan_till(globals.buffers, init);
let range =
globals.buffers.offset(BufTy::Base, 1)..globals.buffers.offset(BufTy::Base, 2);
globals.buffers.buffer_mut(BufTy::Base)[range.clone()].make_ascii_lowercase();
let str = &globals.buffers.buffer(BufTy::Base)[range];
let res = globals.hash.lookup_str::<BstFn>(globals.pool, str);
match res {
None => {
return skip_token_unknown_function_print(ctx, globals.buffers, globals.pool)
}
Some(loc) if loc == wiz_loc => {
return print_recursion_illegal(ctx, globals.buffers, globals.pool);
}
Some(loc) => {
single_function.push(WizOp::Quote(loc));
}
}
}
b'{' => {
globals
.buffers
.set_offset(BufTy::Base, 2, globals.buffers.offset(BufTy::Base, 2) + 1);
let str = format!("'{}", ctx.impl_fn_num);
let res = globals.hash.lookup_str_insert::<BstFn>(
globals.pool,
str.as_bytes(),
BstFn::Wizard(0),
);
if res.exists {
ctx.write_logs("Already encountered implicit function");
print_confusion(ctx);
return Err(BibtexError::Fatal);
}
ctx.impl_fn_num += 1;
single_function.push(WizOp::Quote(res.loc));
scan_fn_def(ctx, globals, res.loc, wiz_loc)?;
}
_ => {
let init = globals.buffers.init(BufTy::Base);
Scan::new()
.chars(b"}%")
.class(LexClass::Whitespace)
.scan_till(globals.buffers, init);
let range =
globals.buffers.offset(BufTy::Base, 1)..globals.buffers.offset(BufTy::Base, 2);
globals.buffers.buffer_mut(BufTy::Base)[range.clone()].make_ascii_lowercase();
let str = &globals.buffers.buffer(BufTy::Base)[range];
let res = globals.hash.lookup_str::<BstFn>(globals.pool, str);
match res {
None => {
return skip_token_unknown_function_print(ctx, globals.buffers, globals.pool)
}
Some(loc) if loc == wiz_loc => {
return print_recursion_illegal(ctx, globals.buffers, globals.pool);
}
Some(loc) => {
single_function.push(WizOp::Exec(loc));
}
}
}
}
Ok(())
}
pub(crate) fn scan_fn_def(
ctx: &mut Bibtex<'_, '_>,
globals: &mut GlobalItems<'_>,
fn_hash_loc: HashPointer<BstFn>,
wiz_loc: HashPointer<BstFn>,
) -> Result<(), BibtexError> {
let mut single_function = Vec::new();
if !eat_bst_white_space(ctx, globals.buffers) {
eat_bst_print(ctx);
ctx.write_logs("function");
bst_err_print_and_look_for_blank_line(ctx, globals.buffers, globals.pool)?;
return Ok(());
}
let mut char = globals.buffers.at_offset(BufTy::Base, 2);
while char != b'}' {
handle_char(ctx, globals, &mut single_function, wiz_loc, char)?;
if !eat_bst_white_space(ctx, globals.buffers) {
eat_bst_print(ctx);
ctx.write_logs("function");
return bst_err_print_and_look_for_blank_line(ctx, globals.buffers, globals.pool);
}
char = globals.buffers.at_offset(BufTy::Base, 2);
}
single_function.push(WizOp::EndOfDef);
globals
.hash
.set_extra(fn_hash_loc, BstFn::Wizard(globals.other.wiz_func_len()));
globals.other.extend_wiz_data(single_function);
globals
.buffers
.set_offset(BufTy::Base, 2, globals.buffers.offset(BufTy::Base, 2) + 1);
Ok(())
}
fn scan_balanced_braces(
ctx: &mut Bibtex<'_, '_>,
buffers: &mut GlobalBuffer,
pool: &StringPool,
bibs: &mut BibData,
store_field: bool,
bib_command: Option<BibCommand>,
right_str_delim: ASCIICode,
) -> Result<bool, BibtexError> {
buffers.set_offset(BufTy::Base, 2, buffers.offset(BufTy::Base, 2) + 1);
if (LexClass::of(buffers.at_offset(BufTy::Base, 2)) == LexClass::Whitespace
|| buffers.offset(BufTy::Base, 2) == buffers.init(BufTy::Base))
&& !compress_bib_white(ctx, buffers, pool, bibs, bib_command)?
{
return Ok(false);
}
if buffers.offset(BufTy::Ex, 1) > 1
&& buffers.at(BufTy::Ex, buffers.offset(BufTy::Ex, 1) - 1) == b' '
&& buffers.at(BufTy::Ex, buffers.offset(BufTy::Ex, 1) - 2) == b' '
{
buffers.set_offset(BufTy::Ex, 1, buffers.offset(BufTy::Ex, 1) - 1);
}
let mut brace_level = 0;
if store_field {
while buffers.at_offset(BufTy::Base, 2) != right_str_delim {
match buffers.at_offset(BufTy::Base, 2) {
b'{' => {
brace_level += 1;
if buffers.offset(BufTy::Ex, 1) >= buffers.len() {
ctx.write_log_file("Field filled up at '{', reallocating.\n");
buffers.grow_all();
}
buffers.set_at(BufTy::Ex, buffers.offset(BufTy::Ex, 1), b'{');
buffers.set_offset(BufTy::Ex, 1, buffers.offset(BufTy::Ex, 1) + 1);
buffers.set_offset(BufTy::Base, 2, buffers.offset(BufTy::Base, 2) + 1);
if (LexClass::of(buffers.at_offset(BufTy::Base, 2)) == LexClass::Whitespace
|| buffers.offset(BufTy::Base, 2) == buffers.init(BufTy::Base))
&& !compress_bib_white(ctx, buffers, pool, bibs, bib_command)?
{
return Ok(false);
}
loop {
let c = buffers.at_offset(BufTy::Base, 2);
match c {
b'}' => brace_level -= 1,
b'{' => brace_level += 1,
_ => (),
}
if buffers.offset(BufTy::Ex, 1) >= buffers.len() {
match c {
b'}' | b'{' => {
ctx.write_log_file(&format!(
"Field filled up at '{}', reallocating.\n",
c as char
));
}
_ => {
ctx.write_log_file(&format!(
"Field filled up at {}, reallocating.\n",
buffers.offset(BufTy::Base, 2)
));
}
}
buffers.grow_all();
}
buffers.set_at(BufTy::Ex, buffers.offset(BufTy::Ex, 1), c);
buffers.set_offset(BufTy::Ex, 1, buffers.offset(BufTy::Ex, 1) + 1);
buffers.set_offset(BufTy::Base, 2, buffers.offset(BufTy::Base, 2) + 1);
if (LexClass::of(buffers.at_offset(BufTy::Base, 2)) == LexClass::Whitespace
|| buffers.offset(BufTy::Base, 2) == buffers.init(BufTy::Base))
&& !compress_bib_white(ctx, buffers, pool, bibs, bib_command)?
{
return Ok(false);
}
if brace_level == 0 {
break;
}
}
}
b'}' => {
return bib_unbalanced_braces_print(ctx, buffers, pool, bibs, bib_command)
.map(|_| false);
}
c => {
if buffers.offset(BufTy::Ex, 1) >= buffers.len() {
ctx.write_log_file(&format!(
"Field filled up at {}, reallocating.\n",
buffers.offset(BufTy::Base, 2)
));
buffers.grow_all();
}
buffers.set_at(BufTy::Ex, buffers.offset(BufTy::Ex, 1), c);
buffers.set_offset(BufTy::Ex, 1, buffers.offset(BufTy::Ex, 1) + 1);
buffers.set_offset(BufTy::Base, 2, buffers.offset(BufTy::Base, 2) + 1);
if (LexClass::of(buffers.at_offset(BufTy::Base, 2)) == LexClass::Whitespace
|| buffers.offset(BufTy::Base, 2) == buffers.init(BufTy::Base))
&& !compress_bib_white(ctx, buffers, pool, bibs, bib_command)?
{
return Ok(false);
}
}
}
}
} else {
while buffers.at_offset(BufTy::Base, 2) != right_str_delim {
match buffers.at_offset(BufTy::Base, 2) {
b'{' => {
brace_level += 1;
buffers.set_offset(BufTy::Base, 2, buffers.offset(BufTy::Base, 2) + 1);
if !eat_bib_white_space(ctx, buffers, bibs) {
return eat_bib_print(ctx, buffers, pool, bibs, bib_command).map(|_| false);
}
while brace_level > 0 {
let c = buffers.at_offset(BufTy::Base, 2);
match c {
b'{' => brace_level += 1,
b'}' => brace_level -= 1,
_ => (),
}
buffers.set_offset(BufTy::Base, 2, buffers.offset(BufTy::Base, 2) + 1);
let init = buffers.init(BufTy::Base);
if (c == b'{'
|| c == b'}'
|| !Scan::new().chars(b"{}").scan_till(buffers, init))
&& !eat_bib_white_space(ctx, buffers, bibs)
{
return eat_bib_print(ctx, buffers, pool, bibs, bib_command)
.map(|_| false);
}
}
}
b'}' => {
return bib_unbalanced_braces_print(ctx, buffers, pool, bibs, bib_command)
.map(|_| false);
}
_ => {
buffers.set_offset(BufTy::Base, 2, buffers.offset(BufTy::Base, 2) + 1);
let init = buffers.init(BufTy::Base);
if !Scan::new()
.chars(&[right_str_delim, b'{', b'}'])
.scan_till(buffers, init)
&& !eat_bib_white_space(ctx, buffers, bibs)
{
return eat_bib_print(ctx, buffers, pool, bibs, bib_command).map(|_| false);
}
}
}
}
}
buffers.set_offset(BufTy::Base, 2, buffers.offset(BufTy::Base, 2) + 1);
Ok(true)
}
fn scan_a_field_token_and_eat_white(
ctx: &mut Bibtex<'_, '_>,
globals: &mut GlobalItems<'_>,
store_field: bool,
command: Option<BibCommand>,
cur_macro_loc: HashPointer<hash::Macro>,
right_outer_delim: ASCIICode,
) -> Result<bool, BibtexError> {
match globals.buffers.at_offset(BufTy::Base, 2) {
b'{' => {
if !scan_balanced_braces(
ctx,
globals.buffers,
globals.pool,
globals.bibs,
store_field,
command,
b'}',
)? {
return Ok(false);
}
}
b'"' => {
if !scan_balanced_braces(
ctx,
globals.buffers,
globals.pool,
globals.bibs,
store_field,
command,
b'"',
)? {
return Ok(false);
}
}
b'0'..=b'9' => {
let last = globals.buffers.init(BufTy::Base);
if !Scan::new()
.not_class(LexClass::Numeric)
.scan_till_nonempty(globals.buffers, last)
{
ctx.write_logs("A digit disappeared");
print_confusion(ctx);
return Err(BibtexError::Fatal);
}
if store_field {
let range =
globals.buffers.offset(BufTy::Base, 1)..globals.buffers.offset(BufTy::Base, 2);
let len = range.end - range.start;
while len >= globals.buffers.len() {
ctx.write_log_file(&format!(
"Field filled up at {}, reallocating.\n",
globals.buffers.at_offset(BufTy::Base, 1)
));
globals.buffers.grow_all();
}
globals.buffers.copy_within(
BufTy::Base,
BufTy::Ex,
range.start,
globals.buffers.offset(BufTy::Ex, 1),
len,
);
globals.buffers.set_offset(
BufTy::Ex,
1,
globals.buffers.offset(BufTy::Ex, 1) + len,
);
}
}
_ => {
let res = scan_identifier(globals.buffers, b',', right_outer_delim, b'#');
if !matches!(
res,
ScanRes::WhitespaceAdjacent | ScanRes::SpecifiedCharAdjacent
) {
bib_id_print(ctx, globals.buffers, res)?;
ctx.write_logs("a field part");
bib_err_print(ctx, globals.buffers, globals.pool, globals.bibs, command)?;
return Ok(false);
}
if store_field {
let range =
globals.buffers.offset(BufTy::Base, 1)..globals.buffers.offset(BufTy::Base, 2);
globals.buffers.buffer_mut(BufTy::Base)[range.clone()].make_ascii_lowercase();
let str = &globals.buffers.buffer(BufTy::Base)[range];
let res = globals.hash.lookup_str::<hash::Macro>(globals.pool, str);
let mut store_token = res;
if command == Some(BibCommand::String)
&& res.is_some_and(|loc| loc == cur_macro_loc)
{
store_token = None;
macro_warn_print(ctx, globals.buffers);
ctx.write_logs("used in its own definition\n");
bib_warn_print(ctx, globals.pool, globals.bibs)?;
}
if res.is_none() {
store_token = None;
macro_warn_print(ctx, globals.buffers);
ctx.write_logs("undefined\n");
bib_warn_print(ctx, globals.pool, globals.bibs)?;
}
if let Some(loc) = store_token {
let &strnum = globals.hash.get(loc).extra();
let mut str = globals.pool.get_str(strnum);
if globals.buffers.offset(BufTy::Ex, 1) == 0
&& LexClass::of(str[0]) == LexClass::Whitespace
{
if globals.buffers.offset(BufTy::Ex, 1) >= globals.buffers.len() {
ctx.write_log_file("Field filled up at ' ', reallocating.\n");
globals.buffers.grow_all();
}
globals.buffers.set_at(
BufTy::Ex,
globals.buffers.offset(BufTy::Ex, 1),
b' ',
);
globals.buffers.set_offset(
BufTy::Ex,
1,
globals.buffers.offset(BufTy::Ex, 1) + 1,
);
while !str.is_empty() && LexClass::of(str[0]) == LexClass::Whitespace {
str = &str[1..];
}
}
for &c in str {
let msg = if LexClass::of(c) != LexClass::Whitespace {
|c| format!("Field filled up at {}, reallocating.\n", c as char)
} else {
|_| String::from("Field filled up at ' ', reallocating.\n")
};
if globals.buffers.offset(BufTy::Ex, 1) >= globals.buffers.len() {
ctx.write_log_file(&msg(c));
globals.buffers.grow_all();
}
if LexClass::of(c) != LexClass::Whitespace {
globals.buffers.set_at(
BufTy::Ex,
globals.buffers.offset(BufTy::Ex, 1),
c,
);
globals.buffers.set_offset(
BufTy::Ex,
1,
globals.buffers.offset(BufTy::Ex, 1) + 1,
);
} else if c != b' ' {
globals.buffers.set_at(
BufTy::Ex,
globals.buffers.offset(BufTy::Ex, 1),
b' ',
);
globals.buffers.set_offset(
BufTy::Ex,
1,
globals.buffers.offset(BufTy::Ex, 1) + 1,
);
}
}
}
}
}
}
if !eat_bib_white_space(ctx, globals.buffers, globals.bibs) {
return eat_bib_print(ctx, globals.buffers, globals.pool, globals.bibs, command)
.map(|_| false);
}
Ok(true)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn scan_and_store_the_field_value_and_eat_white(
ctx: &mut Bibtex<'_, '_>,
globals: &mut GlobalItems<'_>,
store_field: bool,
command: Option<BibCommand>,
cite_out: Option<&mut HashPointer<hash::LcCite>>,
cur_macro_loc: HashPointer<hash::Macro>,
right_outer_delim: ASCIICode,
field_name_loc: HashPointer<BstFn>,
) -> Result<bool, BibtexError> {
globals.buffers.set_offset(BufTy::Ex, 1, 0);
if !scan_a_field_token_and_eat_white(
ctx,
globals,
store_field,
command,
cur_macro_loc,
right_outer_delim,
)? {
return Ok(false);
}
while globals.buffers.at_offset(BufTy::Base, 2) == b'#' {
globals
.buffers
.set_offset(BufTy::Base, 2, globals.buffers.offset(BufTy::Base, 2) + 1);
if !eat_bib_white_space(ctx, globals.buffers, globals.bibs) {
return eat_bib_print(ctx, globals.buffers, globals.pool, globals.bibs, command)
.map(|_| false);
}
if !scan_a_field_token_and_eat_white(
ctx,
globals,
store_field,
command,
cur_macro_loc,
right_outer_delim,
)? {
return Ok(false);
}
}
if store_field {
if command.is_none()
&& globals.buffers.offset(BufTy::Ex, 1) > 0
&& globals
.buffers
.at(BufTy::Ex, globals.buffers.offset(BufTy::Ex, 1) - 1)
== b' '
{
globals
.buffers
.set_offset(BufTy::Ex, 1, globals.buffers.offset(BufTy::Ex, 1) - 1);
}
let ex_buf_xptr = if command.is_none()
&& globals.buffers.at(BufTy::Ex, 0) == b' '
&& globals.buffers.offset(BufTy::Ex, 1) > 0
{
1
} else {
0
};
let str =
&globals.buffers.buffer(BufTy::Ex)[ex_buf_xptr..globals.buffers.offset(BufTy::Ex, 1)];
let res = globals
.hash
.lookup_str_insert::<hash::Text>(globals.pool, str, ());
if let Some(command) = command {
match command {
BibCommand::Comment => (),
BibCommand::Preamble => globals.bibs.add_preamble(globals.hash.get(res.loc).text()),
BibCommand::String => globals
.hash
.set_extra(cur_macro_loc, globals.hash.get(res.loc).text()),
}
} else {
let &BstFn::Field(field) = globals.hash.get(field_name_loc).extra() else {
panic!("field_name_loc wasn't a BstFn::Field");
};
let field_ptr = globals.cites.entry_ptr() * globals.other.num_fields() + field;
if field_ptr >= globals.other.max_fields() {
ctx.write_logs("field_info index is out of range");
print_confusion(ctx);
return Err(BibtexError::Fatal);
}
if !globals.other.field(field_ptr).is_invalid() {
ctx.write_logs("Warning--I'm ignoring ");
print_a_pool_str(
ctx,
globals.cites.get_cite(globals.cites.entry_ptr()),
globals.pool,
)?;
ctx.write_logs("'s extra \"");
print_a_pool_str(ctx, globals.hash.get(field_name_loc).text(), globals.pool)?;
ctx.write_logs("\" field\n");
bib_warn_print(ctx, globals.pool, globals.bibs)?;
} else {
globals
.other
.set_field(field_ptr, globals.hash.get(res.loc).text());
if field == globals.other.crossref_num() && !ctx.all_entries {
let end = globals.buffers.offset(BufTy::Ex, 1);
globals.buffers.copy_within(
BufTy::Ex,
BufTy::Out,
ex_buf_xptr,
ex_buf_xptr,
end - ex_buf_xptr,
);
globals.buffers.buffer_mut(BufTy::Out)[ex_buf_xptr..end].make_ascii_lowercase();
let str = &globals.buffers.buffer(BufTy::Out)[ex_buf_xptr..end];
let lc_res = globals.hash.lookup_str_insert::<hash::LcCite>(
globals.pool,
str,
HashPointer::default(),
);
if let Some(cite_out) = cite_out {
*cite_out = lc_res.loc;
}
let &cite_loc = globals.hash.get(lc_res.loc).extra();
if lc_res.exists {
let &cite = globals.hash.get(cite_loc).extra();
if cite >= globals.cites.old_num_cites() {
globals
.cites
.set_info(cite, StrNumber::from_raw_dangerous(cite + 1));
}
} else {
let str = &globals.buffers.buffer(BufTy::Ex)
[ex_buf_xptr..globals.buffers.offset(BufTy::Ex, 1)];
let c_res =
globals
.hash
.lookup_str_insert::<hash::Cite>(globals.pool, str, 0);
if c_res.exists {
hash_cite_confusion(ctx);
return Err(BibtexError::Fatal);
}
let new_ptr = add_database_cite(
globals.cites,
globals.other,
globals.hash,
globals.cites.ptr(),
c_res.loc,
lc_res.loc,
);
let &cite = globals.hash.get(c_res.loc).extra();
globals.cites.set_ptr(new_ptr);
globals
.cites
.set_info(cite, StrNumber::from_raw_dangerous(1));
}
}
}
}
}
Ok(true)
}
pub(crate) fn decr_brace_level(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &StringPool,
cites: &CiteInfo,
pop_lit_var: StrNumber,
brace_level: &mut i32,
) -> Result<(), BibtexError> {
if *brace_level == 0 {
braces_unbalanced_complaint(ctx, pool, cites, pop_lit_var)?;
} else {
*brace_level -= 1;
}
Ok(())
}
pub(crate) fn check_brace_level(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &StringPool,
cites: &CiteInfo,
pop_lit_var: StrNumber,
brace_level: i32,
) -> Result<(), BibtexError> {
if brace_level > 0 {
braces_unbalanced_complaint(ctx, pool, cites, pop_lit_var)?;
}
Ok(())
}
pub(crate) fn name_scan_for_and(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &StringPool,
buffers: &mut GlobalBuffer,
cites: &CiteInfo,
pop_lit_var: StrNumber,
brace_level: &mut i32,
) -> Result<(), BibtexError> {
let mut preceding_white = false;
let mut and_found = false;
while !and_found && buffers.offset(BufTy::Ex, 1) < buffers.init(BufTy::Ex) {
match buffers.at_offset(BufTy::Ex, 1) {
b'A' | b'a' => {
buffers.set_offset(BufTy::Ex, 1, buffers.offset(BufTy::Ex, 1) + 1);
if preceding_white
&& buffers.offset(BufTy::Ex, 1) <= buffers.init(BufTy::Ex).saturating_sub(3)
&& buffers.at_offset(BufTy::Ex, 1).eq_ignore_ascii_case(&b'n')
&& buffers
.at(BufTy::Ex, buffers.offset(BufTy::Ex, 1) + 1)
.eq_ignore_ascii_case(&b'd')
&& LexClass::of(buffers.at(BufTy::Ex, buffers.offset(BufTy::Ex, 1) + 2))
== LexClass::Whitespace
{
buffers.set_offset(BufTy::Ex, 1, buffers.offset(BufTy::Ex, 1) + 2);
and_found = true;
}
preceding_white = false;
}
b'{' => {
*brace_level += 1;
buffers.set_offset(BufTy::Ex, 1, buffers.offset(BufTy::Ex, 1) + 1);
while *brace_level > 0 && buffers.offset(BufTy::Ex, 1) < buffers.init(BufTy::Ex) {
match buffers.at_offset(BufTy::Ex, 1) {
b'{' => *brace_level += 1,
b'}' => *brace_level -= 1,
_ => (),
}
buffers.set_offset(BufTy::Ex, 1, buffers.offset(BufTy::Ex, 1) + 1);
}
preceding_white = false;
}
b'}' => {
decr_brace_level(ctx, pool, cites, pop_lit_var, brace_level)?;
buffers.set_offset(BufTy::Ex, 1, buffers.offset(BufTy::Ex, 1) + 1);
preceding_white = false;
}
_ => {
preceding_white =
LexClass::of(buffers.at_offset(BufTy::Ex, 1)) == LexClass::Whitespace;
buffers.set_offset(BufTy::Ex, 1, buffers.offset(BufTy::Ex, 1) + 1);
}
}
}
check_brace_level(ctx, pool, cites, pop_lit_var, *brace_level)
}
pub(crate) fn von_token_found(
buffers: &GlobalBuffer,
hash: &HashData,
pool: &StringPool,
name_bf_ptr: &mut BufPointer,
name_bf_xptr: BufPointer,
) -> bool {
while *name_bf_ptr < name_bf_xptr {
let char = buffers.at(BufTy::Sv, *name_bf_ptr);
match char {
b'A'..=b'Z' => return false,
b'a'..=b'z' => return true,
b'{' => {
let mut nm_brace_level = 1;
*name_bf_ptr += 1;
if *name_bf_ptr + 2 < name_bf_xptr && buffers.at(BufTy::Sv, *name_bf_ptr) == b'\\' {
*name_bf_ptr += 1;
let name_bf_yptr = *name_bf_ptr;
while *name_bf_ptr < name_bf_xptr
&& LexClass::of(buffers.at(BufTy::Sv, *name_bf_ptr)) == LexClass::Alpha
{
*name_bf_ptr += 1;
}
let str = &buffers.buffer(BufTy::Sv)[name_bf_yptr..*name_bf_ptr];
let seq = hash
.lookup_str::<ControlSeq>(pool, str)
.map(|loc| *hash.get(loc).extra());
if let Some(seq) = seq {
return match seq {
ControlSeq::UpperOE
| ControlSeq::UpperAE
| ControlSeq::UpperAA
| ControlSeq::UpperO
| ControlSeq::UpperL => false,
ControlSeq::LowerI
| ControlSeq::LowerJ
| ControlSeq::LowerOE
| ControlSeq::LowerAE
| ControlSeq::LowerAA
| ControlSeq::LowerO
| ControlSeq::LowerL
| ControlSeq::LowerSS => true,
};
}
while *name_bf_ptr < name_bf_xptr && nm_brace_level > 0 {
let char = buffers.at(BufTy::Sv, *name_bf_ptr);
match char {
b'A'..=b'Z' => return false,
b'a'..=b'z' => return true,
b'}' => nm_brace_level -= 1,
b'{' => nm_brace_level += 1,
_ => (),
}
*name_bf_ptr += 1;
}
return false;
} else {
while nm_brace_level > 0 && *name_bf_ptr < name_bf_xptr {
let char = buffers.at(BufTy::Sv, *name_bf_ptr);
match char {
b'{' => nm_brace_level += 1,
b'}' => nm_brace_level -= 1,
_ => (),
}
*name_bf_ptr += 1;
}
}
}
_ => {
*name_bf_ptr += 1;
}
}
}
false
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn von_name_ends_and_last_name_starts_stuff(
buffers: &GlobalBuffer,
hash: &HashData,
pool: &StringPool,
last_end: BufPointer,
von_start: BufPointer,
von_end: &mut BufPointer,
name_bf_ptr: &mut BufPointer,
name_bf_xptr: &mut BufPointer,
) {
*von_end = last_end - 1;
while *von_end > von_start {
*name_bf_ptr = buffers.name_tok(*von_end - 1);
*name_bf_xptr = buffers.name_tok(*von_end);
if von_token_found(buffers, hash, pool, name_bf_ptr, *name_bf_xptr) {
return;
}
*von_end -= 1;
}
}
pub(crate) fn enough_text_chars(
buffers: &GlobalBuffer,
enough_chars: BufPointer,
buf_start: BufPointer,
brace_level: &mut i32,
) -> bool {
let mut num_text_chars = 0;
let mut buf_cur = buf_start;
while buf_cur < buffers.offset(BufTy::Ex, 1) && num_text_chars < enough_chars {
buf_cur += 1;
if buffers.at(BufTy::Ex, buf_cur - 1) == b'{' {
*brace_level += 1;
if *brace_level == 1
&& buf_cur < buffers.offset(BufTy::Ex, 1)
&& buffers.at(BufTy::Ex, buf_cur) == b'\\'
{
buf_cur += 1;
while buf_cur < buffers.offset(BufTy::Ex, 1) && *brace_level > 0 {
match buffers.at(BufTy::Ex, buf_cur) {
b'}' => *brace_level -= 1,
b'{' => *brace_level += 1,
_ => (),
}
buf_cur += 1;
}
}
} else if buffers.at(BufTy::Ex, buf_cur - 1) == b'}' {
*brace_level -= 1;
}
num_text_chars += 1;
}
num_text_chars >= enough_chars
}