# Automatic deletion of unused macros

The following scenario occurs quite often to me: I have a couple of input files where I have my own macros, mainly math-related stuff like definitions for \esssup and so on which are not available in the usual ams-math files. When writing a paper, I input those collection and use the stuff I like. At the end, I cut-copy-paste things into one file, as publishers usually don't get along with multiple LaTeX files etc., Ugly, but prevents many errors and trouble (bad experiences...)

Now the question: is it possible (surely yes) to extract only those macros which are actually used by some clever automatic process (shell script, LaTeX itself, ..), so that I can include only those macros and not the whole collection of them. Again, the reason is to minimize the possible collisions of my own definitions and pre-defined stuff from the publishers.

Ok, so this is a first attempt. It has some problems with commands with arguments which can be circumvented, although not provided here. The below will work for most cases.

Gurus, I encourage you to outline where it could be improved if you feel!

Ok, so basically you want to save every time you call the command. This lead me to require that you need to call a \savecommand when the command is first encountered. This can be achieved several ways. I have chosen to make you require it to be in the declaration of the command. It could also be added afterwards.

This means something like the following on all commands you define:

\newcommand\bfA{\savecommand\bfA%
\mathbf{A}%
}


Then you need to define your \savecommand. One way to make this very easy for you is to save the command definition in a file. This means something like this:

\makeatletter
% Creta fID for writing the commands
\newwrite\command@file
% Save the commands in mycommands.tex (change to your liking)
\immediate\openout\command@file=mycommands.tex
% Define the actual saving command:
\def\savecommand#1{%
% This defines \tmp@ to the contents of the command by gobbling:
%   \savecommand#1
\edef\tmp@{\expandafter\expandafter\expandafter\unexpanded\expandafter\expandafter\expandafter{\expandafter\@gobbletwo#1}}
% Check whether the command has been written
\expandafter\ifx\csname command@write\string#1\endcsname\relax\relax%
% Define variable which ensures that the command does not get written upon subsequent calls.
\expandafter\gdef\csname command@write\string#1\endcsname{Written}%
% Write the definition to the file, by the use of detokenize and zap space
\immediate\write\command@file{\string\def\string#1\string{\expandafter\expandafter\expandafter\zap@space\expandafter\detokenize\expandafter{\tmp@} \@empty\string}}%
\fi%
}
\makeatother


Ok, so this will actually save the command in the file mycommands.tex. As well as check whether it is already written.
There is one problem in the above. And that is the use of \detokenize (which is required if multiple macros are contained in the macro). It inserts a space after each macro. Thus

\newcommand\bfA{\mathbf{A}}
\expandafter\detokenize\expandafter{\bfA} == \mathbf {A}


Thus zap space is inserted which removes all space up until \@empty. This means that if you define commands with spaces you have to use the macro \space instead.

Ok, so a minimal full example:

\documentclass{article}
\usepackage{amsmath}
\begin{document}
\makeatletter
\newwrite\command@file
\immediate\openout\command@file=mycommands.tex
\def\savecommand#1{%
\edef\tmp@{\expandafter\expandafter\expandafter\unexpanded\expandafter\expandafter\expandafter{\expandafter\@gobbletwo#1}}
\expandafter\ifx\csname command@write\string#1\endcsname\relax\relax%
\expandafter\gdef\csname command@write\string#1\endcsname{Written}%
\immediate\write\command@file{\string\newcommand\string#1\string{\expandafter\expandafter\expandafter\zap@space\expandafter\detokenize\expandafter{\tmp@} \@empty\string}}%
\fi%
}
\makeatother
\newcommand\bfE{\savecommand\bfE%
\mathbf{E}%
}
\newcommand\bfA{\savecommand\bfA%
\mathbf{A}
}
\newcommand\with{\savecommand\with%
\mathbf{A}\text{\space}\mathbf{B}%
}

Use bfE:
$\bfE$

Use bfE again:
$\bfE$

Test with space:
$\with$

\makeatletter
% Close the file
\immediate\closeout\command@file
\makeatother
\end{document}


This will create an output file mycommands.tex with the contents:

\newcommand\bfE{\mathbf{E}}
\newcommand\with{\mathbf{A}\text{\space}\mathbf{B}}


It should be noted that this does not work for commands with arguments. I am not too sure how to do this without adding loads of code. But this is at least a starting ground for the majority of single contained commands.
Note that \bfA is not added to the file as it was never called, neither is the command \bfE defined twice as it was caught.

Edit

If you wish to do the commands by other means you can do like the following:

\newcommand#1{\savecommand#1#2}%
}

% Or fully independent on the \savecommand:
\expandafter\gdef\csname command@write\string#1\endcsname{%
\immediate\write\command@file{%
\string\newcommand\string#1\string{\expandafter\expandafter\expandafter\zap@space\expandafter\detokenize\expandafter{#2} \@empty\string}%
}%
\expandafter\gdef\csname command@write\string#1\endcsname{\relax}%
}%
\newcommand#1{\csname command@write\string#1\endcsname#2}%
}


This will let you do:

\mynewcommand\bfE{\mathbf{E}}


and do exactly the same. Still cannot handle several arguments. :(

• thank a lot. That looks very promising indeed. I will have to think about the commands with arguments, though. Apr 17 '12 at 13:30
• @StefanWaldmann yes, they are not too trivially handled... Apr 17 '12 at 13:37

I wrote this simple C++ program to remove all unused macros in a .tex file. To use it, first put all the macros definitions in a file a.txt, then put the whole body of your document (starting from \begin{document}) in b.txt. Now run the program. It should produce c.txt with only those macros that were used in the body.

//C++11

#include <iostream>
#include <stdlib.h>
#include <fstream>
#include <vector>
using namespace std;

string trim_command(string s){
int a = s.find("\\",1);
int b = s.find("}");
int c = s.find("[");
if ((c < b) && (0 <= c)) b = c;

if((a < s.length()) && (b < s.length()) && (a < b))
return s.substr(a, b-a);
else return "";
}

string trim_def(string s){
int a = s.find("\\def",0);
int b = s.find("\{");

if((a < s.length()) && (b < s.length()) && (a < b))
return s.substr(a+4, b-a-4);
else return "";
}

int main() {
vector<string> v1,v2;
string s;

ifstream fin;
fin.open("a.txt");

while(getline(fin,s)){
string key = trim_def(s);
if (key != "")    {
v1.push_back(s);
v2.push_back(key);
}
else{
key = trim_command(s);
if (key!=""){
v1.push_back(s);
v2.push_back(key);
}
}
}

fin.close();

fin.open("b.txt");

string t = "";

while(getline(fin,s)){
t += s;
}

fin.close();

ofstream out;
out.open("c.txt");

int l = t.length();

for(int i=0; i<v2.size(); ++i)
{
int pos = t.find(v2[i]);

if((0 <= pos) && (pos< l))
out << v1[i] << endl;

}

out.close();

return 0;
}