1

I have a rather limited understanding of bst's postfix syntax, and am hoping that someone with more knowledge of the system could kindly point out the source of a problem I am experiencing from a .bst file provided by a journal. Use of this particular bibliographic style file results in failure to print page ranges for most @article entries in my .bib file. I am using pdflatex and bibtex.

The relevant snippet from the .bst file for articles and the formatting of their volume/page numbers is as follows:

FUNCTION {field.or.null}
{ duplicate$ empty$
    { pop$ "" } 
    'skip$
  if$  
}

FUNCTION {bolden}
{ duplicate$ empty$
    { pop$ "" } 
    { "{\bf " swap$ * "}" * }
  if$  
}

FUNCTION {bbl.pages}
{ "pp." }

FUNCTION {format.pages}
{ pages empty$
    { "" }
    { pages multi.page.check
        { bbl.pages pages n.dashify tie.or.space.connect }
        { bbl.page pages tie.or.space.connect }
      if$
    }
  if$
}
FUNCTION {first.page}
{ 't :=
  ""
    {  t empty$ not t #1 #1 substring$ "-" = not and }
    { t #1 #1 substring$ *
      t #2 global.max$ substring$ 't :=
    }
  while$
}

FUNCTION {format.journal.pages}
{ pages empty$
    'skip$
    { duplicate$ empty$
        { pop$ format.pages }
        {
          ", " *
          pages first.page *
        }
      if$
    }
  if$
}

FUNCTION {format.vol.num.pages}
{ volume field.or.null
  bolden
  format.journal.pages
}

FUNCTION {article}
{ output.bibitem
  format.authors "author" output.check
  crossref missing$
    { journal
      emphasize
      "journal" output.check
      add.blank
      format.vol.num.pages output
      format.date "year" output.check
    }
    { format.article.crossref output.nonnull
      format.pages output
    }
  if$
  new.sentence
  format.note output
  fin.entry
}

As can be seen, the article function calls format.vol.num.pages, which is tasked with inserting the volume and number of pages. After adding and emboldening the volume, format.journal.pages is called. In this function, the pages field from the @article bibliographic entry gets added to the stack. Provided that it is non-empty, then it is duplicated and another check for emptiness is made. I do not understand the purpose of duplicating and checking emptiness after having already established non-emptiness. If it is empty after duplication, then the page range is printed (this is the path I want); otherwise, only the first page is printed, which is the route that almost always seems to be taken. If I use { pop$ format.pages } for both true/false routes of the if statement, then the full page range is printed, but in this case the volume is dropped (perhaps because it was popped prematurely off the stack).

I have also removed the "pp" added by bbl.pages by rewriting is as follows:

FUNCTION {bbl.pages}
{ "" }

However, I am wondering if there is a cleaner way to remove the "pp". Deleting bbl.pages from format.pages again upsets the stack.


Here is an MWE together with its output:

\documentclass{article}
\usepackage{scicite}

\begin{document}

According to the theory of special relativity~\cite{einstein1905}, \ldots

\bibliography{a}
\bibliographystyle{Science}

\end{document}

Bib file (a.bib):

@article{einstein1905,
  title={Zur elektrodynamik bewegter k{\"o}rper},
  author={Einstein, Albert},
  journal={Annalen der physik},
  volume={322},
  number={10},
  pages={891--921},
  year={1905},
  publisher={Wiley Online Library}
}

enter image description here

The full bst file can be accessed here.

The scicite style file can be accessed here.

  • Would it be possible to link to the full .bst file (and add a short example document with a relevant citation). I always need to actually see the .bst working to understand what is going on. – moewe Apr 4 at 11:49
  • 1
    For removing the "pp." your method seems the quickest and requires the fewest changes to the code. If I'm guessing correctly (and I have to guess, since I can't see all definitions) an alternative would be to replace the entire pages multi.page.check { bbl.pages pages n.dashify tie.or.space.connect } { bbl.page pages tie.or.space.connect } if$ block with just pages n.dashify. – moewe Apr 4 at 11:53
  • @moewe: Hi moewe. Thanks a lot for your help. I've added a URL for the bst file to the question and will prepare a short example forthwith. – user001 Apr 4 at 11:57
1

Easy things first. Yes, setting

FUNCTION {bbl.pages}
{ "" }

and

FUNCTION {bbl.page}
{ "" }

is probably the cheapest way to get rid of the "p."/"pp." in the bibliography.

It might be slightly nicer, though, to just replace the entire function format.pages with the simpler.

FUNCTION {format.pages}
{ pages empty$
    { "" }
    { pages n.dashify }
  if$
}

Note that both approaches will remove the "p."/"pp." for all entry types and not just for @articles. This may or may not be desired.


Now to your question about format.journal.pages. The function is called in format.vol.num.pages. At that point the previous functions have already pushed quite a few things onto the stack. In particular the stack will contain

  • the formatted volume: {\bf 322} and
  • the formatted journal (with a trailing space) {\it Annalen der Physik/}

In comes format.journal.pages and tests for emptiness of the pages field. Since nothing exciting happens if the field is empty, let us assume it is non-empty. If the field is non-empty the function calls duplicate$, which pops the top literate from the stack ({\bf 322}) and pushed two copies of it. The stack now contains

  • a copy of the formatted volume {\bf 332}
  • the formatted volume: {\bf 322} and
  • the formatted journal (with a trailing space) {\it Annalen der Physik/}

empty$ now pops the top literal (the copy of the volume) and checks it for emptiness. Once we are in the if$ the stack contains the two items from before.

  • the formatted volume: {\bf 322} and
  • the formatted journal (with a trailing space) {\it Annalen der Physik/}

If the popped and tested literal was empty (which means there was no volume field) the function pop$s the next literal from the stack (which is also empty) and proceeds to format the pages field as normal. If the tested literal was not empty, the function adds , to the end of the first literal on the stack (which is the original of the copy we just tested for emptiness and contains the volume number), formats the pages field with first.page and then concatenates the resulting two literals on the stack (so that you get the <formatted volume>, <formatted pages>).

The duplicate$ empty$ thus branches on whether or not there was a volume field in the entry: Try it out, you'll find that the full page range is printed if there is no volume, but that only the first page is printed if there is a volume. It does not test the pages field.

If you want to see the full page range in all cases, you could try

FUNCTION {format.journal.pages}
{ pages empty$
    'skip$
    { duplicate$ empty$
        { pop$ format.pages }
        {
          ", " *
          format.pages *
        }
      if$
    }
  if$
}

It does not look much simpler than the previous code, but we now call format.pages in both branches. The structure of the file makes it quite tricky to simplify/unify this if$ further, though.

  • That's a really fine explanation. I appreciate you walking through the stack mechanics. It has given me a much better grasp of what is occurring. Also your solution for adding a page range regardless of the presence of a volume worked perfectly. PS: What does the asterisk (*) signify in the format.journal.pages function? – user001 Apr 4 at 12:40
  • 1
    @user001 Sorry, I only just saw your PS. The * pops the top two literals from the stack, concatenates the two and then pushes the result back onto the stack. So ", " * comes down to: "Add , to the end of the last string on the stack." Similarly format.pages * adds the formatted pages to the end of the last literal on the stack. – moewe Apr 4 at 12:51
  • No problem, and thanks again for the tremendously helpful explanations. – user001 Apr 4 at 12:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.