dwdiff-2.1.1/ 0000775 0001750 0001750 00000000000 13074617326 014077 5 ustar gertjan gertjan 0000000 0000000 dwdiff-2.1.1/install-manpages 0000775 0001750 0001750 00000001454 13074617326 017270 0 ustar gertjan gertjan 0000000 0000000 #!/bin/sh
# Install manual page
not() {
if "$@" ; then
false
else
true
fi
}
for lingua in ${LINGUAS} ; do
if not [ -f "man/${lingua}/dwdiff.1" ] ; then
echo "No manual page for dwfiff for lingua ${lingua}"
continue
fi
${INSTALL} -d "${mandir}/${lingua}.UTF-8/man1"
${INSTALL} -m 644 "man/${lingua}/dwdiff.1" "${mandir}/${lingua}.UTF-8/man1"
${INSTALL} -d "${mandir}/${lingua}/man1"
${INSTALL} -m 644 "man/${lingua}/dwdiff.1" "${mandir}/${lingua}/man1"
if not [ "x$1" = "xyes" ] ; then
continue
fi
if not [ -f "man/${lingua}/dwfilter.1" ] ; then
echo "No manual page for dwfilter for lingua ${lingua}"
continue
fi
${INSTALL} -m 644 "man/${lingua}/dwfilter.1" "${mandir}/${lingua}.UTF-8/man1"
${INSTALL} -m 644 "man/${lingua}/dwfilter.1" "${mandir}/${lingua}/man1"
done
exit 0
dwdiff-2.1.1/man/ 0000775 0001750 0001750 00000000000 13074617326 014652 5 ustar gertjan gertjan 0000000 0000000 dwdiff-2.1.1/man/nl/ 0000775 0001750 0001750 00000000000 13074617326 015263 5 ustar gertjan gertjan 0000000 0000000 dwdiff-2.1.1/man/nl/dwdiff.1 0000664 0001750 0001750 00000026733 13074617326 016623 0 ustar gertjan gertjan 0000000 0000000 .\" Generated by manscript from nl/dwdiff.1.txt
.TH "DWDIFF" "1" "2017/04/15" "Versie $VERSION$" "dwdiff afgebakend woord verschil programma"
.hw /usr/share/doc/dwdiff-$VERSION$ http://os.ghalkes.nl/dwdiff.html
.SH NAAM
dwdiff \- afgebakend woord verschil programma
.SH OVERZICHT
\fBdwdiff\fR [\fIOPTIES\fR] \fIOUD BESTAND\fR \fINIEUW BESTAND\fR
.br
\fBdwdiff\fR [\fIOPTIES\fR] \fB\-\-diff\-input\fR [\fIDIFF BESTAND\fR]
.SH BESCHRIJVING
\fBdwdiff\fR is een programma dat het verschil tussen twee bestanden bepaalt in
woorden, in plaats van in regels. Het verschilt van \fBwdiff\fR in dat het de
gebruiker toestaat om te specificeren wat witruimte is, en in dat het een
optionele lijst van afbakeningskarakters kan gebruiken. Afbakeningskarakters
worden behandeld als of ze afzonderlijke woorden zijn, zelfs als er geen
witruimte is gelaten tussen het karakter en het voorafgaande of volgende woord.
\fBdwdiff\fR is grotendeels commando-regel compatibel met \fBwdiff\fR. Slechts the
\-\-autopager, \-\-terminal en \-\-avoid\-wraps opties worden niet ondersteund.
.PP
De standaard uitvoer van \fBdwdiff\fR is de nieuwe tekst, met daarin
gemarkeerde verwijderde en ingevoegde stukken. Er zijn commando-regel opties
beschikbaar om de uitvoer te veranderen.
.SH OPTIES
\fBdwdiff\fR accepteert de volgende opties (In alle karaktersequenties worden
eerst de \\-escapecodes verwerkt. Alle standaard codes worden ondersteund,
evenals de \\u en \\U Unicode escapecodes):
.PP
.TP
\fB\-h\fR, \fB\-\-help\fR
Toon een kort help bericht.
.TP
\fB\-v\fR, \fB\-\-version\fR
Toon versie en auteursrecht informatie.
.TP
\fB\-d\fR \fIdelimiters\fR, \fB\-\-delimiters\fR=\fIdelimiters\fR
Specificeer de lijst van afbakeningskarakters. De standaard waarde is leeg.
.TP
\fB\-P\fR, \fB\-\-punctuation\fR
Gebruik alle leestekens als afbakeningskarakters. De set van leestekens is
locale-afhankelijk.
.TP
\fB\-W\fR \fIwhitespace\fR, \fB\-\-white\-space\fR=\fIwhitespace\fR
Specificeer de lijst van witruimte karakters. De standaard set van witruimte
is locale-afhankelijk.
.TP
\fB\-u\fR, \fB\-\-diff\-input\fR
Interpreteer de invoer als de uitvoer van \fBdiff\fR in het Unified Diff
formaat (meestal geproduceerd door \fBdiff \-u\fR). In dit geval is er maar één
invoerbestand toegestaan. Deze optie maakt het mogelijk om de diff uitvoer
te herformateren met \fBdwdiff\fR, en is bijvoorbeeld nuttig om de uitvoer van
\fBsvn diff\fR na te bewerken.
.TP
\fB\-1\fR, \fB\-\-no\-deleted\fR
Toon de woorden die verwijderd zijn uit het eerste bestand niet.
.TP
\fB\-2\fR, \fB\-\-no\-inserted\fR
Toon de woorden die ingevoegd zijn in het tweede bestand niet.
.TP
\fB\-3\fR, \fB\-\-no\-common\fR
Toon de woorden die in beide bestanden voorkomen niet.
.TP
\fB\-L\fR[\fIwidth\fR], \fB\-\-line\-numbers\fR[=\fIwidth\fR]
Toon regelnummers aan het begin van iedere regel. De getoonde regelnummers
zijn respectievelijk het regelnummer in het oude bestand en het regelnummer
in het nieuwe bestand. Het optionele \fIwidth\fR argument is het minimum
aantal posities per regelnummer.
.TP
\fB\-C\fR\fInum\fR, \fB\-\-context\fR=\fInum\fR
Toon \fInum\fR regels context voor en na veranderingen. Voegt een regel met
alleen \-\- toe tussen ieder blok veranderingen.
.TP
\fB\-s\fR, \fB\-\-statistics\fR
Toon statistieken bij het afsluiten van het programma. De getoonde getallen
zijn de aantallen woorden in beide bestanden, het aantal verwijderde woorden,
het aantal ingevoegde woorden, en het aantal veranderde woorden. Het aantal
veranderde woorden wordt geteld als het aantal woorden dat is verwijderd in het
eerste bestand, en als het aantal vervangende woorden uit het tweede bestand.
Al deze getallen worden ook uitgedrukt als een percentage van het totaal aantal
woorden van het bestand waaruit ze afkomstig waren.
.TP
\fB\-i\fR, \fB\-\-ignore\-case\fR
Negeer het verschil tussen hoofdletters en kleine letters bij het vergelijken
van woorden. Deze optie is alleen beschikbaar als het onderliggende \fBdiff\fR
programma deze optie biedt.
.TP
\fB\-I\fR, \fB\-\-ignore\-formatting\fR
Negeer verschillen in opmaak van karakters. Deze optie schakelt naar het
gebruik van de Unicode compatibiliteitsdecompositie van karakters in plaats
van de canonieke decompositie. De compatibiliteitsdecompositie gooit opmaak
informatie weg. Bijvoorbeeld, de ligatuur fi wordt vertaald naar twee losse
letters voor de vergelijking. Echter, ook super- en subscript worden ook als
gelijk gezien, evenals verschillende rotaties van hetzelfde karakter.
.TP
\fB\-c\fR[\fIspec\fR], \fB\-\-color\fR[=\fIspec\fR]
Kleurenmodus. De optionele specificatie \fIspec\fR kan gebruikt worden om
de gebruikte kleuren aan te passen. \fIspec\fR bestaat uit
[\fIverwijderd\fR],[\fItoegevoegd\fR]. Als een van beide ontbreekt wordt
zijn standaard kleur gebruikt (respectievelijk helder rood en helder groen).
Beide delen van \fIspec\fR bestaan uit [\fIvoorgrond\fR][:\fIachtergrond\fR]. Een lijst
met toelaatbare kleurnamen kan worden verkregen door het woord ``list'' te
gebruiken op de plaats van \fIspec\fR. Alternatief kan een willekeurige
escapecode om attributen te zetten worden gebruikt als kleur door er \fIe:\fR
voor te zetten.
.IP
De standaard markeringen voor het begin en het einde van verwijderde en
toegevoegde tekst worden onderdrukt, maar markeringen die op de commando-regel
zijn gespecificeerd zullen worden afgedrukt.
.TP
\fB\-l\fR, \fB\-\-less\-mode\fR
Als \-p, maar voer het doorhalen ook uit op verwijderde witruimte karakters.
.TP
\fB\-p\fR, \fB\-\-printer\fR
Gebruik doorhalen met een liggend streepje en dik gedrukte tekst om
veranderingen te benadrukken. Dit is geïmplementeerd door eerst een liggend
streepje of een duplicaat van het karakter af te drukken, gevolgd door een
backspace karakter, gevolgd door het karakter zelf. Op de meeste terminals
heeft dit geen effect. Het \fBless\fR(1) programma zal echter doorgehaalde en
dik gedrukte tekst laten zien.
.IP
De standaard markeringen voor het begin en het einde van verwijderde en
toegevoegde tekst worden onderdrukt, maar markeringen die op de commando-regel
zijn gespecificeerd zullen worden afgedrukt.
.TP
\fB\-m\fR[\fInum\fR], \fB\-\-match\-context\fR[=\fInum\fR]
Gebruik \fInum\fR woorden context voor en na ieder woord voor vergelijking.
Woorden in de oude tekst komen dan alleen overeen met woorden in de nieuwe
tekst als de woorden er omheen eveneens overeenkomen. Dit verbeterd de uitvoer
van \fBdwdiff\fR voor grote aanpassingen met veel voorkomende woorden. Echter,
het gebruik van context vergroot de benodigde schijfruimte en vergt meer
rekentijd. De standaard waarde voor deze optie is 1. Zet deze optie op 0 om het
gedrag van versies voor 1.5 te gebruiken.
.TP
\fB\-\-aggregate\-changes\fR
Laat nabije veranderingen versmelten tot één verandering, als context woorden
gebruikt worden (zie \fB\-\-match\-context\fR). Deze optie verminderd de
benodigde rekentijd door de veranderingen die het \fBdiff\fR programma
rapporteerd niet nader te verfijnen.
.TP
\fB\-A\fR \fIalgorithm\fR, \fB\-\-algorithm\fR=\fIalgorithm\fR
Kies het algoritme dat gebruikt wordt om de verschillen te bepalen. Er zijn
drie mogelijke waarden voor \fIalgorithm\fR: \fIbest\fR, welke een minimaal
aantal verschillen probeert te vinden, \fInormal\fR, welke enige optimaliteit
uitruilt voor snelheid, en \fIfast\fR, welke er van uit gaat dat er slechts
enkele wijzigingen zijn op een lange tekst. Standaard wordt het \fInormal\fR
algoritme gebruikt.
.TP
\fB\-S\fR[\fImarker\fR], \fB\-\-paragraph\-separator\fR[=\fImarker\fR]
Toon toegevoegde of verwijderde blokken van regels met alleen maar witruimte
karakters. Een speciale markering wordt aan de uitvoer toegevoegd om deze
blokken aan te geven. De standaard markering is \-\-.
.TP
\fB\-\-wdiff\-output\fR
Maak uitvoer die overeenkomt met die van het \fBwdiff\fR programma. \fBdwdiff\fR
maakt gebruik van een ander uitvoer algoritme, welke een meer intuïtieve
uitvoer produceert.
.TP
\fB\-w\fR \fIstring\fR, \fB\-\-start\-delete\fR=\fIstring\fR
Tekenreeks om het begin van verwijderde tekst te markeren. De standaard waarde
is [\-.
.TP
\fB\-x\fR \fIstring\fR, \fB\-\-stop\-delete\fR=\fIstring\fR
Tekenreeks om het einde van verwijderde tekst te markeren. De standaard waarde
is \-].
.TP
\fB\-y\fR \fIstring\fR, \fB\-\-start\-insert\fR=\fIstring\fR
Tekenreeks om het begin van toegevoegde tekst te markeren. De standaard waarde
is {+.
.TP
\fB\-z\fR \fIstring\fR, \fB\-\-stop\-insert\fR=\fIstring\fR
Tekenreeks om het einde van toegevoegde tekst te markeren. De standaard waarde
is +}.
.TP
\fB\-R\fR, \fB\-\-repeat\-markers\fR
Herhaal de begin en eind markeringen aan het begin en het eind van een regel
als de verandering over een regeleinde loopt.
.TP
\fB\-\-profile\fR=\fIname\fR
Laad het profiel met de naam \fIname\fR uit het .dwdiffrc bestand in de home
directory van de gebruiker. Alleen het laatst genoemde profiel wordt
daadwerkelijk gelezen, tenzij het gevolgd wordt door een \fB\-\-no\-profile\fR optie.
Standaard wordt het profiel genaamd \fIdefault\fR gelezen.
.TP
\fB\-\-no\-profile\fR
Schakel het lezen van profielen uit, tenzij gevolgd door nog een \fB\-\-profile\fR
optie. Dit verhindert ook het lezen van het \fIdefault\fR profiel.
.PP
Een enkel minteken (\-) als bestandsnaam kan worden gebruikt om aan te geven dat
de tekst van de standaard invoer moet worden gelezen. Slechts één bestand kan
van standaard invoer worden gelezen. Om te voorkomen dat \fBdwdiff\fR een
bestandsnaam die begint met een minteken interpreteert als een optie kan een
dubbel minteken (\-\-) opgegeven worden, waarna \fBdwdiff\fR alle volgende argumenten
als bestandsnamen zal interpreteren.
.PP
De stopstatus van \fBdwdiff\fR geeft het resultaat van de vergelijking aan: 0 als de
bestanden gelijk zijn, 1 als er verschillen zijn. In geval er een fout optreedt,
zal \fBdwdiff\fR stoppen met status 2.
.SH PROFIELEN
Sinds versie 2.1.0 biedt \fBdwdiff\fR de gebruiker de mogelijkheid profielen te
creëren. Een profiel is een set opties die geladen kan worden middels de
\fB\-\-profile\fR optie. Profielen worden vastgelegd in het .dwdiffrc bestand in de
home directory van de gebruiker. De inhoud van het .dwdiffrc bestand dient er
als volgt uit te zien:
.PP
Een regel bevat ofwel een profiel kop (tekst tussen []), of een lange optie naam
zonder de voorloop streepjes, optioneel gevolgd door het bijbehorende argument.
Het argument wordt van witruimtekarakters ontdaan. Als het argument
corresponderende dubbele of enkele aanhalingstekens bevat, dan worden deze ook
verwijderd. Commentaar kan toegevoegd worden door een regel met een hekje (#) te
beginnen.
.PP
Als er geen profiel naam is gespecificeerd, dan wordt het profiel met de naam
\fIdefault\fR geladen, tenzij de optie \fB\-\-no\-profile\fR gespecificeerd is. Hier is
een voorbeeld .dwdiffrc:
.PP
.po 5
[default]
.br
color red,blue
.br
punctuation
.PP
[html]
.br
start-delete
.br
stop-delete
.br
start-insert
.br
stop-insert
.PP
[space-only]
.br
# Use only a space as whitespace.
.br
white-space " "
.br
.po
.SH BUGS
Als u denkt een bug gevonden te hebben, controleer dan dat u de nieuwste versie
van \fBdwdiff\fR gebruikt. Als u een bug wil
rapporteren, voeg dan een minimaal voorbeeld dat het probleem demonstreert toe
aan uw melding.
.SH AUTEUR
G.P. Halkes
.SH COPYRIGHT
Copyright \(co 2006\-2011 G.P. Halkes and others
.br
\fBdwdiff\fR is gelicenseerd onder de GNU General Public License version 3.
.br
Voor meer informatie over de licentie, zie het bestand COPYING in de
documentatie map. Op Un*x systemen is dit meestal
/usr/share/doc/dwdiff-$VERSION$.
.SH ZIE\ OOK
\fBdwfilter\fR(1), \fBwdiff\fR(1), \fBdiff\fR(1)
dwdiff-2.1.1/man/nl/dwfilter.1 0000664 0001750 0001750 00000005674 13074617326 017201 0 ustar gertjan gertjan 0000000 0000000 .\" Generated by manscript from nl/dwfilter.1.txt
.TH "DWFILTER" "1" "2015/09/21" "Version $VERSION$" "reformat text for processing"
.hw /usr/share/doc/dwdiff-$VERSION$ http://os.ghalkes.nl/dwdiff.html
.SH NAAM
dwfilter \- herformateer tekst met dwdiff voor verdere verwerking
.SH OVERZICHT
\fBdwfilter\fR [\fIOPTIES\fR] \fIOUD BESTAND\fR \fINIEUW BESTAND\fR \fINABEWERKER\fR [\fINABEWERKER OPTIES\fR]
.SH BESCHRIJVING
\fBdwfilter\fR herformateert de tekst in het oude bestand volgens de inhoud
van het nieuwe bestand (of vice versa) en geeft vervolgens het geherformateerde
oude bestand en het nieuwe bestand aan een volgend filter. De belangrijkste
functie is het mogelijk maken om programma's als \fBmeld\fR and \fBkdiff3\fR
te gebruiken, zelfs als een bestand geherformateerd is na bewerking. Een
verdere functie is het maken van kleine verschil bestanden zelfs als het nieuwe
bestand geherformateerd is.
.SH OPTIES
.TP
\fB\-r\fR, \fB\-\-reverse\fR
Herformateer het nieuwe bestand volgens de inhoud van het oude bestand in
plaats van de standaard operatie waarbij het oude bestand wordt geherformateerd
volgens de inhoud van het nieuwe bestand.
.PP
\fBdwfilter\fR accepteerd de volgende \fBdwdiff\fR opties:
.PP
.RS
\fB\-d\fR \fIdelimiters\fR, \fB\-\-delimiters\fR=\fIdelimiters\fR
.br
\fB\-P\fR, \fB\-\-punctuation\fR
.br
\fB\-W\fR \fIwhitespace\fR, \fB\-\-white\-space\fR=\fIwhitespace\fR
.br
\fB\-i\fR, \fB\-\-ignore\-case\fR
.br
\fB\-I\fR, \fB\-\-ignore\-formatting\fR
.br
\fB\-D\fR \fIoption\fR, \fB\-\-diff\-option\fR=\fIoption\fR
.br
\fB\-C\fR\fInum\fR, \fB\-\-context\fR=\fInum\fR
.br
\fB\-m\fR\fInum\fR, \fB\-\-match\-context\fR=\fInum\fR
.br
\fB\-\-aggregate\-changes\fR
.br
\fB\-\-wdiff\-output\fR
.br
\fB\-A\fR \fIalgorithm\fR, \fB\-\-algorithm\fR=\fIalgorithm\fR
.br
\fB\-\-profile\fR=\fIname\fR, \fB\-\-no\-profile\fR
.RE
.PP
Zie de \fBdwdiff\fR handleiding voor de betekenis.
.PP
Een enkel minteken (\-) als bestandsnaam kan worden gebruikt om aan te geven dat
de tekst van de standaard invoer moet worden gelezen. Slechts één bestand kan
van standaard invoer worden gelezen. Om te voorkomen dat \fBdwfilter\fR een
bestandsnaam die begint met een minteken interpreteert als een optie kan een
dubbel minteken (\-\-) opgegeven worden, waarna \fBdwfilter\fR alle volgende argumenten
als bestandsnamen zal interpreteren.
.SH BUGS
Als u denkt een bug gevonden te hebben, controleer dan dat u de nieuwste versie
van \fBdwdiff\fR gebruikt. Als u een bug wil
rapporteren, voeg dan een minimaal voorbeeld dat het probleem demonstreert toe
aan uw melding.
.SH AUTEUR
G.P. Halkes
.SH COPYRIGHT
Copyright \(co 2006\-2010 G.P. Halkes
.br
dwdiff is gelicenseerd onder de GNU General Public License version 3.
.br
Voor meer informatie over de licentie, zie het bestand COPYING in de
documentatie map. Op Un*x systemen is dit meestal
/usr/share/doc/dwdiff-$VERSION$.
.SH ZIE\ OOK
\fBdwdiff\fR(1), \fBdiff\fR(1), \fBmeld\fR(1), \fBkdiff3\fR(1)
dwdiff-2.1.1/man/dwdiff.1 0000664 0001750 0001750 00000024501 13074617326 016201 0 ustar gertjan gertjan 0000000 0000000 .\" Generated by manscript from dwdiff.1.txt
.TH "DWDIFF" "1" "2017/04/15" "$VERSION$" "Delimited word diff program"
.hw /usr/share/doc/dwdiff-$VERSION$ http://os.ghalkes.nl/dwdiff.html
.SH NAME
dwdiff \- a delimited word diff program
.SH SYNOPSIS
\fBdwdiff\fR [\fIOPTIONS\fR] \fIOLD FILE\fR \fINEW FILE\fR
.br
\fBdwdiff\fR [\fIOPTIONS\fR] \fB\-\-diff\-input\fR [\fIDIFF FILE\fR]
.SH DESCRIPTION
\fBdwdiff\fR is a \fBdiff\fR program that operates at the word level instead of the line
level. It is different from \fBwdiff\fR in that it allows the user to specify what
should be considered whitespace, and in that it takes an optional list of
characters that should be considered delimiters. Delimiters are single
characters that are treated as if they are words, even when there is no
whitespace separating them from preceding words or delimiters. \fBdwdiff\fR is mostly
command-line compatible with \fBwdiff\fR. Only the \-\-autopager, \-\-terminal and
\-\-avoid\-wraps options are not supported.
.PP
The default output from \fBdwdiff\fR is the new text, with the deleted and inserted
parts annotated with markers. Command line options are available to change both
what is printed, and the markers.
.SH OPTIONS
\fBdwdiff\fR accepts the following options (Note that all strings will first be
escape expanded. All standard \\-escapes are supported, as well as \\u and \\U
Unicode escapes):
.PP
.TP
\fB\-h\fR, \fB\-\-help\fR
Display a short help message.
.TP
\fB\-v\fR, \fB\-\-version\fR
Print version and copyright information.
.TP
\fB\-d\fR \fIdelimiters\fR, \fB\-\-delimiters\fR=\fIdelimiters\fR
Specify a list of characters to be used as delimiters. The default is empty.
.TP
\fB\-P\fR, \fB\-\-punctuation\fR
Use punctuation characters as delimiters. The exact set of punctuation
characters depends on the current locale.
.TP
\fB\-W\fR \fIwhitespace\fR, \fB\-\-white\-space\fR=\fIwhitespace\fR
Specify a list of characters to be used as whitespace. The default set of
whitespace characters depends on the current locale.
.TP
\fB\-u\fR, \fB\-\-diff\-input\fR
Interpret the input as the output from \fBdiff\fR in the Unified Diff format
(usually produced by \fBdiff \-u\fR). In this case only one input file is
allowed. This option allows reformating diff output with \fBdwdiff\fR, and is
useful for example to post-process the output of \fBsvn diff\fR.
.TP
\fB\-1\fR, \fB\-\-no\-deleted\fR
Suppress printing of words deleted from the first file.
.TP
\fB\-2\fR, \fB\-\-no\-inserted\fR
Suppress printing of words inserted in the second file.
.TP
\fB\-3\fR, \fB\-\-no\-common\fR
Suppress printing of words common to both files.
.TP
\fB\-L\fR[\fIwidth\fR], \fB\-\-line\-numbers\fR[=\fIwidth\fR]
Show line numbers at the start of each line. The line numbers displayed are
the line number in the old file and the line number in the new file
respectively. The optional \fIwidth\fR argument is the minimum number of
positions per line number.
.TP
\fB\-C\fR\fInum\fR, \fB\-\-context\fR=\fInum\fR
Show \fInum\fR lines of context before and after each changes. A line
with only \-\- is printed between blocks of changes.
.TP
\fB\-s\fR, \fB\-\-statistics\fR
Print statistics when done. The numbers printed include the number of words
from in both files, the number of deleted words, the number of
inserted words, and the number of changed words. The number of changed words
is counted as the number of words that are removed from the first file, and
the number of words that replace them from the second file. All of these
numbers are also expressed as a percentage of the total number of words in the
file the words came from.
.TP
\fB\-i\fR, \fB\-\-ignore\-case\fR
Ignore differences in case when comparing words. This option is only
available if the diff program that is called provides it.
.TP
\fB\-I\fR, \fB\-\-ignore\-formatting\fR
Ignore differences in formatting of characters. This option switches to using
the Unicode compatibility decomposition instead of the canonical decomposition.
The compatibility decomposition discards formatting information. For example,
the ligature fi will be decomposed into two separate characters for the
purposes of comparison. However, also super- and subscript will be regarded
equal as well as different rotations of the same character.
.TP
\fB\-c\fR[\fIspec\fR], \fB\-\-color\fR[=\fIspec\fR]
Color mode. The optional \fIspec\fR can be used to customize the colors.
\fIspec\fR consists of [\fIdelete\fR],[\fIinsert\fR]. If either is
omited it will be set to its default color (bright red or bright green
respectively). Both parts of the \fIspec\fR consist of
[\fIforeground\fR][:\fIbackground\fR]. To obtain a list of permissible
color names, use the word ``list'' as \fIspec\fR. Alternatively, you can
specify any escape sequence to set attributes as a color by prepending
\fIe:\fR.
.IP
The standard markers for the begin and end of deleted and inserted text are
suppressed, but any markers specified on the command line will still be
printed.
.TP
\fB\-l\fR, \fB\-\-less\-mode\fR
As \-p but also overstrike deleted whitespace.
.TP
\fB\-p\fR, \fB\-\-printer\fR
Use overstriking with an underscore and bold text to emphasize changes. This
is implemented by first printing the underscore or a duplicate of the
character to be printed, followed by a backspace, followed by the character.
On regular terminals you won't see any effect. The \fBless\fR(1) command will
however show underlined and bold text.
.IP
The standard markers for the begin and end of deleted and inserted text are
suppressed, but any markers specified on the command line will still be
printed.
.TP
\fB\-m\fR\fInum\fR, \fB\-\-match\-context\fR=\fInum\fR
Use \fInum\fR words of context before and after words for matching. Words in
the old text will then only match words in the new text if words surrounding
them are also equal. This improves the output for \fBdwdiff\fR for large changes
with frequently occuring words. However, using context requires more disk
space and more processing time. The default value is 1. Set this option to 0
to revert to the pre 1.5 behavior.
.TP
\fB\-\-aggregate\-changes\fR
Allow multiple close changes to be treated as one change, if context words are
used (see \fB\-\-match\-context\fR). This option reduces the processing time as the
changes reported by the \fBdiff\fR program are not post-processed to give more
precise results.
.TP
\fB\-A\fR \fIalgorithm\fR, \fB\-\-algorithm\fR=\fIalgorithm\fR
Select the algorithm to be used for determining differences. There are three
possible values for \fIalgorithm\fR: \fIbest\fR, which tries to find the minimal set
of changes, \fInormal\fR, which trades some optimality for speed, and \fIfast\fR,
which assumes that the input is large and contains few changes. By default
the \fInormal\fR algorithm is used.
.TP
\fB\-S\fR[\fImarker\fR], \fB\-\-paragraph\-separator\fR[=\fImarker\fR]
Show insertion or deletion of blocks of lines with only whitespace characters.
A special marker is inserted into the output to indicate these blocks. The
default marker is \-\-.
.TP
\fB\-\-wdiff\-output\fR
Create \fBwdiff\fR compatible output. The \fBdwdiff\fR program uses a different output
algorithm, which provides a more intuitive output.
.TP
\fB\-w\fR \fIstring\fR, \fB\-\-start\-delete\fR=\fIstring\fR
Specify a string to mark begin of deleted text. The default is [\-.
.TP
\fB\-x\fR \fIstring\fR, \fB\-\-stop\-delete\fR=\fIstring\fR
Specify a string to mark end of deleted text. The default is \-].
.TP
\fB\-y\fR \fIstring\fR, \fB\-\-start\-insert\fR=\fIstring\fR
Specify a string to mark begin of inserted text. The default is {+.
.TP
\fB\-z\fR \fIstring\fR, \fB\-\-stop\-insert\fR=\fIstring\fR
Specify a string to mark end of inserted text. The default is +}.
.TP
\fB\-R\fR, \fB\-\-repeat\-markers\fR
Repeat the begin and end markers at the start and end of line if a change
crosses a newline.
.TP
\fB\-\-profile\fR=\fIname\fR
Load the profile with name \fIname\fR from the .dwdiffrc file in the user's home
directory. Only the last profile named is actually read, unless it is followed
by a \fB\-\-no\-profile\fR option. By default the profile named \fIdefault\fR is read.
.TP
\fB\-\-no\-profile\fR
Disable reading of profiles, unless followed by another \fB\-\-profile\fR option.
This also disables reading the \fIdefault\fR profile.
.PP
A single dash (\-) as a file can be used to denote standard input. Only one
file can be read from standard input. To stop \fBdwdiff\fR from interpreting
file names that start with a dash as options, one can specify a double dash
(\-\-) after which \fBdwdiff\fR will interpret any following arguments as files
to read.
.PP
The exit status of \fBdwdiff\fR indicates the result of the comparison: 0 if the files
are the same, 1 if the files are different. Should an error occur, \fBdwdiff\fR will
exit with status 2.
.SH PROFILES
Since version 2.1.0, \fBdwdiff\fR allows users to create profiles. A profile is a set
of options that can be loaded using the \fB\-\-profile\fR option. Profiles are
specified in the .dwdiffrc in the user's home directory. The format of
the .dwdiffrc file is as follows:
.PP
A line contains either a profile header (a text enclosed in []) or a long option
name without leading dashes, optionally followed by it's argument. Arguments are
stripped of whitespace surrounding it. If an argument starts and ends with
matching double or single quotes, these are removed as well. Comments can be
included by starting a line with a hash mark (#).
.PP
If no profile is specified, the profile named \fIdefault\fR will be loaded, unless
the \fB\-\-no\-profile\fR option is specified. Here is an example .dwdiffrc:
.PP
.po +5
[default]
.br
color red,blue
.br
punctuation
.PP
[html]
.br
start-delete
.br
stop-delete
.br
start-insert
.br
stop-insert
.PP
[space-only]
.br
# Use only a space as whitespace.
.br
white-space " "
.br
.po
.SH BUGS
If you think you have found a bug, please check that you are using the latest
version of \fBdwdiff\fR . When reporting
bugs, please include a minimal example that demonstrates the problem.
.SH AUTHOR
G.P. Halkes
.SH COPYRIGHT
Copyright \(co 2006\-2017 G.P. Halkes and others
.br
\fBdwdiff\fR is licensed under the GNU General Public License version 3.
.br
For more details on the license, see the file COPYING in the documentation
directory. On Un*x systems this is usually /usr/share/doc/dwdiff-$VERSION$.
.SH SEE\ ALSO
\fBdwfilter\fR(1), \fBwdiff\fR(1), \fBdiff\fR(1)
dwdiff-2.1.1/man/dwfilter.1 0000664 0001750 0001750 00000005402 13074617326 016555 0 ustar gertjan gertjan 0000000 0000000 .\" Generated by manscript from dwfilter.1.txt
.TH "DWFILTER" "1" "2015/09/21" "Version $VERSION$" "reformat text for processing"
.hw /usr/share/doc/dwdiff-$VERSION$ http://os.ghalkes.nl/dwdiff.html
.SH NAME
dwfilter \- reformat text with dwdiff for further processing
.SH SYNOPSIS
\fBdwfilter\fR [\fIOPTIONS\fR] \fIOLD FILE\fR \fINEW FILE\fR \fIPOST PROCESSOR\fR [\fIPOST PROCESSOR OPTIONS\fR]
.SH DESCRIPTION
\fBdwfilter\fR reformats the text in the old file according to the contents of
the new file (or vice versa) and subsequently passes the reformated old file
and the new file through a secondary filter. It's main use is to allow visual
diff programs such as \fBmeld\fR and \fBkdiff3\fR to be used, eventhough a text
file has been reformated after editing. A further use is to allow the creation
of small patches even when the new text has been reformated. \fBdwfilter\fR
uses \fBdwdiff\fR for reformatting.
.SH OPTIONS
.TP
\fB\-r\fR, \fB\-\-reverse\fR
Reformat the new file based on the contents of the old file instead of the
default where the old file is reformated based on the contents of the new file.
.PP
\fBdwfilter\fR accepts the following \fBdwdiff\fR options:
.PP
.RS
\fB\-d\fR \fIdelimiters\fR, \fB\-\-delimiters\fR=\fIdelimiters\fR
.br
\fB\-P\fR, \fB\-\-punctuation\fR
.br
\fB\-W\fR \fIwhitespace\fR, \fB\-\-white\-space\fR=\fIwhitespace\fR
.br
\fB\-i\fR, \fB\-\-ignore\-case\fR
.br
\fB\-I\fR, \fB\-\-ignore\-formatting\fR
.br
\fB\-D\fR \fIoption\fR, \fB\-\-diff\-option\fR=\fIoption\fR
.br
\fB\-C\fR\fInum\fR, \fB\-\-context\fR=\fInum\fR
.br
\fB\-m\fR\fInum\fR, \fB\-\-match\-context\fR=\fInum\fR
.br
\fB\-\-aggregate\-changes\fR
.br
\fB\-\-wdiff\-output\fR
.br
\fB\-A\fR \fIalgorithm\fR, \fB\-\-algorithm\fR=\fIalgorithm\fR
.br
\fB\-\-profile\fR=\fIname\fR, \fB\-\-no\-profile\fR
.RE
.PP
See the \fBdwdiff\fR manual page for the meaning.
.PP
A single dash (\-) as a file can be used to denote standard input. Only one
file can be read from standard input. To stop \fBdwfilter\fR from interpreting
file names that start with a dash as options, one can specify a double dash
(\-\-) after which \fBdwfilter\fR will interpret any following arguments as files
to read.
.SH BUGS
If you think you have found a bug, please check that you are using the latest
version of \fBdwdiff\fR . When reporting
bugs, please include a minimal example that demonstrates the problem.
.SH AUTHOR
G.P. Halkes
.SH COPYRIGHT
Copyright \(co 2006\-2010 G.P. Halkes
.br
dwdiff is licensed under the GNU General Public License version 3.
.br
For more details on the license, see the file COPYING in the documentation
directory. On Un*x systems this is usually /usr/share/doc/dwdiff-$VERSION$.
.SH SEE\ ALSO
\fBdwdiff\fR(1), \fBdiff\fR(1), \fBmeld\fR(1), \fBkdiff3\fR(1)
dwdiff-2.1.1/install.sh 0000775 0001750 0001750 00000006052 13074617326 016107 0 ustar gertjan gertjan 0000000 0000000 #!/bin/sh
# Copyright (C) 2009 G.P. Halkes
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3, as
# published by the Free Software Foundation.
#
# 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, see .
# Set zsh to emulate sh (otherwise all kinds of eval's wont work)
[ -n "${ZSH_VERSION}" ] && emulate sh
unset directory sources dest mode owner group strip
# Provide an alternative to !
not() {
if "$@" ; then
false
else
true
fi
}
# Default mode for install is rwxr-xr-x
mode="0755"
while [ $# -gt 0 ]
do
case "$1" in
-d)
directory="1"
;;
-m)
mode="$2"
shift
;;
-m*)
mode=`echo "$1" | sed 's/^-m//'`
;;
-o)
owner="$2"
shift
;;
-o*)
owner=`echo "$1" | sed 's/^-o//'`
;;
-g)
group="$2"
shift
;;
-g*)
group=`echo "$1" | sed 's/^-g//'`
;;
-s)
strip=1
;;
*)
break
;;
esac
shift
done
if [ "1" = "${directory}" ] ; then
if [ $# -eq 0 ] ; then
echo "No directory names supplied" >&2
exit 1
fi
# Create directories
while [ $# -gt 0 ]
do
if [ ! -d "$1" ] ; then
# Find name of dir to apply chown/chgrp -R to, i.e., the first part
# of the path that does not yet exist.
firstDir="$1"
testDir=`dirname "$1"`
while [ ! -d "${testDir}" ] ; do
firstDir="${testDir}"
testDir=`dirname "${firstDir}"`
done
# Make the directory
if not mkdir -p "$1" ; then
exit 1
fi
if not chmod -R "${mode}" "${firstDir}" ; then
exit 1
fi
# Set owner and group, if specified
if [ -n "${owner}" ] ; then
if not chown -R "${owner}" "${firstDir}" ; then
exit 1
fi
fi
if [ -n "${group}" ] ; then
if not chgrp -R "${group}" "${firstDir}" ; then
exit 1
fi
fi
fi
shift
done
exit
fi
if [ $# -lt 2 ] ; then
echo "No destination and/or source(s) supplied" >&2
exit 1
fi
install_file() {
if [ -d "$2" ] ; then
echo "Cannot install $2: directory in the way" >&2
exit 1
fi
# Copy file
if not cp "$1" "$2" ; then
exit 1
fi
# Set attributes
if [ -n "${mode}" ] ; then
if not chmod "${mode}" "$2" ; then
exit 1
fi
fi
if [ -n "${owner}" ] ; then
if not chown "${owner}" "$2" ; then
exit 1
fi
fi
if [ -n "${group}" ] ; then
if not chgrp "${group}" "$2" ; then
exit 1
fi
fi
if [ -n "${strip}" ] ; then
if not strip "$2" ; then
exit 1
fi
fi
}
eval dest="\"\${$#}\""
if [ -d "${dest}" ] ; then
# Loop over all sources, but do not process destination as source
while [ $# -gt 1 ]
do
install_file "$1" "${dest}/`basename \"$1\"`"
shift
done
elif [ $# -eq 2 ] ; then
install_file "$1" "${dest}"
else
echo "Multiple sources specified, but destination is not a directory" >&2
exit 1
fi
dwdiff-2.1.1/Makefile.in 0000664 0001750 0001750 00000007576 13074617326 016163 0 ustar gertjan gertjan 0000000 0000000 # Copyright (C) 2006-2010 G.P. Halkes
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3, as
# published by the Free Software Foundation.
#
# 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, see .
.POSIX:
# C-compiler flags
# If strdup is not provided by the library, add -DNO_STRDUP.
CFLAGS=-O2
# Installation prefix
prefix=/usr/local
# Gettext configuration
# GETTEXTFLAGS should contain -DUSE_GETTEXT to enable gettext translations
# GETTEXTLIBS should contain all link flags to allow linking with gettext, if
# it has been enabled. The GNU libc already contains the gettext library, so
# there is no need to add any flags. Otherwise, -lintl is usually required, and
# sometimes -liconv as well.
GETTEXTFLAGS=
GETTEXTLIBS=
# Gettext related
# LOCALEDIR: the directory where the locale dependant files should be installed.
# LINGUAS: translations to be installed. Look in po directory for available
# translations.
LOCALEDIR=$(prefix)/share/locale
LINGUAS=
# Unicode/ICU config
# If unicode support is to be enabled, the following flags should be set
# ICUFLAGS: should at least be set to -DUSE_UNICODE. If the nl_langinfo
# function is available it should also include -DUSE_NL_LANGINFO. Furthermore
# it should include any C preprocessor flags that are required for building.
# Usually this can be `icu-config --cppflags`.
# ICULIBS: Flags to link to ICU, usually `icu-config --ldflags`
ICUFLAGS=
ICULIBS=
# Install program to use (should provide -m and -d options)
INSTALL=install
# dwfilter on/off. To disable the compilation of dwfilter, comment the next
# line or set DWFILTER to empty
DWFILTER=yes
# Miscelaneous install paths
bindir=$(prefix)/bin
docdir=$(prefix)/share/doc/dwdiff-2.1.1
mandir=$(prefix)/share/man
all: dwdiff $(DWFILTER:yes=dwfilter) linguas
.PHONY: all clean dist-clean install dwdiff-install lingua-install linguas
OBJECTS_DWDIFF=src/doDiff.o src/diff/analyze.o src/file.o src/option.o src/unicode.o src/buffer.o src/hashtable.o src/profile.o src/dwdiff.o src/util.o src/tempfile.o src/stream.o
OBJECTS_DWFILTER=src/dwfilter.o src/util.o
clean:
rm -rf src/*.o po/*.mo
dist-clean: clean
rm -rf dwdiff config.log Makefile
.c.o:
$(CC) $(CFLAGS) -Isrc $(GETTEXTFLAGS) -DOPTION_STRDUP=strdupA -DLOCALEDIR=\"$(LOCALEDIR)\" $(ICUFLAGS) -c -o $@ $<
dwdiff: $(OBJECTS_DWDIFF)
$(CC) $(CFLAGS) $(LDFLAGS) -o dwdiff $(OBJECTS_DWDIFF) $(LDLIBS) $(ICULIBS) $(GETTEXTLIBS)
dwfilter: $(OBJECTS_DWFILTER)
$(CC) $(CFLAGS) $(LDFLAGS) -o dwfilter $(OBJECTS_DWFILTER) $(LDLIBS) $(ICULIBS) $(GETTEXTLIBS)
linguas:
cd po && $(MAKE) "LINGUAS=$(LINGUAS)" linguas
# Macros to make DESTDIR support more readable
_bindir=$(DESTDIR)$(bindir)
_docdir=$(DESTDIR)$(docdir)
_mandir=$(DESTDIR)$(mandir)
install: dwdiff-install lingua-install
dwdiff-install: dwdiff $(DWFILTER:yes=dwfilter)
$(INSTALL) -d "$(_bindir)"
$(INSTALL) dwdiff "$(_bindir)"
if [ -n "$(DWFILTER)" ] ; then $(INSTALL) dwfilter "$(_bindir)" ; fi
$(INSTALL) -d "$(_mandir)/man1"
$(INSTALL) -m 644 "man/dwdiff.1" "$(_mandir)/man1"
if [ -n "$(DWFILTER)" ] ; then $(INSTALL) -m 644 "man/dwfilter.1" "$(_mandir)/man1" ; fi
$(INSTALL) -d "$(_docdir)"
$(INSTALL) -m 644 README COPYING Changelog "$(_docdir)"
# Work around empty LINGUAS list. Some shells don't like empty lists in for-loops
lingua-install:
if [ -n "$(LINGUAS)" ] ; then \
mandir="$(_mandir)" LINGUAS="$(LINGUAS)" INSTALL="$(INSTALL)" ./install-manpages $(DWFILTER) ;\
cd po && $(MAKE) "LOCALEDIR=$(DESTDIR)$(LOCALEDIR)" "INSTALL=$(INSTALL)" "LINGUAS=$(LINGUAS)" lingua-install ;\
fi
dwdiff-2.1.1/config.pkg 0000664 0001750 0001750 00000013716 13074617326 016057 0 ustar gertjan gertjan 0000000 0000000 # Copyright (C) 2006-2011 G.P. Halkes
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3, as
# published by the Free Software Foundation.
#
# 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, see .
EXTENSIONS="c verbose_compile gettext"
SWITCHES="+unicode +dwfilter"
OPTIONS="icu-config"
COMPILERULE='$(CC) $(CFLAGS) $(GETTEXTFLAGS) $(ICUFLAGS) -c -o $@ $<'
LINKRULE='$(CC) $(CFLAGS) $(LDFLAGS) -o .config .config.o $(LDLIBS) $(GETTEXTLIBS) $(ICULIBS)'
DEFAULT_LINGUAS="de es fr nl"
[ -f config.pkg.langpack ] && . config.pkg.langpack
USER_HELP=" --without-unicode Disable Unicode support
--icu-config= Path to icu-config script
Environment variables:
LINGUAS List of languages to install. Available languages are:
${DEFAULT_LINGUAS}"
checkfunction() {
clean .config.o
CHECKFOR="$1"
CODE="$2"
shift 2
{
for INCLUDE
do
echo "#include ${INCLUDE}"
done
cat < .config.c
clean .config.o
test_link "${CHECKFOR}"
}
config() {
# Test for all required functionality
cat > .config.c <
#include
#include
#include
#include
int main(int argc, char *argv[]) {
int fd;
char buffer[10];
fd = mkstemp("fooXXXXXX");
fd = open("foo", O_RDONLY);
read(fd, buffer, 10);
lseek(fd, 0, SEEK_SET);
write(fd, buffer, 10);
close(fd);
umask(~S_IRWXU);
snprintf(buffer, 10, "%s", "foo");
return 0;
}
EOF
clean .config.o
test_link "required functions" || {
check_message_result "Testing required functions seperatly for debugging purposes"
checkfunction "mkstemp" 'int fd; fd = mkstemp("fooXXXXXX");' ""
checkfunction "open" 'int fd; fd = open("foo", O_RDONLY);' "" ""
checkfunction "read" 'int fd; char buffer[10]; read(fd, buffer, 10);' ""
checkfunction "lseek" "int fd; lseek(fd, 0, SEEK_SET);" ""
checkfunction "write" "int fd; char buffer[10]; write(fd, buffer, 10);" ""
checkfunction "close" 'int fd; close(fd);' ""
checkfunction "umask" 'umask(~S_IRWXU);' ""
checkfunction "snprintf" 'char buffer[10]; snprintf(buffer, 10, "%s", "foo");' ""
check_message_result "!! A required function is not available."
exit 1
}
if [ "yes" = "${with_dwfilter}" ] ; then
cat > .config.c <
#include
int main(int argc, char *argv[]) {
pid_t pid = fork();
execvp("test", NULL);
}
EOF
clean .config.o
test_link "required functions for dwfilter" || {
checkfunction "fork" 'pid_t pid = fork();' "" ""
checkfunction "execvp" 'execvp("test", NULL);' ""
check_message_result "!! A required function for dwfilter is not availabe. Try configuring with --without-dwfilter"
exit 1
}
DWFILTER="DWFILTER=yes"
else
DWFILTER="DWFILTER="
fi
checkfunction "strdup" "strdup(\"foo\");" "" || CFLAGS="${CFLAGS} -DNO_STRDUP"
if [ "yes" = "${with_gettext}" ] || [ "yes" = "${with_unicode}" ] ; then
checkfunction "setlocale" "setlocale(LC_ALL, \"\");" "" || {
check_message_result "!! Gettext and Unicode support unavailable. Try configuring with --without-gettext --without-unicode"
exit 1
}
fi
if [ "yes" = "${with_unicode}" ] ; then
ICU_CONFIG="${option_icu_config:-icu-config}"
{
{
check_message "Checking for icu-config... "
echo "Running which ${ICU_CONFIG} >/dev/null" >> config.log
if not which "${ICU_CONFIG}" >/dev/null ; then
check_message_result "no"
check_message_result "!! Could not find icu-config. Ensure that you have installed the development files for libicu, or try configuring with --without-unicode"
exit 1
fi
echo "Running ${ICU_CONFIG} --cppflags --ldflags" >> config.log
if "${ICU_CONFIG}" --cppflags --ldflags 2>&1 > /dev/null ; then
check_message_result "yes"
true
else
check_message_result "no"
false
fi
} && {
check_message "Checking ICU version >= 3.4... "
echo "Running ${ICU_CONFIG} --version" >> config.log
ICUVERSION="`\"${ICU_CONFIG}\" --version`"
ICUMAJOR="`echo \"${ICUVERSION}\" | sed 's/\..*//'`"
ICUMINOR="`echo \"${ICUVERSION}\" | sed -e 's/^[^.]*\.//' -e 's/[^0-9]*$//'`"
if expr 3 \< "${ICUMAJOR}" \| \( 3 = "${ICUMAJOR}" \& 4 \<= "${ICUMINOR}" \) >/dev/null ; then
check_message_result "yes (${ICUVERSION})"
true
else
check_message_result "no (${ICUVERSION})"
false
fi
} && {
cat > .config.c <
#include
#include
int main(int argc, char *argv[]) {
UChar32 c;
UChar *a, *b;
UErrorCode error = U_ZERO_ERROR;
u_isUWhiteSpace(c);
U_GET_GC_MASK(c);
u_strFoldCase(a, 10, b, 10, U_FOLD_CASE_DEFAULT, &error);
unorm_normalize(a, 10, UNORM_NFKD, 0, b, 10, &error);
u_getIntPropertyValue(c, UCHAR_GRAPHEME_CLUSTER_BREAK);
return 0;
}
EOF
clean .config.o
test_link "functions in libicu" ICUFLAGS="\`\"${ICU_CONFIG}\" --cppflags\`" ICULIBS="\`\"${ICU_CONFIG}\" --ldflags\`"
}
} || {
check_message_result "!! Could not compile with Unicode support. Try configuring with --without-unicode."
exit 1
}
ICUFLAGS="-DUSE_UNICODE \`\"${ICU_CONFIG}\" --cppflags\`"
ICULIBS="\`\"${ICU_CONFIG}\" --ldflags\`"
if checkfunction "nl_langinfo" "nl_langinfo(CODESET);" "" ; then
ICUFLAGS="${ICUFLAGS} -DUSE_NL_LANGINFO"
fi
fi
create_makefile ${option_localedir:+"LOCALEDIR=${option_localedir}"} \
"ICUFLAGS=${ICUFLAGS}" "ICULIBS=${ICULIBS}" $DWFILTER
}
sed_lines() {
add_replace_settings "$@"
}
dwdiff-2.1.1/Changelog 0000664 0001750 0001750 00000020564 13074617326 015720 0 ustar gertjan gertjan 0000000 0000000 Version 2.1.1:
New features:
- The --diff-input option now also has short option name -u.
Bug fixes:
- Parsing multiple short options passed as a single string (e.g. -Pc for
-P -c) works again.
- Diff input correctly parses the input when used in non-unicode locales.
- Correctly identify the second input if that is stdin.
Version 2.1.0:
New features:
- A new config file with profiles has been introduced. Profile selection is
done with the --profile option. Disabling the default profile can be done
thorugh the --no-profile option.
Bug fixes:
- Display the version with the --version flag.
Version 2.0.10:
Bug fixes:
- The flag --less-mode was advertised, but the actual accepted name was
--less. This has been changed to accept --less-mode and emit a warning on
the old flag.
Version 2.0.9:
Fix typo in configure script which prevents it from running.
Version 2.0.8:
Bug fixes:
- Fix build against glibc 2.16 and newer.
Miscelaneous changes:
- Added correct breaking for Unicode Regional Indicators.
Version 2.0.7:
Fix incorrect release of 2.0.6, which omitted changes from the 2.0.5
release.
Version 2.0.6
Bug fixes:
- Fix incorrect line numbering caused by optimized printing of differences
preceeded in both files by whitespace containing newlines.
Version 2.0.5:
Bug fixes:
- fix a bug in the handling of context, which caused incorrect diffs to be
printed. In some cases text was reported both as removed and as common.
Version 2.0.4:
Bug fixes:
- fix a memory allocation bug, which would be triggered by large values
for the -m option.
Version 2.0.3:
Updated translations for German (incomplete), French (incomplete) and
Spanish.
Version 2.0.2:
Bug fixes:
- add support for DESTDIR in the Makefile install target.
- fix build error on --without-unicode compiles.
Version 2.0.1:
Bug fixes:
- escape parsing for unknown escapes (like for example \;) would result
in incorrect strings.
- when the space character is not considered whitespace, dwdiff will no
longer use it to make changes more readable.
Internal changes:
- the token file no longer uses a newline character as separator, but a nul
character, making reparsing easier and the whole of dwdiff more robust.
Version 2.0:
New features:
- dwdiff is no longer a front-end for the diff program, but includes its
own diff-algorithm implementation (taken from GNU diff). This makes
dwdiff much faster, and ensures that results are the same across all
platforms.
Bug fixes:
- when two additions are made with a single common token in between, dwdiff
incorrectly reported the common token as replaced by the two additions
and the common token.
- when using the --diff-input option, in some cases dwdiff would report
changes spanning multiple diff sections.
Version 1.9:
New features:
- an option for reading the output of diff (in Unified Diff) format was
added (--diff-input) which can be used to reformat diffs for enhanced
readability.
Version 1.8.2:
New features:
- when parsing options, \e is understood to mean the escape character
Bug fixes:
- the -p and -l options did not correctly handle UTF-8 characters.
Version 1.8.1:
New features:
- the context matching now decreases the matched context size exponentially,
instead of immediately using the context-less mode if the changes found
with context need refining. This results in more intuitive output when
increasing the matching context size.
Version 1.8:
New features:
- added an option to repeat the begin and end markers at the start and end
of line if a change crosses a newline
Bug fixes:
- newline characters in the delimiter set where handled incorrectly in
UTF-8 mode
Version 1.7:
New features:
- added dwfilter program which reformats the old text such that it matches
the new text to allow other programs such as visual diff tools to show
the differences better. dwfilter can also be used to generate regular
diffs to patch the old text by reformating the new text as the old text
before executing diff [with caveats].
Bug fixes:
- \u and \U escapes were parsed incorrectly
- dwdiff crashed when generating statistics for empty files
Version 1.6.1:
Bug fixes:
- the line clearing implemented for better background color handling caused
(parts of) lines to disappear in the output with cr/nl line endings
- the new paragraph separators were not handled correctly with the context
matching feature, causing crashes
Version 1.6:
New features:
- the output for deleted text was changed such that the whitespace following
the deletion is now the whitespace that followed the deleted text, rather
than the whitespace that is now before the next word in the new file
- the output for changed text was changed in the case that the whitespace
before the old text contains a newline. In this case, depending on whether
the whitespace before the new text contains a newline, the new file's
whitespace is replaced by a single space or the new text is printed first
- the --wdiff-output option was added to revert to the old output mode
- the color specification now also allows specification of the background
color and custom escape sequences
- a new option to add markers for deleted or inserted blocks of lines
containing only whitespace (paragraph separators)
Bug fixes:
- octal escapes are now properly parsed
- icu-config was called directly rather than through $ICU_CONFIG in the
configure script, which prevented proper operation of the --icu-config
option
- compilation on systems without nl_langinfo was fixed
Version 1.5.2
New features:
- better option parsing, which allows short options to be grouped. For
example -iPc instead of -i -P -c
Bug fixes:
- compatibility fixes to configure script
Version 1.5.1:
Bug fixes:
- Statistics are now printed to stderr instead of stdout such that the only
thing printed on stdout is the text
- Repaired code for diff programs that do not support -a option
- Fixed Makefile to pass settings for diff program to C compiler
Version 1.5:
New features:
- added match context which ensures better results for large edits with
frequently occuring words
Version 1.4:
New features:
- an option to use all punctuation characters as delimiters
- Unicode (UTF-8) support including formatting insensitive comparison
- case-insensitive comparison when the underlying diff program does not
support it
Version 1.3:
New features:
- an option to print the line numbers at which the text appears in the old
and new file respectively
- an option to print only a user specified number of context lines
surrounding lines with changes
Bug fixes:
- added printing of color mark-up codes at the start of every new line to
improve viewing the output with less
- the check for overlap between the delimiter and the whitespace sets was
incomplete and incorrect
- an inverted condition caused incorrect output if the newline character
was not part of the whitespace set
Version 1.2.1:
Bug fixes:
- fixed a bug in color mode where the resetting of a color would cause
extra characters to be printed
Version 1.2:
New features:
- color mode, enabled by -c
- internationalisation. dwdiff can now use gettext and includes translated
manual pages. Languages included in the distribution are: en (default)
and nl.
Version 1.1:
Bug fixes:
- fixes bug in \x-escape expansion of whitespace and delimiter lists
New features:
- will work on POSIX compatible diff, i.e. diff without -a and -i options
- handles empty whitespace specification
- allows nul character in whitespace and delimiter specification
- \-escape expansion of start/stop-delete/insert markers
- a single dash (-) can be specified as a file to read from standard input
- a double dash (--) will stop dwdiff from interpreting arguments starting
with a dash as options
- allows the user to specify further options to the diff program to allow
minimal diffs, half-hearted algorithms etc.
dwdiff-2.1.1/COPYING 0000664 0001750 0001750 00000104513 13074617326 015136 0 ustar gertjan gertjan 0000000 0000000 GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
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 3 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, see .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Copyright (C)
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
.
dwdiff-2.1.1/src/ 0000775 0001750 0001750 00000000000 13074617326 014666 5 ustar gertjan gertjan 0000000 0000000 dwdiff-2.1.1/src/profile.h 0000664 0001750 0001750 00000001627 13074617326 016505 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2015 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#ifndef PROFILE_H
#define PROFILE_H
typedef struct ProfileOption {
char *arg;
struct ProfileOption *next;
} ProfileOption;
ProfileOption *loadProfile(const char *name, const char *profile);
void applyProfileOptions(ProfileOption *options, void (*parse)(int argc, char *argv[]), char *argv0);
#endif
dwdiff-2.1.1/src/stream.h 0000664 0001750 0001750 00000003747 13074617326 016345 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2008-2011 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#ifndef STREAM_H
#define STREAM_H
typedef struct Stream Stream;
#include "definitions.h"
#include "file.h"
#include "util.h"
#include "unicode.h"
typedef struct StreamVtable {
int (*getChar)(struct Stream *);
int (*ungetChar)(struct Stream *, int c);
} StreamVtable;
struct Stream {
StreamVtable *vtable;
union {
File *file;
struct {
const char *string;
size_t length;
size_t index;
} string;
} data;
#ifdef USE_UNICODE
/* Buffered character for grapheme cluster breaking. */
UChar32 bufferedChar;
int32_t lastClusterCategory;
/* High surrogate buffer for converting from UTF-16 to UTF-8. */
UChar32 highSurrogate;
/* Character already read when checking for low surrogate. */
UChar32 nextChar;
#endif
};
Stream *newFileStream(File *file);
Stream *newStringStream(const char *string, size_t length);
bool isFileStream(const Stream *stream);
#define sferror(s) (fileError((s)->data.file))
#define sfflush(s) (fileFlush((s)->data.file))
#define srewind(s) (fileRewind((s)->data.file, FILE_READ))
#define sfeof(s) (fileEof((s)->data.file))
// Note: this gets a single byte character, rather than a UTF-8 character
#define sgetc(s) (fileGetc((s)->data.file))
#define sputc(s, c) (filePutc((s)->data.file, c))
#define swrite(s, buf, bytes) (fileWrite((s)->data.file, buf, bytes))
#define sgeterrno(s) (fileGetErrno((s)->data.file))
void sfclose(Stream *stream);
#endif
dwdiff-2.1.1/src/file.h 0000664 0001750 0001750 00000004605 13074617326 015763 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2008-2011 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#ifndef FILE_H
#define FILE_H
#include "buffer.h"
/* Note: files opened for write can also be read. However, not vice versa. */
typedef enum {
FILE_READ,
FILE_WRITE
} FileMode;
typedef enum {
EOF_NO,
EOF_COMING,
EOF_HIT
} EOFState;
#ifdef PAGE_SIZE
#define FILE_BUFFER_SIZE PAGE_SIZE
#else
#define FILE_BUFFER_SIZE 4096
#endif
struct FileVtable;
typedef struct File {
/* FD this struct is buffering */
int fd;
/* 0, or the error code of the failed operation. Further operations will
fail immediately. */
int errNo;
/* Current mode associated with the File. */
FileMode mode;
/* Buffer containing data from the file. */
char buffer[FILE_BUFFER_SIZE];
int bufferFill;
/* Current index in the buffer. */
int bufferIndex;
/* Flag to indicate whether filling the buffer hit end of file. */
EOFState eof;
struct FileVtable *vtable;
} File;
typedef struct FileVtable {
/* Functions to call for fileWrite and filePutc. Different implementations
are provided such that diffing with context can be easily done. */
int (*fileWrite)(struct File *file, const char *buffer, int bytes);
int (*filePutc)(struct File *file, int c);
int (*fileFlush)(struct File *file);
} FileVtable;
#define fileFlush(file) ((file)->vtable->fileFlush(file))
#define filePutc(file, c) ((file)->vtable->filePutc((file), (c)))
#define fileWrite(file, buffer, bytes) ((file)->vtable->fileWrite((file), (buffer), (bytes)))
File *fileWrapFD(int fd, FileMode mode);
File *fileOpen(const char *name, FileMode mode);
int fileGetc(File *file);
int fileUngetc(File *file, int c);
int fileClose(File *file);
int filePuts(File *file, const char *string);
int fileRewind(File *file, FileMode mode);
int fileError(File *file);
int fileGetErrno(File *file);
int fileEof(File *file);
void fileClearEof(File *file);
#endif
dwdiff-2.1.1/src/unicode.c 0000664 0001750 0001750 00000040505 13074617326 016464 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2008-2011 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#ifdef USE_UNICODE
#include
#include
#include
#include
#include "definitions.h"
#include "unicode.h"
#include "static_assert.h"
#include "option.h"
/*****************************************************************************
Input and output of UTF-8 streams
*****************************************************************************/
/** Read in one UTF-8 character.
@param stream The @a Stream to read.
@retval The first UCS4 character found in the file or an error code.
This function does not try to decode sequences that should not occur in
valid UTF-8. Because it is more likely that the input is non-UTF-8 than
that some program created invalid UTF-8 this is the more sane option.
*/
static UChar32 getuc(Stream *stream) {
int c, bytesLeft;
UChar32 retval, least;
if ((c = stream->vtable->getChar(stream)) == EOF)
return EOF;
switch (c) {
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15:
case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23:
case 24: case 25: case 26: case 27: case 28: case 29: case 30: case 31:
case 32: case 33: case 34: case 35: case 36: case 37: case 38: case 39:
case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47:
case 48: case 49: case 50: case 51: case 52: case 53: case 54: case 55:
case 56: case 57: case 58: case 59: case 60: case 61: case 62: case 63:
case 64: case 65: case 66: case 67: case 68: case 69: case 70: case 71:
case 72: case 73: case 74: case 75: case 76: case 77: case 78: case 79:
case 80: case 81: case 82: case 83: case 84: case 85: case 86: case 87:
case 88: case 89: case 90: case 91: case 92: case 93: case 94: case 95:
case 96: case 97: case 98: case 99: case 100: case 101: case 102: case 103:
case 104: case 105: case 106: case 107: case 108: case 109: case 110: case 111:
case 112: case 113: case 114: case 115: case 116: case 117: case 118: case 119:
case 120: case 121: case 122: case 123: case 124: case 125: case 126: case 127:
return c;
case 128: case 129: case 130: case 131: case 132: case 133: case 134: case 135:
case 136: case 137: case 138: case 139: case 140: case 141: case 142: case 143:
case 144: case 145: case 146: case 147: case 148: case 149: case 150: case 151:
case 152: case 153: case 154: case 155: case 156: case 157: case 158: case 159:
case 160: case 161: case 162: case 163: case 164: case 165: case 166: case 167:
case 168: case 169: case 170: case 171: case 172: case 173: case 174: case 175:
case 176: case 177: case 178: case 179: case 180: case 181: case 182: case 183:
case 184: case 185: case 186: case 187: case 188: case 189: case 190: case 191:
return UEINVALID;
case 192: case 193:
return UEOVERLONG;
case 194: case 195: case 196: case 197: case 198: case 199: case 200: case 201:
case 202: case 203: case 204: case 205: case 206: case 207: case 208: case 209:
case 210: case 211: case 212: case 213: case 214: case 215: case 216: case 217:
case 218: case 219: case 220: case 221: case 222: case 223:
least = 0x80;
bytesLeft = 1;
retval = c & 0x1F;
break;
case 224: case 225: case 226: case 227: case 228: case 229: case 230: case 231:
case 232: case 233: case 234: case 235: case 236: case 237: case 238: case 239:
least = 0x800;
bytesLeft = 2;
retval = c & 0x0F;
break;
case 240: case 241: case 242: case 243: case 244:
least = 0x10000L;
bytesLeft = 3;
retval = c & 0x07;
break;
case 245: case 246: case 247: case 248: case 249: case 250: case 251: case 252:
case 253: case 254: case 255:
return UETOOLARGE;
default:
PANIC();
}
for (; bytesLeft > 0; bytesLeft--) {
if ((c = stream->vtable->getChar(stream)) == EOF)
return UEINVALID;
if ((c & 0xC0) != 0x80) {
stream->vtable->ungetChar(stream, c);
return UEINVALID;
}
retval = (retval << 6) | (c & 0x3F);
}
if (retval < least)
return UEOVERLONG;
if (retval > 0x10FFFFL)
return UETOOLARGE;
return retval;
}
#define REPLACEMENT_CHARACTER 0xFFFD;
/** Get a character from a @a Stream, converting strange characters to REPLACEMENT CHARACTER.
@param stream The @a Stream to read from.
@return a UCS-4 character or EOF on end-of-file or error.
*/
UChar32 getucFiltered(Stream *stream) {
UChar32 c;
if (stream->nextChar >= 0) {
c = stream->nextChar;
stream->nextChar = -1;
} else {
c = getuc(stream);
}
if (c < 0) {
return c == EOF ? EOF : REPLACEMENT_CHARACTER;
} else if ((c & 0xF800L) == 0xD800L) {
UChar32 clow;
/* If we just encountered a low surrogate, just replace. */
if ((c & 0xDC00) == 0xDC00)
return REPLACEMENT_CHARACTER;
clow = getuc(stream);
if ((clow & 0xFC00) != 0xDC00) {
/* Buffer last read char so we can return next time. Return
replacement for high surrogate encountered earlier. */
stream->nextChar = clow;
return REPLACEMENT_CHARACTER;
}
c = clow + (c << 10) + 0x10000 - (0xD800 << 10) - 0xDC00;
}
return c;
}
/** Convert one UCS-4 character to UTF-8.
@param c The character to convert.
@param buffer The buffer to write the result.
@return the number of @a char's written to @a buffer.
This function does not check for high/low surrogates.
*/
int convertToUTF8(UChar32 c, char *buffer) {
int bytes, i;
if (c > 0x10000L)
bytes = 4;
else if (c > 0x800)
bytes = 3;
else if (c > 0x80)
bytes = 2;
else {
buffer[0] = c;
return 1;
}
for (i = bytes - 1; i >= 0; i--) {
buffer[i] = (c & 0x3F) | 0x80;
c >>= 6;
}
buffer[0] |= 0xF0 << (4 - bytes);
return bytes;
}
/** Convert one UCS-4 character to UTF-8, filtering surrogates.
@param c The character to convert.
@param buffer The buffer to write the result.
@param highSurrogate The location where highSurrogates are stored for this conversion.
@return the number of @a char's written to @a buffer.
This function also translates high/low surrogate pairs to a single UCS-4
character. Therefore it can also be used to convert UTF-16 to UTF-8. Note
that the characters written are supposed to form a valid UTF-16 string, so
every high surrogate has to be followed by a low surrogate, and every low
surrogate has to be preceeded by a high surrogate.
*/
int filteredConvertToUTF8(UChar32 c, char *buffer, UChar32 *highSurrogate) {
if ((c & 0xFC00) == 0xD800) {
*highSurrogate = c;
return 0;
}
if ((c & 0xFC00) == 0xDC00) {
ASSERT(*highSurrogate != 0);
c = c + ((*highSurrogate) << 10) + 0x10000 - (0xD800 << 10) - 0xDC00;
*highSurrogate = 0;
}
return convertToUTF8(c, buffer);
}
/** Write out one UCS-4 character as UTF-8.
@param stream The @a Stream to write.
@param c The character to write.
@return 0 for succes, EOF for failure.
See @a filteredConvertToUTF8 for more details on the conversion.
*/
int putuc(Stream *stream, UChar32 c) {
char encoded[4];
int bytes = filteredConvertToUTF8(c, encoded, &stream->highSurrogate);
if (bytes == 0)
return 0;
return fileWrite(stream->data.file, encoded, bytes) < bytes ? EOF : 0;
}
#define MAX_GCB_CLASS 13
/* This will warn us if we compile against a new library with more cluster
break classes. */
#ifdef DEBUG
static_assert(U_GCB_COUNT <= MAX_GCB_CLASS);
#endif
/* Latest version of the algorithm, with all its classes, can be found at
http://www.unicode.org/reports/tr29/
*/
/* The values of the constants _should_ not change from version to version,
but better safe than sorry. */
static_assert(U_GCB_OTHER == 0);
static_assert(U_GCB_CONTROL == 1);
static_assert(U_GCB_CR == 2);
static_assert(U_GCB_EXTEND == 3);
static_assert(U_GCB_L == 4);
static_assert(U_GCB_LF == 5);
static_assert(U_GCB_LV == 6);
static_assert(U_GCB_LVT == 7);
static_assert(U_GCB_T == 8);
static_assert(U_GCB_V == 9);
#if ICU_VERSION_MAJOR_NUM > 3
static_assert(U_GCB_SPACING_MARK == 10);
static_assert(U_GCB_PREPEND == 11);
#if ICU_VERSION_MAJOR_NUM > 4
static_assert(U_GCB_REGIONAL_INDICATOR == 12);
#endif
#endif
/** Table used for determining wheter a grapheme break exists between two characters. */
static char clusterContinuationTable[MAX_GCB_CLASS][MAX_GCB_CLASS] = {
{0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, CRLF_GRAPHEME_CLUSTER_BREAK, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0},
{0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0},
{1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1},
{0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1}
};
/** Table used for determining wheter a break exists between two characters when using backspace to overstrike characters. */
static char backspaceContinuationTable[MAX_GCB_CLASS][MAX_GCB_CLASS] = {
{0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
static void addUCS4ToUTF16Buffer(UTF16Buffer *buffer, UChar32 c);
/** Retrieve the Grapheme cluster break category for a character.
This ensures that cluster categories above the compiled-in maximum are
folded to the "other" category. Should a newer version of the ICU library
return values higher than the expected value, this will not pose
significant problems.
*/
static int getClusterCategory(UChar32 c) {
int category = u_getIntPropertyValue(c, UCHAR_GRAPHEME_CLUSTER_BREAK);
return category >= MAX_GCB_CLASS ? U_GCB_OTHER : category;
}
/** Get the next Cluster from a stream.
@param stream The @a Stream to read.
@param buffer The @a UTF16Buffer to store the Cluster.
@param continuationTable The table to use for looking up continuations.
@return a boolean indicating whether the reading was successful.
*/
static bool getClusterInternal(Stream *stream, UTF16Buffer *buffer, char continuationTable[MAX_GCB_CLASS][MAX_GCB_CLASS]) {
int32_t newClusterCategory;
/* Empty buffer. */
buffer->used = 0;
/* Check if we have already read a character on a previous occasion. */
if (stream->bufferedChar < 0) {
if ((stream->bufferedChar = getucFiltered(stream)) < 0)
return false;
newClusterCategory = getClusterCategory(stream->bufferedChar);
} else {
newClusterCategory = stream->lastClusterCategory;
}
/* Append characters as long as the cluster categories dictate. */
do {
stream->lastClusterCategory = newClusterCategory;
addUCS4ToUTF16Buffer(buffer, stream->bufferedChar);
if ((stream->bufferedChar = getucFiltered(stream)) < 0) {
if (isFileStream(stream))
fileClearEof(stream->data.file);
newClusterCategory = 0;
break;
}
newClusterCategory = getClusterCategory(stream->bufferedChar);
} while (continuationTable[stream->lastClusterCategory][newClusterCategory]);
/* Save grapheme category for next call. */
stream->lastClusterCategory = newClusterCategory;
return true;
}
/** Get the next Grapheme Cluster from a stream.
@param stream The @a Stream to read.
@param buffer The @a UTF16Buffer to store the Grapheme Cluster.
@return a boolean indicating whether the reading was successful.
*/
bool getCluster(Stream *stream, UTF16Buffer *buffer) {
return getClusterInternal(stream, buffer, clusterContinuationTable);
}
/** Get the next Backspace Cluster from a stream.
@param stream The @a Stream to read.
@param buffer The @a UTF16Buffer to store the Backspace Cluster.
@return a boolean indicating whether the reading was successful.
*/
bool getBackspaceCluster(Stream *stream, UTF16Buffer *buffer) {
return getClusterInternal(stream, buffer, backspaceContinuationTable);
}
/*****************************************************************************
UTF16Buffer operations
*****************************************************************************/
/** Append a 24 bit UCS-4 character to a UTF-16 encoded buffer.
@param buffer The UTF16Buffer structure to append to.
@param c The character to append.
*/
static void addUCS4ToUTF16Buffer(UTF16Buffer *buffer, UChar32 c) {
if (c > 0x10000) {
VECTOR_APPEND(*buffer, 0xD800 - (0x10000 >> 10) + (c >> 10));
VECTOR_APPEND(*buffer, 0xDC00 | (c & 0x3FF));
} else {
VECTOR_APPEND(*buffer, (UChar) c);
}
}
/** Decompose a Grapheme Cluster according to the standard decomposition.
@param c The Grapheme Cluster to decompose.
*/
void decomposeChar(CharData *c) {
UErrorCode error = U_ZERO_ERROR;
size_t requiredLength;
requiredLength = unorm_normalize(c->UTF8Char.original.data, c->UTF8Char.original.used, option.decomposition, 0,
c->UTF8Char.converted.data, c->UTF8Char.converted.allocated, &error);
if (requiredLength > c->UTF8Char.converted.allocated) {
ASSERT(error == U_BUFFER_OVERFLOW_ERROR);
error = U_ZERO_ERROR;
VECTOR_ALLOCATE(c->UTF8Char.converted, requiredLength * sizeof(UChar));
requiredLength = unorm_normalize(c->UTF8Char.original.data, c->UTF8Char.original.used, option.decomposition, 0,
c->UTF8Char.converted.data, c->UTF8Char.converted.allocated, &error);
}
ASSERT(U_SUCCESS(error));
c->UTF8Char.converted.used = requiredLength;
}
/** Fold the case of a Grapheme Cluster.
@param c The Grapheme Cluster to case-fold.
*/
void casefoldChar(CharData *c) {
UErrorCode error = U_ZERO_ERROR;
size_t requiredLength;
requiredLength = u_strFoldCase(c->UTF8Char.casefolded.data, c->UTF8Char.casefolded.allocated, c->UTF8Char.converted.data,
c->UTF8Char.converted.used, U_FOLD_CASE_DEFAULT, &error);
if (requiredLength > c->UTF8Char.casefolded.allocated) {
ASSERT(error == U_BUFFER_OVERFLOW_ERROR);
error = U_ZERO_ERROR;
c->UTF8Char.casefolded.data = realloc(c->UTF8Char.casefolded.data, requiredLength * sizeof(UChar));
c->UTF8Char.casefolded.allocated = requiredLength;
requiredLength = u_strFoldCase(c->UTF8Char.casefolded.data, c->UTF8Char.casefolded.allocated, c->UTF8Char.converted.data,
c->UTF8Char.converted.used, U_FOLD_CASE_DEFAULT, &error);
}
ASSERT(U_SUCCESS(error));
c->UTF8Char.casefolded.used = requiredLength;
}
/* An initial size of 4 will provide sufficient space for most graphmes. There
may be some cases in which this is not enough, but the buffer will be grown
if this is the case. The reason for the ifndef is that for testing of the
code to grow the buffers, we need to be able to set the size to 1.
*/
#ifndef UTF16BUFFER_INITIAL_SIZE
#define UTF16BUFFER_INITIAL_SIZE 4
#endif
/** Compare two UTF16Buffer's for sorting purposes */
int compareUTF16Buffer(const UTF16Buffer *a, const UTF16Buffer *b) {
size_t minlen = a->used < b->used ? a->used : b->used;
int result;
result = memcmp(a->data, b->data, minlen * sizeof(UChar));
if (result)
return result;
if (b->used > minlen)
return -1;
if (a->used > minlen)
return 1;
return 0;
}
#define UTF16CharCondition(name, condition) bool isUTF16##name(UTF16Buffer *buffer) { \
UChar32 c; size_t i; \
for (i = 0; i < buffer->used; i++) { \
\
if ((buffer->data[i] & 0xFC00) == 0xD800) { \
ASSERT(i + 1 < buffer->used); \
c = ((((UChar32) buffer->data[i]) & 0x3FF) << 10) + 0x10000 + (buffer->data[i + 1] & 0x3FF); \
i++; \
} else { \
c = buffer->data[i]; \
} \
if (!(condition)) \
return false; \
} \
return true; \
} \
/** Check if a @a UTF16Buffer contains a punctuation character. */
UTF16CharCondition(Punct, U_GET_GC_MASK(c) & option.punctuationMask)
/** Check if a @a UTF16Buffer contains a punctuation character. */
UTF16CharCondition(Whitespace, u_isUWhiteSpace(c))
#endif
dwdiff-2.1.1/src/tempfile.h 0000664 0001750 0001750 00000001665 13074617326 016654 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2008-2011 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#ifndef TEMPFILE_H
#define TEMPFILE_H
#include "stream.h"
#define TEMPLATE "/tmp/dwdiffXXXXXXXX"
#define TEMPLATE_LENGTH sizeof(TEMPLATE)
typedef struct {
Stream *stream;
char name[TEMPLATE_LENGTH];
bool closed;
} TempFile;
TempFile *tempFile(void);
void closeTempFile(TempFile *file);
void resetTempFiles(void);
#endif
dwdiff-2.1.1/src/hashtable.c 0000664 0001750 0001750 00000007643 13074617326 016777 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2011 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#include
#include
#include
#include "definitions.h"
#include "hashtable.h"
#define MEMBLOCK_SIZE 32768
#define ALIGNOF(type) offsetof(struct { char c; type member; }, member)
#define ROUNDUP(x, to) (((x + (to - 1)) / to) * to)
typedef struct MemBlock {
struct MemBlock *next;
size_t size;
size_t idx;
} MemBlock;
static MemBlock *head;
#define HASHTABLE_SIZE 8091
typedef struct Tuple {
ValueType value;
size_t stringLength;
struct Tuple *next;
char string[1];
} Tuple;
static Tuple *hashtable[HASHTABLE_SIZE];
static ValueType nextValue;
ValueType baseHashMax;
static Tuple *allocFromBlock(size_t size) {
Tuple *result;
size = ROUNDUP(size, ALIGNOF(Tuple));
if (head == NULL || size > head->size - head->idx) {
MemBlock *newBlock;
if (size > 2048) {
newBlock = safe_malloc(size + sizeof(MemBlock));
newBlock->size = size + sizeof(MemBlock);
} else {
newBlock = safe_malloc(MEMBLOCK_SIZE);
newBlock->size = MEMBLOCK_SIZE;
}
newBlock->idx = ROUNDUP(sizeof(MemBlock), ALIGNOF(Tuple));
newBlock->next = head;
head = newBlock;
}
result = (Tuple *)(((char *) head) + head->idx);
head->idx += size;
return result;
}
static void freeBlocks(void) {
MemBlock *ptr;
while (head != NULL) {
ptr = head;
head = head->next;
free(ptr);
}
}
#ifdef PROFILE_HASH
static int collisions;
static int hits;
void printHashStatistics(void) {
fprintf(stderr, "Hash statistics: unique words: %d, collisions %d (%.2f%%), hits %d\n",
(int) nextValue, collisions, (double) collisions * 100.0 / nextValue, hits);
collisions = 0;
hits = 0;
}
#endif
/** Calculate a hash value for a string.
@param key The string to hash.
@return The hash value associated with the string.
This function uses the djb2 hash function.
*/
static unsigned int hash(void *data, size_t size) {
const unsigned char *ptr;
size_t i;
unsigned int hashValue = 5381;
ptr = data;
for (i = 0; i < size; i++)
hashValue = (hashValue << 5) + hashValue + (unsigned int) *ptr++;
return hashValue;
}
/** Get the value associated with a word.
@param word The word to get the value for.
This function gets the value associated with a word. If the word has not been
seen before a new value will be given.
*/
ValueType getValueFromContext(CharBuffer *word) {
return getValue(word->data, word->used);
}
ValueType getValue(void *data, size_t size) {
Tuple *tuple;
unsigned int hashValue = hash(data, size) % HASHTABLE_SIZE;
tuple = hashtable[hashValue];
/* Search the singly linked list. */
while (tuple != NULL && !(size == tuple->stringLength && memcmp(data, tuple->string, size) == 0))
tuple = tuple->next;
if (tuple == NULL) {
ASSERT(nextValue != VALUE_MAX);
#ifdef PROFILE_HASH
if (hashtable[hashValue] != NULL)
collisions++;
#endif
tuple = allocFromBlock(sizeof(Tuple) - 1 + size);
tuple->value = nextValue++;
tuple->stringLength = size;
memcpy(tuple->string, data, size);
tuple->next = hashtable[hashValue];
hashtable[hashValue] = tuple;
}
#ifdef PROFILE_HASH
else
hits++;
#endif
return tuple->value;
}
ValueType getHashMax(void) {
ValueType hashMax = nextValue;
int i;
#ifdef PROFILE_HASH
printHashStatistics();
#endif
/* Reset the hashtable for the next iteration. */
for (i = 0; i < HASHTABLE_SIZE; i++)
hashtable[i] = NULL;
freeBlocks();
nextValue = 0;
return hashMax;
}
dwdiff-2.1.1/src/buffer.h 0000664 0001750 0001750 00000001707 13074617326 016315 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2008-2011 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#ifndef BUFFER_H
#define BUFFER_H
#include "vector.h"
typedef VECTOR(char, CharBuffer);
void initContextBuffers(void);
void addchar(char c, bool common);
void printLineNumbers(int oldLineNumber, int newLineNumber);
void writeString(const char *string, size_t bytes);
#define INITIAL_BUFFER_SIZE 80
#define INITIAL_WORD_BUFFER_SIZE 32
#endif
dwdiff-2.1.1/src/dwfilter.c 0000664 0001750 0001750 00000013451 13074617326 016656 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2010-2011 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include "definitions.h"
#include "util.h"
#include "optionMacros.h"
#define DWFILTER_COMPILE
#include "optionDescriptions.h"
#define TEMPLATE "/tmp/dwdiffXXXXXXXX"
#define TEMPLATE_LENGTH sizeof(TEMPLATE)
#ifndef DWDIFF
# define DWDIFF "dwdiff"
#endif
struct {
int oldFile, newFile;
bool reverse;
int postProcessorStart;
} option;
static PARSE_FUNCTION(parseCmdLine)
int noOptionCount = 0;
bool discard;
/* Initialise options to correct values */
memset(&option, 0, sizeof(option));
OPTIONS
OPTION('d', "delimiters", REQUIRED_ARG)
END_OPTION
OPTION('P', "punctuation", NO_ARG)
END_OPTION
OPTION('W', "white-space", REQUIRED_ARG)
END_OPTION
OPTION('h', "help", NO_ARG)
int i = 0;
printf(_("Usage: dwfilter [OPTIONS] [POST PROCESSOR OPTIONS]\n"));
while (descriptions[i] != NULL)
fputs(_(descriptions[i++]), stdout);
exit(EXIT_SUCCESS);
END_OPTION
OPTION('v', "version", NO_ARG)
fputs("dwfilter " VERSION_STRING "\n", stdout);
fputs(
/* TRANSLATORS:
- If the (C) symbol (that is the c in a circle) is not available,
leave as it as is. (Unicode code point 0x00A9)
- G.P. Halkes is name and should be left as is. */
_("Copyright (C) 2006-2011 G.P. Halkes\nLicensed under the GNU General Public License version 3\n"), stdout);
exit(EXIT_SUCCESS);
END_OPTION
OPTION('i', "ignore-case", NO_ARG)
END_OPTION
OPTION('I', "ignore-formatting", NO_ARG)
END_OPTION
SINGLE_DASH
switch (noOptionCount++) {
case 0:
option.oldFile = optargind;
break;
case 1:
option.newFile = optargind;
break;
default:
option.postProcessorStart = optargind;
STOP_OPTION_PROCESSING;
break;
}
END_OPTION
DOUBLE_DASH
NO_MORE_OPTIONS;
END_OPTION
OPTION('C', "context", REQUIRED_ARG)
END_OPTION
OPTION('m', "match-context", REQUIRED_ARG)
END_OPTION
BOOLEAN_LONG_OPTION("aggregate-changes", discard)
OPTION('A', "algorithm", REQUIRED_ARG)
END_OPTION
BOOLEAN_LONG_OPTION("wdiff-output", discard)
/* FIXME: make this work again, after fixing dwdiff */
/* OPTION('S', "paragraph-separator", OPTIONAL_ARG)
END_OPTION */
BOOLEAN_OPTION('r', "reverse", option.reverse)
LONG_OPTION("profile", REQUIRED_ARG)
END_OPTION
LONG_OPTION("no-profile", NO_ARG)
END_OPTION
fatal(_("Option %.*s does not exist\n"), OPTPRARG);
NO_OPTION
switch (noOptionCount++) {
case 0:
option.oldFile = optargind;
break;
case 1:
option.newFile = optargind;
break;
default:
option.postProcessorStart = optargind;
STOP_OPTION_PROCESSING;
break;
}
END_OPTIONS
/* Check that we have something to work with. */
if (noOptionCount < 2)
fatal(_("Need two files to compare\n"));
if (noOptionCount < 3)
fatal(_("No post processor specified\n"));
END_FUNCTION
static char tempfile[TEMPLATE_LENGTH];
static void cleanup(void) {
unlink(tempfile);
}
static void createTempfile(void) {
int fd;
/* Create temporary file. */
/* Make sure the umask is set so that we don't introduce a security risk. */
umask(~S_IRWXU);
/* Make sure we will remove temporary files on exit. */
atexit(cleanup);
strcpy(tempfile, TEMPLATE);
if ((fd = mkstemp(tempfile)) < 0)
fatal(_("Could not create temporary file: %s\n"), strerror(errno));
close(fd);
}
static int execute(char * const args[]) {
pid_t pid;
int status;
if ((pid = fork()) == 0) {
execvp(args[0], args);
exit(127);
} else if (pid < 0) {
fatal(_("Could not execute %s: %s\n"), args[0], strerror(errno));
}
pid = wait(&status);
if (pid < 0)
fatal(_("Error waiting for child process to terminate: %s\n"), strerror(errno));
return WEXITSTATUS(status);
}
#define OUTPUT_OPTION "--dwfilter="
int main(int argc, char *argv[]) {
char **cmdArgs;
char *outputArg;
int i, j;
#if defined(USE_GETTEXT) || defined(USE_UNICODE)
setlocale(LC_ALL, "");
#endif
#ifdef USE_GETTEXT
bindtextdomain("dwdiff", LOCALEDIR);
textdomain("dwdiff");
#endif
parseCmdLine(argc, argv);
createTempfile();
cmdArgs = safe_malloc(sizeof(char *) * (option.postProcessorStart + 3));
outputArg = safe_malloc(strlen(tempfile) + sizeof(OUTPUT_OPTION) + 1);
cmdArgs[0] = safe_strdup(DWDIFF);
/* argument has been allocated to correct size above, so we can safely use sprintf */
sprintf(outputArg, OUTPUT_OPTION "%s", tempfile);
cmdArgs[1] = outputArg;
for (i = 1, j = 2; i < option.postProcessorStart; i++)
if (i != option.oldFile && i != option.newFile)
cmdArgs[j++] = argv[i];
cmdArgs[j++] = argv[option.reverse ? option.newFile : option.oldFile];
cmdArgs[j++] = argv[option.reverse ? option.oldFile : option.newFile];
cmdArgs[j++] = NULL;
if (execute(cmdArgs) > 2)
fatal(_("dwdiff returned an error\n"));
free(outputArg);
free(cmdArgs);
cmdArgs = safe_malloc(sizeof(char *) * (argc - option.postProcessorStart + 3));
for (i = option.postProcessorStart, j = 0; i < argc; i++, j++)
cmdArgs[j] = argv[i];
cmdArgs[j++] = option.reverse ? argv[option.oldFile] : tempfile;
cmdArgs[j++] = option.reverse ? tempfile : argv[option.newFile];
cmdArgs[j++] = NULL;
return execute(cmdArgs);
}
dwdiff-2.1.1/src/stream.c 0000664 0001750 0001750 00000005452 13074617326 016333 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2008-2011 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#include
#include "stream.h"
/** Initialise the shared parts of a @a Stream structure.
@param stream The @a Stream to initialise.
*/
static void initStreamDefault(Stream *stream) {
(void) stream;
#ifdef USE_UNICODE
stream->bufferedChar = -1;
stream->highSurrogate = 0;
stream->lastClusterCategory = 0;
stream->nextChar = -1;
#endif
}
/** @a getChar for @a FILE based streams. */
static int getCharFile(Stream *stream) {
return fileGetc(stream->data.file);
}
/** @a ungetChar for @a FILE based streams. */
static int ungetCharFile(Stream *stream, int c) {
return fileUngetc(stream->data.file, c);
}
static StreamVtable fileVtable = { getCharFile, ungetCharFile };
/** Create a new @a File based stream.
@param file The @a File to wrap.
@return a new @a Stream wrapping the supplied @a File object.
*/
Stream *newFileStream(File *file) {
Stream *retval;
if (file == NULL)
return NULL;
retval = safe_malloc(sizeof(Stream));
retval->vtable = &fileVtable;
retval->data.file = file;
initStreamDefault(retval);
return retval;
}
/** @a getChar for string based streams. */
static int getCharString(Stream *stream) {
return stream->data.string.index >= stream->data.string.length ? EOF : (unsigned char) stream->data.string.string[stream->data.string.index++];
}
/** @a ungetChar for string based streams. */
static int ungetCharString(Stream *stream, int c) {
if (stream->data.string.index > 0) {
ASSERT(stream->data.string.string[--stream->data.string.index] == c);
return c;
} else {
return EOF;
}
}
static StreamVtable stringVtable = { getCharString, ungetCharString };
/** Create a new string based stream.
@param string The string to wrap.
@return a new @a Stream wrapping the supplied string.
*/
Stream *newStringStream(const char *string, size_t length) {
Stream *retval;
retval = safe_malloc(sizeof(Stream));
retval->vtable = &stringVtable;
retval->data.string.string = string;
retval->data.string.index = 0;
retval->data.string.length = length;
initStreamDefault(retval);
return retval;
}
bool isFileStream(const Stream *stream) {
return stream->vtable == &fileVtable;
}
void sfclose(Stream *stream) {
fileClose(stream->data.file);
free(stream);
}
dwdiff-2.1.1/src/static_assert.h 0000664 0001750 0001750 00000002545 13074617326 017715 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2008 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#ifndef STATIC_ASSERT
#define STATIC_ASSERT
/* The static_assert is achieved by creating a struct definition which contains
an array of negative size if the condition is false. To prevent multiple
static assertions to define the same struct, the line number on which the
assertion name of the struct is made is appended to the name of the struct.
However, to do this we need another layer of indirection because the
arguments of ## are not expanded before pasting.
*/
#define __static_assert(_condition, _line) struct __static_assert_##_line { int static_assert_failed[_condition ? 1 : -1]; }
#define _static_assert(_condition, _line) __static_assert(_condition, _line)
#define static_assert(_condition) _static_assert(_condition, __LINE__)
#endif
dwdiff-2.1.1/src/tempfile.c 0000664 0001750 0001750 00000004747 13074617326 016653 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2006-2011 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#include
#include
#include
#include
#include "definitions.h"
#include "stream.h"
static TempFile files[6];
static unsigned openIndex = 0;
#ifndef LEAVE_FILES
static bool inited;
#endif
/** Create a temporary file. */
TempFile *tempFile(void) {
#ifndef LEAVE_FILES
int fd;
#endif
ASSERT(openIndex < sizeof(files) / sizeof(files[0]));
#ifdef LEAVE_FILES
strcpy(files[openIndex].name, "dwdiffTemp");
sprintf(files[openIndex].name + strlen(files[openIndex].name), "%d", openIndex);
if ((files[openIndex].stream = newFileStream(fileOpen(files[openIndex].name, FILE_WRITE))) == NULL)
return NULL;
#else
/* Create temporary file. */
if (!inited) {
/* Make sure the umask is set so that we don't introduce a security risk. */
umask(~S_IRWXU);
/* Make sure we will remove temporary files on exit. */
atexit(resetTempFiles);
inited = true;
}
strcpy(files[openIndex].name, TEMPLATE);
if ((fd = mkstemp(files[openIndex].name)) < 0)
return NULL;
if ((files[openIndex].stream = newFileStream(fileWrapFD(fd, FILE_WRITE))) == NULL)
return NULL;
#endif
return files + openIndex++;
}
/** Closes a temporary file by closing its stream.
@param file The ::TempFile to close.
This function will close the stream, but it does not remove the temporary
file. This will be done by the ::cleanup function at exit. It is
permissible to call this function with the same argument more than once.
*/
void closeTempFile(TempFile *file) {
if (!file->closed) {
sfclose(file->stream);
file->closed = true;
}
}
/** Remove all the created temporary files and reset the data structures. */
void resetTempFiles(void) {
unsigned i;
for (i = 0; i < openIndex; i++) {
closeTempFile(&files[i]);
remove(files[i].name);
files[i].closed = false;
files[i].stream = NULL;
memset(files[i].name, 0, sizeof(files[i].name));
}
openIndex = 0;
}
dwdiff-2.1.1/src/optionDescriptions.h 0000664 0001750 0001750 00000010052 13074617326 020734 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2010-2011 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#ifndef OPTIONDESCRIPTIONS_H
#define OPTIONDESCRIPTIONS_H
const char *descriptions[] = {
/* Options showing info about the program */
N_("-h, --help Print this help message\n"),
N_("-v, --version Print version and copyright information\n"),
/* Options changing what is considered (non-)whitespace */
N_("-d , --delimiters= Specifiy delimiters\n"),
N_("-P, --punctuation Use punctuation characters as delimiters\n"),
N_("-W , --white-space= Specify whitespace characters\n"),
#ifdef DWDIFF_COMPILE
N_("-u, --diff-input Read the input as the output from diff\n"),
N_("-S[], --paragraph-separator[=] Show inserted or deleted blocks\n"
" of empty lines, optionally overriding the marker\n"),
/* Options changing what is output */
N_("-1, --no-deleted Do not print deleted words\n"),
N_("-2, --no-inserted Do not print inserted words\n"),
N_("-3, --no-common Do not print common words\n"),
N_("-L[], --line-numbers[] Prepend line numbers\n"),
N_("-C, --context= Show lines of context\n"),
N_("-s, --statistics Print statistics when done\n"),
#endif
N_("--wdiff-output Produce wdiff compatible output\n"),
/* Options changing the matching */
N_("-i, --ignore-case Ignore differences in case\n"),
N_("-I, --ignore-formatting Ignore formatting differences\n"),
/* TRANSLATORS:
The context meant here are words preceeding and succeeding each word in
the text. By using these extra context words when applying the diff program,
frequently occuring words will be more likely to be matched to the
correct corresponding word in the other text, thus giving a better result. */
N_("-m , --match-context= Use words of context for matching\n"),
/* TRANSLATORS:
The use of context words for matching is more expensive, because after the
first pass of diff the changes reported need refining. However, if the user
can live with multiple changes that are within (2 * match-context + 1) words
from eachother being reported as a single change, they can use this option. */
N_("--aggregate-changes Allow close changes to aggregate\n"),
N_("-A , --algorithm= Choose algorithm: best, normal, fast\n"),
#ifdef DWDIFF_COMPILE
/* Options changing the appearance of the output */
N_("-c[], --color[=] Color mode\n"),
N_("-l, --less-mode As -p but also overstrike whitespace\n"),
N_("-p, --printer Use overstriking and bold text\n"),
N_("-w , --start-delete= String to mark begin of deleted text\n"),
N_("-x , --stop-delete= String to mark end of deleted text\n"),
N_("-y , --start-insert= String to mark begin of inserted text\n"),
N_("-z , --stop-insert= String to mark end of inserted text\n"),
N_("-R, --repeat-markers Repeat markers at newlines\n"),
#endif
#ifdef DWFILTER_COMPILE
N_("-r, --reverse Format new as old\n"),
#endif
N_("--profile= Use profile \n"),
N_("--no-profile Disable profile reading\n"),
NULL};
#endif
dwdiff-2.1.1/src/hashtable.h 0000664 0001750 0001750 00000001563 13074617326 016777 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2011 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#ifndef HASHTABLE_H
#define HASHTABLE_H
#include
#include "definitions.h"
#include "buffer.h"
ValueType getValueFromContext(CharBuffer *word);
ValueType getValue(void *data, size_t size);
ValueType getHashMax(void);
extern ValueType baseHashMax;
#endif
dwdiff-2.1.1/src/option.c 0000664 0001750 0001750 00000057274 13074617326 016361 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2006-2015 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#include
#include
#include "definitions.h"
#include "stream.h"
#include "unicode.h"
#include "optionMacros.h"
#include "option.h"
#include "util.h"
#include "buffer.h"
#include "dispatch.h"
#include "profile.h"
#define DWDIFF_COMPILE
#include "optionDescriptions.h"
typedef struct {
const char *name;
const char *description;
const char *sequence;
const char *backgroundSequence;
} Color;
Color colors[] = {
{ "black", N_("Black"), "30", "40" },
{ "red", N_("Red"), "31", "41" },
{ "green", N_("Green"), "32", "42" },
{ "brown", N_("Brown"), "33", "43" },
{ "blue", N_("Blue"), "34", "44" },
{ "magenta", N_("Magenta"), "35", "45" },
{ "cyan", N_("Cyan"), "36", "46" },
{ "gray", N_("Gray"), "37", "47" },
{ "dgray", N_("Dark gray"), "30;1", NULL },
{ "bred", N_("Bright red"), "31;1", NULL },
{ "bgreen", N_("Bright green"), "32;1", NULL },
{ "yellow", N_("Yellow"), "33;1", NULL },
{ "bblue", N_("Bright blue"), "34;1", NULL },
{ "bmagenta", N_("Bright magenta"), "35;1", NULL },
{ "bcyan", N_("Bright cyan"), "36;1", NULL },
{ "white", N_("White"), "37;1", NULL },
{ NULL, NULL, NULL, NULL }
};
/** Compare two strings, ignoring case.
@param a The first string to compare.
@param b The first string to compare.
@return an integer smaller, equal, or larger than zero to indicate that @a a
sorts before, the same, or after @a b.
*/
int strCaseCmp(const char *a, const char *b) {
int ca, cb;
while ((ca = tolower(*a++)) == (cb = tolower(*b++)) && ca != 0) {}
return ca == cb ? 0 : (ca < cb ? -1 : 1);
}
/** Convert a string from the input format to an internally usable string.
@param string A @a Token with the string to be converted.
@param descr A description of the string to be included in error messages.
@return The length of the resulting string.
The use of this function processes escape characters. The converted
characters are written in the original string.
*/
static size_t parseEscapes(char *string, const char *descr) {
size_t maxReadPosition = strlen(string);
size_t readPosition = 0, writePosition = 0;
size_t i;
while (readPosition < maxReadPosition) {
if (string[readPosition] == '\\') {
readPosition++;
if (readPosition == maxReadPosition) {
fatal(_("Single backslash at end of %s argument\n"), descr);
}
switch(string[readPosition++]) {
case 'n':
string[writePosition++] = '\n';
break;
case 'r':
string[writePosition++] = '\r';
break;
case '\'':
string[writePosition++] = '\'';
break;
case '\\':
string[writePosition++] = '\\';
break;
case 't':
string[writePosition++] = '\t';
break;
case 'b':
string[writePosition++] = '\b';
break;
case 'f':
string[writePosition++] = '\f';
break;
case 'a':
string[writePosition++] = '\a';
break;
case 'v':
string[writePosition++] = '\v';
break;
case '?':
string[writePosition++] = '\?';
break;
case '"':
string[writePosition++] = '"';
break;
case 'e':
string[writePosition++] = '\033';
break;
case 'x': {
/* Hexadecimal escapes */
unsigned int value = 0;
/* Read at most two characters, or as many as are valid. */
for (i = 0; i < 2 && (readPosition + i) < maxReadPosition && isxdigit(string[readPosition + i]); i++) {
value <<= 4;
if (isdigit(string[readPosition + i]))
value += (int) (string[readPosition + i] - '0');
else
value += (int) (tolower(string[readPosition + i]) - 'a') + 10;
if (value > UCHAR_MAX)
/* TRANSLATORS:
The %s argument is a long option name without preceding dashes. */
fatal(_("Invalid hexadecimal escape sequence in %s argument\n"), descr);
}
readPosition += i;
if (i == 0)
/* TRANSLATORS:
The %s argument is a long option name without preceding dashes. */
fatal(_("Invalid hexadecimal escape sequence in %s argument\n"), descr);
string[writePosition++] = (char) value;
break;
}
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7': {
/* Octal escapes */
int value = (int)(string[readPosition - 1] - '0');
size_t maxIdx = string[readPosition - 1] < '4' ? 2 : 1;
for (i = 0; i < maxIdx && readPosition + i < maxReadPosition && string[readPosition + i] >= '0' && string[readPosition + i] <= '7'; i++)
value = value * 8 + (int)(string[readPosition + i] - '0');
readPosition += i;
string[writePosition++] = (char) value;
break;
}
#ifdef USE_UNICODE
case 'u':
case 'U': {
UChar32 value = 0;
size_t chars = string[readPosition - 1] == 'U' ? 8 : 4;
if (maxReadPosition < readPosition + chars)
/* TRANSLATORS:
The %c argument will be either 'u' or 'U'. The %s argument is a long
option name without preceding dashes. */
fatal(_("Too short \\%c escape in %s argument\n"), string[readPosition - 1], descr);
for (i = 0; i < chars; i++) {
if (!isxdigit(string[readPosition + i]))
/* TRANSLATORS:
The %c argument will be either 'u' or 'U'. The %s argument is a long
option name without preceding dashes. */
fatal(_("Too short \\%c escape in %s argument\n"), string[readPosition - 1], descr);
value <<= 4;
if (isdigit(string[readPosition + i]))
value += (int) (string[readPosition + i] - '0');
else
value += (int) (tolower(string[readPosition + i]) - 'a') + 10;
}
if (value > 0x10FFFFL)
/* TRANSLATORS:
The %s argument is a long option name without preceding dashes. */
fatal(_("\\U escape out of range in %s argument\n"), descr);
if ((value & 0xF800L) == 0xD800L)
/* TRANSLATORS:
The %s argument is a long option name without preceding dashes. */
fatal(_("\\%c escapes for surrogate codepoints are not allowed in %s argument\n"), string[readPosition - 1], descr);
/* The conversion won't overwrite subsequent characters because
\uxxxx is already the as long as the max utf-8 length */
writePosition += convertToUTF8(value, string + writePosition);
readPosition += chars;
break;
}
#endif
default:
string[writePosition++] = string[readPosition - 1];
break;
}
} else {
string[writePosition++] = string[readPosition++];
}
}
/* Terminate string. */
string[writePosition] = 0;
return writePosition;
}
static void noDefaultMarkers(void) {
/* Note: then lenghts are 0 by default. */
if (option.addStart == NULL)
option.addStart = "";
if (option.addStop == NULL)
option.addStop = "";
if (option.delStart == NULL)
option.delStart = "";
if (option.delStop == NULL)
option.delStop = "";
}
/*===============================================================*/
/* Single character (SC) versions of the addCharacters and checkOverlap routines.
Descriptions can be found in the definition of the DispatchTable struct. */
/** Add all characters to the specified list or bitmap.
@param chars The string with characters to add to the list or bitmap.
@param list The list to add to.
@param bitmap. The bitmap to add to.
If in UTF-8 mode, the characters will be added to the list. Otherwise to
the bitmap.
*/
void addCharactersSC(const char *chars, size_t length, CHARLIST *list, char bitmap[BITMASK_SIZE]) {
size_t i;
/* Suppress unused parameter warning in semi-portable way. */
(void) list;
for (i = 0; i < length; i++) {
SET_BIT(bitmap, chars[i]);
}
}
/** Check for overlap in whitespace and delimiter sets. */
void checkOverlapSC(void) {
int i;
if (!option.whitespaceSet)
return;
for (i = 0; i <= UCHAR_MAX; i++) {
if (!TEST_BIT(option.delimiters, i))
continue;
if (TEST_BIT(option.whitespace, i))
fatal(_("Whitespace and delimiter sets overlap\n"));
}
}
void setPunctuationSC(void) {
int i;
for (i = 0; i <= UCHAR_MAX; i++) {
if (!ispunct(i))
continue;
SET_BIT(option.delimiters, i);
}
}
void initOptionsSC(void) {}
void postProcessOptionsSC(void) {
if (!option.whitespaceSet) {
int i;
for (i = 0; i <= UCHAR_MAX; i++) {
if (!TEST_BIT(option.delimiters, i) && isspace(i))
SET_BIT(option.whitespace, i);
}
}
if (!TEST_BIT(option.whitespace, ' '))
option.wdiffOutput = true;
}
#ifdef USE_UNICODE
/*===============================================================*/
/* UTF-8 versions of the addCharacters and checkOverlap routines.
Descriptions can be found in the definition of the DispatchTable struct. */
/** Add all characters to the specified list.
@param chars The string with characters to add to the list or bitmap.
@param list The list to add to.
@param bitmap. The bitmap to add to (unused).
*/
void addCharactersUTF8(const char *chars, size_t length, CHARLIST *list, char bitmap[BITMASK_SIZE]) {
Stream *charStream;
CharData cluster;
/* Suppress unused parameter warning in semi-portable way. */
(void) bitmap;
VECTOR_INIT_ALLOCATED(cluster.UTF8Char.original);
VECTOR_INIT_ALLOCATED(cluster.UTF8Char.converted);
charStream = newStringStream(chars, length);
while (getCluster(charStream, &cluster.UTF8Char.original)) {
UTF16Buffer tmp;
size_t i;
decomposeChar(&cluster);
/* Check for duplicates */
for (i = 0; i < list->used; i++)
if (compareUTF16Buffer(&cluster.UTF8Char.converted, &list->data[i]) == 0)
break; /* continue won't work because we're in a for-loop */
if (i != list->used)
continue;
tmp.used = cluster.UTF8Char.converted.used;
tmp.data = safe_malloc(cluster.UTF8Char.converted.used * sizeof(UChar));
tmp.allocated = cluster.UTF8Char.converted.used;
memcpy(tmp.data, cluster.UTF8Char.converted.data, cluster.UTF8Char.converted.used * sizeof(UChar));
VECTOR_APPEND(*list, tmp);
}
VECTOR_FREE(cluster.UTF8Char.original);
VECTOR_FREE(cluster.UTF8Char.converted);
free(charStream);
}
/** Check for overlap in whitespace and delimiter sets. */
void checkOverlapUTF8(void) {
size_t i, j;
if (!option.whitespaceSet)
return;
/* Check for overlap can be done in O(N) because the lists have already been sorted in postProcessOptionsUTF8. */
for (i = 0, j = 0; i < option.delimiterList.used; i++) {
for (; j < option.whitespaceList.used && compareUTF16Buffer(&option.delimiterList.data[i],
&option.whitespaceList.data[j]) > 0; j++) /* NO-OP */;
if (j == option.whitespaceList.used)
break;
if (compareUTF16Buffer(&option.delimiterList.data[i], &option.whitespaceList.data[j]) == 0)
fatal(_("Whitespace and delimiter sets overlap\n"));
}
if (option.punctuationMask == 0)
return;
for (i = 0; i < option.whitespaceList.used; i++) {
if (isUTF16Punct(&option.whitespaceList.data[i]))
fatal(_("Whitespace and delimiter sets overlap\n"));
}
return;
}
void setPunctuationUTF8(void) {
option.punctuationMask = U_GC_P_MASK | U_GC_S_MASK;
}
void initOptionsUTF8(void) {
VECTOR_INIT_ALLOCATED(option.whitespaceList);
VECTOR_INIT_ALLOCATED(option.delimiterList);
}
void postProcessOptionsUTF8(void) {
static_assert(CRLF_GRAPHEME_CLUSTER_BREAK == 0);
qsort(option.delimiterList.data, option.delimiterList.used, sizeof(UTF16Buffer),
(int (*)(const void *, const void *)) compareUTF16Buffer);
qsort(option.whitespaceList.data, option.whitespaceList.used, sizeof(UTF16Buffer),
(int (*)(const void *, const void *)) compareUTF16Buffer);
VECTOR_APPEND(charData.UTF8Char.converted, ' ');
if (classifyChar() != CAT_WHITESPACE)
option.wdiffOutput = true;
charData.UTF8Char.converted.used = 0;
}
/*===============================================================*/
#endif
#define SEQUENCE_BUFFER_LEN 20
static char *parseColor(const char *_color) {
char sequenceBuffer[SEQUENCE_BUFFER_LEN];
char *colon, *color;
int i, fg = -1, bg = -1;
color = strdupA(_color);
if (strncmp("e:", color, 2) == 0) {
/* Custom color string */
parseEscapes(color + 2, "color");
memmove(color, color + 2, strlen(color) + 1);
return color;
}
colon = strchr(color, ':');
if (colon != NULL && strrchr(color, ':') != colon)
fatal(_("Invalid color specification %s\n"), color);
if (colon != NULL)
*colon++ = 0;
if (*color != 0) {
for (i = 0; colors[i].name != NULL; i++) {
if (strCaseCmp(color, colors[i].name) == 0) {
fg = i;
break;
}
}
if (colors[i].name == NULL)
fatal(_("Invalid color %s\n"), color);
}
if (colon != NULL) {
if (*colon != 0) {
for (i = 0; colors[i].backgroundSequence != NULL; i++) {
if (strCaseCmp(colon, colors[i].name) == 0) {
bg = i;
break;
}
}
if (colors[i].backgroundSequence == NULL)
fatal(_("Invalid background color %s\n"), colon);
}
}
if (fg >= 0) {
if (bg >= 0)
snprintf(sequenceBuffer, SEQUENCE_BUFFER_LEN, "\033[0;%s;%sm", colors[fg].sequence, colors[bg].backgroundSequence);
else
snprintf(sequenceBuffer, SEQUENCE_BUFFER_LEN, "\033[0;%sm", colors[fg].sequence);
} else {
if (bg >= 0)
snprintf(sequenceBuffer, SEQUENCE_BUFFER_LEN, "\033[0%sm", colors[bg].backgroundSequence);
}
free(color);
return strdupA(sequenceBuffer);
}
static void initializeOptions(void) {
/* Initialise options to correct values */
memset(&option, 0, sizeof(option));
option.printDeleted = option.printAdded = option.printCommon = true;
option.matchContext = 2;
option.output = stdout;
initOptions();
ONLY_UNICODE(option.decomposition = UNORM_NFD;)
option.needStartStop = true;
option.paraDelimMarker = "<-->";
option.paraDelimMarkerLength = strlen(option.paraDelimMarker);
option.profile = "default";
}
static void completeDefaults(void) {
/* Check and set some values */
if (!option.dwfilterMode) {
if (option.delStart == NULL) {
option.delStart = "[-";
option.delStartLen = 2;
}
if (option.delStop == NULL) {
option.delStop = "-]";
option.delStopLen = 2;
}
if (option.addStart == NULL) {
option.addStart = "{+";
option.addStartLen = 2;
}
if (option.addStop == NULL) {
option.addStop = "+}";
option.addStopLen = 2;
}
if (!option.printCommon && !option.printAdded && !option.printDeleted)
option.needMarkers = false;
if ((!option.printAdded + !option.printDeleted + !option.printCommon) == 2)
option.needStartStop = false;
} else {
option.delStart = "";
option.delStartLen = 0;
option.delStop = "";
option.delStopLen = 0;
option.addStart = "";
option.addStartLen = 0;
option.addStop = "";
option.addStopLen = 0;
option.printAdded = false;
option.needMarkers = false;
option.needStartStop = true;
}
}
static PARSE_FUNCTION(parseArgs)
char *comma;
int noOptionCount = 0;
OPTIONS
OPTION('d', "delimiters", REQUIRED_ARG)
size_t length = parseEscapes(optArg, "delimiters");
addCharacters(optArg, length, SWITCH_UNICODE(&option.delimiterList, NULL), option.delimiters);
END_OPTION
OPTION('P', "punctuation", NO_ARG)
setPunctuation();
END_OPTION
OPTION('W', "white-space", REQUIRED_ARG)
size_t length = parseEscapes(optArg, "whitespace");
option.whitespaceSet = true;
addCharacters(optArg, length, SWITCH_UNICODE(&option.whitespaceList, NULL), option.whitespace);
END_OPTION
OPTION('h', "help", NO_ARG)
/* START_KEEP */
int i = 0;
printf(_("Usage: dwdiff [OPTIONS] \n"));
while (descriptions[i] != NULL)
fputs(_(descriptions[i++]), stdout);
exit(EXIT_SUCCESS);
/* STOP_KEEP */
END_OPTION
OPTION('v', "version", NO_ARG)
fputs("dwdiff " VERSION_STRING "\n", stdout);
fputs(
/* TRANSLATORS:
- If the (C) symbol (that is the c in a circle) is not available,
leave as it as is. (Unicode code point 0x00A9)
- G.P. Halkes is name and should be left as is. */
_("Copyright (C) 2006-2015 G.P. Halkes and others\nLicensed under the GNU General Public License version 3\n"), stdout);
exit(EXIT_SUCCESS);
END_OPTION
OPTION('1', "no-deleted", NO_ARG)
if (!option.dwfilterMode) {
option.printDeleted = false;
if (!option.printAdded)
option.needMarkers = true;
if (!option.printCommon || !option.printAdded)
option.needStartStop = false;
}
END_OPTION
OPTION('2', "no-inserted", NO_ARG)
if (!option.dwfilterMode) {
option.printAdded = false;
if (!option.printDeleted)
option.needMarkers = true;
if (!option.printCommon || !option.printDeleted)
option.needStartStop = false;
}
END_OPTION
OPTION('3', "no-common", NO_ARG)
if (!option.dwfilterMode) {
option.printCommon = false;
option.needMarkers = true;
}
END_OPTION
OPTION('i', "ignore-case", NO_ARG)
option.ignoreCase = true;
END_OPTION
#ifdef USE_UNICODE
OPTION('I', "ignore-formatting", NO_ARG)
if (UTF8Mode)
option.decomposition = UNORM_NFKD;
else
fatal(_("Option %.*s is only supported for UTF-8 mode\n"), OPTPRARG);
#else
OPTION('I', "ignore-formatting", NO_ARG)
fatal(_("Support for option %.*s is not compiled into this version of dwdiff\n"), OPTPRARG);
#endif
END_OPTION
OPTION('s', "statistics", NO_ARG)
if (!option.dwfilterMode) {
option.statistics = true;
}
END_OPTION
OPTION('a', "autopager", NO_ARG)
fatal(_("Option %.*s is not supported\n"), OPTPRARG);
END_OPTION
OPTION('p', "printer", NO_ARG)
if (!option.dwfilterMode) {
option.printer = true;
noDefaultMarkers();
}
END_OPTION
OPTION('l', "less-mode", NO_ARG)
if (!option.dwfilterMode) {
option.less = true;
noDefaultMarkers();
}
END_OPTION
LONG_OPTION("less", NO_ARG)
if (!option.dwfilterMode) {
option.less = true;
noDefaultMarkers();
fprintf(stderr, "WARNING: the --less argument is deprecated. Use --less-mode instead.\n");
}
END_OPTION
OPTION('t', "terminal", NO_ARG)
fatal(_("Option %.*s is not supported\n"), OPTPRARG);
END_OPTION
OPTION('w', "start-delete", REQUIRED_ARG)
option.delStartLen = parseEscapes(optArg, "start-delete");
option.delStart = optArg;
END_OPTION
OPTION('x', "stop-delete", REQUIRED_ARG)
option.delStopLen = parseEscapes(optArg, "stop-delete");
option.delStop = optArg;
END_OPTION
OPTION('y', "start-insert", REQUIRED_ARG)
option.addStartLen = parseEscapes(optArg, "start-insert");
option.addStart = optArg;
END_OPTION
OPTION('z', "stop-insert", REQUIRED_ARG)
option.addStopLen = parseEscapes(optArg, "stop-insert");
option.addStop = optArg;
END_OPTION
OPTION('n', "avoid-wraps", NO_ARG)
fatal(_("Option %.*s is not supported\n"), OPTPRARG);
END_OPTION
SINGLE_DASH
switch (noOptionCount++) {
case 0:
option.oldFile.input = newFileStream(fileWrapFD(STDIN_FILENO, FILE_READ));
break;
case 1:
if (option.oldFile.name == NULL)
fatal(_("Can't read both files from standard input\n"));
option.newFile.input = newFileStream(fileWrapFD(STDIN_FILENO, FILE_READ));
break;
default:
fatal(_("Too many files to compare\n"));
}
END_OPTION
DOUBLE_DASH
NO_MORE_OPTIONS;
END_OPTION
OPTION('c', "color", OPTIONAL_ARG)
if (!option.dwfilterMode) {
option.colorMode = true;
if (optArg != NULL) {
int i;
if (strCaseCmp(optArg, "list") == 0) {
fputs(
/* TRANSLATORS:
"Name" and "Description" are table headings for the color name list.
Make sure you keep the alignment of the headings over the text. */
_("Name Description\n"), stdout);
fputs("-- --\n", stdout);
for (i = 0; colors[i].name != NULL; i++)
printf("%-15s %s\n", colors[i].name, _(colors[i].description));
fputc('\n', stdout);
fputs(_("The colors black through gray are also usable as background color\n"), stdout);
exit(EXIT_SUCCESS);
}
comma = strchr(optArg, ',');
if (comma != NULL && strrchr(optArg, ',') != comma)
fatal(_("Invalid color specification %s\n"), optArg);
if (comma != NULL)
*comma++ = 0;
option.delColor = parseColor(optArg[0] == 0 ? "bred" : optArg);
option.addColor = parseColor(comma == NULL ? "bgreen" : comma);
} else {
option.delColor = parseColor("bred");
option.addColor = parseColor("bgreen");
}
option.delColorLen = strlen(option.delColor);
option.addColorLen = strlen(option.addColor);
noDefaultMarkers();
}
END_OPTION
OPTION('L', "line-numbers", OPTIONAL_ARG)
if (optArg != NULL)
PARSE_INT(option.lineNumbers, 1, INT_MAX);
else
option.lineNumbers = DEFAULT_LINENUMBER_WIDTH;
END_OPTION
OPTION('C', "context", REQUIRED_ARG)
PARSE_INT(option.contextLines, 0, INT_MAX);
option.context = true;
initContextBuffers();
END_OPTION
OPTION('m', "match-context", REQUIRED_ARG)
PARSE_INT(option.matchContext, 0, (INT_MAX - 1) / 2);
option.matchContext *= 2;
END_OPTION
BOOLEAN_LONG_OPTION("aggregate-changes", option.aggregateChanges)
OPTION('S', "paragraph-separator", OPTIONAL_ARG)
if (!option.dwfilterMode) {
option.paraDelim = true;
if (optArg != NULL) {
option.paraDelimMarker = optArg;
option.paraDelimMarkerLength = parseEscapes(optArg, "paragraph-separator");
}
}
END_OPTION
BOOLEAN_LONG_OPTION("wdiff-output", option.wdiffOutput)
LONG_OPTION("dwfilter", REQUIRED_ARG)
/* START_KEEP */
option.dwfilterMode = true;
/* STOP_KEEP */
if ((option.output = fopen(optArg, "r+")) == NULL)
fatal(_("Error opening temporary output file: %s\n"), strerror(errno));
END_OPTION
OPTION('r', "reverse", NO_ARG)
if (!option.dwfilterMode)
fatal(_("Option %.*s does not exist\n"), OPTPRARG);
END_OPTION
OPTION('R', "repeat-markers", NO_ARG)
if (!option.dwfilterMode) {
option.repeatMarkers = true;
}
END_OPTION
OPTION('u', "diff-input", NO_ARG)
if (!option.dwfilterMode) {
option.diffInput = true;
}
END_OPTION
OPTION('A', "algorithm", REQUIRED_ARG)
if (strcmp(optArg, "best") == 0) {
minimal = true;
speed_large_files = false;
} else if (strcmp(optArg, "normal") == 0) {
minimal = false;
speed_large_files = false;
} else if (strcmp(optArg, "fast") == 0) {
minimal = false;
speed_large_files = true;
} else {
fatal(_("Invalid algorithm name\n"));
}
END_OPTION
LONG_OPTION("profile", REQUIRED_ARG)
/* START_KEEP */
option.profile = optArg;
/* STOP_KEEP */
END_OPTION
LONG_OPTION("no-profile", NO_ARG)
/* START_KEEP */
option.profile = NULL;
/* STOP_KEEP */
END_OPTION
fatal(_("Option %.*s does not exist\n"), OPTPRARG);
NO_OPTION
switch (noOptionCount++) {
case 0:
option.oldFile.name = optcurrent;
break;
case 1:
option.newFile.name = optcurrent;
break;
default:
fatal(_("Too many files to compare\n"));
}
END_OPTIONS
END_FUNCTION
static PARSE_FUNCTION(preParseArgs)
#include "option_stripped.inc"
END_FUNCTION
void parseCmdLine(int argc, char *argv[]) {
ProfileOption *profileOptions = NULL;
initializeOptions();
preParseArgs(argc, argv);
if (option.profile != NULL)
profileOptions = loadProfile(".dwdiffrc", option.profile);
applyProfileOptions(profileOptions, parseArgs, argv[0]);
parseArgs(argc, argv);
/* Check that we have something to work with. */
if (option.diffInput) {
if (option.newFile.name != NULL)
fatal(_("Only one input file accepted with --diff-input\n"));
if (option.oldFile.name == NULL)
option.oldFile.input = newFileStream(fileWrapFD(STDIN_FILENO, FILE_READ));
} else {
if (option.newFile.name == NULL && option.newFile.input == NULL)
fatal(_("Need two files to compare\n"));
}
completeDefaults();
postProcessOptions();
checkOverlap();
}
dwdiff-2.1.1/src/util.c 0000664 0001750 0001750 00000003646 13074617326 016020 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2008-2011 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#include
#include
#include
#include
#include
#include "definitions.h"
/** Alert the user of a fatal error and quit.
@param fmt The format string for the message. See fprintf(3) for details.
@param ... The arguments for printing.
*/
void fatal(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
exit(2);
}
/** Print an out of memory message and exit. */
void outOfMemory(void) {
fatal("%s", _("Out of memory"));
}
/** Perform tolower for the ASCII character set. */
int ASCIItolower(int c) {
return (c >= 0x41 && c <= 0x5A) ? c | 0x20 : c;
}
#ifdef NO_STRDUP
char *strdupA(const char *orig) {
char *result;
if ((result = malloc(strlen(orig) * sizeof(char))) == NULL)
return NULL;
return strcpy(result, orig);
}
#endif
char *safe_strdup(const char *orig) {
char *result = strdupA(orig);
if (result == NULL)
outOfMemory();
return result;
}
void *safe_malloc(size_t size) {
void *result = malloc(size);
if (result == NULL)
outOfMemory();
return result;
}
void *safe_calloc(size_t size) {
void *result = calloc(1, size);
if (result == NULL)
outOfMemory();
return result;
}
void *safe_realloc(void *ptr, size_t size) {
if ((ptr = realloc(ptr, size)) == NULL)
outOfMemory();
return ptr;
}
dwdiff-2.1.1/src/util.h 0000664 0001750 0001750 00000002454 13074617326 016021 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2008-2011 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#ifndef UTIL_H
#define UTIL_H
#ifdef __GNUC__
void fatal(const char *fmt, ...) __attribute__((format (printf, 1, 2))) __attribute__((noreturn));
#else
/*@noreturn@*/ void fatal(const char *fmt, ...);
#endif
void outOfMemory(void);
int ASCIItolower(int c);
#ifdef DEBUG
#define PANIC() do { fprintf(stderr, _("Program-logic error at %s:%d\n"), __FILE__, __LINE__); abort(); } while (0)
#else
#define PANIC() fatal(_("Program-logic error at %s:%d\n"), __FILE__, __LINE__)
#endif
#define ASSERT(_condition) do { if (!(_condition)) PANIC(); } while(0)
char *safe_strdup(const char *orig);
void *safe_malloc(size_t size);
void *safe_calloc(size_t size);
void *safe_realloc(void *ptr, size_t size);
#endif
dwdiff-2.1.1/src/definitions.h 0000664 0001750 0001750 00000005660 13074617326 017361 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2006-2011 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#ifndef DEFINITIONS_H
#define DEFINITIONS_H
#include
#include
#if defined(USE_GETTEXT) || defined(USE_UNICODE)
# include
#endif
#if defined(USE_UNICODE) && defined(USE_NL_LANGINFO)
# include
#endif
#ifdef USE_GETTEXT
# include
# define _(String) gettext(String)
#else
# define _(String) (String)
#endif
#define N_(String) String
#ifdef USE_UNICODE
#define ONLY_UNICODE(_x) _x
#define SWITCH_UNICODE(a, b) a
#else
#define ONLY_UNICODE(_x)
#define SWITCH_UNICODE(a, b) b
#endif
/*==== Misc definitions ====*/
/* Define a bool type if not already defined (C++ and C99 do)*/
#if !(defined(__cplusplus) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 19990601L))
/*@-incondefs@*/
typedef enum {false, true} bool;
/*@+incondefs@*/
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 19990601L
#include
#endif
/*==== Configuration definitions ====*/
#ifndef NO_STRDUP
#define strdupA strdup
#endif
#define VERSION_STRING "2.1.1"
typedef struct CharData CharData;
#include "vector.h"
#include "stream.h"
#include "tempfile.h"
#include "unicode.h"
#include "buffer.h"
#include "diff/diff.h"
typedef lin ValueType;
#define VALUE_MAX LIN_MAX
typedef VECTOR(ValueType, ValueTypeVector);
typedef struct {
const char *name;
Stream *input;
ValueTypeVector diffTokens;
TempFile *tokens;
TempFile *whitespace;
int lastPrinted;
CharBuffer whitespaceBuffer;
bool whitespaceBufferUsed;
} InputFile;
typedef struct {
int added,
deleted,
oldChanged,
newChanged,
oldTotal,
newTotal;
} Statistics;
extern Statistics statistics;
extern int differences;
#define SET_BIT(x, b) do { (x)[(b)>>3] |= 1 << ((b) & 0x7); } while (0);
#define RESET_BIT(x, b) do { (x)[(b)>>3] &= ~(1 << ((b) & 0x7)); } while (0);
#define TEST_BIT(x, b) ((x)[(b)>>3] & (1 << ((b) & 0x7)))
struct CharData {
int singleChar;
#ifdef USE_UNICODE
struct {
UTF16Buffer original; /* UTF-16 encoded original input after clean-up */
UTF16Buffer converted; /* UTF-16 encoded string for comparison purposes. */
UTF16Buffer casefolded; /* UTF-16 encoded string for comparison purposes, case folded version. */
} UTF8Char;
#endif
};
#ifdef USE_UNICODE
extern bool UTF8Mode;
#endif
extern CharData charData;
void doDiff(void);
enum {
CAT_OTHER,
CAT_DELIMITER,
CAT_WHITESPACE
};
int classifyChar(void);
#endif
dwdiff-2.1.1/src/file.c 0000664 0001750 0001750 00000014163 13074617326 015756 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2008-2011 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#include
#include
#include
#include
#include
#include "definitions.h"
#include "file.h"
#include "util.h"
#include "option.h"
static int filePutcReal(File *file, int c);
static int fileWriteReal(File *file, const char *buffer, int bytes);
static int fileFlushReal(File *file);
static FileVtable vtableReal = { fileWriteReal, filePutcReal, fileFlushReal };
/** Wrap a file descriptor in a @a File struct. */
File *fileWrapFD(int fd, FileMode mode) {
File *retval = malloc(sizeof(File));
if (retval == NULL)
return NULL;
retval->fd = fd;
retval->errNo = 0;
retval->bufferFill = 0;
retval->bufferIndex = 0;
retval->eof = EOF_NO;
retval->mode = mode;
retval->vtable = &vtableReal;
return retval;
}
/** Open a file.
@param name The name of the file to open.
@param mode The mode of the file to open.
@a mode must be one of @a FILE_READ or @a FILE_WRITE. @a FILE_WRITE can
only be used if compiled with LEAVE_FILES.
*/
File *fileOpen(const char *name, FileMode mode) {
int fd, openMode;
switch (mode) {
case FILE_READ:
openMode = O_RDONLY;
break;
#ifdef LEAVE_FILES
case FILE_WRITE:
openMode = O_RDWR | O_CREAT | O_TRUNC;
break;
#endif
default:
PANIC();
}
if ((fd = open(name, openMode, 0600)) < 0)
return NULL;
return fileWrapFD(fd, mode);
}
/** Get the next character from a @a File. */
int fileGetc(File *file) {
ASSERT(file->mode == FILE_READ);
if (file->errNo != 0)
return EOF;
if (file->bufferIndex >= file->bufferFill) {
ssize_t bytesRead = 0;
if (file->eof != EOF_NO) {
file->eof = EOF_HIT;
return EOF;
}
/* Use while loop to allow interrupted reads */
while (1) {
ssize_t retval = read(file->fd, file->buffer + bytesRead, FILE_BUFFER_SIZE - bytesRead);
if (retval == 0) {
file->eof = EOF_COMING;
break;
} else if (retval < 0) {
if (errno == EINTR)
continue;
file->errNo = errno;
break;
} else {
bytesRead += retval;
if (bytesRead == FILE_BUFFER_SIZE)
break;
}
}
if (file->errNo != 0)
return EOF;
if (bytesRead == 0) {
file->eof = EOF_HIT;
return EOF;
}
file->bufferFill = bytesRead;
file->bufferIndex = 0;
}
return (unsigned char) file->buffer[file->bufferIndex++];
}
/** Push a character back into the buffer for a @a File. */
int fileUngetc(File *file, int c) {
ASSERT(file->mode == FILE_READ);
if (file->errNo != 0)
return EOF;
ASSERT(file->bufferIndex > 0);
return file->buffer[--file->bufferIndex] = (unsigned char) c;
}
/** Flush the buffer associated with a @a File to disk. */
static int flushBuffer(File *file) {
ssize_t bytesWritten = 0;
if (file->mode == FILE_READ)
return 0;
if (file->errNo != 0)
return EOF;
if (file->bufferFill == 0)
return 0;
/* Use while loop to allow interrupted reads */
while (1) {
ssize_t retval = write(file->fd, file->buffer + bytesWritten, file->bufferFill - bytesWritten);
if (retval == 0) {
PANIC();
} else if (retval < 0) {
if (errno == EINTR)
continue;
file->errNo = errno;
return EOF;
} else {
bytesWritten += retval;
if (bytesWritten == file->bufferFill)
break;
}
}
file->bufferFill = 0;
return bytesWritten;
}
/** Close a @a File. */
int fileClose(File *file) {
int retval = flushBuffer(file);
if (close(file->fd) < 0 && retval == 0) {
retval = errno;
} else {
retval = 0;
}
free(file);
return retval;
}
static int fileFlushReal(File *file) {
return flushBuffer(file) == EOF ? -1 : 0;
/* The code below also fsync's the data to disk. However, this should not
be necessary to allow another program to read the entire file. It does
however slow the program down, so we skip it. */
/* if (flushBuffer(file) == EOF)
return -1;
fsync(file->fd);
return 0; */
}
/** Write a character to a @a File. */
static int filePutcReal(File *file, int c) {
ASSERT(file->mode == FILE_WRITE);
if (file->errNo != 0)
return EOF;
if (file->bufferFill >= FILE_BUFFER_SIZE) {
if (flushBuffer(file) == EOF)
return EOF;
}
file->buffer[file->bufferFill++] = (unsigned char) c;
return 0;
}
/** Write a buffer to a @a File. */
static int fileWriteReal(File *file, const char *buffer, int bytes) {
ASSERT(file->mode == FILE_WRITE);
if (file->errNo != 0)
return EOF;
while (1) {
size_t minLength = FILE_BUFFER_SIZE - file->bufferFill < bytes ? FILE_BUFFER_SIZE - file->bufferFill : bytes;
memcpy(file->buffer + file->bufferFill, buffer, minLength);
file->bufferFill += minLength;
bytes -= minLength;
if (bytes == 0)
return 0;
if (flushBuffer(file) == EOF)
return EOF;
buffer += minLength;
}
}
/** Write a string to a @a File. */
int filePuts(File *file, const char *string) {
size_t length;
ASSERT(file->mode == FILE_WRITE);
if (file->errNo != 0)
return EOF;
length = strlen(string);
return fileWrite(file, string, length);
}
/** Rewind a @a File, changing its mode. */
int fileRewind(File *file, FileMode mode) {
ASSERT(mode == FILE_READ || (file->mode == FILE_WRITE && mode == FILE_WRITE));
if (flushBuffer(file) != 0)
return -1;
if (lseek(file->fd, 0, SEEK_SET) < 0) {
file->errNo = errno;
return -1;
}
file->eof = EOF_NO;
file->mode = mode;
return 0;
}
/** Return if a @a File is in error state. */
int fileError(File *file) {
return file->errNo != 0;
}
/** Return if a @a File is at the end of file. */
int fileEof(File *file) {
return file->eof == EOF_HIT;
}
/** Get the errno for the failing action on a @a File. */
int fileGetErrno(File *file) {
return file->errNo;
}
void fileClearEof(File *file) {
file->eof = EOF_COMING;
}
dwdiff-2.1.1/src/dwdiff.c 0000664 0001750 0001750 00000043456 13074617326 016311 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2006-2011 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#include
#include
#include
#include
#include
#include
#include "definitions.h"
#include "option.h"
#include "util.h"
#include "stream.h"
#include "unicode.h"
#include "dispatch.h"
#include "buffer.h"
#include "hashtable.h"
typedef enum {
NONE,
WHITESPACE,
WORD
} MatchState;
int differences = 0;
Statistics statistics;
bool UTF8Mode;
/** Contains the (partial) word currently being read in. We only need one copy
of this for all files, because all files are read sequentially. */
static CharBuffer currentWord;
CharBuffer whitespaceBuffer;
bool tokenWritten;
/** Contains the last read character. This is a global variable, because many
routines use the same data and would require constant passing of either
@a charData or a pointer to @a charData. Using a global-variable makes more
sense in this case. */
CharData charData;
static void writeEndOfToken(InputFile *file) {
ValueType wordValue;
sputc(file->tokens->stream, 0);
wordValue = getValueFromContext(¤tWord);
tokenWritten = true;
VECTOR_APPEND(file->diffTokens, wordValue);
/* Reset current word */
currentWord.used = 0;
}
/*===============================================================*/
/* Single character (SC) versions of the classification and storage routines.
Descriptions can be found in the definition of the DispatchTable struct. */
bool getNextCharSC(Stream *file) {
return (charData.singleChar = sgetc(file)) != EOF;
}
bool isWhitespaceSC(void) {
return TEST_BIT(option.whitespace, charData.singleChar);
}
bool isDelimiterSC(void) {
return TEST_BIT(option.delimiters, charData.singleChar);
}
void writeTokenCharSC(InputFile *file) {
int diffChar = option.ignoreCase ? tolower(charData.singleChar) : charData.singleChar;
VECTOR_APPEND(currentWord, diffChar);
if (charData.singleChar == 0 || charData.singleChar == 1)
filePutc(file->tokens->stream->data.file, 1);
filePutc(file->tokens->stream->data.file, charData.singleChar);
}
void writeWhitespaceCharSC(InputFile *file) {
/* Don't want to change interface (yet), so prevent warning. */
(void) file;
if (charData.singleChar == 0 || charData.singleChar == 1)
VECTOR_APPEND(whitespaceBuffer, 1);
VECTOR_APPEND(whitespaceBuffer, charData.singleChar);
}
void writeWhitespaceDelimiterSC(InputFile *file) {
sputc(file->whitespace->stream, 0);
}
#ifdef USE_UNICODE
/*===============================================================*/
/* UTF-8 versions of the classification and storage routines.
Descriptions can be found in the definition of the DispatchTable struct. */
bool getNextCharUTF8(Stream *file) {
bool retval = getCluster(file, &charData.UTF8Char.original);
if (retval)
decomposeChar(&charData);
return retval;
}
bool isWhitespaceUTF8(void) {
if (option.whitespaceSet)
return bsearch(&charData.UTF8Char.converted, option.whitespaceList.data, option.whitespaceList.used,
sizeof(UTF16Buffer), (int (*)(const void *, const void *)) compareUTF16Buffer) != NULL;
else
return isUTF16Whitespace(&charData.UTF8Char.converted);
return false;
}
bool isDelimiterUTF8(void) {
return bsearch(&charData.UTF8Char.converted, option.delimiterList.data, option.delimiterList.used,
sizeof(UTF16Buffer), (int (*)(const void *, const void *)) compareUTF16Buffer) != NULL ||
(option.punctuationMask && isUTF16Punct(&charData.UTF8Char.converted));
}
void writeTokenCharUTF8(InputFile *file) {
UChar32 highSurrogate = 0;
UTF16Buffer *writeBuffer;
size_t i;
if (option.ignoreCase) {
casefoldChar(&charData);
writeBuffer = &charData.UTF8Char.casefolded;
} else {
writeBuffer = &charData.UTF8Char.converted;
}
for (i = 0; i < writeBuffer->used; i++) {
char utf8char[4];
size_t bytes;
if ((bytes = filteredConvertToUTF8(writeBuffer->data[i], utf8char, &highSurrogate)) == 0)
continue;
VECTOR_ALLOCATE(currentWord, bytes);
memcpy(currentWord.data + currentWord.used, &utf8char, bytes);
currentWord.used += bytes;
}
/* Write the "original" characters. Note that high and low surrogates
and other invalid characters have been converted to REPLACEMENT
CHARACTER. */
if (charData.UTF8Char.original.data[0] == 0 || charData.UTF8Char.original.data[0] == 1) {
sputc(file->tokens->stream, 1);
sputc(file->tokens->stream, charData.UTF8Char.original.data[0]);
} else {
for (i = 0; i < charData.UTF8Char.original.used; i++)
putuc(file->tokens->stream, charData.UTF8Char.original.data[i]);
}
}
void writeWhitespaceCharUTF8(InputFile *file) {
UChar32 highSurrogate = 0;
size_t i;
/* Don't want to change interface (yet), so prevent warning. */
(void) file;
/* 0 and 1 are always considered to be a grapheme cluster on their own, and
are therefore always the only thing in the charData buffer if we
encouter them. Furthermore, we use 0 as line end, and 1 as escape
character in the temporary file. So we handle them separately here. */
if (charData.UTF8Char.original.data[0] == 0 || charData.UTF8Char.original.data[0] == 1) {
VECTOR_APPEND(whitespaceBuffer, 1);
VECTOR_APPEND(whitespaceBuffer, charData.UTF8Char.original.data[0]);
return;
}
for (i = 0; i < charData.UTF8Char.original.used; i++) {
char utf8char[4];
size_t bytes;
if ((bytes = filteredConvertToUTF8(charData.UTF8Char.original.data[i], utf8char, &highSurrogate)) == 0)
continue;
VECTOR_ALLOCATE(whitespaceBuffer, bytes);
memcpy(whitespaceBuffer.data + whitespaceBuffer.used, utf8char, bytes);
whitespaceBuffer.used += bytes;
}
}
void writeWhitespaceDelimiterUTF8(InputFile *file) {
putuc(file->whitespace->stream, 0);
}
/*===============================================================*/
#endif
DEF_TABLE(SC)
ONLY_UNICODE(DEF_TABLE(UTF8))
DispatchTable *dispatch = &SCDispatch;
/** Handle the end of a whitespace sequence.
@param file The @a InputFile from which the whitespace came.
If the paragraph delimiter mode is selected, this will check whether such
a delimiter should be written and break up the whitespace if necessary.
*/
void handleWhitespaceEnd(InputFile *file) {
if (option.paraDelim) {
size_t i, firstNewline = 0;
bool firstNewlineFound = false;
for (i = 0; i < whitespaceBuffer.used; i++) {
if (whitespaceBuffer.data[i] != '\n')
continue;
if (!firstNewlineFound) {
firstNewlineFound = true;
firstNewline = i;
continue;
}
break;
}
/* The whitespace preceeding any text must be treated differently, as a
newline there results in an empty line. This is different from other
whitespace where two newlines are required for an empty line. */
if (firstNewlineFound && !tokenWritten) {
/* Write everything upto but excluding the first newline */
swrite(file->whitespace->stream, whitespaceBuffer.data, firstNewline);
writeWhitespaceDelimiter(file);
swrite(file->whitespace->stream, whitespaceBuffer.data + firstNewline, whitespaceBuffer.used - firstNewline);
writeWhitespaceDelimiter(file);
writeEndOfToken(file);
whitespaceBuffer.used = 0;
return;
}
if (i != whitespaceBuffer.used) {
/* Write everything upto and including the first newline */
swrite(file->whitespace->stream, whitespaceBuffer.data, firstNewline + 1);
writeWhitespaceDelimiter(file);
swrite(file->whitespace->stream, whitespaceBuffer.data + firstNewline + 1, whitespaceBuffer.used - (firstNewline + 1));
writeWhitespaceDelimiter(file);
writeEndOfToken(file);
whitespaceBuffer.used = 0;
return;
}
/* Fall through to default case */
}
swrite(file->whitespace->stream, whitespaceBuffer.data, whitespaceBuffer.used);
writeWhitespaceDelimiter(file);
whitespaceBuffer.used = 0;
}
/** Classify the character read in ::charData. */
int classifyChar(void) {
/* Need to make sure we test delimiters first, because in UTF8Mode
we can't simply remove any overlapping delimiters from the
whitespace list. */
return isDelimiter() ? CAT_DELIMITER : (isWhitespace() ? CAT_WHITESPACE : CAT_OTHER);
}
/** Read a file and separate whitespace from the rest.
@param file The @a InputFile to read.
@return The number of "words" in @a file.
The separated parts of @a file are put into temporary files. The temporary
files' information is stored in the @a InputFile structure.
For runs in which the newline character is not included in the whitespace list,
the newline character is transliterated into the first character of the
whitespace list. Just before writing the output the characters are again
transliterated to restore the original text.
*/
static int readFile(InputFile *file) {
MatchState state = NONE;
int wordCount = 0;
int category;
if (file->name != NULL && (file->input = newFileStream(fileOpen(file->name, FILE_READ))) == NULL)
fatal(_("Can't open file %s: %s\n"), file->name, strerror(errno));
if ((file->tokens = tempFile()) == NULL)
fatal(_("Could not create temporary file: %s\n"), strerror(errno));
VECTOR_INIT(file->diffTokens);
if ((file->whitespace = tempFile()) == NULL)
fatal(_("Could not create temporary file: %s\n"), strerror(errno));
tokenWritten = false;
while (getNextChar(file->input)) {
category = classifyChar();
switch (state) {
case NONE:
if (category == CAT_WHITESPACE) {
writeWhitespaceChar(file);
state = WHITESPACE;
break;
}
handleWhitespaceEnd(file);
writeTokenChar(file);
if (category == CAT_DELIMITER) {
writeEndOfToken(file);
state = WHITESPACE;
} else {
state = WORD;
}
break;
case WORD:
if (category == CAT_WHITESPACE) {
/* Found the end of a "word". Go to whitespace mode. */
wordCount++;
writeEndOfToken(file);
writeWhitespaceChar(file);
state = WHITESPACE;
} else if (category == CAT_DELIMITER) {
/* Found a delimiter. Finish the current word, add a zero length whitespace
to the whitespace file, add the delimiter as a word, and go into
whitespace mode. */
wordCount += 2;
writeEndOfToken(file);
writeTokenChar(file);
writeEndOfToken(file);
handleWhitespaceEnd(file);
state = WHITESPACE;
} else {
writeTokenChar(file);
}
break;
case WHITESPACE:
if (category == CAT_WHITESPACE) {
writeWhitespaceChar(file);
} else if (category == CAT_DELIMITER) {
/* Found a delimiter. Finish the current whitespace, and add the delimiter
as a word. Then start new whitespace. */
wordCount++;
writeTokenChar(file);
writeEndOfToken(file);
handleWhitespaceEnd(file);
} else {
/* Found the start of a word. Finish the whitespace, and go into
word mode. */
handleWhitespaceEnd(file);
writeTokenChar(file);
state = WORD;
}
break;
default:
PANIC();
}
}
if (sferror(file->input))
fatal(_("Error reading file %s: %s\n"), file->name, strerror(sgeterrno(file->input)));
/* Make sure there is whitespace to end the output with. This may
be zero-length. */
handleWhitespaceEnd(file);
/* Make sure the word is terminated, or otherwise diff will add
extra output. */
if (state == WORD) {
wordCount++;
writeEndOfToken(file);
}
/* Close the input, and make sure the output is in the filesystem.
Then rewind so we can start reading from the start. */
sfclose(file->input);
sfflush(file->whitespace->stream);
if (sferror(file->whitespace->stream))
fatal(_("Error writing to temporary file %s: %s\n"), file->name, strerror(sgeterrno(file->whitespace->stream)));
srewind(file->whitespace->stream);
sfflush(file->tokens->stream);
if (sferror(file->tokens->stream))
fatal(_("Error writing to temporary file %s: %s\n"), file->name, strerror(sgeterrno(file->tokens->stream)));
srewind(file->tokens->stream);
return wordCount;
}
/** Read the input files and perform the diff. */
static void prepareAndExecuteDiff(void) {
statistics.oldTotal = readFile(&option.oldFile);
statistics.newTotal = readFile(&option.newFile);
baseHashMax = getHashMax();
/* Whitespace buffer and currentWord won't be used after this. */
VECTOR_FREE(currentWord);
VECTOR_FREE(whitespaceBuffer);
doDiff();
}
typedef enum {
FIRST_HEADER,
FIRST,
OLD,
NEW,
COMMON,
HEADER,
LINE_COUNTS
} DiffInputMode;
/** Split the input, if it is the output from diff -u or similar. */
void splitDiffInput(void) {
Stream *input;
TempFile *oldFile, *newFile;
DiffInputMode mode = FIRST_HEADER;
if (option.oldFile.name == NULL) {
input = option.oldFile.input;
} else {
if ((input = newFileStream(fileOpen(option.oldFile.name, FILE_READ))) == NULL)
fatal(_("Can't open file %s: %s\n"), option.oldFile.name, strerror(errno));
}
oldFile = tempFile();
newFile = tempFile();
while (getNextCharSC(input)) {
switch (mode) {
case FIRST_HEADER:
putchar(charData.singleChar);
mode = charData.singleChar != '@' ? HEADER : LINE_COUNTS;
break;
case FIRST:
if (charData.singleChar == '+') {
sputc(newFile->stream, ' ');
mode = NEW;
} else if (charData.singleChar == '-') {
sputc(oldFile->stream, ' ');
mode = OLD;
} else if (charData.singleChar == ' ') {
sputc(oldFile->stream, ' ');
sputc(newFile->stream, ' ');
mode = COMMON;
} else {
int savedChar = charData.singleChar;
closeTempFile(oldFile);
closeTempFile(newFile);
option.oldFile.name = oldFile->name;
option.newFile.name = newFile->name;
prepareAndExecuteDiff();
resetTempFiles();
oldFile = tempFile();
newFile = tempFile();
putchar(savedChar);
mode = savedChar == '@' ? LINE_COUNTS : HEADER;
}
break;
case OLD:
sputc(oldFile->stream, charData.singleChar);
if (charData.singleChar == '\n')
mode = FIRST;
break;
case NEW:
sputc(newFile->stream, charData.singleChar);
if (charData.singleChar == '\n')
mode = FIRST;
break;
case COMMON:
sputc(oldFile->stream, charData.singleChar);
sputc(newFile->stream, charData.singleChar);
if (charData.singleChar == '\n')
mode = FIRST;
break;
case HEADER:
putchar(charData.singleChar);
if (charData.singleChar == '\n')
mode = FIRST_HEADER;
break;
case LINE_COUNTS:
putchar(charData.singleChar);
if (charData.singleChar == '\n')
mode = FIRST;
break;
default:
PANIC();
}
}
closeTempFile(oldFile);
closeTempFile(newFile);
option.oldFile.name = oldFile->name;
option.newFile.name = newFile->name;
prepareAndExecuteDiff();
}
/** Main. */
int main(int argc, char *argv[]) {
#if defined(USE_GETTEXT) || defined(USE_UNICODE)
setlocale(LC_ALL, "");
#endif
#ifdef USE_GETTEXT
bindtextdomain("dwdiff", LOCALEDIR);
textdomain("dwdiff");
#endif
#ifdef USE_UNICODE
/* Check whether the input is UTF-8 encoded. */
#ifdef USE_NL_LANGINFO
if (strcmp(nl_langinfo(CODESET), "UTF-8") == 0)
UTF8Mode = true;
#else
{
char *lc_ctype, *location;
int i;
if ((lc_ctype = setlocale(LC_CTYPE, NULL)) == NULL)
goto end_utf8_check;
lc_ctype = safe_strdup(lc_ctype);
/* Use ASCII specific tolower function here, because it is not certain
that using tolower with the user locale will give correct results. */
for (i = strlen(lc_ctype) - 1; i >= 0; i--)
lc_ctype[i] = ASCIItolower(lc_ctype[i]);
if ((location = strstr(lc_ctype, ".utf8")) != NULL) {
if (location[5] == 0 || location[5] == '@')
UTF8Mode = true;
} else if ((location = strstr(lc_ctype, ".utf-8")) != NULL) {
if (location[6] == 0 || location[6] == '@')
UTF8Mode = true;
}
}
end_utf8_check:
#endif // USE_NL_LANGINFO
#endif // USE_UNICODE
#ifdef USE_UNICODE
if (UTF8Mode) {
VECTOR_INIT_ALLOCATED(charData.UTF8Char.original);
VECTOR_INIT_ALLOCATED(charData.UTF8Char.converted);
VECTOR_INIT_ALLOCATED(charData.UTF8Char.casefolded);
dispatch = &UTF8Dispatch;
}
#endif
VECTOR_INIT(currentWord);
parseCmdLine(argc, argv);
VECTOR_INIT(whitespaceBuffer);
/* If we are reading the output from diff -u, then we need to first split
the input into two separate files. After that, we can use our normal
algorithm for determining the difference between two files. */
if (option.diffInput)
splitDiffInput();
else
prepareAndExecuteDiff();
fflush(option.output);
if (option.statistics) {
int common = statistics.oldTotal - statistics.deleted - statistics.oldChanged;
if (statistics.oldTotal == 0) {
fprintf(stderr, _("old: 0 words\n"));
} else {
fprintf(stderr, _("old: %d words %d %d%% common %d %d%% deleted %d %d%% changed\n"), statistics.oldTotal,
common, (common * 100)/statistics.oldTotal,
statistics.deleted, (statistics.deleted * 100) / statistics.oldTotal,
statistics.oldChanged, (statistics.oldChanged * 100) / statistics.oldTotal);
}
common = statistics.newTotal - statistics.added - statistics.newChanged;
if (statistics.newTotal == 0) {
fprintf(stderr, _("new: 0 words\n"));
} else {
fprintf(stderr, _("new: %d words %d %d%% common %d %d%% inserted %d %d%% changed\n"), statistics.newTotal,
common, (common * 100)/statistics.newTotal,
statistics.added, (statistics.added * 100) / statistics.newTotal,
statistics.newChanged, (statistics.newChanged * 100) / statistics.newTotal);
}
}
#ifdef DEBUG_MEMORY
free(option.oldFile.diffTokens.data);
free(option.newFile.diffTokens.data);
#ifdef USE_UNICODE
free(charData.UTF8Char.original.data);
free(charData.UTF8Char.converted.data);
free(charData.UTF8Char.casefolded.data);
#endif
free(option.delColor);
free(option.addColor);
#endif
return differences;
}
dwdiff-2.1.1/src/optionMacros.h 0000664 0001750 0001750 00000024125 13074617326 017520 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2006-2010 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#ifndef OPTIONMACROS_H
#define OPTIONMACROS_H
#include
#include
#include
#include
/* INSTRUCTIONS:
- A fatal routine should be provided.
- If gettext is required, define the _ macro. Otherwise, the empty definition
here will be used. However, if the macro USE_GETTEXT is defined, the automatic
definition in this file will not be used to make sure a wrong order of
definitions is detected.
- Option parsing may change the argument vector. If this should not happen,
define the macro OPTION_STRDUP such that it allocates a copy of the string
passed to it.
- Printing options should be done by using the "%.*s" format and
"(int) optlength, optcurrent" arguments or even better, the OPTFMT format
macor and OPTPRARG arg.
A simple example argument parser is shown below:
PARSE_FUNCTION(parse_options)
OPTIONS
OPTION('f', "long-f", REQUIRED_ARG)
END_OPTION
DOUBLE_DASH
NO_MORE_OPTIONS;
END_OPTION
printf("Unknown option " OPTFMT "\n", OPTPRARG);
NO_OPTION
printf("Non-option argument: " OPTFMT "\n", OPTPRARG);
END_OPTIONS
END_FUNCTION
*/
/* Definitions to make the macro's work regardless of configuration. */
#if !defined _ && !defined USE_GETTEXT
#define _(_x) (_x)
#endif
#ifndef OPTION_STRDUP
#define OPTION_STRDUP(_x) (_x)
#define OPTION_FREE(_x) (void) 0
#else
#define OPTION_FREE(_x) free(_x)
#endif
/** Format string for printing options. */
#define OPTFMT "%.*s"
/** Arguments for printf style functions, to be used in combination with @a OPTFMT. */
#define OPTPRARG (int) optlength, optcurrent
/** Define an option parsing function.
@param name The name of the function to define.
*/
#define PARSE_FUNCTION(name) void name(int argc, char **argv) {\
char *optArg; \
int optargind; \
int optnomore = 0;
/** Declare a chile option parsing function.
@param name The name of the function to define.
*/
#define CHILD_PARSE_FUNCTION_DECL(name) int name(int argc, char **argv, char *optcurrent, char *optArg, size_t optlength, ArgType opttype, int _optargind);
/** Define a chile option parsing function.
@param name The name of the function to define.
*/
#define CHILD_PARSE_FUNCTION(name) int name(int argc, char **argv, char *optcurrent, char *optArg, size_t optlength, ArgType opttype, int _optargind) { \
int optargind = _optargind, optcontrol = 0;
/** Signal the end of a child option parsing function. */
#define END_CHILD_FUNCTION return -1; check_next: if (optargind != _optargind) return 4; return optcontrol; }
/** Call a child option parsing function.
@param name The name of the function to call.
*/
#define CALL_CHILD(name) do { int retval = name(argc, argv, optcurrent, optArg, optlength, opttype, optargind); \
if (retval == -1) break; \
else if (retval == 4) optargind++; \
else if (retval == 1) optcontrol++; \
goto check_next; } while (0)
/** Indicate the start of option processing.
This is separte from @a PARSE_FUNCTION so that local variables can be
defined.
*/
#define OPTIONS \
for (optargind = 1; optargind < argc; optargind++) { \
char optcontrol = 0; \
char *optcurrent, *optptr; \
optcurrent = argv[optargind]; \
if (optcurrent[0] == '-' && !optnomore) { \
size_t optlength; \
ArgType opttype; \
\
if (optcurrent[1] == '-') { \
if ((optArg = strchr(optcurrent, '=')) == NULL) { \
optlength = strlen(optcurrent); \
} else { \
optlength = optArg - optcurrent; \
optArg++; \
} \
opttype = LONG; \
} else { \
optlength = 2; \
if (optcurrent[1] != 0 && optcurrent[2] != 0) \
optArg = optcurrent + 2; \
else \
optArg = NULL; \
opttype = SHORT; \
} \
if (optlength > INT_MAX) optlength = INT_MAX; \
next_opt:
/* The last line above is to make sure the cast to int in error messages does
not overflow. */
/** Signal the start of non-switch option processing. */
#define NO_OPTION } else {
/** Signal the end of option processing. */
#define END_OPTIONS check_next: if (optcontrol == 1 || optcontrol == 3) { \
if (optcontrol == 1) { \
optptr = optcurrent = OPTION_STRDUP(optcurrent); \
} \
optcontrol = 2; \
optcurrent++; \
optcurrent[0] = '-'; \
optArg = optcurrent[2] != 0 ? optcurrent + 2 : NULL; \
goto next_opt; \
} else if (optcontrol == 2) { \
OPTION_FREE(optptr); \
} }} goto stop_opt_parse; stop_opt_parse:;
/** Signal the end of the option processing function. */
#define END_FUNCTION }
/** Internal macro to check whether the requirements regarding option arguments
have been met. */
#define CHECK_ARG(argReq) \
switch(argReq) { \
case NO_ARG: \
if (optArg != NULL) { \
if (opttype == SHORT) { \
optcontrol++; \
optArg = NULL; \
} else { \
fatal(_("Option %.*s does not take an argument\n"), OPTPRARG); \
} \
} \
break; \
case REQUIRED_ARG: \
if (optArg == NULL && (optargind+1 >= argc)) { \
fatal(_("Option %.*s requires an argument\n"), OPTPRARG); \
} \
if (optArg == NULL) optArg = argv[++optargind]; \
break; \
default: \
break; \
}
/** Check for a short style (-o) option.
@param shortName The name of the short style option.
@param argReq Whether or not an argument is required/allowed. One of NO_ARG,
OPTIONAL_ARG or REQUIRED_ARG.
*/
#define SHORT_OPTION(shortName, argReq) if (opttype == SHORT && optcurrent[1] == shortName) { CHECK_ARG(argReq) {
/** Check for a single dash as option.
This is usually used to signal standard input/output.
*/
#define SINGLE_DASH SHORT_OPTION('\0', NO_ARG)
/** Check for a double dash as option.
This is usually used to signal the end of options.
*/
#define DOUBLE_DASH LONG_OPTION("", NO_ARG)
/** Check for a short style (-o) or long style (--option) option.
@param shortName The name of the short style option.
@param longName The name of the long style option.
@param argReq Whether or not an argument is required/allowed. One of NO_ARG,
OPTIONAL_ARG or REQUIRED_ARG.
*/
#define OPTION(shortName, longName, argReq) if ((opttype == SHORT && optcurrent[1] == shortName) || (opttype == LONG && strlen(longName) == optlength - 2 && strncmp(optcurrent + 2, longName, optlength - 2) == 0)) { CHECK_ARG(argReq) {
/** Check for a long style (--option) option.
@param longName The name of the long style option.
@param argReq Whether or not an argument is required/allowed. One of NO_ARG,
OPTIONAL_ARG or REQUIRED_ARG.
*/
#define LONG_OPTION(longName, argReq) if (opttype == LONG && strlen(longName) == optlength - 2 && strncmp(optcurrent + 2, longName, optlength - 2) == 0) { CHECK_ARG(argReq) {
/** Signal the end of processing for the previous (SHORT_|LONG_)OPTION. */
#define END_OPTION } goto check_next; }
/** Check for presence of a short style (-o) option and set the variable if so.
@param shortName The name of the short style option.
@param var The variable to set.
*/
#define BOOLEAN_SHORT_OPTION(shortName, var) SHORT_OPTION(shortName, NO_ARG) var = 1; END_OPTION
/** Check for presence of a long style (--option) option and set the variable
if so.
@param longName The name of the long style option.
@param var The variable to set.
*/
#define BOOLEAN_LONG_OPTION(longName, var) LONG_OPTION(longName, NO_ARG) var = 1; END_OPTION
/** Check for presence of a short style (-o) or long style (--option) option
and set the variable if so.
@param shortName The name of the short style option.
@param longName The name of the long style option.
@param var The variable to set.
*/
#define BOOLEAN_OPTION(shortName, longName, var) OPTION(shortName, longName, NO_ARG) var = 1; END_OPTION
/** Tell option processor that all further arguments are non-option arguments. */
#define NO_MORE_OPTIONS do { optnomore = 1; } while(0)
/** Tell option processor to jump out of option processing. */
#define STOP_OPTION_PROCESSING do { goto stop_opt_parse; } while(0)
/** Check an option argument for an integer value.
@param var The variable to store the result in.
@param min The minimum allowable value.
@param max The maximum allowable value.
*/
#define PARSE_INT(var, min, max) do {\
char *endptr; \
long value; \
errno = 0; \
\
value = strtol(optArg, &endptr, 10); \
if (*endptr != 0) { \
fatal(_("Garbage after value for %.*s option\n"), OPTPRARG); \
} \
if (errno != 0 || value < min || value > max) { \
fatal(_("Value for %.*s option (%ld) is out of range\n"), OPTPRARG, value); \
} \
var = (int) value; } while(0)
/** Check an option argument for a double value.
@param var The variable to store the result in.
@param min The minimum allowable value.
@param max The maximum allowable value.
*/
/* #define PARSE_DOUBLE(var, min, max) do {\
char *endptr; \
double value; \
errno = 0; \
\
value = strtod(optArg, &endptr); \
if (*endptr != 0) { \
fatal(_("Garbage after value for %.*s option\n"), OPTPRARG); \
} \
if (errno != 0 || value < min || value > max) { \
fatal(_("Value for %.*s option (%f) is out of range\n"), OPTPRARG, value); \
} \
var = value; } while(0) */
/** Check an option argument for a boolean value.
@param var The variable to store the result in.
*/
#define PARSE_BOOLEAN(var) do {\
if (optArg == NULL || strcmp(optArg, "true") == 0 || strcmp(optArg, "t") == 0 || strcmp(optArg, "yes") == 0 || \
strcmp(optArg, "y") == 0 || strcmp(optArg, "1") == 0) \
(var) = 1; \
else if (strcmp(optArg, "false") == 0 || strcmp(optArg, "f") == 0 || strcmp(optArg, "no") == 0 || \
strcmp(optArg, "n") == 0 || strcmp(optArg, "0") == 0) \
(var) = 0; \
else \
fatal(_("Value for %.*s option (%s) is not a valid boolean value\n"), OPTPRARG, optArg); \
} while (0)
typedef enum {
SHORT,
LONG
} ArgType;
enum {
NO_ARG,
OPTIONAL_ARG,
REQUIRED_ARG
};
#endif
dwdiff-2.1.1/src/profile.c 0000664 0001750 0001750 00000011165 13074617326 016476 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2015 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include "profile.h"
#include "definitions.h"
#include "util.h"
#define SPACE " \t\v\f\n\r"
static void stripSpaces(char *text, size_t length, char **first, size_t *stripped_length) {
while (length > 0 && strchr(SPACE, *text) != NULL) {
text++;
length--;
}
while (length > 0 && strchr(SPACE, text[length - 1]) != NULL) {
length--;
}
*first = text;
*stripped_length = length;
}
ProfileOption *loadProfile(const char *name, const char *profile) {
ProfileOption *profileOptions = NULL;
struct passwd pwEntry;
struct passwd *result;
char buffer[4096];
int error;
const char *dir;
char *pathname;
FILE *file;
char *line = NULL;
size_t n = 0;
ssize_t bytesRead;
int lineNumber = 0;
bool saveProfile = false;
if ((error = getpwuid_r(getuid(), &pwEntry, buffer, sizeof(buffer), &result)) != 0 || result != &pwEntry) {
const char *env = getenv("HOME");
if (env == NULL || strlen(env) == 0) {
errno = ENOENT;
return NULL;
}
dir = env;
} else {
dir = pwEntry.pw_dir;
}
if ((pathname = malloc(strlen(dir) + 1 + strlen(name) + 1)) == NULL)
return NULL;
strcpy(pathname, dir);
strcat(pathname, "/");
strcat(pathname, name);
if ((file = fopen(pathname, "r")) == NULL) {
if (errno == ENOENT && strcmp(profile, "default") == 0)
return NULL;
fatal(_("Could not open configuration file %s: %s\n"), pathname, strerror(errno));
}
bool profileFound = false;
while ((bytesRead = getline(&line, &n, file)) > 0) {
char *first, *space;
size_t len;
lineNumber++;
stripSpaces(line, strlen(line), &first, &len);
// Skip empty lines
if (len == 0) continue;
// Skip comments
if (first[0] == '#') continue;
// Check for profile headers.
if (first[0] == '[' && first[len - 1] == ']') {
stripSpaces(first + 1, len - 2, &first, &len);
if (len == 0) {
saveProfile = false;
fprintf(stderr, _("Warning: empty profile name on line %s:%d\n"), pathname, lineNumber);
continue;
}
first[len] = 0;
saveProfile = strcmp(profile, first) == 0;
profileFound |= saveProfile;
continue;
}
// If we're not interested in this profile, skip.
if (!saveProfile)
continue;
ProfileOption *new_option = malloc(sizeof(ProfileOption));
if (new_option == NULL)
outOfMemory();
// Find the first space, or end of line.
space = first;
while (space < first + len && strchr(SPACE, *space) == NULL) space++;
// Copy the first word as the option name. This keeps space for copying
// in the argument as well.
if ((new_option->arg = malloc(len + 3)) == NULL)
outOfMemory();
new_option->arg[0] = '-';
new_option->arg[1] = '-';
memcpy(new_option->arg + 2, first, space - first);
size_t arg_len = space - first + 2;
new_option->arg[arg_len] = 0;
// If there is no text left, the option has no value, otherwise copy the value.
if (space != first + len) {
stripSpaces(space, len - (space - first), &first, &len);
// Check for quotes.
if (len > 0 && first[0] == first[len - 1] && (first[0] == '"' || first[0] == '\'')) {
first++;
len -= 2;
}
new_option->arg[arg_len++] = '=';
memcpy(new_option->arg + arg_len, first, len);
arg_len += len;
new_option->arg[arg_len] = 0;
}
new_option->next = profileOptions;
profileOptions = new_option;
}
// Reverse the option list.
{
ProfileOption *ptr, *prev = NULL;
ptr = profileOptions;
while (ptr != NULL) {
ProfileOption *next = ptr->next;
if (next == NULL)
profileOptions = ptr;
ptr->next = prev;
prev = ptr;
ptr = next;
}
}
free(pathname);
fclose(file);
if (!profileFound && strcmp(profile, "default") != 0)
fatal("Profile %s not found\n", profile);
return profileOptions;
}
void applyProfileOptions(ProfileOption *options, void (*parse)(int argc, char *argv[]), char *argv0) {
ProfileOption *ptr = options;
while (ptr != NULL) {
char *argv[3];
argv[0] = argv0;
argv[1] = ptr->arg;
argv[2] = NULL;
parse(2, argv);
ptr = ptr->next;
}
}
dwdiff-2.1.1/src/vector.h 0000664 0001750 0001750 00000003470 13074617326 016345 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2011 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#ifndef VECTOR_H
#define VECTOR_H
#define VECTOR_INITIAL_SIZE 32
#define VECTOR(type, name) struct { type *data; size_t allocated, used; } name
#define VECTOR_INIT(name) do { (name).data = NULL; (name).allocated = 0; (name).used = 0; } while (0)
#define VECTOR_INIT_ALLOCATED(name) do { \
(name).allocated = VECTOR_INITIAL_SIZE; \
(name).data = safe_malloc((name).allocated * sizeof((name).data[0])); \
(name).used = 0; \
} while (0)
#define VECTOR_APPEND(name, value) do { \
if ((name).allocated <= (name).used) { \
(name).allocated = (name).allocated == 0 ? VECTOR_INITIAL_SIZE : (name).allocated * 2; \
(name).data = safe_realloc((name).data, (name).allocated * sizeof((name).data[0])); \
} \
(name).data[(name).used++] = value; \
} while (0)
#define VECTOR_ALLOCATE(name, value) do { \
if ((name).allocated > (name).used + (value)) break; \
if ((name).allocated == 0) (name).allocated = VECTOR_INITIAL_SIZE; \
while ((name).allocated <= (name).used + (value)) (name).allocated *= 2; \
(name).data = safe_realloc((name).data, (name).allocated * sizeof((name).data[0])); \
} while (0)
#define VECTOR_FREE(name) do { free((name).data); (name).data = NULL; (name).used = 0; (name).allocated = 0; } while (0)
#endif
dwdiff-2.1.1/src/dispatch_autogen.sh 0000775 0001750 0001750 00000002123 13074617326 020544 0 ustar gertjan gertjan 0000000 0000000 #!/bin/sh
{
echo "/* WARNING: THIS FILE IS AUTOGENERATED. DO NOT EDIT! */"
echo "#ifndef DISPATCH_AUTOGEN_H"
echo "#define DISPATCH_AUTOGEN_H"
echo
echo "/* Definition of the DEF_TABLE macro which allows easy creation of dispatch table */"
echo "#define DEF_TABLE(suffix) DispatchTable suffix##Dispatch = { \\"
grep '^[[:space:]]*FPTR' dispatch.h | sed 's/^[[:space:]]*FPTR[[:space:]][[:space:]]*\([^(]*\)(\*\([^)]*\)DT)\(([^)]*)\).*/ \2##suffix, \\/' | sed '$s/,//'
echo "};"
echo
echo "/* Definitions to allow acces without prepending dispatch-> to all calls. */"
grep '^[[:space:]]*FPTR' dispatch.h | sed 's/^[[:space:]]*FPTR[[:space:]][[:space:]]*\([^(]*\)(\*\([^)]*\)DT)\(([^)]*)\).*/#define \2 (dispatch->\2DT)/'
echo
echo "/* Definitions of all external functions. */"
grep '^[[:space:]]*FPTR' dispatch.h | sed 's/^[[:space:]]*FPTR[[:space:]][[:space:]]*\([^(]*\)(\*\([^)]*\)DT)\(([^)]*)\).*/\1 \2SC\3;/'
grep '^[[:space:]]*FPTR' dispatch.h | sed 's/^[[:space:]]*FPTR[[:space:]][[:space:]]*\([^(]*\)(\*\([^)]*\)DT)\(([^)]*)\).*/\1 \2UTF8\3;/'
echo "#endif"
} > dispatch_autogen.h
dwdiff-2.1.1/src/diff/ 0000775 0001750 0001750 00000000000 13074617326 015576 5 ustar gertjan gertjan 0000000 0000000 dwdiff-2.1.1/src/diff/analyze.c 0000664 0001750 0001750 00000033144 13074617326 017412 0 ustar gertjan gertjan 0000000 0000000 /* Analyze file differences for GNU DIFF.
Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006-2007,
2009-2011 Free Software Foundation, Inc.
This file is part of GNU DIFF.
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, version 3 of the License.
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, see . */
/* This file has been heavily stripped and slightly modified by G.P. Halkes, 2011. */
/* G.P. Halkes: Additions: */
#include "diff.h"
static struct file_data files[2];
bool minimal;
bool speed_large_files;
/* /Additions. */
/* The core of the Diff algorithm. */
#define ELEMENT lin
#define EQUAL(x,y) ((x) == (y))
#define OFFSET lin
#define EXTRA_CONTEXT_FIELDS /* none */
#define NOTE_DELETE(c, xoff) (files[0].changed[files[0].realindexes[xoff]] = 1)
#define NOTE_INSERT(c, yoff) (files[1].changed[files[1].realindexes[yoff]] = 1)
#define USE_HEURISTIC 1
#define lint
#include "diffseq.h"
/* Discard lines from one file that have no matches in the other file.
A line which is discarded will not be considered by the actual
comparison algorithm; it will be as if that line were not in the file.
The file's `realindexes' table maps virtual line numbers
(which don't count the discarded lines) into real line numbers;
this is how the actual comparison algorithm produces results
that are comprehensible when the discarded lines are counted.
When we discard a line, we also mark it as a deletion or insertion
so that it will be printed in the output. */
static void
discard_confusing_lines (struct file_data filevec[])
{
int f;
lin i;
char *discarded[2];
lin *equiv_count[2];
lin *p;
/* Allocate our results. */
p = xmalloc ((filevec[0].buffered_lines + filevec[1].buffered_lines)
* (2 * sizeof *p));
for (f = 0; f < 2; f++)
{
filevec[f].undiscarded = p; p += filevec[f].buffered_lines;
filevec[f].realindexes = p; p += filevec[f].buffered_lines;
}
/* Set up equiv_count[F][I] as the number of lines in file F
that fall in equivalence class I. */
p = zalloc (filevec[0].equiv_max * (2 * sizeof *p));
equiv_count[0] = p;
equiv_count[1] = p + filevec[0].equiv_max;
for (i = 0; i < filevec[0].buffered_lines; ++i)
++equiv_count[0][filevec[0].equivs[i]];
for (i = 0; i < filevec[1].buffered_lines; ++i)
++equiv_count[1][filevec[1].equivs[i]];
/* Set up tables of which lines are going to be discarded. */
discarded[0] = zalloc (filevec[0].buffered_lines
+ filevec[1].buffered_lines);
discarded[1] = discarded[0] + filevec[0].buffered_lines;
/* Mark to be discarded each line that matches no line of the other file.
If a line matches many lines, mark it as provisionally discardable. */
for (f = 0; f < 2; f++)
{
size_t end = filevec[f].buffered_lines;
char *discards = discarded[f];
lin *counts = equiv_count[1 - f];
const lin *equivs = filevec[f].equivs;
size_t many = 5;
size_t tem = end / 64;
/* Multiply MANY by approximate square root of number of lines.
That is the threshold for provisionally discardable lines. */
while ((tem = tem >> 2) > 0)
many *= 2;
for (i = 0; i < (lin) end; i++)
{
size_t nmatch;
if (equivs[i] == 0)
continue;
nmatch = counts[equivs[i]];
if (nmatch == 0)
discards[i] = 1;
else if (nmatch > many)
discards[i] = 2;
}
}
/* Don't really discard the provisional lines except when they occur
in a run of discardables, with nonprovisionals at the beginning
and end. */
for (f = 0; f < 2; f++)
{
lin end = filevec[f].buffered_lines;
register char *discards = discarded[f];
for (i = 0; i < end; i++)
{
/* Cancel provisional discards not in middle of run of discards. */
if (discards[i] == 2)
discards[i] = 0;
else if (discards[i] != 0)
{
/* We have found a nonprovisional discard. */
register lin j;
lin length;
lin provisional = 0;
/* Find end of this run of discardable lines.
Count how many are provisionally discardable. */
for (j = i; j < end; j++)
{
if (discards[j] == 0)
break;
if (discards[j] == 2)
++provisional;
}
/* Cancel provisional discards at end, and shrink the run. */
while (j > i && discards[j - 1] == 2)
discards[--j] = 0, --provisional;
/* Now we have the length of a run of discardable lines
whose first and last are not provisional. */
length = j - i;
/* If 1/4 of the lines in the run are provisional,
cancel discarding of all provisional lines in the run. */
if (provisional * 4 > length)
{
while (j > i)
if (discards[--j] == 2)
discards[j] = 0;
}
else
{
register lin consec;
lin minimum = 1;
lin tem = length >> 2;
/* MINIMUM is approximate square root of LENGTH/4.
A subrun of two or more provisionals can stand
when LENGTH is at least 16.
A subrun of 4 or more can stand when LENGTH >= 64. */
while (0 < (tem >>= 2))
minimum <<= 1;
minimum++;
/* Cancel any subrun of MINIMUM or more provisionals
within the larger run. */
for (j = 0, consec = 0; j < length; j++)
if (discards[i + j] != 2)
consec = 0;
else if (minimum == ++consec)
/* Back up to start of subrun, to cancel it all. */
j -= consec;
else if (minimum < consec)
discards[i + j] = 0;
/* Scan from beginning of run
until we find 3 or more nonprovisionals in a row
or until the first nonprovisional at least 8 lines in.
Until that point, cancel any provisionals. */
for (j = 0, consec = 0; j < length; j++)
{
if (j >= 8 && discards[i + j] == 1)
break;
if (discards[i + j] == 2)
consec = 0, discards[i + j] = 0;
else if (discards[i + j] == 0)
consec = 0;
else
consec++;
if (consec == 3)
break;
}
/* I advances to the last line of the run. */
i += length - 1;
/* Same thing, from end. */
for (j = 0, consec = 0; j < length; j++)
{
if (j >= 8 && discards[i - j] == 1)
break;
if (discards[i - j] == 2)
consec = 0, discards[i - j] = 0;
else if (discards[i - j] == 0)
consec = 0;
else
consec++;
if (consec == 3)
break;
}
}
}
}
}
/* Actually discard the lines. */
for (f = 0; f < 2; f++)
{
char *discards = discarded[f];
lin end = filevec[f].buffered_lines;
lin j = 0;
for (i = 0; i < end; ++i)
if (minimal || discards[i] == 0)
{
filevec[f].undiscarded[j] = filevec[f].equivs[i];
filevec[f].realindexes[j++] = i;
}
else
filevec[f].changed[i] = 1;
filevec[f].nondiscarded_lines = j;
}
free (discarded[0]);
free (equiv_count[0]);
}
/* Adjust inserts/deletes of identical lines to join changes
as much as possible.
We do something when a run of changed lines include a
line at one end and have an excluded, identical line at the other.
We are free to choose which identical line is included.
`compareseq' usually chooses the one at the beginning,
but usually it is cleaner to consider the following identical line
to be the "change". */
static void
shift_boundaries (struct file_data filevec[])
{
int f;
for (f = 0; f < 2; f++)
{
char *changed = filevec[f].changed;
char *other_changed = filevec[1 - f].changed;
lin const *equivs = filevec[f].equivs;
lin i = 0;
lin j = 0;
lin i_end = filevec[f].buffered_lines;
while (1)
{
lin runlength, start, corresponding;
/* Scan forwards to find beginning of another run of changes.
Also keep track of the corresponding point in the other file. */
while (i < i_end && !changed[i])
{
while (other_changed[j++])
continue;
i++;
}
if (i == i_end)
break;
start = i;
/* Find the end of this run of changes. */
while (changed[++i])
continue;
while (other_changed[j])
j++;
do
{
/* Record the length of this run of changes, so that
we can later determine whether the run has grown. */
runlength = i - start;
/* Move the changed region back, so long as the
previous unchanged line matches the last changed one.
This merges with previous changed regions. */
while (start && equivs[start - 1] == equivs[i - 1])
{
changed[--start] = 1;
changed[--i] = 0;
while (changed[start - 1])
start--;
while (other_changed[--j])
continue;
}
/* Set CORRESPONDING to the end of the changed run, at the last
point where it corresponds to a changed run in the other file.
CORRESPONDING == I_END means no such point has been found. */
corresponding = other_changed[j - 1] ? i : i_end;
/* Move the changed region forward, so long as the
first changed line matches the following unchanged one.
This merges with following changed regions.
Do this second, so that if there are no merges,
the changed region is moved forward as far as possible. */
while (i != i_end && equivs[start] == equivs[i])
{
changed[start++] = 0;
changed[i++] = 1;
while (changed[i])
i++;
while (other_changed[++j])
corresponding = i;
}
}
while (runlength != i - start);
/* If possible, move the fully-merged run of changes
back to a corresponding run in the other file. */
while (corresponding < i)
{
changed[--start] = 1;
changed[--i] = 0;
while (other_changed[--j])
continue;
}
}
}
}
/* Cons an additional entry onto the front of an edit script OLD.
LINE0 and LINE1 are the first affected lines in the two files (origin 0).
DELETED is the number of lines deleted here from file 0.
INSERTED is the number of lines inserted here in file 1.
If DELETED is 0 then LINE0 is the number of the line before
which the insertion was done; vice versa for INSERTED and LINE1. */
static struct change *
add_change (lin line0, lin line1, lin deleted, lin inserted,
struct change *old)
{
struct change *new = xmalloc (sizeof *new);
new->line0 = line0;
new->line1 = line1;
new->inserted = inserted;
new->deleted = deleted;
new->link = old;
return new;
}
/* Scan the tables of which lines are inserted and deleted,
producing an edit script in forward order. */
static struct change *
build_script (struct file_data const filevec[])
{
struct change *script = 0;
char *changed0 = filevec[0].changed;
char *changed1 = filevec[1].changed;
lin i0 = filevec[0].buffered_lines, i1 = filevec[1].buffered_lines;
/* Note that changedN[-1] does exist, and is 0. */
while (i0 >= 0 || i1 >= 0)
{
if (changed0[i0 - 1] | changed1[i1 - 1])
{
lin line0 = i0, line1 = i1;
/* Find # lines changed here in each file. */
while (changed0[i0 - 1]) --i0;
while (changed1[i1 - 1]) --i1;
/* Record this change. */
script = add_change (i0, i1, line0 - i0, line1 - i1, script);
}
/* We have reached lines in the two files that match each other. */
i0--, i1--;
}
return script;
}
/* Report the differences of two files. */
struct change *diff_2_files (struct comparison *cmp)
{
struct change *script;
struct context ctxt;
lin diags;
lin too_expensive;
/* Allocate vectors for the results of comparison:
a flag for each line of each file, saying whether that line
is an insertion or deletion.
Allocate an extra element, always 0, at each end of each vector. */
size_t s = cmp->file[0].buffered_lines + cmp->file[1].buffered_lines + 4;
char *flag_space = zalloc (s);
cmp->file[0].changed = flag_space + 1;
cmp->file[1].changed = flag_space + cmp->file[0].buffered_lines + 3;
/* Some lines are obviously insertions or deletions
because they don't match anything. Detect them now, and
avoid even thinking about them in the main comparison algorithm. */
discard_confusing_lines (cmp->file);
/* Now do the main comparison algorithm, considering just the
undiscarded lines. */
ctxt.xvec = cmp->file[0].undiscarded;
ctxt.yvec = cmp->file[1].undiscarded;
diags = (cmp->file[0].nondiscarded_lines
+ cmp->file[1].nondiscarded_lines + 3);
ctxt.fdiag = xmalloc (diags * (2 * sizeof *ctxt.fdiag));
ctxt.bdiag = ctxt.fdiag + diags;
ctxt.fdiag += cmp->file[1].nondiscarded_lines + 1;
ctxt.bdiag += cmp->file[1].nondiscarded_lines + 1;
ctxt.heuristic = speed_large_files;
/* Set TOO_EXPENSIVE to be approximate square root of input size,
bounded below by 256. */
too_expensive = 1;
for (; diags != 0; diags >>= 2)
too_expensive <<= 1;
ctxt.too_expensive = MAX (256, too_expensive);
files[0] = cmp->file[0];
files[1] = cmp->file[1];
compareseq (0, cmp->file[0].nondiscarded_lines,
0, cmp->file[1].nondiscarded_lines, minimal, &ctxt);
free (ctxt.fdiag - (cmp->file[1].nondiscarded_lines + 1));
/* Modify the results slightly to make them prettier
in cases where that can validly be done. */
shift_boundaries (cmp->file);
/* Get the results of comparison in the form of a chain
of `struct change's -- an edit script. */
script = build_script (cmp->file);
free (cmp->file[0].undiscarded);
free (flag_space);
return script;
}
dwdiff-2.1.1/src/diff/diffseq.h 0000664 0001750 0001750 00000041525 13074617326 017377 0 ustar gertjan gertjan 0000000 0000000 /* Analyze differences between two vectors.
Copyright (C) 1988-1989, 1992-1995, 2001-2004, 2006-2011 Free Software
Foundation, Inc.
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; version 3 of the License.
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, see . */
/* Minor modifications done to prevent compiler warnings by G.P. Halkes, 2011 */
/* The basic idea is to consider two vectors as similar if, when
transforming the first vector into the second vector through a
sequence of edits (inserts and deletes of one element each),
this sequence is short - or equivalently, if the ordered list
of elements that are untouched by these edits is long. For a
good introduction to the subject, read about the "Levenshtein
distance" in Wikipedia.
The basic algorithm is described in:
"An O(ND) Difference Algorithm and its Variations", Eugene Myers,
Algorithmica Vol. 1 No. 2, 1986, pp. 251-266;
see especially section 4.2, which describes the variation used below.
The basic algorithm was independently discovered as described in:
"Algorithms for Approximate String Matching", E. Ukkonen,
Information and Control Vol. 64, 1985, pp. 100-118.
Unless the 'find_minimal' flag is set, this code uses the TOO_EXPENSIVE
heuristic, by Paul Eggert, to limit the cost to O(N**1.5 log N)
at the price of producing suboptimal output for large inputs with
many differences. */
/* Before including this file, you need to define:
ELEMENT The element type of the vectors being compared.
EQUAL A two-argument macro that tests two elements for
equality.
OFFSET A signed integer type sufficient to hold the
difference between two indices. Usually
something like ssize_t.
EXTRA_CONTEXT_FIELDS Declarations of fields for 'struct context'.
NOTE_DELETE(ctxt, xoff) Record the removal of the object xvec[xoff].
NOTE_INSERT(ctxt, yoff) Record the insertion of the object yvec[yoff].
EARLY_ABORT(ctxt) (Optional) A boolean expression that triggers an
early abort of the computation.
USE_HEURISTIC (Optional) Define if you want to support the
heuristic for large vectors.
It is also possible to use this file with abstract arrays. In this case,
xvec and yvec are not represented in memory. They only exist conceptually.
In this case, the list of defines above is amended as follows:
ELEMENT Undefined.
EQUAL Undefined.
XVECREF_YVECREF_EQUAL(ctxt, xoff, yoff)
A three-argument macro: References xvec[xoff] and
yvec[yoff] and tests these elements for equality.
Before including this file, you also need to include:
#include
#include
#include "minmax.h"
*/
/* Maximum value of type OFFSET. */
#define OFFSET_MAX \
((((OFFSET)1 << (sizeof (OFFSET) * CHAR_BIT - 2)) - 1) * 2 + 1)
/* Default to no early abort. */
#ifndef EARLY_ABORT
# define EARLY_ABORT(ctxt) false
#endif
/* Use this to suppress gcc's `...may be used before initialized' warnings.
Beware: The Code argument must not contain commas. */
#ifndef IF_LINT
# ifdef lint
# define IF_LINT(Code) Code
# else
# define IF_LINT(Code) /* empty */
# endif
#endif
/* As above, but when Code must contain one comma. */
#ifndef IF_LINT2
# ifdef lint
# define IF_LINT2(Code1, Code2) Code1, Code2
# else
# define IF_LINT2(Code1, Code2) /* empty */
# endif
#endif
/*
* Context of comparison operation.
*/
struct context
{
#ifdef ELEMENT
/* Vectors being compared. */
ELEMENT const *xvec;
ELEMENT const *yvec;
#endif
/* Extra fields. */
EXTRA_CONTEXT_FIELDS
/* Vector, indexed by diagonal, containing 1 + the X coordinate of the point
furthest along the given diagonal in the forward search of the edit
matrix. */
OFFSET *fdiag;
/* Vector, indexed by diagonal, containing the X coordinate of the point
furthest along the given diagonal in the backward search of the edit
matrix. */
OFFSET *bdiag;
#ifdef USE_HEURISTIC
/* This corresponds to the diff -H flag. With this heuristic, for
vectors with a constant small density of changes, the algorithm is
linear in the vectors size. */
bool heuristic;
#endif
/* Edit scripts longer than this are too expensive to compute. */
OFFSET too_expensive;
/* Snakes bigger than this are considered `big'. */
#define SNAKE_LIMIT 20
};
struct partition
{
/* Midpoints of this partition. */
OFFSET xmid;
OFFSET ymid;
/* True if low half will be analyzed minimally. */
bool lo_minimal;
/* Likewise for high half. */
bool hi_minimal;
};
/* Find the midpoint of the shortest edit script for a specified portion
of the two vectors.
Scan from the beginnings of the vectors, and simultaneously from the ends,
doing a breadth-first search through the space of edit-sequence.
When the two searches meet, we have found the midpoint of the shortest
edit sequence.
If FIND_MINIMAL is true, find the minimal edit script regardless of
expense. Otherwise, if the search is too expensive, use heuristics to
stop the search and report a suboptimal answer.
Set PART->(xmid,ymid) to the midpoint (XMID,YMID). The diagonal number
XMID - YMID equals the number of inserted elements minus the number
of deleted elements (counting only elements before the midpoint).
Set PART->lo_minimal to true iff the minimal edit script for the
left half of the partition is known; similarly for PART->hi_minimal.
This function assumes that the first elements of the specified portions
of the two vectors do not match, and likewise that the last elements do not
match. The caller must trim matching elements from the beginning and end
of the portions it is going to specify.
If we return the "wrong" partitions, the worst this can do is cause
suboptimal diff output. It cannot cause incorrect diff output. */
static void
diag (OFFSET xoff, OFFSET xlim, OFFSET yoff, OFFSET ylim, bool find_minimal,
struct partition *part, struct context *ctxt)
{
OFFSET *const fd = ctxt->fdiag; /* Give the compiler a chance. */
OFFSET *const bd = ctxt->bdiag; /* Additional help for the compiler. */
#ifdef ELEMENT
ELEMENT const *const xv = ctxt->xvec; /* Still more help for the compiler. */
ELEMENT const *const yv = ctxt->yvec; /* And more and more . . . */
#define XREF_YREF_EQUAL(x,y) EQUAL (xv[x], yv[y])
#else
#define XREF_YREF_EQUAL(x,y) XVECREF_YVECREF_EQUAL (ctxt, x, y)
#endif
const OFFSET dmin = xoff - ylim; /* Minimum valid diagonal. */
const OFFSET dmax = xlim - yoff; /* Maximum valid diagonal. */
const OFFSET fmid = xoff - yoff; /* Center diagonal of top-down search. */
const OFFSET bmid = xlim - ylim; /* Center diagonal of bottom-up search. */
OFFSET fmin = fmid;
OFFSET fmax = fmid; /* Limits of top-down search. */
OFFSET bmin = bmid;
OFFSET bmax = bmid; /* Limits of bottom-up search. */
OFFSET c; /* Cost. */
bool odd = (fmid - bmid) & 1; /* True if southeast corner is on an odd
diagonal with respect to the northwest. */
fd[fmid] = xoff;
bd[bmid] = xlim;
for (c = 1;; ++c)
{
OFFSET d; /* Active diagonal. */
bool big_snake = false;
/* Extend the top-down search by an edit step in each diagonal. */
if (fmin > dmin)
fd[--fmin - 1] = -1;
else
++fmin;
if (fmax < dmax)
fd[++fmax + 1] = -1;
else
--fmax;
for (d = fmax; d >= fmin; d -= 2)
{
OFFSET x;
OFFSET y;
OFFSET tlo = fd[d - 1];
OFFSET thi = fd[d + 1];
OFFSET x0 = tlo < thi ? thi : tlo + 1;
for (x = x0, y = x0 - d;
x < xlim && y < ylim && XREF_YREF_EQUAL (x, y);
x++, y++)
continue;
if (x - x0 > SNAKE_LIMIT)
big_snake = true;
fd[d] = x;
if (odd && bmin <= d && d <= bmax && bd[d] <= x)
{
part->xmid = x;
part->ymid = y;
part->lo_minimal = part->hi_minimal = true;
return;
}
}
/* Similarly extend the bottom-up search. */
if (bmin > dmin)
bd[--bmin - 1] = OFFSET_MAX;
else
++bmin;
if (bmax < dmax)
bd[++bmax + 1] = OFFSET_MAX;
else
--bmax;
for (d = bmax; d >= bmin; d -= 2)
{
OFFSET x;
OFFSET y;
OFFSET tlo = bd[d - 1];
OFFSET thi = bd[d + 1];
OFFSET x0 = tlo < thi ? tlo : thi - 1;
for (x = x0, y = x0 - d;
xoff < x && yoff < y && XREF_YREF_EQUAL (x - 1, y - 1);
x--, y--)
continue;
if (x0 - x > SNAKE_LIMIT)
big_snake = true;
bd[d] = x;
if (!odd && fmin <= d && d <= fmax && x <= fd[d])
{
part->xmid = x;
part->ymid = y;
part->lo_minimal = part->hi_minimal = true;
return;
}
}
if (find_minimal)
continue;
#ifdef USE_HEURISTIC
/* Heuristic: check occasionally for a diagonal that has made lots
of progress compared with the edit distance. If we have any
such, find the one that has made the most progress and return it
as if it had succeeded.
With this heuristic, for vectors with a constant small density
of changes, the algorithm is linear in the vector size. */
if (200 < c && big_snake && ctxt->heuristic)
{
{
OFFSET best = 0;
for (d = fmax; d >= fmin; d -= 2)
{
OFFSET dd = d - fmid;
OFFSET x = fd[d];
OFFSET y = x - d;
OFFSET v = (x - xoff) * 2 - dd;
if (v > 12 * (c + (dd < 0 ? -dd : dd)))
{
if (v > best
&& xoff + SNAKE_LIMIT <= x && x < xlim
&& yoff + SNAKE_LIMIT <= y && y < ylim)
{
/* We have a good enough best diagonal; now insist
that it end with a significant snake. */
int k;
for (k = 1; XREF_YREF_EQUAL (x - k, y - k); k++)
if (k == SNAKE_LIMIT)
{
best = v;
part->xmid = x;
part->ymid = y;
break;
}
}
}
}
if (best > 0)
{
part->lo_minimal = true;
part->hi_minimal = false;
return;
}
}
{
OFFSET best = 0;
for (d = bmax; d >= bmin; d -= 2)
{
OFFSET dd = d - bmid;
OFFSET x = bd[d];
OFFSET y = x - d;
OFFSET v = (xlim - x) * 2 + dd;
if (v > 12 * (c + (dd < 0 ? -dd : dd)))
{
if (v > best
&& xoff < x && x <= xlim - SNAKE_LIMIT
&& yoff < y && y <= ylim - SNAKE_LIMIT)
{
/* We have a good enough best diagonal; now insist
that it end with a significant snake. */
int k;
for (k = 0; XREF_YREF_EQUAL (x + k, y + k); k++)
if (k == SNAKE_LIMIT - 1)
{
best = v;
part->xmid = x;
part->ymid = y;
break;
}
}
}
}
if (best > 0)
{
part->lo_minimal = false;
part->hi_minimal = true;
return;
}
}
}
#endif /* USE_HEURISTIC */
/* Heuristic: if we've gone well beyond the call of duty, give up
and report halfway between our best results so far. */
if (c >= ctxt->too_expensive)
{
OFFSET fxybest;
OFFSET fxbest IF_LINT (= 0);
OFFSET bxybest;
OFFSET bxbest IF_LINT (= 0);
/* Find forward diagonal that maximizes X + Y. */
fxybest = -1;
for (d = fmax; d >= fmin; d -= 2)
{
OFFSET x = MIN (fd[d], xlim);
OFFSET y = x - d;
if (ylim < y)
{
x = ylim + d;
y = ylim;
}
if (fxybest < x + y)
{
fxybest = x + y;
fxbest = x;
}
}
/* Find backward diagonal that minimizes X + Y. */
bxybest = OFFSET_MAX;
for (d = bmax; d >= bmin; d -= 2)
{
OFFSET x = MAX (xoff, bd[d]);
OFFSET y = x - d;
if (y < yoff)
{
x = yoff + d;
y = yoff;
}
if (x + y < bxybest)
{
bxybest = x + y;
bxbest = x;
}
}
/* Use the better of the two diagonals. */
if ((xlim + ylim) - bxybest < fxybest - (xoff + yoff))
{
part->xmid = fxbest;
part->ymid = fxybest - fxbest;
part->lo_minimal = true;
part->hi_minimal = false;
}
else
{
part->xmid = bxbest;
part->ymid = bxybest - bxbest;
part->lo_minimal = false;
part->hi_minimal = true;
}
return;
}
}
#undef XREF_YREF_EQUAL
}
/* Compare in detail contiguous subsequences of the two vectors
which are known, as a whole, to match each other.
The subsequence of vector 0 is [XOFF, XLIM) and likewise for vector 1.
Note that XLIM, YLIM are exclusive bounds. All indices into the vectors
are origin-0.
If FIND_MINIMAL, find a minimal difference no matter how
expensive it is.
The results are recorded by invoking NOTE_DELETE and NOTE_INSERT.
Return false if terminated normally, or true if terminated through early
abort. */
static bool
compareseq (OFFSET xoff, OFFSET xlim, OFFSET yoff, OFFSET ylim,
bool find_minimal, struct context *ctxt)
{
#ifdef ELEMENT
ELEMENT const *xv = ctxt->xvec; /* Help the compiler. */
ELEMENT const *yv = ctxt->yvec;
#define XREF_YREF_EQUAL(x,y) EQUAL (xv[x], yv[y])
#else
#define XREF_YREF_EQUAL(x,y) XVECREF_YVECREF_EQUAL (ctxt, x, y)
#endif
/* Slide down the bottom initial diagonal. */
while (xoff < xlim && yoff < ylim && XREF_YREF_EQUAL (xoff, yoff))
{
xoff++;
yoff++;
}
/* Slide up the top initial diagonal. */
while (xoff < xlim && yoff < ylim && XREF_YREF_EQUAL (xlim - 1, ylim - 1))
{
xlim--;
ylim--;
}
/* Handle simple cases. */
if (xoff == xlim)
while (yoff < ylim)
{
NOTE_INSERT (ctxt, yoff);
if (EARLY_ABORT (ctxt))
return true;
yoff++;
}
else if (yoff == ylim)
while (xoff < xlim)
{
NOTE_DELETE (ctxt, xoff);
if (EARLY_ABORT (ctxt))
return true;
xoff++;
}
else
{
struct partition part IF_LINT2 (= { .xmid = 0, .ymid = 0 });
/* Find a point of correspondence in the middle of the vectors. */
diag (xoff, xlim, yoff, ylim, find_minimal, &part, ctxt);
/* Use the partitions to split this problem into subproblems. */
if (compareseq (xoff, part.xmid, yoff, part.ymid, part.lo_minimal, ctxt))
return true;
if (compareseq (part.xmid, xlim, part.ymid, ylim, part.hi_minimal, ctxt))
return true;
}
return false;
#undef XREF_YREF_EQUAL
}
#undef ELEMENT
#undef EQUAL
#undef OFFSET
#undef EXTRA_CONTEXT_FIELDS
#undef NOTE_DELETE
#undef NOTE_INSERT
#undef EARLY_ABORT
#undef USE_HEURISTIC
#undef XVECREF_YVECREF_EQUAL
#undef OFFSET_MAX
dwdiff-2.1.1/src/diff/diff.h 0000664 0001750 0001750 00000010253 13074617326 016660 0 ustar gertjan gertjan 0000000 0000000 /* Shared definitions for GNU DIFF
Copyright (C) 1988-1989, 1991-1995, 1998, 2001-2002, 2004, 2009-2011 Free
Software Foundation, Inc.
This file is part of GNU DIFF.
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, version 3 of the License.
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, see . */
/* This file has been heavily stripped and slightly modified by G.P. Halkes, 2011.
Definitions regarding the lin type and the MIN and MAX macros were taken
from diffutils-3.2/src/system.h, which is distributed under the same
license as this file.
*/
#ifndef DIFF_H
#define DIFF_H
#include
#include
#include
#include
#include "static_assert.h"
typedef ptrdiff_t lin;
#define LIN_MAX PTRDIFF_MAX
static_assert((lin) -1 < 0);
static_assert(sizeof (ptrdiff_t) <= sizeof (lin));
static_assert(sizeof (lin) <= sizeof (long int));
#include "definitions.h"
#include "util.h"
#define MIN(a, b) ((a) <= (b) ? (a) : (b))
#define MAX(a, b) ((a) >= (b) ? (a) : (b))
#define xmalloc safe_malloc
#define zalloc safe_calloc
/* Use heuristics for better speed with large files with a small
density of changes. */
extern bool speed_large_files;
/* Don't discard lines. This makes things slower (sometimes much
slower) but will find a guaranteed minimal set of changes. */
extern bool minimal;
/* The result of comparison is an "edit script": a chain of `struct change'.
Each `struct change' represents one place where some lines are deleted
and some are inserted.
LINE0 and LINE1 are the first affected lines in the two files (origin 0).
DELETED is the number of lines deleted here from file 0.
INSERTED is the number of lines inserted here in file 1.
If DELETED is 0 then LINE0 is the number of the line before
which the insertion was done; vice versa for INSERTED and LINE1. */
struct change
{
struct change *link; /* Previous or next edit command */
lin inserted; /* # lines of file 1 changed here. */
lin deleted; /* # lines of file 0 changed here. */
lin line0; /* Line number of 1st deleted line. */
lin line1; /* Line number of 1st inserted line. */
bool ignore; /* Flag used in context.c. */
};
/* Structures that describe the input files. */
/* Data on one input file being compared. */
struct file_data {
/* linbuf_base <= buffered_lines <= valid_lines <= alloc_lines.
linebuf[linbuf_base ... buffered_lines - 1] are possibly differing.
linebuf[linbuf_base ... valid_lines - 1] contain valid data.
linebuf[linbuf_base ... alloc_lines - 1] are allocated. */
lin buffered_lines;
/* Vector, indexed by line number, containing an equivalence code for
each line. It is this vector that is actually compared with that
of another file to generate differences. */
const lin *equivs;
/* Vector, like the previous one except that
the elements for discarded lines have been squeezed out. */
lin *undiscarded;
/* Vector mapping virtual line numbers (not counting discarded lines)
to real ones (counting those lines). Both are origin-0. */
lin *realindexes;
/* Total number of nondiscarded lines. */
lin nondiscarded_lines;
/* Vector, indexed by real origin-0 line number,
containing 1 for a line that is an insertion or a deletion.
The results of comparison are stored here. */
char *changed;
/* 1 more than the maximum equivalence value used for this or its
sibling file. */
lin equiv_max;
};
/* Data on two input files being compared. */
struct comparison
{
struct file_data file[2];
struct comparison const *parent; /* parent, if a recursive comparison */
};
struct change *diff_2_files(struct comparison *cmp);
#endif
dwdiff-2.1.1/src/dispatch_autogen.h 0000664 0001750 0001750 00000004221 13074617326 020357 0 ustar gertjan gertjan 0000000 0000000 /* WARNING: THIS FILE IS AUTOGENERATED. DO NOT EDIT! */
#ifndef DISPATCH_AUTOGEN_H
#define DISPATCH_AUTOGEN_H
/* Definition of the DEF_TABLE macro which allows easy creation of dispatch table */
#define DEF_TABLE(suffix) DispatchTable suffix##Dispatch = { \
getNextChar##suffix, \
isWhitespace##suffix, \
isDelimiter##suffix, \
writeTokenChar##suffix, \
writeWhitespaceChar##suffix, \
writeWhitespaceDelimiter##suffix, \
addCharacters##suffix, \
checkOverlap##suffix, \
setPunctuation##suffix, \
initOptions##suffix, \
postProcessOptions##suffix \
};
/* Definitions to allow acces without prepending dispatch-> to all calls. */
#define getNextChar (dispatch->getNextCharDT)
#define isWhitespace (dispatch->isWhitespaceDT)
#define isDelimiter (dispatch->isDelimiterDT)
#define writeTokenChar (dispatch->writeTokenCharDT)
#define writeWhitespaceChar (dispatch->writeWhitespaceCharDT)
#define writeWhitespaceDelimiter (dispatch->writeWhitespaceDelimiterDT)
#define addCharacters (dispatch->addCharactersDT)
#define checkOverlap (dispatch->checkOverlapDT)
#define setPunctuation (dispatch->setPunctuationDT)
#define initOptions (dispatch->initOptionsDT)
#define postProcessOptions (dispatch->postProcessOptionsDT)
/* Definitions of all external functions. */
bool getNextCharSC(Stream *file);
bool isWhitespaceSC(void);
bool isDelimiterSC(void);
void writeTokenCharSC(InputFile *file);
void writeWhitespaceCharSC(InputFile *file);
void writeWhitespaceDelimiterSC(InputFile *file);
void addCharactersSC(const char *chars, size_t length, CHARLIST *list, char bitmap[BITMASK_SIZE]);
void checkOverlapSC(void);
void setPunctuationSC(void);
void initOptionsSC(void);
void postProcessOptionsSC(void);
bool getNextCharUTF8(Stream *file);
bool isWhitespaceUTF8(void);
bool isDelimiterUTF8(void);
void writeTokenCharUTF8(InputFile *file);
void writeWhitespaceCharUTF8(InputFile *file);
void writeWhitespaceDelimiterUTF8(InputFile *file);
void addCharactersUTF8(const char *chars, size_t length, CHARLIST *list, char bitmap[BITMASK_SIZE]);
void checkOverlapUTF8(void);
void setPunctuationUTF8(void);
void initOptionsUTF8(void);
void postProcessOptionsUTF8(void);
#endif
dwdiff-2.1.1/src/option_stripped.inc 0000664 0001750 0001750 00000004334 13074617326 020607 0 ustar gertjan gertjan 0000000 0000000 OPTIONS
OPTION('d', "delimiters", REQUIRED_ARG)
END_OPTION
OPTION('P', "punctuation", NO_ARG)
END_OPTION
OPTION('W', "white-space", REQUIRED_ARG)
END_OPTION
OPTION('h', "help", NO_ARG)
int i = 0;
printf(_("Usage: dwdiff [OPTIONS] \n"));
while (descriptions[i] != NULL)
fputs(_(descriptions[i++]), stdout);
exit(EXIT_SUCCESS);
END_OPTION
OPTION('v', "version", NO_ARG)
END_OPTION
OPTION('1', "no-deleted", NO_ARG)
END_OPTION
OPTION('2', "no-inserted", NO_ARG)
END_OPTION
OPTION('3', "no-common", NO_ARG)
END_OPTION
OPTION('i', "ignore-case", NO_ARG)
END_OPTION
#ifdef USE_UNICODE
OPTION('I', "ignore-formatting", NO_ARG)
#else
OPTION('I', "ignore-formatting", NO_ARG)
#endif
END_OPTION
OPTION('s', "statistics", NO_ARG)
END_OPTION
OPTION('a', "autopager", NO_ARG)
END_OPTION
OPTION('p', "printer", NO_ARG)
END_OPTION
OPTION('l', "less-mode", NO_ARG)
END_OPTION
LONG_OPTION("less", NO_ARG)
END_OPTION
OPTION('t', "terminal", NO_ARG)
END_OPTION
OPTION('w', "start-delete", REQUIRED_ARG)
END_OPTION
OPTION('x', "stop-delete", REQUIRED_ARG)
END_OPTION
OPTION('y', "start-insert", REQUIRED_ARG)
END_OPTION
OPTION('z', "stop-insert", REQUIRED_ARG)
END_OPTION
OPTION('n', "avoid-wraps", NO_ARG)
END_OPTION
SINGLE_DASH
END_OPTION
DOUBLE_DASH
NO_MORE_OPTIONS;
END_OPTION
OPTION('c', "color", OPTIONAL_ARG)
END_OPTION
OPTION('L', "line-numbers", OPTIONAL_ARG)
END_OPTION
OPTION('C', "context", REQUIRED_ARG)
END_OPTION
OPTION('m', "match-context", REQUIRED_ARG)
END_OPTION
BOOLEAN_LONG_OPTION("aggregate-changes", option.aggregateChanges)
OPTION('S', "paragraph-separator", OPTIONAL_ARG)
END_OPTION
BOOLEAN_LONG_OPTION("wdiff-output", option.wdiffOutput)
LONG_OPTION("dwfilter", REQUIRED_ARG)
option.dwfilterMode = true;
END_OPTION
OPTION('r', "reverse", NO_ARG)
END_OPTION
OPTION('R', "repeat-markers", NO_ARG)
END_OPTION
OPTION('u', "diff-input", NO_ARG)
END_OPTION
OPTION('A', "algorithm", REQUIRED_ARG)
END_OPTION
LONG_OPTION("profile", REQUIRED_ARG)
option.profile = optArg;
END_OPTION
LONG_OPTION("no-profile", NO_ARG)
option.profile = NULL;
END_OPTION
NO_OPTION
END_OPTIONS
dwdiff-2.1.1/src/doDiff.c 0000664 0001750 0001750 00000055416 13074617326 016240 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2006-2011 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#include
#include
#include
#include
#include "definitions.h"
#include "option.h"
#include "util.h"
#include "stream.h"
#include "buffer.h"
#include "unicode.h"
#include "diff/diff.h"
#include "hashtable.h"
static const char resetColor[] = "\033[0m";
static const char eraseLine[] = "\033[K";
static unsigned int oldLineNumber = 1, newLineNumber = 1;
static bool lastWasLinefeed = true, lastWasDelete = false, lastWasCarriageReturn = false;
/** Check whether the last-read character equals a certain value.
This only works correctly if @p c is a character which will always be put
into its own grapheme cluster, like control characters.
*/
static bool charDataEquals(int c) {
#ifdef USE_UNICODE
if (UTF8Mode)
return charData.UTF8Char.original.data[0] == c;
#endif
return charData.singleChar == c;
}
static bool readNextChar(Stream *stream) {
#ifdef USE_UNICODE
if (UTF8Mode)
return getBackspaceCluster(stream, &charData.UTF8Char.original);
#endif
charData.singleChar = stream->vtable->getChar(stream);
return charData.singleChar != EOF;
}
static void addCharData(bool common) {
#ifdef USE_UNICODE
if (UTF8Mode) {
size_t i;
char encoded[4];
int bytes, j;
UChar32 highSurrogate = 0;
for (i = 0; i < charData.UTF8Char.original.used; i++) {
bytes = filteredConvertToUTF8(charData.UTF8Char.original.data[i], encoded, &highSurrogate);
for (j = 0; j < bytes; j++)
addchar(encoded[j], common);
}
return;
}
#endif
addchar(charData.singleChar, common);
}
/* Note: ADD should be 0, OLD_COMMON should be DEL + COMMON. */
typedef enum {ADD, DEL, COMMON, OLD_COMMON} Mode;
/** If the last character printed was a newline, do some special handling.
@param mode What kind of output is generated next.
*/
static void doPostLinefeed(Mode mode) {
if (lastWasLinefeed) {
if (mode & COMMON)
mode = COMMON;
lastWasLinefeed = false;
if (option.lineNumbers) {
if (option.colorMode && mode != COMMON)
writeString(resetColor, sizeof(resetColor) - 1);
printLineNumbers(oldLineNumber, newLineNumber);
}
if (option.needStartStop && mode != COMMON) {
if (option.colorMode) {
if (mode == ADD)
writeString(option.addColor, option.addColorLen);
else
writeString(option.delColor, option.delColorLen);
}
if (option.repeatMarkers) {
if (mode == ADD)
writeString(option.addStart, option.addStartLen);
else
writeString(option.delStart, option.delStartLen);
}
}
}
}
/** Handle a single whitespace character.
@param print Skip or print.
@param mode What type of output to generate.
*/
static void handleWhitespaceChar(bool print, Mode mode) {
if (print) {
doPostLinefeed(mode);
/* Less mode also over-strikes whitespace */
if (option.less && !charDataEquals('\n') && !charDataEquals('\r') && mode == DEL) {
addchar('_', mode & COMMON);
addchar('\010', mode & COMMON);
} else if (option.needStartStop && (mode & COMMON) != COMMON && (charDataEquals('\r') || (!lastWasCarriageReturn && charDataEquals('\n')))) {
if (option.repeatMarkers) {
if (mode == ADD)
writeString(option.addStop, option.addStopLen);
else
writeString(option.delStop, option.delStopLen);
}
if (option.colorMode)
/* Erase rest of line so it will use the correct background color */
writeString(eraseLine, sizeof(eraseLine) - 1);
}
addCharData(mode & COMMON);
if (charDataEquals('\n'))
lastWasLinefeed = true;
lastWasCarriageReturn = charDataEquals('\r');
}
if (charDataEquals('\n')) {
switch (mode) {
case COMMON:
case ADD:
newLineNumber++;
break;
case OLD_COMMON:
case DEL:
oldLineNumber++;
break;
default:
PANIC();
}
}
}
/** Skip or print the next bit of whitespace from @a file.
@param file The file with whitespace.
@param print Skip or print.
@param mode What type of output to generate.
*/
static void handleNextWhitespace(InputFile *file, bool print, Mode mode) {
if (file->whitespaceBufferUsed) {
Stream *stream = newStringStream(file->whitespaceBuffer.data, file->whitespaceBuffer.used);
while (readNextChar(stream))
handleWhitespaceChar(print, mode);
free(stream);
file->whitespaceBuffer.used = 0;
file->whitespaceBufferUsed = false;
} else {
while (readNextChar(file->whitespace->stream)) {
if (charDataEquals(0))
return;
if (charDataEquals(1)) {
if (!readNextChar(file->whitespace->stream))
fatal(_("Error reading back input\n"));
}
handleWhitespaceChar(print, mode);
}
}
}
/** Skip or print the next bit of whitespace from the new or old file, keeping
the other file synchronized as far as line numbers are concerned.
@param printNew Use the new file for printing instead of the old file.
This is only for printing common text, and will skip over the old
whitespace.
*/
static void handleSynchronizedNextWhitespace(bool printNew) {
#ifdef USE_UNICODE
static_assert(CRLF_GRAPHEME_CLUSTER_BREAK == 0);
#endif
bool BValid = true;
unsigned int *lineNumberA, *lineNumberB;
Stream *whitespaceA, *whitespaceB;
Stream *oldFileWhitespaceStream = option.oldFile.whitespaceBufferUsed ?
newStringStream(option.oldFile.whitespaceBuffer.data, option.oldFile.whitespaceBuffer.used) :
option.oldFile.whitespace->stream;
Stream *newFileWhitespaceStream = option.newFile.whitespaceBufferUsed ?
newStringStream(option.newFile.whitespaceBuffer.data, option.newFile.whitespaceBuffer.used) :
option.newFile.whitespace->stream;
if (printNew) {
whitespaceA = newFileWhitespaceStream;
whitespaceB = oldFileWhitespaceStream;
lineNumberA = &newLineNumber;
lineNumberB = &oldLineNumber;
} else {
whitespaceA = oldFileWhitespaceStream;
whitespaceB = newFileWhitespaceStream;
lineNumberA = &oldLineNumber;
lineNumberB = &newLineNumber;
}
while (readNextChar(whitespaceA)) {
if (charDataEquals(0))
break;
if (charDataEquals(1)) {
if (!readNextChar(whitespaceA))
fatal(_("Error reading back input\n"));
}
if (option.printCommon) {
doPostLinefeed(COMMON);
/* Note that we don't have to check less mode here as we only print
common whitespace. */
addCharData(true);
if (charDataEquals('\n'))
lastWasLinefeed = true;
lastWasCarriageReturn = charDataEquals('\r');
}
/* If a newline was found, see if the B file also has a newline. */
if (charDataEquals('\n')) {
(*lineNumberA)++;
/* Only process the B file if it has not reached then end of the token yet. */
if (BValid) {
bool result;
while ((result = readNextChar(whitespaceB))) {
if (charDataEquals(0))
break;
if (charDataEquals(1)) {
if (!readNextChar(whitespaceB))
fatal(_("Error reading back input\n"));
}
if (charDataEquals('\n')) {
(*lineNumberB)++;
break;
}
}
if (charDataEquals(0) || !result)
BValid = false;
}
}
}
/* Process any remaining whitespace from the BS file. */
if (BValid) {
while (readNextChar(whitespaceB)) {
if (charDataEquals(0))
break;
if (charDataEquals(1)) {
if (!readNextChar(whitespaceB))
fatal(_("Error reading back input\n"));
}
if (charDataEquals('\n'))
(*lineNumberB)++;
}
}
if (option.oldFile.whitespaceBufferUsed)
free(oldFileWhitespaceStream);
if (option.newFile.whitespaceBufferUsed)
free(newFileWhitespaceStream);
}
/** Wrapper for addchar which takes printer and less mode into account
@param mode What type of output to generate.
*/
void addTokenChar(Mode mode) {
/* Printer mode and less mode do special stuff, per character. */
if ((option.printer || option.less) && !charDataEquals('\n') && !charDataEquals('\r')) {
if (mode == DEL) {
addchar('_', mode & COMMON);
addchar('\010', mode & COMMON);
} else if (mode == ADD) {
addCharData(mode & COMMON);
addchar('\010', mode & COMMON);
}
}
addCharData(mode & COMMON);
}
/** Skip or print the next token from @a file.
@param file The file with tokens.
@param print Skip or print.
@param mode What type of output to generate.
*/
static void handleNextToken(TempFile *file, bool print, Mode mode) {
bool empty = true;
while (readNextChar(file->stream)) {
if (charDataEquals(0)) {
/* Check for option.paraDelim _should_ be superfluous, unless there is a bug elsewhere. */
if (option.paraDelim && print && empty && mode != COMMON) {
Stream *stream = newStringStream(option.paraDelimMarker, option.paraDelimMarkerLength);
while (readNextChar(stream)) {
/* doPostLinefeed only does something if the last character was a line feed. However,
the paragraph delimiter may contain line feeds as well, so call doPostLinefeed
every time a character was printed. */
doPostLinefeed(mode);
addCharData(mode);
}
free(stream);
}
return;
}
empty = false;
/* Unescape the characters, if necessary. */
if (charDataEquals(1)) {
if (!readNextChar(file->stream))
fatal(_("Error reading back input\n"));
}
if (print) {
doPostLinefeed(mode);
if (option.needStartStop && (mode & COMMON) != COMMON && (charDataEquals('\r') || (!lastWasCarriageReturn && charDataEquals('\n')))) {
if (option.repeatMarkers) {
if (mode == ADD)
writeString(option.addStop, option.addStopLen);
else
writeString(option.delStop, option.delStopLen);
}
if (option.colorMode)
/* Erase rest of line so it will use the correct background color */
writeString(eraseLine, sizeof(eraseLine) - 1);
}
addTokenChar(mode);
if (charDataEquals('\n'))
lastWasLinefeed = true;
lastWasCarriageReturn = charDataEquals('\r');
}
if (charDataEquals('\n')) {
switch (mode) {
/* When the newline is a word character rather than a whitespace
character, we can safely count old and new lines together
for common words. This will keep the line numbers in synch
for these cases. Note that this also means that for
OLD_COMMON the line counter is not incremented. */
case COMMON:
oldLineNumber++;
case ADD:
newLineNumber++;
break;
case DEL:
oldLineNumber++;
break;
case OLD_COMMON:
break;
default:
PANIC();
}
}
}
}
/** Skip or print the next whitespace and tokens from @a file.
@param file The @a InputFile to use.
@param idx The last word to print or skip.
@param print Skip or print.
@param mode What type of output to generate.
*/
static void handleWord(InputFile *file, int idx, bool print, Mode mode) {
while (file->lastPrinted < idx) {
handleNextWhitespace(file, print, mode);
handleNextToken(file->tokens, print, mode);
file->lastPrinted++;
}
}
/** Print (or skip if the user doesn't want to see) the common words.
@param idx The last word to print (or skip).
*/
void printToCommonWord(int idx) {
while (option.newFile.lastPrinted < idx) {
handleSynchronizedNextWhitespace(!lastWasDelete);
lastWasDelete = false;
handleNextToken(option.newFile.tokens, option.printCommon, COMMON);
handleNextToken(option.oldFile.tokens, false, OLD_COMMON);
option.newFile.lastPrinted++;
option.oldFile.lastPrinted++;
}
}
/** Print (or skip if the user doesn't want to see) words from @a file.
@param range The range of words to print (or skip).
@param file The @a InputFile to print from.
@param mode Either ADD or DEL, used for printing of start/stop markers.
*/
static void printWords(lin start, lin count, InputFile *file, bool print, Mode mode) {
ASSERT(file->lastPrinted == start);
/* Print the first word. As we need to add the markers AFTER the first bit of
white space, we can't just use handleWord */
/* Print preceding whitespace. Should not be overstriken, so print as common */
handleNextWhitespace(file, print, mode + COMMON);
/* Ensure that the the line numbers etc. get printed before the markers */
if (print)
doPostLinefeed(COMMON);
/* Print start marker */
if (print && option.needStartStop) {
if (mode == ADD) {
if (option.colorMode)
writeString(option.addColor, option.addColorLen);
writeString(option.addStart, option.addStartLen);
} else {
if (option.colorMode)
writeString(option.delColor, option.delColorLen);
writeString(option.delStart, option.delStartLen);
}
}
/* Print first word */
handleNextToken(file->tokens, print, mode);
file->lastPrinted++;
/* Print following words */
handleWord(file, start + count, print, mode);
if (print)
doPostLinefeed(mode);
/* Print stop marker */
if (print && option.needStartStop) {
if (mode == ADD)
writeString(option.addStop, option.addStopLen);
else
writeString(option.delStop, option.delStopLen);
if (option.colorMode)
writeString(resetColor, sizeof(resetColor) - 1);
}
}
/** Print (or skip if the user doesn't want to see) deleted words.
@param range The range of words to print (or skip).
*/
void printDeletedWords(struct change *script) {
printWords(script->line0, script->deleted, &option.oldFile, option.printDeleted, DEL);
}
/** Print (or skip if the user doesn't want to see) inserted words.
@param range The range of words to print (or skip).
*/
void printAddedWords(struct change *script) {
printWords(script->line1, script->inserted, &option.newFile, option.printAdded, ADD);
}
/** Print (or skip if the user doesn't want to see) the last (common) words of both files. */
void printEnd(void) {
if (!option.printCommon)
return;
while(!sfeof(option.newFile.tokens->stream)) {
handleSynchronizedNextWhitespace(!lastWasDelete);
lastWasDelete = false;
handleNextToken(option.newFile.tokens, true, COMMON);
handleNextToken(option.oldFile.tokens, false, OLD_COMMON);
}
}
/** Load a piece of whitespace from file.
@param file The @a InputFile to read from and store the result.
@return a boolean indicating whether a newline was found within the whitespace.
*/
static bool loadNextWhitespace(InputFile *file) {
bool newlineFound = false;
file->whitespaceBufferUsed = true;
file->whitespaceBuffer.used = 0;
while (readNextChar(file->whitespace->stream)) {
if (charDataEquals(0))
return newlineFound;
if (charDataEquals(1)) {
if (!readNextChar(file->whitespace->stream))
fatal(_("Error reading back input\n"));
}
if (charDataEquals('\n'))
newlineFound = true;
#ifdef USE_UNICODE
if (UTF8Mode) {
size_t i;
char encoded[4];
int bytes, j;
UChar32 highSurrogate = 0;
for (i = 0; i < charData.UTF8Char.original.used; i++) {
bytes = filteredConvertToUTF8(charData.UTF8Char.original.data[i], encoded, &highSurrogate);
for (j = 0; j < bytes; j++)
VECTOR_APPEND(file->whitespaceBuffer, encoded[j]);
}
} else
#endif
{
VECTOR_APPEND(file->whitespaceBuffer, charData.singleChar);
}
}
return newlineFound;
}
/** Create an array of integers to represent the aggregation of several tokens to a token with context.
@param diffTokens The array of integers representing the words in the input.
@param range An array of two integers representing the start and length in
@p diffTokens (or @c NULL for complete array).
@param context The number of context tokens to use (half on either side).
@param file The ::file_data struct to fill.
*/
static ValueType *initializeContextDiffTokens(ValueTypeVector *diffTokens, lin *range, unsigned context, struct file_data *file) {
ValueType *contextDiffTokens, *dataBase;
size_t dataRange;
size_t i, idx = 0;
ValueType edgeArray[context + 1];
if (range == NULL)
dataRange = diffTokens->used;
else
dataRange = range[1];
contextDiffTokens = safe_malloc((dataRange + context) * sizeof(ValueType));
dataBase = diffTokens->data;
if (range != NULL)
dataBase += range[0];
memcpy(edgeArray + 1, dataBase, context * sizeof(ValueType));
edgeArray[0] = -1;
for (i = 1; i <= context; i++)
contextDiffTokens[idx++] = getValue(edgeArray, (i + 1) * sizeof(ValueType));
for (i = 0; i < dataRange - context; i++)
contextDiffTokens[idx++] = getValue(dataBase + i, (context + 1) * sizeof(ValueType));
memcpy(edgeArray, dataBase + dataRange - context, context * sizeof(ValueType));
edgeArray[context] = -1;
for (i = 0; i < context; i++)
contextDiffTokens[idx++] = getValue(edgeArray + i, (context - i + 1) * sizeof(ValueType));
ASSERT(idx == dataRange + context);
file->equivs = contextDiffTokens;
file->buffered_lines = dataRange + context;
return contextDiffTokens;
}
/** Read the output of the diff command, and call the appropriate print routines.
@param baseRange The range associated with the diff-token files, or NULL if the whole file.
@param context The size of the context used.
*/
static void doDiffInternal(lin *baseRange, unsigned context) {
enum { C_ADD, C_DEL, C_CHANGE } command;
bool reverseDeleteAdd = false;
struct change *script = NULL, *ptr;
struct comparison cmp;
if (context == 0) {
if (baseRange == NULL) {
cmp.file[0].equivs = option.oldFile.diffTokens.data;
cmp.file[0].buffered_lines = option.oldFile.diffTokens.used;
cmp.file[1].equivs = option.newFile.diffTokens.data;
cmp.file[1].buffered_lines = option.newFile.diffTokens.used;
} else {
cmp.file[0].equivs = option.oldFile.diffTokens.data + baseRange[0];
cmp.file[0].buffered_lines = baseRange[1];
cmp.file[1].equivs = option.newFile.diffTokens.data + baseRange[2];
cmp.file[1].buffered_lines = baseRange[3];
}
cmp.file[0].equiv_max = cmp.file[1].equiv_max = baseHashMax;
script = diff_2_files(&cmp);
} else {
ValueType *oldDiffTokens, *newDiffTokens;
/* Because we don't actually produce the empty tokens at the start and
end of the file or range, we can't produce a set of context tokens
when the context is larger than the range we try to cover. Therefore
we trim the context to the smallest range. This may move some
changes forward a little. */
if (baseRange != 0) {
if ((lin) context > baseRange[1])
context = baseRange[1];
if ((lin) context > baseRange[3])
context = baseRange[3];
} else {
if (context > option.oldFile.diffTokens.used)
context = option.oldFile.diffTokens.used;
if (context > option.newFile.diffTokens.used)
context = option.newFile.diffTokens.used;
}
/* Make sure the context is a multiple of 2. */
context &= ~1;
if (context == 0) {
doDiffInternal(baseRange, context);
return;
}
oldDiffTokens = initializeContextDiffTokens(&option.oldFile.diffTokens, baseRange, context, cmp.file);
newDiffTokens = initializeContextDiffTokens(&option.newFile.diffTokens,
baseRange == NULL ? NULL : baseRange + 2, context, cmp.file + 1);
cmp.file[0].equiv_max = getHashMax();
script = diff_2_files(&cmp);
free(oldDiffTokens);
free(newDiffTokens);
}
if (option.needMarkers && baseRange == NULL)
puts("======================================================================");
while (script != NULL) {
if (baseRange != NULL) {
script->line0 += baseRange[0];
script->line1 += baseRange[2];
}
command = script->inserted == 0 ? C_DEL : (script->deleted == 0 ? C_ADD : C_CHANGE);
differences = 1;
if (option.matchContext && context != 0) {
/* If the match-context option was specified, the diff output will
generally be too long. Here we trim the ranges to the minimum
range required to show the diff. However, the ranges are only
too long when the difference is a change, not when it is an add
or delete. */
if (command == C_CHANGE) {
if (script->deleted <= (lin) context || script->inserted <= (lin) context) {
ASSERT(script->deleted != script->inserted);
if (script->deleted < script->inserted) {
script->inserted -= script->deleted;
command = C_ADD;
} else if (script->inserted < script->deleted) {
script->deleted -= script->inserted;
command = C_DEL;
}
} else if (!option.aggregateChanges) {
/* Result must be multiple of two, so divide by 4 first, and
then multiply by 2. */
unsigned newContext = (context / 4) * 2;
lin range[4] = { script->line0, script->deleted - context, script->line1, script->inserted - context };
doDiffInternal(range, newContext);
goto nextDiff;
} else {
script->deleted -= context;
script->inserted -= context;
}
}
}
/* Print common words. */
printToCommonWord(script->line1);
/* Load whitespace for both files and analyse (same func). If old ws does not
contain a newline, don't bother with loading the new because we need
regular printing. Otherwise, determine if we need reverse printing or
need to change the new ws into a single space */
if (command == C_CHANGE && !option.wdiffOutput) {
if (loadNextWhitespace(&option.oldFile)) {
if (loadNextWhitespace(&option.newFile)) {
/* Because of the line feeds in both pieces of whitespace, we need to
print the pieces synchronized to keep the line counters correct. We
then empty the old whitespace buffer because that has already been
printed, and we set the new whitespace buffer to a space such that
the old and the new word are at least separated by a single space. */
handleSynchronizedNextWhitespace(!lastWasDelete);
option.newFile.whitespaceBuffer.data[0] = ' ';
option.newFile.whitespaceBuffer.used = 1;
option.oldFile.whitespaceBuffer.used = 0;
} else {
reverseDeleteAdd = true;
}
}
}
if (reverseDeleteAdd) {
/* This only happens for change commands, so we don't have to check
the command anymore. */
printAddedWords(script);
printDeletedWords(script);
reverseDeleteAdd = false;
} else {
if (command != C_ADD)
printDeletedWords(script);
if (command != C_DEL)
printAddedWords(script);
}
if (option.needMarkers) {
puts("\n======================================================================");
lastWasLinefeed = true;
}
if (command == C_DEL && option.printDeleted && !option.wdiffOutput)
lastWasDelete = true;
/* Update statistics */
switch (command) {
case C_ADD:
statistics.added += script->inserted;
break;
case C_DEL:
statistics.deleted += script->deleted;
break;
case C_CHANGE:
statistics.newChanged += script->inserted;
statistics.oldChanged += script->deleted;
break;
default:
PANIC();
}
nextDiff:
ptr = script;
script = script->link;
free(ptr);
}
}
/** Do the difference action. */
void doDiff(void) {
VECTOR_INIT_ALLOCATED(option.oldFile.whitespaceBuffer);
VECTOR_INIT_ALLOCATED(option.newFile.whitespaceBuffer);
option.oldFile.lastPrinted = 0;
option.newFile.lastPrinted = 0;
doDiffInternal(NULL, option.matchContext);
printEnd();
}
dwdiff-2.1.1/src/unicode.h 0000664 0001750 0001750 00000004530 13074617326 016467 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2008-2011 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#ifndef UNICODE_H
#define UNICODE_H
#ifdef USE_UNICODE
#include "definitions.h"
#define UEOF -1
#define UEOVERLONG -2
#define UEINVALID -3
#define UETOOLARGE -4
/* Officially there should be no break between CR and LF. However, on the
command line one would commonly write \n to match a line end. On Unix systems
it is customary to simply use LF as the line break, so not allowing a break
between CR and LF would break expected behaviour if the user were to write
\r\n on the command line. Conversely, if the user writes \n on the command
line, and the text contains CRLF, the user would expect the \n to match the
line feed.
The problem here is that there is hardly a good solution, that will satisfy
all cases in an intuitive manner. Therefore, I'm going to stick with the
Unix EOL convention instead of using the official Unicode way. Users can run
dos2unix to convert their documents if need be. */
/* WARNING: changing this to 1 will break the code. DO NOT change!. Places
which will definately break if this is changed to 1 will have a static
assert on this value being 0. */
#define CRLF_GRAPHEME_CLUSTER_BREAK 0
#include
#include
typedef VECTOR(UChar, UTF16Buffer);
typedef VECTOR(UTF16Buffer, CharList);
bool getCluster(Stream *stream, UTF16Buffer *buffer);
bool getBackspaceCluster(Stream *stream, UTF16Buffer *buffer);
int convertToUTF8(UChar32 c, char *buffer);
int filteredConvertToUTF8(UChar32 c, char *buffer, UChar32 *highSurrogate);
int putuc(Stream *stream, UChar32 c);
void decomposeChar(CharData *c);
void casefoldChar(CharData *c);
int compareUTF16Buffer(const UTF16Buffer *a, const UTF16Buffer *b);
bool isUTF16Punct(UTF16Buffer *buffer);
bool isUTF16Whitespace(UTF16Buffer *buffer);
#endif
#endif
dwdiff-2.1.1/src/option.h 0000664 0001750 0001750 00000003315 13074617326 016351 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2008-2011 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#ifndef OPTION_H
#define OPTION_H
#define DEFAULT_LINENUMBER_WIDTH 4
#define BITMASK_SIZE (UCHAR_MAX+7)/8
struct {
InputFile oldFile,
newFile;
const char *delStart,
*delStop,
*addStart,
*addStop;
char *delColor,
*addColor;
size_t delStartLen,
delStopLen,
addStartLen,
addStopLen,
delColorLen,
addColorLen;
/* Bitmaps for single byte checking. */
char delimiters[BITMASK_SIZE],
whitespace[BITMASK_SIZE];
/* Lists for UTF8 mode. */
#ifdef USE_UNICODE
CharList delimiterList;
CharList whitespaceList;
uint32_t punctuationMask;
UNormalizationMode decomposition;
#endif
bool whitespaceSet;
bool printDeleted,
printAdded,
printCommon,
needMarkers,
needStartStop;
bool printer,
less,
statistics,
ignoreCase,
colorMode;
int lineNumbers;
bool context;
int contextLines;
unsigned matchContext;
bool aggregateChanges;
bool paraDelim;
const char *paraDelimMarker;
size_t paraDelimMarkerLength;
bool wdiffOutput;
const char *profile;
FILE *output;
bool dwfilterMode;
bool repeatMarkers;
bool diffInput;
} option;
void parseCmdLine(int argc, char *argv[]);
#endif
dwdiff-2.1.1/src/buffer.c 0000664 0001750 0001750 00000016316 13074617326 016312 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2007-2011 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#include
#include
#include
#include
#include "definitions.h"
#include "util.h"
#include "option.h"
#include "buffer.h"
typedef enum {
PRINTING_CHANGED,
PRINTING_AFTER_CONTEXT,
PRE_BUFFERING,
BUFFERING,
/* The initial states are only used at the start of printing, to prevent
printing of the separator string (--) before the first block of output. */
PRE_BUFFERING_INITIAL,
BUFFERING_INITIAL
} PrintState;
static CharBuffer *contextBuffers;
static PrintState state = PRE_BUFFERING_INITIAL;
static int bufferIndex;
static int afterContextLines;
/** Initialize the context buffers. */
void initContextBuffers(void) {
int i;
errno = 0;
contextBuffers = (CharBuffer *) safe_malloc((option.contextLines + 1) * sizeof(CharBuffer));
for (i = 0; i <= option.contextLines; i++)
VECTOR_INIT(contextBuffers[i]);
}
/** Write a character, buffering if necessary.
@param c The character to write.
@param common Boolean to indicate whether the character belongs to both
the old and the new file.
*/
void addchar(char c, bool common) {
/* Using ignore variable to shut up gcc about the warn_unused_result attribute set on fwrite. */
int i, ignore;
if (!option.context) {
putc(c, option.output);
return;
}
/* Check whether we have buffered output that we need to print because of
encountering a non-common character. */
if (!common) {
switch (state) {
case PRINTING_CHANGED:
case PRINTING_AFTER_CONTEXT:
break;
/* If we have entered a regular buffering state, all buffers are in use.
However, the set of buffers is used as a ring buffer so first print all
buffers after the current one, then print all buffers before the current. */
case BUFFERING:
ignore = fwrite("--\n", 1, 3, option.output);
case BUFFERING_INITIAL:
for (i = bufferIndex + 1; i <= option.contextLines; i++)
ignore = fwrite(contextBuffers[i].data, 1, contextBuffers[i].used, option.output);
/* FALLTHROUGH */
/* In pre-buffering state, only the first buffers are used. Once we start
wrapping around, we change state to a regular buffering state. */
case PRE_BUFFERING:
case PRE_BUFFERING_INITIAL:
for (i = 0; i <= bufferIndex; i++)
ignore = fwrite(contextBuffers[i].data, 1, contextBuffers[i].used, option.output);
break;
default:
PANIC();
}
state = PRINTING_CHANGED;
}
/* Decided whether to buffer or print the character. */
switch (state) {
case BUFFERING:
case PRE_BUFFERING:
case BUFFERING_INITIAL:
case PRE_BUFFERING_INITIAL:
VECTOR_APPEND(contextBuffers[bufferIndex], c);
break;
case PRINTING_AFTER_CONTEXT:
case PRINTING_CHANGED:
putc(c, option.output);
break;
default:
PANIC();
}
if (c == '\n') {
switch (state) {
case PRINTING_CHANGED:
if (option.contextLines == 0) {
state = PRE_BUFFERING;
break;
}
state = PRINTING_AFTER_CONTEXT;
afterContextLines = 0;
break;
case PRINTING_AFTER_CONTEXT:
afterContextLines++;
if (afterContextLines == option.contextLines) {
state = PRE_BUFFERING;
bufferIndex = 0;
}
break;
case PRE_BUFFERING_INITIAL:
case PRE_BUFFERING:
bufferIndex++;
/* Once we have filled all buffers, we change to a regular buffering state. */
if (bufferIndex > option.contextLines) {
state = state == PRE_BUFFERING ? BUFFERING : BUFFERING_INITIAL;
bufferIndex = 0;
}
break;
case BUFFERING_INITIAL:
case BUFFERING:
bufferIndex++;
if (bufferIndex > option.contextLines)
bufferIndex = 0;
break;
default:
PANIC();
}
/* We just saw a newline, so we have either entered a buffering state,
or already were in one, and finished filling the old buffer. The new
buffer must be filled from the start. */
contextBuffers[bufferIndex].used = 0;
}
}
/* Macro to ensure we use the same format and arguments for printing the line
number information in all print statements. */
#define LINENUMBERS_FMT_ARGS "%*d:%-*d ", option.lineNumbers, oldLineNumber, option.lineNumbers, newLineNumber
/** Print line number information, buffering if necessary.
@param oldLineNumber The line number in the old file.
@param newLineNumber The line number in the new file.
*/
void printLineNumbers(int oldLineNumber, int newLineNumber) {
int printed, allowed;
if (!option.context) {
printf(LINENUMBERS_FMT_ARGS);
return;
}
switch (state) {
case BUFFERING:
case PRE_BUFFERING:
case BUFFERING_INITIAL:
case PRE_BUFFERING_INITIAL:
allowed = contextBuffers[bufferIndex].allocated - contextBuffers[bufferIndex].used;
/* SUSv2 specification of snprintf does not handle zero sized buffers nicely :-(
Therefore, we have to ensure that there is at least room for one byte, but as
we will be printing 2 numbers and a colon and a space, we might as well ask for
4 bytes. */
VECTOR_ALLOCATE(contextBuffers[bufferIndex], 4);
printed = snprintf(contextBuffers[bufferIndex].data + contextBuffers[bufferIndex].used, allowed, LINENUMBERS_FMT_ARGS);
/* If there was not enough room to hold all the bytes for the line numbers,
resize the buffer and try again. */
if (printed > allowed) {
VECTOR_ALLOCATE(contextBuffers[bufferIndex], printed);
allowed = contextBuffers[bufferIndex].allocated - contextBuffers[bufferIndex].used;
printed = snprintf(contextBuffers[bufferIndex].data + contextBuffers[bufferIndex].used, allowed, LINENUMBERS_FMT_ARGS);
}
/* Sanity check: the number of characters printed should be at least 2
[probably 4, but I don't know all possible number systems]. */
ASSERT(printed >= 2);
contextBuffers[bufferIndex].used += printed;
break;
case PRINTING_AFTER_CONTEXT:
case PRINTING_CHANGED:
printf(LINENUMBERS_FMT_ARGS);
break;
default:
PANIC();
}
}
/** Print a string, buffering if necessary.
@param string The string to print.
@param bytes The length of the string.
The string should not contain newline characters.
*/
void writeString(const char *string, size_t bytes) {
/* Using ignore variable to shut up gcc about the warn_unused_result attribute set on fwrite. */
int ignore;
if (!option.context) {
ignore = fwrite(string, 1, bytes, option.output);
return;
}
switch (state) {
case BUFFERING:
case PRE_BUFFERING:
case BUFFERING_INITIAL:
case PRE_BUFFERING_INITIAL:
VECTOR_ALLOCATE(contextBuffers[bufferIndex], bytes);
memcpy(contextBuffers[bufferIndex].data + contextBuffers[bufferIndex].used, string, bytes);
contextBuffers[bufferIndex].used += bytes;
break;
case PRINTING_AFTER_CONTEXT:
case PRINTING_CHANGED:
ignore = fwrite(string, 1, bytes, option.output);
break;
default:
PANIC();
}
}
dwdiff-2.1.1/src/dispatch.h 0000664 0001750 0001750 00000004462 13074617326 016644 0 ustar gertjan gertjan 0000000 0000000 /* Copyright (C) 2008 G.P. Halkes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
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, see .
*/
#ifndef DISPATH_H
#define DISPATH_H
#include "definitions.h"
#define CHARLIST SWITCH_UNICODE(CharList, void)
/* The FPTR macro serves only as a placeholder to allow easy generation of the
code in dispatch_autogen.h. */
#define FPTR
typedef struct {
/** Get the next character from the input.
@param file The file to read.
@return True if the end of the file has not been reached.
*/
FPTR bool (*getNextCharDT)(Stream *file);
/** Test if a character is a whitespace character. */
FPTR bool (*isWhitespaceDT)(void);
/** Test if a character is a delimiter character. */
FPTR bool (*isDelimiterDT)(void);
/** Write a character to a token file.
@param file The file to write to.
*/
FPTR void (*writeTokenCharDT)(InputFile *file);
/** Write a character to a token file.
@param file The file to write to.
*/
FPTR void (*writeWhitespaceCharDT)(InputFile *file);
/** Write a delimiter to the whitespace file.
@param file The file to write to.
*/
FPTR void (*writeWhitespaceDelimiterDT)(InputFile *file);
/** Add all characters to the specified list or bitmap.
@param chars The string with characters to add to the list or bitmap.
@param list The list to add to.
@param bitmap. The bitmap to add to.
If in UTF-8 mode, the characters will be added to the list. Otherwise to
the bitmap.
*/
FPTR void (*addCharactersDT)(const char *chars, size_t length, CHARLIST *list, char bitmap[BITMASK_SIZE]);
/** Check for overlap in whitespace and delimiter sets. */
FPTR void (*checkOverlapDT)(void);
FPTR void (*setPunctuationDT)(void);
FPTR void (*initOptionsDT)(void);
FPTR void (*postProcessOptionsDT)(void);
} DispatchTable;
extern DispatchTable *dispatch;
#include "dispatch_autogen.h"
#endif
dwdiff-2.1.1/src/grab_options.py 0000775 0001750 0001750 00000001003 13074617326 017723 0 ustar gertjan gertjan 0000000 0000000 #!/usr/bin/python
import sys
in_parse = False
keeping = False
for line in sys.stdin.readlines():
if not in_parse:
if "PARSE_FUNCTION" in line:
in_parse = True
continue
if not keeping:
if "START_KEEP" in line:
keeping = True
if "OPTION" in line or "DOUBLE_DASH" in line or "SINGLE_DASH" in line:
sys.stdout.write(line)
elif "#ifdef" in line or "#else" in line or "#endif" in line:
sys.stdout.write(line)
else:
if "STOP_KEEP" in line:
keeping = False
else:
sys.stdout.write(line)
dwdiff-2.1.1/README 0000664 0001750 0001750 00000005454 13074617326 014767 0 ustar gertjan gertjan 0000000 0000000 Introduction
============
dwdiff is a diff program that operates at the word level instead of the line
level. It is different from wdiff in that it allows the user to specify what
should be considered whitespace, and in that it takes an optional list of
characters that should be considered delimiters. Delimiters are single
characters that are treated as if they are words, even when there is no
whitespace separating them from preceding words or delimiters. dwdiff is mostly
commandline compatible with wdiff. Only the --autopager, --terminal and
--avoid-wraps options are not supported.
The default output from dwdiff is the new text, with the deleted and inserted
parts annotated with markers. Command line options are available to change
both what is printed, and the markers.
dwdiff is licensed under the GNU General Public License version 3. See the file
COPYING for details.
Motivation
==========
I wrote dwdiff because when diff'ing C code with wdiff, wdiff would often find
differences that were larger than I wanted. For example, when one modifies the
function header of a program:
void someFunction(SomeType var)
into
void someFunction(SomeOtherType var)
wdiff would say that "someFunction(SomeType" was replaced by
"someFunction(SomeOtherType", while what I wanted it to say was that "SomeType"
was replaced by "SomeOtherType".
Prerequisites and installation
==============================
dwdiff relies on the GNU gettext library for providing localised messages, and
on libicu (http://icu-project.org) for Unicode support. dwdiff can be compiled
without support for either of these libraries, which means all messages will be
in English and Unicode will not be supported.
There are two ways in which to compile dwdiff:
Using the configure script:
---
$ ./configure
or
$ ./configure --prefix=/usr
(see ./configure --help for more tuning options)
$ make all
$ make install
(assumes working install program)
Manually editing the Makefile to suit your computer:
---
$ cp Makefile.in Makefile
Edit the values for GETTEXTFLAGS, GETTEXTLIBS, LOCALEDIR, LINGUAS, ICUFLAGS,
ICULIBS and prefix in Makefile
$ make all
$ make install
(assumes working install program)
The Makefile.in in the distribution should work on all POSIX compatible make's.
I have tested it with both GNU make and BSD make.
dwdiff uses several POSIX functions, namely: mkstemp, open, read, lseek, write,
close, umask, snprintf. dwdiff should compile on any Un*x system that provides
these functions.
Reporting bugs
==============
If you think you have found a bug, please check that you are using the latest
version of dwdiff [http://os.ghalkes.nl/dwdiff.html]. When reporting bugs,
please include a minimal example that demonstrates the problem.
Author
======
Gertjan Halkes
Language files were contributed by the authors named in them.
dwdiff-2.1.1/configure 0000775 0001750 0001750 00000031213 13074617326 016006 0 ustar gertjan gertjan 0000000 0000000 #!/bin/sh
# Copyright (C) 2006-2007,2009,2011 G.P. Halkes
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3, as
# published by the Free Software Foundation.
#
# 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, see .
# Set zsh to emulate sh (otherwise all kinds of eval's wont work)
[ -n "${ZSH_VERSION}" ] && emulate sh
#==============================
# Functions
#==============================
error() {
echo "$*"
[ -n "$DUMP_LOG" ] && cat config.log
exit 1
}
not() {
if "$@" ; then
false
else
true
fi
}
check() {
if not "$@" ; then
error "Error executing $*. Aborting."
fi
}
check_message() {
printf "$*"
echo "----------------------------------" >> config.log
echo "$*" >> config.log
}
check_message_result() {
echo "$*"
echo "$*" >> config.log
}
clean() {
rm -rf "$@" >/dev/null 2>&1
}
test_make() {
printf "%s" "Running: ${MAKE} -f .Makefile" >> config.log
for ARG in "$@"
do
printf " '%s'" "${ARG}" >> config.log
done
echo >> config.log
"${MAKE}" -f .Makefile "$@"
}
test_install() {
clean .foo
check_message "Checking for working install ($*)... "
if test_make "INSTALL=$*" "install" >> config.log 2>&1 ; then
check_message_result "yes"
true
else
check_message_result "no"
false
fi
}
test_and_set() {
SETTING="$4${5+ }$5"
if $1 "$2${SETTING+ in }${SETTING}" "TESTFLAGS=$4" "TESTLIBS=$5" ; then
eval "${3}_FLAGS=\"$4\""
eval "${3}_LIBS=\"$5\""
true
else
false
fi
}
add_replace_settings() {
for SETTING
do
echo "/^${SETTING%%=*}/s/^/# Disabled by configure: /"
echo "/\.POSIX/a\\
${SETTING}"
done
}
replace_default() {
if [ -n "$2" ] ; then
echo "/^$1=/c\\
$1=$2"
fi
}
insert() {
[ -n "$2" ] && echo "/^\\.POSIX:/a\\
$1=$2"
}
create_makefile() {
for _INSTALL in ${TEST_INSTALL}
do
# Don't test the sun install program, because it is dangerous.
if [ "`uname`" = "SunOS" ] && [ "$_INSTALL" = "install" ] ; then
continue
fi
test_install "${_INSTALL}" && INSTALL="${_INSTALL}" && break
done
if [ -z "${INSTALL}" ] ; then
check_message_result "!! No working install program found. The install target will not work."
fi
{
echo '/\.POSIX/a\
# Settings from configure script'
replace_default prefix "${PREFIX}"
for INSTALLDIR in ${INSTALLDIRS}
do
[ -n "`eval echo \\${option_${INSTALLDIR}}`" ] && replace_default "${INSTALLDIR}" "`eval echo \\${option_${INSTALLDIR}}`"
done
replace_default INSTALL "${INSTALL}"
insert LDFLAGS "${LDFLAGS}"
insert LDLIBS "${LDLIBS}"
for EXT in ${EXTENSIONS}
do
sed_rules_${EXT}
done
add_replace_settings "$@"
[ -n "${SEDEXTRA}" ] && ${SEDEXTRA}
} > .sedscript
for MAKEFILE in ${MAKEFILES:-Makefile}
do
echo "----------------------------------">> config.log
check_message_result "Creating ${MAKEFILE}"
cat ${MAKEFILE}.in | sed -f .sedscript > ${MAKEFILE}
done
clean .sedscript .Makefile .foo .config_base
for EXT in ${EXTENSIONS}
do
clean_${EXT}
done
exit 0
}
#==============================
# Setup
#==============================
unset LINKRULE COMPILERULE USERRULES USERHELP EXTENSIONS SWITCHES PRECHECKFUNC INSTALLDIRS DUMP_LOG
. ./config.pkg
for MAKEFILE in ${MAKEFILES:-Makefile}
do
[ -f ${MAKEFILE}.in ] || error "${MAKEFILE}.in does not exist"
grep "^\\.POSIX:" ${MAKEFILE}.in > /dev/null || error "${MAKEFILE}.in does not contain .POSIX target"
done
#@INCLUDE_START
[ "${EXTENSIONS}" = "c verbose_compile gettext" ] || error "EXTENSIONS changed after running merge_config. Run merge_config again."
SUFFIXES="${SUFFIXES} .c .o"
[ -z "${LINKRULE}" ] && LINKRULE='$(CC) $(CFLAGS) $(LDFLAGS) -o $@ .config.o $(LDLIBS) $(TESTLIBS)'
[ -z "${COMPILERULE}" ] && COMPILERULE='$(CC) $(CFLAGS) $(TESTFLAGS) -c -o $@ $<'
clean_c() {
clean .config .config.o .config.c
}
show_help_c() {
if [ "$1" = VARIABLES ] ; then
cat < .config.c <> config.log 2>&1 ; then
check_message_result "yes"
true
else
check_message_result "no"
echo "Source of the failed compile:" >> config.log
nl -ba .config.c >> config.log
false
fi
}
test_link() {
clean .config
check_message "Checking for $1... "
shift
if test_make "$@" .config >> config.log 2>&1 ; then
check_message_result "yes"
true
else
check_message_result "no"
echo "Source of the failed compile:" >> config.log
nl -ba .config.c >> config.log
false
fi
}
touch_c() {
[ -f .config.c ] && touch .config.c
}
SWITCHES="${SWITCHES} -verbose-compile"
clean_verbose_compile() {
:
}
show_help_verbose_compile() {
if [ "$1" = OPTIONS ] ; then
cat < Install directory for locales [prefix/share/locale]"
fi
}
print_rules_gettext() {
cat < .config.c <
#include
int main(int argc, char *argv[]) {
setlocale(LC_ALL, "");
bindtextdomain("package", "/usr/share/locale");
dgettext("package", "string");
textdomain("package");
gettext("package");
return 0;
}
EOF
{
test_compile "gettext and related functions" && {
test_link "gettext in standard library" ||
{ test_link "gettext in -lintl" TESTLIBS=-lintl && GETTEXTLIBS=-lintl ; } ||
{ test_link "gettext in -lintl -liconv" "TESTLIBS=-lintl -liconv" && GETTEXTLIBS="-lintl -liconv" ; }
} && {
GETTEXTFLAGS="-DUSE_GETTEXT"
clean .config.po .config.mo
cat > .config.po <> config.log 2>&1 ; then
check_message_result "yes"
true
else
check_message_result "no"
false
fi
}
} || {
error "!! Could not compile with gettext. Try configuring with --without-gettext."
}
GETTEXTFLAGS="-DUSE_GETTEXT"
if [ -n "${LINGUAS+set}" ] ; then
check_message "Checking for available selected translations... "
for lingua in ${LINGUAS}
do
found=0
for test_lingua in ${DEFAULT_LINGUAS}
do
if [ "${test_lingua}" = "${lingua}" ] ; then
found=1
break
fi
done
[ "${found}" = 1 ] && linguas="${linguas}${linguas+ }${lingua}"
done
check_message_result "done [${linguas}]"
else
linguas="${DEFAULT_LINGUAS}"
fi
fi
}
#@INCLUDE_END
for _switch in ${SWITCHES}
do
_name=`echo "${_switch}" | sed -e 's/^[+-]//' -e 's/-/_/g'`
_pm="${_switch%%[!-+]*}"
if [ "${_pm}" = "+" ] ; then
eval with_${_name}="yes"
else
eval with_${_name}="no"
fi
done
_SWITCHES=`echo " ${SWITCHES}" | sed 's/ [+-]/ /g'`
for PARAM
do
case "${PARAM}" in
-h|--help)
cat <] [=]
--dump-log Dump config.log to stdout on error
--prefix= Prefix for installation [/usr/local]
EOF
for INSTALLDIR in ${INSTALLDIRS}
do
case "${INSTALLDIR}" in
bindir) echo ' --bindir= Binaries directory [/bin]' ;;
sbindir) echo ' --sbindir= System binaries directory [/sbin]' ;;
libdir) echo ' --libdir= Library directory [/lib]' ;;
includedir) echo ' --includedir= Include file directory [/include]' ;;
datadir) echo ' --datadir= Directory for data [/share]' ;;
mandir) echo ' --mandir= Manual page directory [/share/man]' ;;
infodir) echo ' --infodir= Info page directory [/share/man]' ;;
docdir) echo ' --docdir= Document dir [/share/doc/-]' ;;
esac
done
[ -n "${USERINSTALLDIRSHELP}" ] && echo "${USERINSTALLDIRSHELP}"
for EXT in ${EXTENSIONS}
do
show_help_$EXT OPTIONS
done
cat <
INSTALL The install program to use
LDFLAGS Linker flags to use (default determined by make)
LDLIBS Extra libraries to link
EOF
for EXT in ${EXTENSIONS}
do
show_help_$EXT VARIABLES
done
[ -n "${USERHELP}" ] && { echo ; echo "Package specific settings" ; ${USERHELP} ; }
cat <&2
;;
--*)
unset _match
for _switch in ${_SWITCHES}
do
case "${PARAM}" in
--with-${_switch})
_name=`echo "${_switch}" | sed 's/-/_/g'`
eval with_${_name}="yes"
_match="1"
break
;;
--without-${_switch})
_name=`echo "${_switch}" | sed 's/-/_/g'`
eval with_${_name}="no"
_match="1"
break
;;
esac
done
[ -z "${_match}" ] && echo "WARNING: ignoring unknown parameter: ${PARAM}" >&2
;;
*=*)
name="${PARAM%%=*}"
value="${PARAM#*=}"
eval "${name}"="\"${value}\""
;;
*)
error "Error on commandline: ${PARAM}"
;;
esac
done
if [ -n "${INSTALL}" ] ; then
TEST_INSTALL="${INSTALL}"
unset INSTALL
else
TEST_INSTALL="install ./install.sh"
fi
echo "Configuration test log created at `date`" > config.log
echo "-- configure called with $0 $@" >> config.log
clean config.log .Makefile .sedscript .config_base
for EXT in ${EXTENSIONS}
do
clean_${EXT}
done
{
echo ".POSIX:"
[ -z "${LDFLAGS}" ] || echo "LDFLAGS=${LDFLAGS}"
[ -z "${LDLIBS}" ] || echo "LDLIBS=${LDLIBS}"
[ -z "${SUFFIXES}" ] || echo ".SUFFIXES: ${SUFFIXES}"
[ -z "${USERRULES}" ] || ${USERRULES}
cat < .Makefile.in
cp .Makefile.in .Makefile
echo "Using settings:" >> config.log
grep '^[[:upper:]_]\+=' .Makefile >> config.log
[ -n "${MAKE}" ] || MAKE=make
[ -z "${PRECHECKFUNC}" ] || ${PRECHECKFUNC}
check_message "Checking for working make (${MAKE})... "
if test_make .config_base >> config.log 2>&1 ; then
check_message_result "yes"
else
check_message_result "no"
error "${MAKE} failed. See config.log for errors."
fi
for EXT in ${EXTENSIONS}
do
basic_test_${EXT}
sed_rules_${EXT} >> .sedscript
done
sed -f .sedscript .Makefile.in > .Makefile
config
error "Error in config.pkg. Cannot continue."
dwdiff-2.1.1/po/ 0000775 0001750 0001750 00000000000 13074617326 014515 5 ustar gertjan gertjan 0000000 0000000 dwdiff-2.1.1/po/de.po 0000664 0001750 0001750 00000033560 13074617326 015454 0 ustar gertjan gertjan 0000000 0000000 # German translations for dwdiff package.
# Copyright (C) 2007-2010 G.P. Halkes
# This file is distributed under the same license as the dwdiff package.
# G.P. Halkes , 2007.
#
msgid ""
msgstr ""
"Project-Id-Version: dwdiff 1.2\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-02-12 17:23+0100\n"
"PO-Revision-Date: 2006-09-27 11:53+0200\n"
"Last-Translator: Dr. D. Steuer \n"
"Language-Team: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: util.h:28 util.h:30
#, c-format
msgid "Program-logic error at %s:%d\n"
msgstr "Programmlogikfehler bei %s:%d\n"
#: doDiff.c:153 doDiff.c:192 doDiff.c:224 doDiff.c:247 doDiff.c:307
#: doDiff.c:471
msgid "Error reading back input\n"
msgstr "Fehler beim zurücklesen der Eingabe\n"
#: doDiff.c:495 doDiff.c:497 dwdiff.c:278 dwdiff.c:281 dwdiff.c:290
#: dwfilter.c:133
#, c-format
msgid "Could not create temporary file: %s\n"
msgstr "Konnte temporäre Datei nicht anlegen: %s\n"
#: dwdiff.c:275
#, c-format
msgid "Can't open file %s: %s\n"
msgstr "Kann Datei %s nicht öffnen: %s\n"
#: dwdiff.c:360
#, c-format
msgid "Error reading file %s: %s\n"
msgstr "Fehler bei Lesen der Datei %s: %s\n"
#: dwdiff.c:379 dwdiff.c:384 dwdiff.c:389
#, c-format
msgid "Error writing to temporary file %s: %s\n"
msgstr "Fehler beim Schreiben in temporäre Datei %s: %s\n"
#: dwdiff.c:466
#, c-format
msgid "old: 0 words\n"
msgstr "alt: 0 Wörter\n"
#: dwdiff.c:468
#, c-format
msgid "old: %d words %d %d%% common %d %d%% deleted %d %d%% changed\n"
msgstr "alt: %d Wörter %d %d%% gemeinsam %d %d%% gelöscht %d %d%% geändert\n"
#: dwdiff.c:475
#, c-format
msgid "new: 0 words\n"
msgstr "neu: 0 Wörter\n"
#: dwdiff.c:477
#, c-format
msgid "new: %d words %d %d%% common %d %d%% inserted %d %d%% changed\n"
msgstr "neu: %d Wörter %d %d%% gleich %d %d%% eingefügt %d %d%% geändert\n"
#: dwfilter.c:44
#, c-format
msgid "Usage: dwfilter [OPTIONS] [POST PROCESSOR OPTIONS]\n"
msgstr ""
#. TRANSLATORS:
#. - If the (C) symbol (that is the c in a circle) is not available,
#. leave as it as is. (Unicode code point 0x00A9)
#. - G.P. Halkes is name and should be left as is.
#: dwfilter.c:56 option.c:524
msgid ""
"Copyright (C) 2006-2011 G.P. Halkes\n"
"Licensed under the GNU General Public License version 3\n"
msgstr ""
"Copyright © 2006-2011 G.P. Halkes\n"
"Lizensiert unter der GNU General Public License version 3\n"
#: dwfilter.c:94 option.c:683 option.c:686
#, c-format
msgid "Option %.*s does not exist\n"
msgstr "Option %.*s gibt es nicht\n"
#: dwfilter.c:111 option.c:701
msgid "Need two files to compare\n"
msgstr "Benötige zwei Dateien, um zu vergleichen\n"
#: dwfilter.c:113
msgid "No post processor specified\n"
msgstr ""
#: dwfilter.c:149
#, c-format
msgid "Error waiting for child process to terminate: %s\n"
msgstr ""
#: dwfilter.c:196
msgid "dwdiff returned an error\n"
msgstr ""
#: optionMacros.h:168
#, c-format
msgid "Option %.*s does not take an argument\n"
msgstr "Option %.*s hat kein Argument\n"
#: optionMacros.h:174
#, c-format
msgid "Option %.*s requires an argument\n"
msgstr "Option %.*s benötigt ein Argument\n"
#: optionMacros.h:258
#, c-format
msgid "Garbage after value for %.*s option\n"
msgstr "Müll nach dem Wert für die %.*s Option\n"
#: optionMacros.h:261
#, c-format
msgid "Value for %.*s option (%ld) is out of range\n"
msgstr "Wert für die %*s Option (%ld) ist außerhalb des zulässigen Bereichs\n"
#: optionMacros.h:295
#, c-format
msgid "Value for %.*s option (%s) is not a valid boolean value\n"
msgstr ""
#: optionDescriptions.h:6
msgid "-h, --help Print this help message\n"
msgstr "-h, --help Diese Hilfeseite ausgeben\n"
#: optionDescriptions.h:7
msgid "-v, --version Print version and copyright information\n"
msgstr ""
"-v, --version Versions- und Copyrightinformationen\n"
" ausgeben\n"
#: optionDescriptions.h:10
msgid "-d , --delimiters= Specifiy delimiters\n"
msgstr "-d , --delimiters= Begrenzer spezifizieren\n"
#: optionDescriptions.h:11
msgid "-P, --punctuation Use punctuation characters as delimiters\n"
msgstr "-P, --punctuation Nutze Zeichensetzungszeichen alsBegrenzer\n"
#: optionDescriptions.h:12
msgid "-W , --white-space= Specify whitespace characters\n"
msgstr "-W , --white-space= Character für Whitespace spezifizieren\n"
#: optionDescriptions.h:14
msgid ""
"-S[], --paragraph-separator[=] Show inserted or deleted blocks\n"
" of empty lines, optionally overriding the marker\n"
msgstr ""
#: optionDescriptions.h:18
msgid "-1, --no-deleted Do not print deleted words\n"
msgstr "-1, --no-deleted Gelöschte Wörter nicht ausgeben\n"
#: optionDescriptions.h:19
msgid "-2, --no-inserted Do not print inserted words\n"
msgstr "-2, --no-inserted Eingefügte Wörter nicht ausgeben\n"
#: optionDescriptions.h:20
msgid "-3, --no-common Do not print common words\n"
msgstr "-3, --no-common Gemeinsame Wörter nicht ausgeben\n"
#: optionDescriptions.h:21
msgid "-L[], --line-numbers[] Prepend line numbers\n"
msgstr "-L[], --line-numbers[] Voranstellen von Zeilennummern\n"
#: optionDescriptions.h:22
msgid "-C, --context= Show lines of context\n"
msgstr "-C, --context= Zeige Zeilen Kontext\n"
#: optionDescriptions.h:23
msgid "-s, --statistics Print statistics when done\n"
msgstr "-s, --statistics Statistiken ausgeben\n"
#: optionDescriptions.h:25
msgid "--wdiff-output Produce wdiff compatible output\n"
msgstr ""
#: optionDescriptions.h:28
msgid "-i, --ignore-case Ignore differences in case\n"
msgstr "-i, --ignore-case Groß- und Kleinschreibung ignorieren\n"
#: optionDescriptions.h:29
msgid "-I, --ignore-formatting Ignore formatting differences\n"
msgstr "-I, --ignore-formatting Ignoriere Formatierungsunterschiede\n"
#. TRANSLATORS:
#. The context meant here are words preceeding and succeeding each word in
#. the text. By using these extra context words when applying the diff
#. program,
#. frequently occuring words will be more likely to be matched to the
#. correct corresponding word in the other text, thus giving a better result.
#: optionDescriptions.h:36
msgid "-m , --match-context= Use words of context for matching\n"
msgstr "-m , --match-context= Benutze Wörter im Kontext für das Matching\n"
#. TRANSLATORS:
#. The use of context words for matching is more expensive, because after the
#. first pass of diff the changes reported need refining. However, if the user
#. can live with multiple changes that are within (2 * match-context + 1)
#. words
#. from eachother being reported as a single change, they can use this option.
#: optionDescriptions.h:42
msgid "--aggregate-changes Allow close changes to aggregate\n"
msgstr "--aggregate-changes Erlaube Zusammenfassung benachbarter Veränderungen\n"
#: optionDescriptions.h:46
msgid "-c[], --color[=] Color mode\n"
msgstr "-c[], --color[=] Farbmodus\n"
#: optionDescriptions.h:47
msgid "-l, --less-mode As -p but also overstrike whitespace\n"
msgstr "-l, --less-mode Wie -p, mit durchgestrichenen Whitespace\n"
#: optionDescriptions.h:48
msgid "-p, --printer Use overstriking and bold text\n"
msgstr "-p, --printer Gestrichenen und fetten Text nutzen\n"
#: optionDescriptions.h:49
msgid "-w , --start-delete= String to mark begin of deleted text\n"
msgstr ""
"-w , --start-delete= Zeichenkette um den Beginn von gelöschtem\n"
" Text zu markieren\n"
#: optionDescriptions.h:50
msgid "-x , --stop-delete= String to mark end of deleted text\n"
msgstr ""
"-x , --stop-delete= Zeichenkette um das Ende von gelöschtem\n"
" Text zu markieren\n"
#: optionDescriptions.h:51
msgid "-y , --start-insert= String to mark begin of inserted text\n"
msgstr ""
"-y , --start-insert= Zeichenkette um den Beginn von eigefügtem\n"
" Text zu markieren\n"
#: optionDescriptions.h:52
msgid "-z , --stop-insert= String to mark end of inserted text\n"
msgstr ""
"-z , --stop-insert= Zeichenkette um das Ende von eigefügtem\n"
" Text zu markieren\n"
#: optionDescriptions.h:56
msgid "-r, --reverse Format new as old\n"
msgstr ""
#: option.c:38
msgid "Black"
msgstr "Schwarz"
#: option.c:39
msgid "Red"
msgstr "Rot"
#: option.c:40
msgid "Green"
msgstr "Grün"
#: option.c:41
msgid "Brown"
msgstr "Braun"
#: option.c:42
msgid "Blue"
msgstr "Blau"
#: option.c:43
msgid "Magenta"
msgstr "Magenta"
#: option.c:44
msgid "Cyan"
msgstr "Cyan"
#: option.c:45
msgid "Gray"
msgstr "Grau"
#: option.c:46
msgid "Dark gray"
msgstr "Dunkelgrau"
#: option.c:47
msgid "Bright red"
msgstr "Hellrot"
#: option.c:48
msgid "Bright green"
msgstr "Hellgrün"
#: option.c:49
msgid "Yellow"
msgstr "Gelb"
#: option.c:50
msgid "Bright blue"
msgstr "Hellblau"
#: option.c:51
msgid "Bright magenta"
msgstr "Hellmagenta"
#: option.c:52
msgid "Bright cyan"
msgstr "Hellcyan"
#: option.c:53
msgid "White"
msgstr "Weiß"
#: option.c:88
#, c-format
msgid "Single backslash at end of %s argument\n"
msgstr "Einfacher backslash am Ende des %s Argumentes\n"
#. TRANSLATORS:
#. The %s argument is a long option name without preceding dashes.
#: option.c:137 option.c:144
#, c-format
msgid "Invalid hexadecimal escape sequence in %s argument\n"
msgstr "Ungültige hexadezimale Escape-Sequenz im %s Argument\n"
#. TRANSLATORS:
#. The %c argument will be either 'u' or 'U'. The %s argument is a long
#. option name without preceding dashes.
#: option.c:178 option.c:184
#, c-format
msgid "Too short \\%c escape in %s argument\n"
msgstr "Zu kurze \\%c Escapesequenz im Argument %s\n"
#. TRANSLATORS:
#. The %s argument is a long option name without preceding dashes.
#: option.c:195
#, c-format
msgid "\\U escape out of range in %s argument\n"
msgstr "\\U Escapesequenz ist außerhalb des zulässigen Bereichs im Argument %s\n"
#. TRANSLATORS:
#. The %s argument is a long option name without preceding dashes.
#: option.c:200
#, c-format
msgid "\\%c escapes for surrogate codepoints are not allowed in %s argument\n"
msgstr "\\%c Escapes für Surrogatkodepunkte sind im %s Argument nicht erlaubt\n"
#: option.c:270 option.c:369 option.c:377
msgid "Whitespace and delimiter sets overlap\n"
msgstr "Whitespace und Begrenzerspezifikation überlappen\n"
#: option.c:435 option.c:637
#, c-format
msgid "Invalid color specification %s\n"
msgstr "Ungültige Farbspezifikation %s\n"
#: option.c:448 option.c:460
#, c-format
msgid "Invalid color %s\n"
msgstr "Ungültige Farbe %s\n"
#: option.c:445
#, c-format
msgid "Invalid background color %s\n"
msgstr ""
#: option.c:512
#, c-format
msgid "Usage: dwdiff [OPTIONS] \n"
msgstr "Gebrauch: dwdiff [OPTIONEN] \n"
#: option.c:553
#, c-format
msgid "Option %.*s is only supported for UTF-8 mode\n"
msgstr "Option %.*s ist nur für den UTF-8 Modus unterstützt\n"
#: option.c:556
#, c-format
msgid "Support for option %.*s is not compiled into this version of dwdiff\n"
msgstr "Unterstützung für die Option %.*s ist in diese Version von dwdiff nicht integriert\n"
#: option.c:563 option.c:574 option.c:593
#, c-format
msgid "Option %.*s is not supported\n"
msgstr "Option %.*s wird nicht unterstützt\n"
#: option.c:602
msgid "Can't read both files from standard input\n"
msgstr "Kann nicht beide Dateien von der Standardeingabe lesen\n"
#: option.c:606 option.c:696
msgid "Too many files to compare\n"
msgstr "Zu viele Dateien zu vergleichen\n"
#. TRANSLATORS:
#. "Name" and "Description" are table headings for the color name list.
#. Make sure you keep the alignment of the headings over the text.
#: option.c:626
msgid "Name Description\n"
msgstr "Name Beschreibung\n"
#: option.c:631
msgid "The colors black through gray are also usable as background color\n"
msgstr ""
#: option.c:679
#, c-format
msgid "Error opening temporary output file: %s\n"
msgstr ""
#: util.c:38
msgid "Out of memory"
msgstr "Hauptspeicher erschöpft"
#~ msgid "Option %.*s has zero length argument\n"
#~ msgstr "Option %.*s hat ein Argument der Länge Null\n"
#: dwfilter.c:159
#, c-format
msgid "Could not execute %s: %s\n"
msgstr ""
#: optionDescriptions.h:28
msgid ""
"--diff-input Read the input as the output from "
"diff\n"
msgstr ""
#: optionDescriptions.h:57
msgid ""
"-A , --algorithm= Choose algorithm: best, normal, fast\n"
msgstr ""
#: optionDescriptions.h:68
msgid "-R, --repeat-markers Repeat markers at newlines\n"
msgstr ""
#. TRANSLATORS:
#. - If the (C) symbol (that is the c in a circle) is not available,
#. leave as it as is. (Unicode code point 0x00A9)
#. - G.P. Halkes is name and should be left as is.
#: option.c:507
msgid ""
"Copyright (C) 2006-2011 G.P. Halkes and others\n"
"Licensed under the GNU General Public License version 3\n"
msgstr ""
#: option.c:681
msgid "Invalid algorithm name\n"
msgstr ""
#: option.c:701
msgid "Only one input file accepted with --diff-input\n"
msgstr ""
dwdiff-2.1.1/po/Makefile 0000664 0001750 0001750 00000002001 13074617326 016146 0 ustar gertjan gertjan 0000000 0000000 # Copyright (C) 2006-2010 G.P. Halkes
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3, as
# published by the Free Software Foundation.
#
# 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, see .
.POSIX:
.PHONY: linguas lingua-install
.SUFFIXES: .mo .po
LINGUAOBJECTS=$(LINGUAS:=.mo)
linguas: $(LINGUAOBJECTS)
.po.mo:
msgfmt -o $@ $<
lingua-install: $(LINGUAOBJECTS)
for lingua in $(LINGUAS) ; do \
if [ ! -f "$${lingua}.mo" ] ; then continue ; fi ;\
$(INSTALL) -d "$(LOCALEDIR)/$${lingua}/LC_MESSAGES" ;\
$(INSTALL) -m 644 "$${lingua}.mo" "$(LOCALEDIR)/$${lingua}/LC_MESSAGES/dwdiff.mo" ;\
done
dwdiff-2.1.1/po/es.po 0000664 0001750 0001750 00000035464 13074617326 015500 0 ustar gertjan gertjan 0000000 0000000 # SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Jesús Franco , 2011.
msgid ""
msgstr ""
"Project-Id-Version: dwdiff\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-01-31 20:18+0100\n"
"PO-Revision-Date: 2011-06-05 06:12+0000\n"
"Last-Translator: Jesús Franco \n"
"Language-Team: LANGUAGE \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: es\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
#: util.h:28 util.h:30
#, c-format
msgid "Program-logic error at %s:%d\n"
msgstr "Error lógico del programa en %s:%d\n"
#: doDiff.c:209 doDiff.c:246 doDiff.c:274 doDiff.c:296 doDiff.c:350
#: doDiff.c:360 doDiff.c:533
msgid "Error reading back input\n"
msgstr "Error al leer de nuevo la entrada\n\n"
#: doDiff.c:584 doDiff.c:586 dwdiff.c:277 dwdiff.c:280 dwdiff.c:289
#: dwfilter.c:147
#, c-format
msgid "Could not create temporary file: %s\n"
msgstr "No se pudo crear el archivo temporal: %s\n"
#: dwdiff.c:274 dwdiff.c:418
#, c-format
msgid "Can't open file %s: %s\n"
msgstr "No se puede abrir archivo %s: %s\n"
#: dwdiff.c:359
#, c-format
msgid "Error reading file %s: %s\n"
msgstr "Error al leer el archivo %s: %s\n"
#: dwdiff.c:378 dwdiff.c:383 dwdiff.c:388
#, c-format
msgid "Error writing to temporary file %s: %s\n"
msgstr "Error al escribir en archivo temporal %s: %s\n"
#: dwdiff.c:562
#, c-format
msgid "old: 0 words\n"
msgstr "Precedente: 0 palabras\n"
#: dwdiff.c:564
#, c-format
msgid "old: %d words %d %d%% common %d %d%% deleted %d %d%% changed\n"
msgstr "Precedente: %d words %d %d%% en común %d %d%% suprimidas %d %d%% cambiaron\n\n"
#: dwdiff.c:571
#, c-format
msgid "new: 0 words\n"
msgstr "Nuevo: 0 palabras\n"
#: dwdiff.c:573
#, c-format
msgid "new: %d words %d %d%% common %d %d%% inserted %d %d%% changed\n"
msgstr "Nuevo: %d palabras %d %d%% en común %d %d%% añadidas %d %d%% cambiaron\n"
#: dwfilter.c:58
#, c-format
msgid ""
"Usage: dwfilter [OPTIONS] [POST "
"PROCESSOR OPTIONS]\n"
msgstr "Modo de uso: dwfilter [OPCIONES] [OPCIONES DE POSTPROCESO]\n"
#. TRANSLATORS:
#. - If the (C) symbol (that is the c in a circle) is not available,
#. leave as it as is. (Unicode code point 0x00A9)
#. - G.P. Halkes is name and should be left as is.
#: dwfilter.c:70 option.c:535
msgid ""
"Copyright (C) 2006-2011 G.P. Halkes\n"
"Licensed under the GNU General Public License version 3\n"
msgstr "Copyright (C) 2006-2011 G.P. Halkes\nLiberado bajo los términos de la Licencia Pública General de GNU Versión 3\n"
#. TRANSLATORS:
#. - If the (C) symbol (that is the c in a circle) is not available,
#. leave as it as is. (Unicode code point 0x00A9)
#. - G.P. Halkes is name and should be left as is.
#: dwfilter.c:70 option.c:535
msgid ""
"Copyright (C) 2006-2011 G.P. Halkes and others\n"
"Licensed under the GNU General Public License version 3\n"
msgstr "Copyright (C) 2006-2011 G.P. Halkes y otros\nLiberado bajo los términos de la Licencia Pública General de GNU Versión 3\n"
#: dwfilter.c:108 option.c:693 option.c:702
#, c-format
msgid "Option %.*s does not exist\n"
msgstr "La opción %.*s no existe\n"
#: dwfilter.c:125 option.c:723
msgid "Need two files to compare\n"
msgstr "Se necesitan dos archivos para comparar\n"
#: dwfilter.c:127
msgid "No post processor specified\n"
msgstr "No se especificó un postprocesador\n"
#: dwfilter.c:159
#, c-format
msgid "Could not execute %s: %s\n"
msgstr "No se puede ejecutar %s: %s\n"
#: dwfilter.c:163
#, c-format
msgid "Error waiting for child process to terminate: %s\n"
msgstr "Ocurrió un error esperando que terminara el proceso hijo: %s\n"
#: dwfilter.c:210
msgid "dwdiff returned an error\n"
msgstr "dwdiff devolvió un error\n"
#: optionMacros.h:168
#, c-format
msgid "Option %.*s does not take an argument\n"
msgstr "La opción %.*s no admite argumentos\n"
#: optionMacros.h:174
#, c-format
msgid "Option %.*s requires an argument\n"
msgstr "La opción %.*s requiere un argumento\n"
#: optionMacros.h:258
#, c-format
msgid "Garbage after value for %.*s option\n"
msgstr "Basura después de valor para la opción %.*s\n"
#: optionMacros.h:261
#, c-format
msgid "Value for %.*s option (%ld) is out of range\n"
msgstr "El valor para la opción %.*s (%ld) está fuera de rango\n"
#: optionMacros.h:295
#, c-format
msgid "Value for %.*s option (%s) is not a valid boolean value\n"
msgstr "El valor para la opción %.*s (%s) no es un valor booleano válido\n"
#: optionDescriptions.h:20
msgid "-h, --help Print this help message\n"
msgstr "-h, --help Muestra este mensaje de ayuda\n"
#: optionDescriptions.h:21
msgid ""
"-v, --version Print version and copyright "
"information\n"
msgstr "-v, --version Muestra versión e información de Copyright\n"
#: optionDescriptions.h:24
msgid "-d , --delimiters= Specifiy delimiters\n"
msgstr "-d , --delimiters= Especifica delimitadores\n"
#: optionDescriptions.h:25
msgid ""
"-P, --punctuation Use punctuation characters as "
"delimiters\n"
msgstr "-P, --punctuation Usa caracteres de puntuación como delimitadores\n"
#: optionDescriptions.h:26
msgid "-W , --white-space= Specify whitespace characters\n"
msgstr "-W , --white-space=