/*
Copyright (C) 2010-2012 Ryan Kavanagh <
[email protected]>
Copyright (C) 1996–2002 Maurizio Loreti:
*------------------------------------------------------*
| Author: Maurizio Loreti, aka MLO or (HAM) I3NOO |
| Work: University of Padova - Department of Physics |
| Via F. Marzolo, 8 - 35131 PADOVA - Italy |
| Phone: ++39(49) 827-7216 FAX: ++39(49) 827-7102 |
| EMail:
[email protected] |
| WWW:
http://wwwcdf.pd.infn.it/~loreti/mlo.html |
*------------------------------------------------------*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
The lintex website now resides at:
http://github.com/ryanakca/lintex
Description: the command "lintex [-i] [-r] [dir1 [dir2 ...]]" scans
the directories given as parameters (the default is the current
directory), looking for TeX-related files no more needed and to be
removed. With the option -i the user is asked before actually
removing any file; with the option -r, the given directories are
scanned recursively.
Environment: the program has been developed under Solaris 2; but
should run on every system supporting opendir/readdir/closedir and
stat. The file names in the struct dirent (defined in <dirent.h>)
are assumed to be null terminated (this is guaranteed under Solaris
2).
History:
1.00 - 1996-07-05 , first release
1.01 - 1996-07-25 , improved (modify time in nodes)
1.02 - 1996-10-16 , list garbage files w/out .tex
1.03 - 1997-05-21 , call to basename; uploaded to CTAN, where lives
in /tex-archive/support/lintex . Added a man
page and a Makefile.
1.04 - 1998-06-22 , multiple directories in the command line; -r
command option; -I and -R accepted, in addition
to -i and -r; more extensions in protoTree; code
cleanup.
1.05 - 2001-12-02 , linked list structure optimized.
1.06 - 2002-09-25 , added .pdf extension.
1.07 - 2010-08-11 , don't delete read only files; delete .bbl BibTeX
files; add -k to keep final product; added
extensions for files generated by Beamer class
1.08 - 2010-10-01 , Add support for different verbosity levels, be
relatively quiet by default; dropped support for
the DEBUG and FULLDEBUG compiler flags. It's all
taken care of with command line options now;
added a -p (pretend) flag that shows what we would
have removed, but doesn't do anything.
1.09 - 2010-11-30 , Add support for removing files older than their
source; remove duplicate entry in usage; update
usage to not be wider than 72 characters.
1.10 - 2011-01-30 , Also remove .thm (generated by ntheorem), .out
(generated by hyperref), .toc.old (memoir?); list
removal extensions in manpage.
1.11 - 2011-11-07 , Also remove .synctex.gz files.
1.12 - 2012-08-30 , Also remove xypic's .xyc files.
1.13 - 2012-09-11 , Add support for configuration file; updated
documentation.
---------------------------------------------------------------------*/
/**
| Included files
**/
#include <stdio.h> /* Standard library */
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h> /* Unix proper */
#include <sys/stat.h>
#include <dirent.h>
#include <libconfig.h> /* Configuration file support */
/**
| Definitions:
| - LONG_ENOUGH: length of the buffer used to read the answer from the
| user, when the -i command option is specified;
| - MAX_B_EXT: maximum length of the extension for backup files (including
| the leading dot and the trailing '\0').
| - TRUE, FALSE: guess what?
| - VERSION: lintex version
| - QUIET, WHISPER, VERBOSE and DEBUG:
| * QUIET: no output
| * WHISPER: only print actions that are taken (ie. list files that are
| removed, default starting as of 1.08)
| * VERBOSE: list files on which actions are taken as well as those which
| aren't (default up to / including 1.07)
| * DEDUG: print debug information (originally FULLDEBUG compiler flag)
| means print everything you can for those who want to debug.
| Errors will be sent to stderr regardless of the output level.
**/
#define LONG_ENOUGH 48
#define MAX_B_EXT 8
#define TRUE 1
#define FALSE 0
#define VERSION "1.13 (2012-09-11)"
#define QUIET 0
#define WHISPER 1
#define VERBOSE 2
#define DEBUG 3
/**
| Type definitions:
| - Froot: the root of a linked list structure, where file names having a
| given extension (pointed to by Froot.extension) will be stored;
| these linked lists are also used to store directory names (with fake
| extension strings).
| - Fnode: an entry in the linked list of the file names; contains the
| file modification time, the file name and a pointer to the next node.
| As a side note, the so called 'struct hack', here used to store the
| file name, is not guaranteed to work by the current C ANSI standard;
| but no environment/compiler where it does not work is currently
| known.
**/
typedef struct sFroot {
char *extension;
struct sFnode *firstNode;
struct sFnode *lastNode;
} Froot;
typedef struct sFnode {
time_t mTime;
struct sFnode *next;
int write;
char name[1];
} Fnode;
/**
| Global variables:
| - confirm: will be 0 or 1 according to the -i command option;
| - recurse: will be 0 or 1 according to the -r command option;
| - keep: will be 0 or 1 according to the -k command option;
| - output_level: See the definitions above for more details;
| - pretend: will be 0 or 1 according to -p command option;
| - older: will be 0 or 1 according to -o command option;
| - bExt: the extension for backup files: defaults to "~" (the emacs
| convention);
| - n_bExt: the length of the previous string;
| - programName: the name of the executable;
| - protoTree: Froot's of the file names having extensions relevant to
| TeX. ".tex" extensions are assumed to be pointed to by protoTree[0].
| - keep_exts: Array containing extensions to keep.
| - protoTreeSize, keep_exts_size: number of elements in protoTree and
| keep_exts.
**/
Froot *protoTree;
char **keep_exts;
int protoTreeSize;
static int confirm = FALSE;
static int recurse = FALSE;
static int keep = FALSE;
static int output_level = WHISPER;
static int pretend = FALSE;
static int older = FALSE;
static char bExt[MAX_B_EXT] = "~";
static size_t n_bExt;
static char *programName;
char *remove_exts[] = {
".tex", /* Must be first */
".aux",
".bbl",
".blg",
".dvi",
".idx",
".ilg",
".ind",
".lof",
".log",
".lot",
".nav",
".out",
".pdf",
".ps",
".snm",
".synctex.gz",
".thm",
".toc",
".toc.old",
".xyc"
};
char *keep_exts_defaults[] = {
".pdf",
".ps",
".dvi"
};
int keep_exts_size = 3;
/**
| Procedure prototypes (in alphabetical order)
**/
static char *baseName(char *);
static Froot *buildTree(char *, Froot *);
static void clean(char *);
static void examineTree(Froot *, char *);
static void insertNode(char *, size_t, time_t, int, Froot *);
static void noMemory(void);
static void nuke(char *);
static void putsMessage(char *, int);
static void printTree(Froot *);
static void releaseTree(Froot *);
static void setupTrees(void);
static void syntax(void);
/*---------------------------*
| And now, our main program |
*---------------------------*/
int main(
int argc,
char *argv[]
){
Froot *dirNames; /* To hold the directories to be scanned */
Fnode *pFN; /* Running pointer over directory names */
int to_bExt = FALSE; /* Flag "next parameter to bExt" */
/**
| Scans the arguments appropriately; the required directories are stored
| in the linked list starting at "dirNames".
**/
programName = baseName(argv[0]);
if ((dirNames = calloc(2, sizeof(Froot))) == 0) {
noMemory();
}
dirNames->extension = "argv";
while (--argc) {
if ((*++argv)[0] == '-') {
switch ( (*argv)[1] ) {
case 'i': case 'I':
confirm = TRUE;
break;
case 'r': case 'R':
recurse = TRUE;
break;
case 'k': case 'K':
keep = TRUE;
break;
case 'b': case 'B':
to_bExt = TRUE;
break;
case 'q': case 'Q':
output_level = QUIET;
break;
case 'v': case 'V':
output_level = VERBOSE;
break;
case 'd': case 'D':
output_level = DEBUG;
break;
case 'p': case 'P':
pretend = TRUE;
break;
case 'o': case 'O':
older = TRUE;
break;
default:
syntax();
}
} else {
if (to_bExt) {
strcpy(bExt, *argv);
to_bExt = FALSE;
} else {
insertNode(*argv, 0, 0, 0, dirNames);
}
}
}
if (to_bExt) {
syntax();
}
n_bExt = strlen(bExt);
setupTrees();
/**
| If no parameter has been given, clean the current directory
**/
if ((pFN = dirNames->firstNode) == 0) {
clean(".");
} else {
while (pFN != 0) {
clean(pFN->name);
pFN = pFN->next;
}
}
releaseTree(dirNames);
return EXIT_SUCCESS;
}
/*------------------------------------------*
| The called procedures (in logical order) |
*------------------------------------------*/
static void setupTrees(void)
{
/**
| Initialise protoTree, keep_exts
**/
config_t cfg; /* To hold our config */
config_setting_t *setting; /* To hold a setting */
int i; /* Iterator */
int needed; /* number of characters in filename */
char *extension, *cfg_file;
if (output_level >= DEBUG)
printf("Initialising protoTree, keep_exts.\n");
protoTreeSize = sizeof(remove_exts) / sizeof(char *) + 1;
if ((protoTree = malloc(sizeof(Froot) * protoTreeSize)) == 0) {
noMemory();
}
/* remove_exts has (protoTreeSize - 1) elements */
if (output_level >= DEBUG)
printf("Created protoTree of size %d\n", protoTreeSize);
for (i=0; i < protoTreeSize - 1; i++) {
protoTree[i].extension = remove_exts[i];
protoTree[i].firstNode = 0;
protoTree[i].lastNode = 0;
if (output_level >= DEBUG)
printf("Added %d-th extension %s to protoTree of size %d at pos %d\n",
i, remove_exts[i], protoTreeSize, i);
}
/* Add the sentinel */
protoTree[protoTreeSize - 1].extension = 0;
protoTree[protoTreeSize - 1].firstNode = 0;
protoTree[protoTreeSize - 1].lastNode = 0;
if (output_level >= DEBUG)
printf("Added sentinal to protoTree at pos %d.\n", protoTreeSize - 1);
keep_exts = keep_exts_defaults;
/**
| Initialise and read the config file
**/
config_init(&cfg);
needed = strlen(getenv("HOME")) + strlen("/.lintexrc") + 1;
cfg_file = (char *) malloc(sizeof(char) * needed);
if (getenv("HOME")) {
strcpy(cfg_file, getenv("HOME"));
strcat(cfg_file, "/.lintexrc");
if (output_level >= DEBUG)
printf("Using config file %s.\n", cfg_file);
}
if (! access(cfg_file, R_OK)) {
/* We have read access to the config file */
if (! config_read_file(&cfg, cfg_file)) {
fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg),
config_error_line(&cfg), config_error_text(&cfg));
config_destroy(&cfg);
exit(EXIT_FAILURE);
}
/* Lets extract our settings */
/* Extensions to remove */
setting = config_lookup(&cfg, "remove-exts");
if (setting != NULL) {
unsigned int count = config_setting_length(setting);
Froot *protoTreeTemp;
if ((protoTreeTemp = (Froot *) malloc(sizeof(Froot) * (count + protoTreeSize))) == 0) {
noMemory();
}
memcpy(protoTreeTemp, protoTree, sizeof(Froot) * protoTreeSize);
if (output_level >= DEBUG)
printf("Copied protoTree to larger location to add config exts.\n");
for (i = 0; i < count; i++) {
extension = (char *) config_setting_get_string_elem(setting, i);
protoTreeTemp[protoTreeSize - 1 + i].extension = extension;
protoTreeTemp[protoTreeSize - 1 + i].firstNode = 0;
protoTreeTemp[protoTreeSize - 1 + i].lastNode = 0;
if (output_level >= DEBUG)
printf("Added %d-th config extension %s to protoTree of size %d at pos %d\n",
i, extension, protoTreeSize + count, protoTreeSize - 1 + i);
}
/* Add the sentinel */
protoTreeTemp[protoTreeSize - 1 + count].extension = 0;
protoTreeTemp[protoTreeSize - 1 + count].firstNode = 0;
protoTreeTemp[protoTreeSize - 1 + count].lastNode = 0;
protoTree = protoTreeTemp;
if (output_level >= DEBUG)
printf("Added sentinel to protoTree at position %d\n", protoTreeSize - 1 + count);
protoTreeSize += count;
}
/* Extensions to keep */
setting = config_lookup(&cfg, "keep-exts");
if (setting != NULL) {
int i;
keep_exts_size = config_setting_length(setting);
if ((keep_exts = (char **) malloc(sizeof(char *) * keep_exts_size)) == 0) {
noMemory();
}
for (i = 0; i < keep_exts_size; i++) {
keep_exts[i] = (char *) config_setting_get_string_elem(setting, i);
if (output_level >= DEBUG)
printf("Added %d-th config extension %s to keep_exts of size %d at pos %d\n",
i, keep_exts[i], keep_exts_size, i);
}
}
} else {
if (! access(cfg_file, F_OK)) {
/* File exists */
fprintf(stderr,
"Warning: Insufficient permissions to read config file $HOME/.lintexrc");
}
}
}
static void insertNode(
char *name,
size_t lName,
time_t mTime,
int write,
Froot *root
){
/**
| Creates a new Fnode, to be inserted at the _end_ of the linked
| list pointed to by root->firstNode (i.e., the list is organized
| as a "queue", a.k.a. "FIFO" list): if a new node cannot be created,
| an error message is printed and the program aborted.
| If "lName" is bigger than zero, the file name is represented by the
| first lName characters of "name"; otherwise by the whole string in
| "name".
**/
Fnode *pFN; /* The new node created by insertNode */
size_t sSize; /* Structure size */
sSize = sizeof(Fnode) + (lName == 0 ? strlen(name) : lName);
if ((pFN = malloc(sSize)) == 0) {
noMemory();
}
pFN->mTime = mTime;
pFN->write = write;
pFN->next = 0;
if (lName == 0) {
strcpy(pFN->name, name);
} else {
strncpy(pFN->name, name, lName);
pFN->name[lName] = '\0';
}
if (root->lastNode == 0) {
root->firstNode = pFN;
} else {
root->lastNode->next = pFN;
}
root->lastNode = pFN;
}
static void noMemory(void)
{
fprintf(stderr, "%s: couldn't obtain heap memory\n", programName);
exit(EXIT_FAILURE);
}
static void clean(
char *dirName
){
/**
| Does the job for the directory "dirName".
|
| Builds a structure holding the TeX-related files, and does the
| required cleanup; finally, removes the file structure.
| If the list appended to "dirs" has been filled, recurse over the
| tree of subdirectories.
**/
Froot *teXTree; /* Root node of the TeX-related files */
Froot *dirs; /* Subdirectories in this directory */
Fnode *pFN; /* Running pointer over subdirectories */
if ((dirs = calloc(2, sizeof(Froot))) == 0) {
noMemory();
}
dirs->extension = "subs";
if ((teXTree = buildTree(dirName, dirs)) != 0) {
if (output_level >= DEBUG) {
printTree(teXTree);
}
examineTree(teXTree, dirName);
releaseTree(teXTree);
}
for (pFN = dirs->firstNode; pFN != 0; pFN = pFN->next) {
clean(pFN->name);
}
releaseTree(dirs);
}
static Froot *buildTree(
char *dirName,
Froot *subDirs
){
/**
| - Opens the required directory;
| - allocates a structure to hold the names of the TeX-related files,
| initialized from the global structure "protoTree";
| - starts a loop over all the files of the given directory.
**/
DIR *pDir; /* Pointer returned from opendir() */
struct dirent *pDe; /* Pointer returned from readdir() */
Froot *teXTree; /* Root node of the TeX-related files */
if (output_level >= DEBUG) {
printf("* Scanning directory \"%s\" - confirm = %c, recurse = %c, ",
dirName, (confirm ? 'Y' : 'N'), (recurse ? 'Y' : 'N')),
printf("keep = %c\n", (keep ? 'Y' : 'N'));
printf("* Editor trailer: \"%s\"\n", bExt);
puts("------------------------------Phase 1: directory scan");
}
if ((pDir = opendir(dirName)) == 0) {
fprintf(stderr,
"%s: \"%s\" cannot be opened (or is not a directory)\n",
programName, dirName);
return 0;
}
if ((teXTree = malloc(protoTreeSize * sizeof(Froot))) == 0) {
noMemory();
}
memcpy(teXTree, protoTree, protoTreeSize * sizeof(Froot));
while ((pDe = readdir(pDir)) != 0) {
char tName[FILENAME_MAX]; /* Fully qualified file name */
struct stat sStat; /* To be filled by stat(2) */
size_t len; /* Lenght of the current file name */
size_t last; /* Index of its last character */
char *pFe; /* Pointer to file extension */
/**
| - Tests for empty inodes (already removed files);
| - skips the . and .. (current and previous directory);
| - tests the trailing part of the file name against the extension of
| the backup files, to be always deleted.
**/
if (pDe->d_ino == 0) continue;
if (strcmp(pDe->d_name, ".") == 0) continue;
if (strcmp(pDe->d_name, "..") == 0) continue;
sprintf(tName, "%s/%s", dirName, pDe->d_name);
len = strlen(pDe->d_name);
last = len - 1;
if (n_bExt != 0) { /* If 0, no backup files to delete */
int crit; /* What exceeds backup extensions */
crit = len - n_bExt;
if (crit > 0 && strcmp(pDe->d_name + crit, bExt) == 0) {
nuke(tName);
continue;
}
}
/**
| If the file is a directory and the -r option has been given, stores
| the directory name in the linked list pointed to by "subDirs", for
| recursive calls.
|
| N.B.: if stat(2) fails, the file is skipped.
**/
if (stat(tName, &sStat) != 0) {
fprintf(stderr, "File \"%s", tName);
perror("\"");
continue;
}
if (S_ISDIR(sStat.st_mode) != 0) {
if (output_level >= DEBUG) {
printf("File %s - is a directory\n", pDe->d_name);
}
if (recurse) {
insertNode(tName, 0, 0, 0, subDirs);
}
continue;
}
/**
| If the file has an extension (the rightmost dot followed by at
| least one character), and if that extension matches one of the
| entries in teXTree[i].extension: stores the file name (with the
| extension stripped) in the appropriate linked list, together with
| its modification time.
**/
if ((pFe = strrchr(pDe->d_name, '.')) != 0) {
size_t nameLen;
nameLen = pFe - pDe->d_name;
if (nameLen < last) {
Froot *pTT;
if (output_level >= DEBUG) {
printf("File %s - extension %s", pDe->d_name, pFe);
}
/**
| Loop on recognized TeX-related file extensions
**/
for (pTT = teXTree; pTT->extension != 0; pTT++) {
if (strcmp(pFe, pTT->extension) == 0) {
int i;
if (keep) {
i = 0;
} else {
i = keep_exts_size;
}
for (i = 0; i < keep_exts_size; i++) {
if (strcmp(pFe, keep_exts[i]) == 0) {
/**
| The current file's extension is in keep_exts, we want to keep
| it.
**/
i = -1;
break;
}
}
if ((!keep) | (i != -1)) {
/**
| Only add the file if we didn't find its extension in keep_exts
**/
insertNode(pDe->d_name, nameLen, sStat.st_mtime, access(tName, W_OK), pTT);
if (output_level >= DEBUG) {
printf(" - inserted in tree");
}
} else if (keep) {
if (output_level >= DEBUG) {
printf(" - not inserted in tree (extension in keep-exts)");
} else if (output_level >= VERBOSE) {
printf("*** %s not removed; keep activated ***\n", pDe->d_name);
}
}
break;
}
} /* loop on known extensions */
if (output_level >= DEBUG) {
puts("");
}
} else {
if (output_level >= VERBOSE) {
printf("File %s - empty extension\n", pDe->d_name);
}
}
} else {
if (output_level >= DEBUG) {
printf("File %s - without extension\n", pDe->d_name);
}
}
} /* while (readdir) ... */
if (closedir(pDir) != 0) {
fprintf(stderr, "Directory \"%s", dirName);
perror("\"");
}
return teXTree;
}
static void printTree(
Froot *teXTree
){
/**
| Prints all the file names archived in the linked lists (for
| debugging purposes).
**/
if (output_level >= DEBUG) {
Froot *pTT; /* Running pointer over teXTree elements */
puts("------------------------------Phase 2: tree printout");;
for (pTT = teXTree; pTT->extension != 0; pTT++) {
Fnode *pTeX; /* Running pointer over TeX-related files */
int nNodes = 0; /* Counter */
for (pTeX = pTT->firstNode; pTeX != 0; pTeX = pTeX->next) {
++nNodes;
printf("%s%s\n", pTeX->name, pTT->extension);
}
printf(" --> %d file%s with extension %s\n", nNodes,
(nNodes == 1 ? "" : "s"), pTT->extension);
}
}
}
static void examineTree(
Froot *teXTree,
char *dirName
){
/**
| Examines the linked lists for this directory doing the effective
| cleanup.
**/
Froot *pTT; /* Pointer over linked list trees */
Fnode *pTeX; /* Running pointer over the .tex files */
/**
| Looks, for all the .tex files, if a corresponding entry with the same
| name exists (with a different extension) in the other lists; if so,
| and if its modification time is later than the one of the related
| .tex file, removes it from the file system.
**/
putsMessage("------------------------------Phase 3: effective cleanup",
DEBUG);
for (pTeX = teXTree->firstNode; pTeX != 0; pTeX = pTeX->next) {
char tName[FILENAME_MAX];
sprintf(tName, "%s/%s.tex", dirName, pTeX->name);
pTT = teXTree;
if (output_level >= DEBUG) {
printf(" Finding files related to %s:\n", tName);
}
for (pTT++; pTT->extension != 0; pTT++) {
Fnode *pComp;
for (pComp = pTT->firstNode; pComp != 0; pComp = pComp->next) {
char cName[FILENAME_MAX];
if (strcmp(pTeX->name, pComp->name) == 0) {
sprintf(cName, "%s/%s%s", dirName, pTeX->name, pTT->extension);
pComp->name[0] = '\0';
/**
| Remove generated file if more recent than source (default) or if
| we permit the removal of files older than source
**/
if (difftime(pComp->mTime, pTeX->mTime) > 0.0 || older) {
if (pComp->write == 0) {
nuke(cName);
} else {
if (output_level >= DEBUG) {
printf("*** %s readonly; perms are %d***\n", cName,
pComp->write);
}
if (output_level >= VERBOSE) {
printf("*** %s not removed; it is read only ***\n", cName);
}
}
} else {
if (output_level >= VERBOSE) {
printf("*** %s not removed; %s is newer ***\n", cName, tName);
}
}
break;
}
}
}
}
/**
| If some garbage file has not been deleted, list it
**/
putsMessage("------------------------------Phase 4: left garbage files",
DEBUG);
pTT = teXTree;
for (pTT++; pTT->extension != 0; pTT++) {
Fnode *pComp;
for (pComp = pTT->firstNode; pComp != 0; pComp = pComp->next) {
if (pComp->name[0] != '\0') {
char cName[FILENAME_MAX];
sprintf(cName, "%s/%s%s", dirName, pComp->name, pTT->extension);
if (output_level >= VERBOSE) {
printf("*** %s not removed; no .tex file found ***\n", cName);
}
}
}
}
}
static void releaseTree(
Froot *teXTree
){
/**
| Cleanup of the file name storage structures: an _array_ of Froot's,
| terminated by a NULL extension pointer as a sentinel, is assumed.
| Every linked list nodes is freed; then releaseTree frees also the
| root structure.
**/
Froot *pFR;
putsMessage("------------------------------Phase 5: tree cleanup", DEBUG);
for (pFR = teXTree; pFR->extension != 0; pFR++) {
Fnode *pFN, *p;
int nNodes = 0;
if (output_level >= DEBUG) {
printf("Dealing with extensions %s ...", pFR->extension);
}
for (pFN = pFR->firstNode; pFN != 0; pFN = p) {
p = pFN->next;
free(pFN);
nNodes++;
}
if (output_level >= DEBUG) {
printf(" %d nodes freed\n", nNodes);
}
}
free(teXTree);
}
static void nuke(
char *name
){
/**
| Removes "name" (the fully qualified file name) from the file system
**/
if ((output_level >= DEBUG) || pretend) {
printf("*** File \"%s\" would have been removed ***\n", name);
}
if (pretend) {
/* We don't need to continue if we aren't going to remove the file */
return;
}
if (confirm) {
char yn[LONG_ENOUGH], c;
do {
printf("Remove %s (y|n) ? ", name);
if (fgets(yn, LONG_ENOUGH, stdin) == 0) return;
if (yn[0] == '\0' || (c = tolower((unsigned char) yn[0])) == 'n') {
return;
}
} while (c != 'y');
}
if (remove(name) != 0) {
fprintf(stderr, "File \"%s", name);
perror("\"");
} else {
if (output_level >= WHISPER) {
printf("%s has been removed\n", name);
}
}
}
static char *baseName(
char *pc
){
char *p;
/**
| Strips the (eventual) path information from the string pointed
| to by 'pc'; if no file name is given, returns an empty string.
**/
p = strrchr(pc, '/');
if (p == 0) return pc;
return ++p;
}
static void putsMessage(
char *message,
int message_level
){
if (message_level <= output_level) {
puts(message);
}
}
static void syntax()
{
printf("lintex version %s\n", VERSION);
puts("Usage:");
printf(" %s [OPTIONS] [DIR [DIR ...]]\n", programName);
puts("Purpose:");
puts(" Removes unneeded TeX auxiliary files and editor backup files from"
" the");
puts(" given directories (default: the current directory); the TeX files"
" are");
puts(" actually removed only if their modification time is more recent"
" than");
puts(" the one of the related TeX source and if they aren't readonly.");
puts(" Please see the manpage for a list of extensions that get removed.");
puts("Options:");
puts(" -i : asks the user before removing any file;");
puts(" -r : scans recursively the subdirectories of the given");
puts(" directories;");
puts(" -b ext : \"ext\" is the trailing string identifying editor backup"
" files");
puts(" (defaults to \"~\"). -b \"\" avoids any cleanup of special");
puts(" files;");
puts(" -p : pretend, show what files would be removed but don't actually");
puts(" remove them;");
puts(" -k : keeps final document (.pdf, .ps, .dvi);");
puts(" -o : permit removal of files older than their sources;");
puts(" -q : quiet, only print error messages;");
puts(" -v : verbose, prints which files were removed and which weren't;");
puts(" -d : debug output, prints the answers to all of life's questions.");
exit(EXIT_SUCCESS);
}