Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to undefine/overwrite a label?

40 views
Skip to first unread message

Domagoj Babic

unread,
Apr 18, 2008, 3:14:50 PM4/18/08
to
Hi,

I'd need to either overwrite or undefine an already existing label.

Essentially, I'd need something like:

\uselastlabel{label_name} % (a)
...
\uselastlabel{label_name} % (b)

and \pageref should refer to (b), rather than to (a).

Any ideas how to achieve this?

Thx,
Domagoj


Robin Fairbairns

unread,
Apr 19, 2008, 6:48:43 AM4/19/08
to

\pageref is particularly tricky, since you can _only_ set it "safely"
using the label mechanism, which happens asynchronously with the text.
this means that overwriting the value of a label's pageref will
inevitably change the value seen by _any_ reference to the label.

i can imagine ways one might hack at what \ref returns, but my
instinct is that you're trying to do something that is better done
some other way. what's your actual application?
--
Robin Fairbairns, Cambridge

Ulrich M. Schwarz

unread,
Apr 19, 2008, 3:21:03 PM4/19/08
to
On Sat, 19 Apr 2008 09:52:14 -0700, Domagoj Babic wrote:
[...]
> I'm using acronym package and have a table of acronyms in my thesis. The
> table just lists the acronyms and their meaning, but my advisor also
> wants to see the page numbers at which each acronym was defined (not
> only mentioned the first time).
[...]
> 2) Other acronyms are expanded 2 times: the first time when the package
> sees it the first time, and the second time if I expand it manually
> (using \acf{acro:ACRONYM}) in the definition.

Hm, can't you just ignore the multiply-defined warning for these labels?
As I read the kernel, in this case the last definition of the source file
stands, which seems to be what you want.

Ulrich
--
'Nerd' is a word just like 'legacy' - not usually meant as a compliment
by those who use it but aren't _in_ it, but to people in the know it
means you've got all your priorities straight and can have a blast
without having to worry about being fashionable. (M. Wiltink)

Domagoj Babic

unread,
Apr 19, 2008, 9:13:44 PM4/19/08
to Ulrich M. Schwarz
Ulrich M. Schwarz wrote:
> Hm, can't you just ignore the multiply-defined warning for these labels?
> As I read the kernel, in this case the last definition of the source file
> stands, which seems to be what you want.

Theoretically, I could ignore the warnings, but that's _really_
inelegant, and might not be portable. I was hoping for a better
solution.

Cheers,
Domagoj

Robin Fairbairns

unread,
Apr 20, 2008, 5:14:47 AM4/20/08
to

you've explained to me, off-line, what you're trying to do; perhaps if
you were to mention it here you might get a quicker response.

or shall i post your mail to me? (i could add thoughts of my own...)
--
Robin Fairbairns, Cambridge

Ulrich Diez

unread,
Apr 20, 2008, 10:39:26 AM4/20/08
to
Domagoj Babic wrote:

Does the following example help you?

(People who use google-groups-web-interface are recommended
to ensure that this message is displayed in "original format" as
otherwise the web-interface tends to change line-breaks in
the code and thus to turn it into something even more useless.)

Ulrich

\documentclass{article}
% \usepackage{hyperref}

\makeatletter
\newcommand*\overridelabel[1]{%
\@bsphack
\protected@write\@auxout{}{\string\undonewlabel{#1}{\on@line}}%
\label{#1}%
\@esphack
}%
\newcommand*\undonewlabel{\@und@newl@bel r}%
\newcommand*\@und@newl@bel[3]{%
\@ifundefined{#1@#2}\relax{%
\@latex@warning@no@line{Label `#2' overridden#3}%
\expandafter\global
\expandafter\let
\csname #1@#2\endcsname\relax
\@ifundefined{s@#2}{\global\@namedef{s@#2}{i}}{%
\expandafter\g@addto@macro\csname s@#2\endcsname{i}%
}%
}%
}%
\AtEndDocument{%
\let\undonewlabel\@gobble
}%
\newcommand*\my@testdef[3]{%
\@ifundefined{s@#2}\@secondoftwo\@firstofone
{%
\expandafter\ifx\csname s@#2\endcsname\empty
\expandafter\@firstofone
\else
\expandafter\xdef\csname s@#2\endcsname{%
\expandafter\expandafter
\expandafter\@gobble
\csname s@#2\endcsname
}%
\expandafter\@gobble
\fi
}%
{%
\def\reserved@a{#3}%
\expandafter\ifx\csname #1@#2\endcsname\reserved@a
\else\@tempswatrue\fi
}%
}%
\protected@write\@auxout{}{%
\string\reset@newl@bel
}%
\newcommand*\reset@newl@bel{%
\ifx\@newl@bel\@testdef
\let\@newl@bel\my@testdef
\fi
}%
\makeatother


\begin{document}

\section{Test}

text \label{testlabel}\\
page-reference: \pageref{testlabel}\\ % -> page 4
secional-reference: \ref{testlabel}\\ % -> section 4
% name-reference: \nameref{testlabel} % -> Still another test

\newpage

\section{Another test}

text \overridelabel{testlabel}\\
page-reference: \pageref{testlabel}\\ % -> page 4
secional-reference: \ref{testlabel}\\ % -> section 4
% name-reference: \nameref{testlabel} % -> Still another test

\newpage

\section{Yet another test}

text \overridelabel{testlabel}\\
page-reference: \pageref{testlabel}\\ % -> page 4
secional-reference: \ref{testlabel}\\ % -> section 4
% name-reference: \nameref{testlabel} % -> Still another test

\newpage

\section{Still another test}

text \overridelabel{testlabel}\\
page-reference: \pageref{testlabel}\\ % -> page 4
secional-reference: \ref{testlabel}\\ % -> section 4
% name-reference: \nameref{testlabel} % -> Still another test

\end{document}

Ulrich Diez

unread,
Apr 20, 2008, 11:40:57 AM4/20/08
to
Domagoj Babic wrote:

Does the following example help you?

Domagoj Babic

unread,
Apr 20, 2008, 2:55:03 PM4/20/08
to Robin Fairbairns
Robin,

Robin Fairbairns wrote:
> you've explained to me, off-line, what you're trying to do; perhaps if
> you were to mention it here you might get a quicker response.
>
> or shall i post your mail to me? (i could add thoughts of my own...)

Strange, I CCed the msg to comp.text.tex as well, and I see it... Yes,
feel free to repost it with your comments. Thx for asking.

Cheers,
Domagoj

Domagoj Babic

unread,
Apr 20, 2008, 3:45:58 PM4/20/08
to Ulrich Diez
Dear Ulrich,

Your solution essentially works, but I don't understand your code. I've
never done any (serious) LaTeX programming, although I've been
using LaTeX for years. Would you mind explaining how it works?

Thanks a bunch!

Cheers,
Domagoj

Ulrich Diez

unread,
Apr 20, 2008, 6:31:03 PM4/20/08
to
Domagoj Babic wrote:

> Your solution essentially works, but I don't understand your code.

"Essentially" - That's a nice one.
Below I corrected a bug from my previous posting - an \@gobble
where you actually need an \@gobbletwo - and introduced
minor (cosmetic) changes.

> I've
> never done any (serious) LaTeX programming, although I've been
> using LaTeX for years. Would you mind explaining how it works?

You need to know about the internals of the \label-reference-
mechanism:

When an instance of the \label-macro occurs in the document
( e.g., \label{foo} ), then a delayed \write (a write which is
performed at shipout-time when the page-number is determined) to
the aux-file is issued and you find in the aux-file something
like:

\newlabel{foo}{{<page-no>}{<sectional no>}{<probably something else>}}

During the beginning of the next LaTeX-run, the aux-file will be
read and

\newlabel{foo}{{<page-no>}{<sectional no>}{<probably something else>}}

is expanded to

\@newl@bel r{foo}{{<page-no>}{<sectional no>}{<probably something else>}}

which in turn gets expanded to something like (pseude-code):

IF (macro \r@foo is already defined)
THEN
- Issue an error-message,
- Make sure that the multiply-defined-labels-warning occurs
in the log-file
ELSE
\def\r@foo{{<page-no>}{<sectional no>}{<probably something else>}}
ENDIF

-----------------------------------------------------------------

Referencing works as follows:

\ref{foo}
->
- expand \r@foo:
{<page-no>}{<sectional no>}{<probably something else>}
- grab the second from the resulting arguments:
<sectional no>

\pageref{foo}
->
- expand \r@foo:
{<page-no>}{<sectional no>}{<probably something else>}
- grab the first from the resulting arguments:
<page-no>

-----------------------------------------------------------------

Back to the \label-mechanism:

- At the beginning of the LaTeX-run, all the \r@<label>-macros
get defined from reading the aux-file. The \r@<label>-macros
get used by the referencing-macros (\ref, \pageref...) during
the LaTeX-run.

- During the LaTeX-run, the aux-file gets rewritten.

- At the end of the LaTeX-run, the aux-file (which was rewritten/newly
created during the LaTeX-run) is read in order to detect whether
references have changed during the current LaTeX-run.

But this time \@newl@bel is redefined (\let equal to \@testdef)
and thus this time

\@newl@bel r{foo}{{<page-no>}{<sectional no>}{<probably something else>}}

expands to something like (pseude-code):

Compare the (newly written) third argument (that is:
{{<page-no>}{<sectional no>}{<probably something else>}} )
to the (current/former) definition of \r@foo.
If the two are different, then some page- or section-number
related to referencing has changed from the last to the current
LaTeX-run, thus in this case issue a message in the log-file:
"References may have changed. Rerun LaTeX in order to get
cross-references right".

So what do you need to do in order to override a label:

- First you need to write to the aux-file to silently undefine
the associated \r@<label>-macro if it is already defined.
That's why \overridelabel writes in terms of \protected@write
to the aux file:

\undonewlabel{<label>}{<code-line-number>}

\undonewlabel "undefines" the \r@<label>-macro.
(How this works will be explained below.)

- Then \overridelabel can call \label{<label>} again and thus
produce another \newlabel{<label>}-entry to the aux-file.

In the aux-file all this results in a sequence like:

% from the former \label-call:
\newlabel{<label>}{..}
-> \r@<label>-macro gets produced.
% from the \overrridelabel-call:
% - call to \undonewlabel within \overrridelabel:
\undonewlabel{<label>}{<code-line-number>}
-> \r@<label>-macro gets destroyed.
% - call to \label within \overrridelabel:
\newlabel{<label>}{..}
-> a new \r@<label>-macro and no multiply-label-defined warning
gets produced.

There is another issue left:

- It was said that aux-file is read at the beginning and at the
end of the LaTeX-run for detecting whether references have
changed.

- When overriding a <label>, there will be several
\newlabel{<label>}-calls associated to the same label-name in
the aux-file.

- At the beginning of the LaTeX-run only the last one counts for
defining the associated \r@<label>-macro.

- But at the end, when the new aux-file is read, they all count
and thus with all these entries but the last one, the above-
mentioned \@testdef-comparison will yield difference and thus
in any case cause a warning-message about references having
changed although that might not be a correct statement.

I decided to catch this up by letting \undonewlabel "count"
within the macro \s@<label> how many times the label was
overridden. That means: Whenever \r@<label> is overridden, the
definition of \s@<label> gets a character "i" attached.

The \@testdef-comparison-mechanism gets enhanced via replacing[1]
it by \my@testdef: Before actually executing \@testdef, check
whether \s@<label> is defined and empty. If undefined or empty,
perform \@testdef. If not empty, remove an "i" from its
definition-text.
This way it is ensured that only the last one of several
\newlabel{<label>}-entries is used for the \@testdef-comparison.


\undonewlabel{<label>}{<code-line-number>}
->
\@und@newl@bel rs{<label>}{<code-line-number>}
->
% IF \r@<label> is undefined just \relax, otherwise:
\@ifundefined{r@<label>}\relax{%
% Inform via log-file about overriding the label:
\@latex@warning@no@line{Label `<label>' overridden<code-line-number>}%
% Undefine \r@label via letting it equal to \relax:
\expandafter\global
\expandafter\let
\csname r@<label>\endcsname\relax
% IF \s@label is undefined, define it to {i} ELSE
% add another i to it via \g@addto@macro ENDIF:
\@ifundefined{s@<label>}{\global\@namedef{s@<label>}{i}}{%
\expandafter\g@addto@macro\csname s@<label>\endcsname{i}%
}%
}%


[1] "replacing" works as follows: When aux-file is read at the
beginning of the LaTeX-run, \@newl@bel is _not_ let equal to
\@testdef. When aux-file is read at the end of the LaTeX-run,
\@newl@bel _is_ let equal to \@testdef. Thus it is sufficient
to write into the beginning of the aux-file a direction which
leads to letting \@newl@bel equal to \my@testdef in case its
definition equals \@testdef. That direction is called
"\reset@newl@bel".
Also, when the aux-file is read at the end of the LaTeX-run,
\undonwelabel-entries therein should do nothing, thus
\undonewlabel is let equal to \@gobbletwo.

Please avoid replying by e-mail and reply only to the newsgroup
instead.

Ulrich


\documentclass{article}
% \usepackage{hyperref}

\makeatletter
\newcommand*\overridelabel[1]{%
\@bsphack
\protected@write\@auxout{}{\string\undonewlabel{#1}{\on@line}}%
\label{#1}%
\@esphack
}%

\newcommand*\undonewlabel{\@und@newl@bel rs}%
\newcommand*\@und@newl@bel[4]{%
\@ifundefined{#1@#3}\relax{%
\@latex@warning@no@line{Label `#3' overridden#4}%
\expandafter\global
\expandafter\let
\csname #1@#3\endcsname\relax
\@ifundefined{#2@#3}{\global\@namedef{#2@#3}{i}}{%
\expandafter\g@addto@macro\csname #2@#3\endcsname{i}%
}%
}%
}%


\newcommand*\my@testdef[3]{%
\@ifundefined{s@#2}\@secondoftwo\@firstofone
{%
\expandafter\ifx\csname s@#2\endcsname\empty
\expandafter\@firstofone
\else
\expandafter\xdef\csname s@#2\endcsname{%
\expandafter\expandafter
\expandafter\@gobble
\csname s@#2\endcsname
}%
\expandafter\@gobble
\fi
}%
{%

\@testdef{#1}{#2}{#3}%


}%
}%
\protected@write\@auxout{}{%
\string\reset@newl@bel
}%
\newcommand*\reset@newl@bel{%
\ifx\@newl@bel\@testdef
\let\@newl@bel\my@testdef

\let\undonewlabel\@gobbletwo
\fi
}%
\makeatother

\begin{document}

\section{Test}

text \label{testlabel}\\
page-reference: \pageref{testlabel}\\ % -> page 4

sectional-reference: \ref{testlabel}% -> section 4


%\\name-reference: \nameref{testlabel}% -> Still another test

\newpage

\section{Another test}

text \overridelabel{testlabel}\\
page-reference: \pageref{testlabel}\\ % -> page 4

sectional-reference: \ref{testlabel}% -> section 4


%\\name-reference: \nameref{testlabel}% -> Still another test

\newpage

\section{Yet another test}

text \overridelabel{testlabel}\\
page-reference: \pageref{testlabel}\\ % -> page 4

sectional-reference: \ref{testlabel}% -> section 4


%\\name-reference: \nameref{testlabel}% -> Still another test

\newpage

\section{Still another test}

text \overridelabel{testlabel}\\
page-reference: \pageref{testlabel}\\ % -> page 4

sectional-reference: \ref{testlabel}% -> section 4

Domagoj Babic

unread,
Apr 20, 2008, 8:14:59 PM4/20/08
to
Dear Ulrich,

This is a really slick solution! Thx for the code and the
explanation.

Your code might end up in the acronym package, and I will
pass your contacts to Tobias (the package maintainer), so
that you get properly credited for your work.

Thanks!

Best,
Domagoj

Domagoj Babic

unread,
Apr 20, 2008, 8:16:16 PM4/20/08
to

Ulrich Diez

unread,
Apr 21, 2008, 9:25:44 AM4/21/08
to
Domagoj Babic wrote:

> Your code might end up in the acronym package,

1. I did not check how my code might interact with the
\include/\includeonly-mechanism.

2. I did not check how my code might interact with
packages like zref.

3. In case of multi-file-projects it is not sufficient to use
\on@line in order to create the log-file-message about
overriding. The name of the current input-file should
also be passed but that name is -afaik - not available in
TeX.

In the example below, the 3rd section/3rd overriding
of the label is sourced out into the external file
"labeloverrideb.tex". In the log-file you get:

LaTeX Warning: Label `testlabel' overridden on input line 74.

LaTeX Warning: Label `testlabel' overridden on input line 3.

LaTeX Warning: Label `testlabel' overridden on input line 87.

Actually the second of these entries does not refer to the
main-tex-file (whose name could be deduced from \jobname)
but to "labeloverrideb.tex". At the moment I don't have an idea
for resolving this.

Ulrich

\documentclass{article}
% \usepackage{hyperref}

\begin{filecontents*}{labeloverrideb.tex}
\section{Yet another test}

text \overridelabel{testlabel}\\
page-reference: \pageref{testlabel}\\ % -> page 4
sectional-reference: \ref{testlabel}% -> section 4
%\\name-reference: \nameref{testlabel}% -> Still another test

\end{filecontents*}

\begin{document}

\section{Test}

text \overridelabel{testlabel}\\


page-reference: \pageref{testlabel}\\ % -> page 4
sectional-reference: \ref{testlabel}% -> section 4
%\\name-reference: \nameref{testlabel}% -> Still another test

\newpage

\section{Another test}

text \overridelabel{testlabel}\\
page-reference: \pageref{testlabel}\\ % -> page 4
sectional-reference: \ref{testlabel}% -> section 4
%\\name-reference: \nameref{testlabel}% -> Still another test

\newpage

\input{labeloverrideb.tex}

Ulrich Diez

unread,
Apr 21, 2008, 5:04:03 PM4/21/08
to
I wrote:

> 3. In case of multi-file-projects it is not sufficient to use
> \on@line in order to create the log-file-message

[...]


> In the example below, the 3rd section/3rd overriding
> of the label is sourced out into the external file
> "labeloverrideb.tex". In the log-file you get:
>
> LaTeX Warning: Label `testlabel' overridden on input line 74.
>
> LaTeX Warning: Label `testlabel' overridden on input line 3.
>
> LaTeX Warning: Label `testlabel' overridden on input line 87.
>
> Actually the second of these entries does not refer to the
> main-tex-file (whose name could be deduced from \jobname)
> but to "labeloverrideb.tex". At the moment I don't have an idea
> for resolving this.

The moment just passed by:

Instead of putting the warning into \undonewlabel/\@und@newl@bel,
which is expanded/executed when reading the aux-fle for the first time, put
the warning into \overridelabel which gets expanded/executed
when reading the input-file in question. Sorry for not seeing this
earlier.

Ulrich

\documentclass{article}
% \usepackage{hyperref}

\begin{filecontents*}{labeloverrideb.tex}
\section{Yet another test}

text \overridelabel{testlabel}\\
page-reference: \pageref{testlabel}\\ % -> page 4
sectional-reference: \ref{testlabel}% -> section 4
%\\name-reference: \nameref{testlabel}% -> Still another test
\end{filecontents*}

\makeatletter
\newcommand*\overridelabel[1]{%
\@bsphack

\protected@write\@auxout{}{\string\undonewlabel{#1}}%
\@overriddenmessage s{#1}%


\label{#1}%
\@esphack
}%

\newcommand*\undonewlabel{\@und@newl@bel r}%
\newcommand*\@und@newl@bel[2]{%
\@ifundefined{#1@#2}{%
\@latex@error{Label `#2' shall be overridden although it does
not yet^^Jexist}{A label which does not exist cannot be
overridden.}%
}{%
\expandafter\global
\expandafter\let
\csname #1@#2\endcsname\relax
}%
}%
\newcommand*\@overriddenmessage[2]{%
\@ifundefined{#1@#2}{\global\@namedef{#1@#2}{i}}{%
\expandafter\g@addto@macro\csname #1@#2\endcsname{i}%
}%
\@latex@warning{Label `#2' overridden}%

\fi
}%
\makeatother

\begin{document}

\section{Test}

text \label{testlabel}\\


page-reference: \pageref{testlabel}\\ % -> page 4
sectional-reference: \ref{testlabel}% -> section 4
%\\name-reference: \nameref{testlabel}% -> Still another test

\newpage

\section{Another test}

text \overridelabel{testlabel}\\
page-reference: \pageref{testlabel}\\ % -> page 4
sectional-reference: \ref{testlabel}% -> section 4
%\\name-reference: \nameref{testlabel}% -> Still another test

\newpage

\input{labeloverrideb.tex}

\newpage%~\newpage

0 new messages