123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966 |
- /*++
- Copyright (c) 2013 Minoca Corp. All Rights Reserved
- Module Name:
- diff.c
- Abstract:
- This module implements the diff utility.
- Author:
- Evan Green 3-Jul-2013
- Environment:
- POSIX
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/lib/types.h>
- #include <assert.h>
- #include <ctype.h>
- #include <errno.h>
- #include <getopt.h>
- #include <libgen.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include "swlib.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define DIFF_VERSION_MAJOR 1
- #define DIFF_VERSION_MINOR 0
- #define DIFF_USAGE \
- "usage: diff [-c | -e | -f | -C n][-br] file1 file2\n" \
- "The diff utility compares the contents of two paths and reports the \n" \
- "differences to standard out. Options are:\n" \
- " -b, --ignore-space-change -- Ignore whitespace changes.\n" \
- " -c -- Produce three lines of context around every diff.\n" \
- " -C, --context=n -- Produce n lines of context around every diff, \n" \
- " where n is a decimal integer.\n" \
- " --color=value -- Turn on or off color printing. Valid values are \n" \
- " always, never, and auto." \
- " -e, --ed -- Output an ed script.\n" \
- " -N, --new-file -- Treat absent files as empty.\n" \
- " -r, --recursive -- Recursively compare any subdirectories found.\n" \
- " -u, --unified=n -- Produce a unified diff format, with n lines of \n" \
- " context.\n" \
- " -x, --exclude=pattern -- exclude file that match the given pattern.\n" \
- " --help -- Show this help text and exit.\n" \
- " --version -- Show the application version information and exit.\n" \
- #define DIFF_OPTIONS_STRING "bcC:eNru::x:"
- //
- // Define the diff option flags.
- //
- #define DIFF_OPTION_IGNORE_BLANKS 0x00000001
- #define DIFF_OPTION_RECURSIVE 0x00000002
- #define DIFF_OPTION_COLOR 0x00000004
- #define DIFF_OPTION_ABSENT_EMPTY 0x00000008
- //
- // Define the default number of context lines when they're asked for.
- //
- #define DIFF_DEFAULT_CONTEXT_LINES 3
- //
- // Define the maximum depth of directories that diff will crawl down.
- //
- #define DIFF_MAX_RECURSION_DEPTH 100
- //
- // Define the initial size of a directory listing array in elements.
- //
- #define DIFF_INITIAL_ARRAY_CAPACITY 32
- //
- // Define the initial size of the line buffer in bytes.
- //
- #define DIFF_INITIAL_LINE_BUFFER 256
- //
- // Define the colors used for insertion and deletion.
- //
- #define DIFF_INSERTION_COLOR ConsoleColorDarkGreen
- #define DIFF_DELETION_COLOR ConsoleColorDarkRed
- //
- // Define the time format string used by context diffs.
- //
- #define CONTEXT_DIFF_TIMESTAMP_FORMAT "%a %b %d %H:%M:%S %Y"
- #define CONTEXT_DIFF_TIMESTAMP_SIZE 26
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- typedef enum _DIFF_OUTPUT_TYPE {
- DiffOutputInvalid,
- DiffOutputDefault,
- DiffOutputEd,
- DiffOutputUnified,
- } DIFF_OUTPUT_TYPE, *PDIFF_OUTPUT_TYPE;
- typedef enum _DIFF_FILE_TYPE {
- DiffFileUnknown,
- DiffFileBlockDevice,
- DiffFileCharacterDevice,
- DiffFileDirectory,
- DiffFileRegularFile,
- DiffFileFifo,
- DiffFileLink,
- DiffFileSocket,
- DiffFileTypeCount
- } DIFF_FILE_TYPE, *PDIFF_FILE_TYPE;
- /*++
- Structure Description:
- This structure defines a diff input line.
- Members:
- Data - Stores a pointer to the allocated null terminated line.
- Size - Stores the size of the line data in bytes.
- Hash - Stores the hash of the file, which is used to quickly determine if
- two files are not equal (but not necessarily if they're equal).
- Modified - Stores a boolean indicating that this line is part of the diff.
- --*/
- typedef struct _DIFF_LINE {
- PSTR Data;
- UINTN Size;
- ULONG Hash;
- BOOL Modified;
- } DIFF_LINE, *PDIFF_LINE;
- /*++
- Structure Description:
- This structure defines an input file of diff.
- Members:
- Name - Stores a pointer to a string containing the name of the file.
- ModificationTime - Stores the files modification date.
- Type - Stores the type of file this thing is.
- Binary - Stores a boolean indicating if this file is binary data or text.
- NoNewlineAtEnd - Stores a boolean indicating if there is no newline at the
- end of the file.
- File - Stores a pointer to the file input handle.
- LineCount - Stores the number of lines in the file.
- Lines - Stores an array of pointers to the lines of the file.
- --*/
- typedef struct _DIFF_FILE {
- PSTR Name;
- time_t ModificationTime;
- DIFF_FILE_TYPE Type;
- BOOL Binary;
- BOOL NoNewlineAtEnd;
- FILE *File;
- INTN LineCount;
- PDIFF_LINE *Lines;
- } DIFF_FILE, *PDIFF_FILE;
- /*++
- Structure Description:
- This structure defines the contents of a directory.
- Members:
- FileCount - Stores the number of files in the directory.
- ArrayCapacity - Stores the size of the array allocation in elements.
- Files - Stores the array of pointers to diff files.
- --*/
- typedef struct _DIFF_DIRECTORY {
- UINTN FileCount;
- UINTN ArrayCapacity;
- PDIFF_FILE *Files;
- } DIFF_DIRECTORY, *PDIFF_DIRECTORY;
- /*++
- Structure Description:
- This structure defines the context for an instantiation of the diff
- application.
- Members:
- Options - Stores the bitfield of diff options.
- OutputType - Stores the type of output to produce.
- ContextLines - Stores the number of lines of context to produce around
- each diff.
- EmptyFile - Stores a pointer to a dummy file that can be used for
- comparisons.
- FileExclusions - Stores a pointer to an array of string containing patterns
- of file names to exclude.
- FileExclusionCount - Stores the number of elements in the file exclusions
- pattern.
- --*/
- typedef struct _DIFF_CONTEXT {
- ULONG Options;
- DIFF_OUTPUT_TYPE OutputType;
- ULONG ContextLines;
- DIFF_FILE EmptyFile;
- PSTR *FileExclusions;
- UINTN FileExclusionCount;
- } DIFF_CONTEXT, *PDIFF_CONTEXT;
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- INT
- DiffComparePaths (
- PDIFF_CONTEXT Context,
- PSTR PathA,
- PSTR PathB
- );
- INT
- DiffCompareFiles (
- PDIFF_CONTEXT Context,
- PSTR DirectoryA,
- PDIFF_FILE FileA,
- PSTR DirectoryB,
- PDIFF_FILE FileB,
- ULONG RecursionLevel
- );
- INT
- DiffCompareDirectories (
- PDIFF_CONTEXT Context,
- PSTR PathA,
- PSTR PathB,
- ULONG RecursionLevel
- );
- INT
- DiffGetDirectoryListing (
- PDIFF_CONTEXT Context,
- PSTR Path,
- PDIFF_DIRECTORY *NewDirectory
- );
- BOOL
- DiffIsFileNameExcluded (
- PDIFF_CONTEXT Context,
- PSTR Name
- );
- VOID
- DiffDestroyDirectory (
- PDIFF_CONTEXT Context,
- PDIFF_DIRECTORY Directory
- );
- INT
- DiffCreateFile (
- PDIFF_CONTEXT Context,
- PSTR Directory,
- PSTR Path,
- PDIFF_FILE *File
- );
- VOID
- DiffDestroyFile (
- PDIFF_CONTEXT Context,
- PDIFF_FILE File
- );
- VOID
- DiffDestroyLine (
- PDIFF_LINE Line
- );
- DIFF_FILE_TYPE
- DiffGetFileType (
- mode_t Mode
- );
- int
- DiffCompareFileNames (
- const void *LeftPointer,
- const void *RightPointer
- );
- VOID
- DiffPrintCommandLine (
- PDIFF_CONTEXT Context,
- PSTR PathA,
- PDIFF_FILE FileA,
- PSTR PathB,
- PDIFF_FILE FileB
- );
- INT
- DiffLoadFile (
- PDIFF_CONTEXT Context,
- PSTR Directory,
- PDIFF_FILE File
- );
- INT
- DiffCompareRegularFiles (
- PDIFF_CONTEXT Context,
- PSTR DirectoryA,
- PDIFF_FILE FileA,
- PSTR DirectoryB,
- PDIFF_FILE FileB,
- ULONG RecursionLevel
- );
- INT
- DiffCompareBinaryFiles (
- PDIFF_CONTEXT Context,
- PDIFF_FILE FileA,
- PDIFF_FILE FileB
- );
- INT
- DiffComputeLongestCommonSubsequence (
- PDIFF_CONTEXT Context,
- PDIFF_FILE FileA,
- PDIFF_FILE FileB,
- INTN LowerA,
- INTN UpperA,
- INTN LowerB,
- INTN UpperB,
- PINTN DownVector,
- PINTN UpVector
- );
- VOID
- DiffComputeShortestMiddleSnake (
- PDIFF_CONTEXT Context,
- PDIFF_FILE FileA,
- PDIFF_FILE FileB,
- INTN LowerA,
- INTN UpperA,
- INTN LowerB,
- INTN UpperB,
- PINTN DownVector,
- PINTN UpVector,
- PINTN MiddleSnakeX,
- PINTN MiddleSnakeY
- );
- INT
- DiffCompareLines (
- PDIFF_CONTEXT Context,
- PDIFF_FILE FileA,
- PDIFF_FILE FileB,
- UINTN LineIndexA,
- UINTN LineIndexB
- );
- VOID
- DiffPrintEdDiffs (
- PDIFF_CONTEXT Context,
- PSTR DirectoryA,
- PDIFF_FILE FileA,
- PSTR DirectoryB,
- PDIFF_FILE FileB
- );
- VOID
- DiffPrintContextDiffs (
- PDIFF_CONTEXT Context,
- PSTR DirectoryA,
- PDIFF_FILE FileA,
- PSTR DirectoryB,
- PDIFF_FILE FileB
- );
- VOID
- DiffPrintUnifiedDiffs (
- PDIFF_CONTEXT Context,
- PSTR DirectoryA,
- PDIFF_FILE FileA,
- PSTR DirectoryB,
- PDIFF_FILE FileB
- );
- VOID
- DiffFindNextHunk (
- PDIFF_CONTEXT Context,
- PDIFF_FILE FileA,
- PDIFF_FILE FileB,
- PINTN LineA,
- PINTN LineB,
- PINTN SizeA,
- PINTN SizeB
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- struct option DiffLongOptions[] = {
- {"ignore-space-change", no_argument, 0, 'b'},
- {"context", optional_argument, 0, 'C'},
- {"color", required_argument, 0, '1'},
- {"ed", no_argument, 0, 'e'},
- {"new-file", no_argument, 0, 'N'},
- {"recursive", no_argument, 0, 'r'},
- {"unified", optional_argument, 0, 'u'},
- {"help", no_argument, 0, 'h'},
- {"version", no_argument, 0, 'V'},
- {NULL, 0, 0, 0},
- };
- PSTR DiffFileTypeNames[DiffFileTypeCount] = {
- "funky thing",
- "block device",
- "character device",
- "directory",
- "file",
- "fifo",
- "symbolic link",
- "socket",
- };
- //
- // ------------------------------------------------------------------ Functions
- //
- INT
- DiffMain (
- INT ArgumentCount,
- CHAR **Arguments
- )
- /*++
- Routine Description:
- This routine is the main entry point for the diff utility.
- Arguments:
- ArgumentCount - Supplies the number of command line arguments the program
- was invoked with.
- Arguments - Supplies a tokenized array of command line arguments.
- Return Value:
- Returns an integer exit code. 0 for success, nonzero otherwise.
- --*/
- {
- PSTR AfterScan;
- ULONG ArgumentIndex;
- DIFF_CONTEXT Context;
- BOOL ContextLinesSpecified;
- PVOID NewBuffer;
- INT Option;
- BOOL OutputIsTerminal;
- int Status;
- memset(&Context, 0, sizeof(DIFF_CONTEXT));
- ContextLinesSpecified = FALSE;
- Context.OutputType = DiffOutputDefault;
- OutputIsTerminal = FALSE;
- if (isatty(STDOUT_FILENO) != 0) {
- OutputIsTerminal = TRUE;
- Context.Options |= DIFF_OPTION_COLOR;
- }
- //
- // Process the control arguments.
- //
- while (TRUE) {
- Option = getopt_long(ArgumentCount,
- Arguments,
- DIFF_OPTIONS_STRING,
- DiffLongOptions,
- NULL);
- if (Option == -1) {
- break;
- }
- if ((Option == '?') || (Option == ':')) {
- Status = 2;
- goto MainEnd;
- }
- switch (Option) {
- case 'b':
- Context.Options |= DIFF_OPTION_IGNORE_BLANKS;
- break;
- case 'u':
- Context.OutputType = DiffOutputUnified;
- //
- // Fall through.
- //
- case 'c':
- case 'C':
- ContextLinesSpecified = TRUE;
- if (Context.OutputType == DiffOutputEd) {
- SwPrintError(0, NULL, "Conflicting output style options");
- Status = EINVAL;
- goto MainEnd;
- }
- Context.ContextLines = DIFF_DEFAULT_CONTEXT_LINES;
- if (optarg != NULL) {
- Context.ContextLines = strtol(optarg, &AfterScan, 10);
- if ((Context.ContextLines < 0) || (AfterScan == optarg)) {
- SwPrintError(0, optarg, "Expected an integer");
- Status = EINVAL;
- goto MainEnd;
- }
- }
- break;
- case '1':
- assert(optarg != NULL);
- if (strcasecmp(optarg, "always") == 0) {
- Context.Options |= DIFF_OPTION_COLOR;
- } else if (strcasecmp(optarg, "never") == 0) {
- Context.Options &= ~DIFF_OPTION_COLOR;
- } else if (strcasecmp(optarg, "auto") == 0) {
- Context.Options &= ~DIFF_OPTION_COLOR;
- if (OutputIsTerminal != FALSE) {
- Context.Options |= DIFF_OPTION_COLOR;
- }
- } else {
- SwPrintError(0, optarg, "Invalid color argument");
- Status = EINVAL;
- goto MainEnd;
- }
- break;
- case 'e':
- if (Context.ContextLines != 0) {
- SwPrintError(0, NULL, "Conflicting output style options");
- Status = EINVAL;
- goto MainEnd;
- }
- Context.OutputType = DiffOutputEd;
- break;
- case 'N':
- Context.Options |= DIFF_OPTION_ABSENT_EMPTY;
- break;
- case 'r':
- Context.Options |= DIFF_OPTION_RECURSIVE;
- break;
- case 'x':
- assert(optarg != NULL);
- NewBuffer = realloc(
- Context.FileExclusions,
- (Context.FileExclusionCount + 1) * sizeof(PSTR));
- if (NewBuffer == NULL) {
- Status = ENOMEM;
- goto MainEnd;
- }
- Context.FileExclusions = NewBuffer;
- Context.FileExclusions[Context.FileExclusionCount] = optarg;
- Context.FileExclusionCount += 1;
- break;
- case 'V':
- SwPrintVersion(DIFF_VERSION_MAJOR, DIFF_VERSION_MINOR);
- return 1;
- case 'h':
- printf(DIFF_USAGE);
- return 1;
- default:
- assert(FALSE);
- Status = 1;
- goto MainEnd;
- }
- }
- //
- // If context was not specified and the format is still default, use the
- // ed format.
- //
- if ((Context.OutputType == DiffOutputDefault) &&
- (ContextLinesSpecified == FALSE)) {
- Context.OutputType = DiffOutputEd;
- }
- ArgumentIndex = optind;
- if (ArgumentIndex > ArgumentCount) {
- ArgumentIndex = ArgumentCount;
- }
- //
- // Fail if there were not enough arguments.
- //
- if (ArgumentIndex >= ArgumentCount) {
- SwPrintError(0,
- NULL,
- "Diff needs two things to compare. Try --help for usage");
- Status = 2;
- goto MainEnd;
- }
- if (ArgumentIndex + 2 != ArgumentCount) {
- SwPrintError(0,
- NULL,
- "Diff needs exactly two arguments. Try --help for usage");
- Status = 2;
- goto MainEnd;
- }
- Status = DiffComparePaths(&Context,
- Arguments[ArgumentIndex],
- Arguments[ArgumentIndex + 1]);
- MainEnd:
- if (Context.FileExclusions != NULL) {
- free(Context.FileExclusions);
- }
- return Status;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- INT
- DiffComparePaths (
- PDIFF_CONTEXT Context,
- PSTR PathA,
- PSTR PathB
- )
- /*++
- Routine Description:
- This routine compares two paths, printing out the differences.
- Arguments:
- Context - Supplies a pointer to the diff application context.
- PathA - Supplies a pointer to a null terminated string containing the first
- path to compare.
- PathB - Supplies a pointer to a null terminated string containing the
- second path to compare.
- Return Value:
- 0 if the files are equal.
- 1 if there are differences.
- Other values on error.
- --*/
- {
- PSTR AppendedPath;
- ULONG AppendedPathSize;
- PSTR BaseName;
- PDIFF_FILE FileA;
- PDIFF_FILE FileB;
- INT Status;
- AppendedPath = NULL;
- FileA = NULL;
- FileB = NULL;
- Status = DiffCreateFile(Context, NULL, PathA, &FileA);
- if (Status != 0) {
- goto ComparePathsEnd;
- }
- Status = DiffCreateFile(Context, NULL, PathB, &FileB);
- if (Status != 0) {
- goto ComparePathsEnd;
- }
- //
- // If only one of A and B is a directory, then the diff is between the file
- // and directory/file (or basename of file).
- //
- if ((FileA->Type == DiffFileDirectory) &&
- (FileB->Type != DiffFileDirectory)) {
- BaseName = basename(PathB);
- Status = SwAppendPath(PathA,
- strlen(PathA) + 1,
- BaseName,
- strlen(BaseName) + 1,
- &AppendedPath,
- &AppendedPathSize);
- if (Status != 0) {
- SwPrintError(Status, NULL, "Could not append paths");
- goto ComparePathsEnd;
- }
- DiffDestroyFile(Context, FileB);
- Status = DiffCreateFile(Context, NULL, AppendedPath, &FileB);
- if (Status != 0) {
- goto ComparePathsEnd;
- }
- } else if ((FileA->Type != DiffFileDirectory) &&
- (FileB->Type == DiffFileDirectory)) {
- BaseName = basename(PathA);
- Status = SwAppendPath(PathB,
- strlen(PathB) + 1,
- BaseName,
- strlen(BaseName) + 1,
- &AppendedPath,
- &AppendedPathSize);
- if (Status != 0) {
- SwPrintError(Status, NULL, "Could not append paths");
- goto ComparePathsEnd;
- }
- DiffDestroyFile(Context, FileA);
- Status = DiffCreateFile(Context, NULL, AppendedPath, &FileA);
- if (Status != 0) {
- goto ComparePathsEnd;
- }
- }
- Status = DiffCompareFiles(Context, NULL, FileA, NULL, FileB, 0);
- ComparePathsEnd:
- if (AppendedPath != NULL) {
- free(AppendedPath);
- }
- if (FileA != NULL) {
- DiffDestroyFile(Context, FileA);
- }
- if (FileB != NULL) {
- DiffDestroyFile(Context, FileB);
- }
- return Status;
- }
- INT
- DiffCompareFiles (
- PDIFF_CONTEXT Context,
- PSTR DirectoryA,
- PDIFF_FILE FileA,
- PSTR DirectoryB,
- PDIFF_FILE FileB,
- ULONG RecursionLevel
- )
- /*++
- Routine Description:
- This routine compares two file structures, printing out the differences.
- Arguments:
- Context - Supplies a pointer to the diff application context.
- DirectoryA - Supplies a pointer to the directory prefix for file A.
- FileA - Supplies a pointer to the first file object to compare.
- DirectoryB - Supplies a pointer to the directory prefix for file B.
- FileB - Supplies a pointer to the second file object to compare.
- RecursionLevel - Supplies the level of recursion this function is operating
- in.
- Return Value:
- 0 if the files are equal.
- 1 if there are differences.
- Other values on error.
- --*/
- {
- PSTR AppendedPathA;
- ULONG AppendedPathASize;
- PSTR AppendedPathB;
- ULONG AppendedPathBSize;
- ULONG DirectorySize;
- INT Status;
- AppendedPathA = NULL;
- AppendedPathB = NULL;
- //
- // If the types are not equal, simply report that.
- //
- if ((DirectoryA != NULL) && (DirectoryB != NULL)) {
- if (FileA->Type != FileB->Type) {
- printf("File %s is a %s while file %s is a %s.\n",
- FileA->Name,
- DiffFileTypeNames[FileA->Type],
- FileB->Name,
- DiffFileTypeNames[FileB->Type]);
- Status = 1;
- goto CompareFilesEnd;
- }
- }
- Status = 0;
- //
- // The file types are equal. If they're files or directories, compare them.
- //
- if (FileA->Type != DiffFileDirectory) {
- Status = DiffCompareRegularFiles(Context,
- DirectoryA,
- FileA,
- DirectoryB,
- FileB,
- RecursionLevel);
- } else {
- //
- // Compare the contents of the directories if either 1) this is the
- // entry directly from the command line or 2) The recursion option is
- // enabled and the current recursion level is below the maximum
- // depth.
- //
- if ((RecursionLevel == 0) ||
- ((Context->Options & DIFF_OPTION_RECURSIVE) != 0)) {
- if (RecursionLevel >= DIFF_MAX_RECURSION_DEPTH) {
- SwPrintError(0, FileA->Name, "Max recursion depth reached");
- Status = ELOOP;
- goto CompareFilesEnd;
- }
- DirectorySize = 0;
- if (DirectoryA != NULL) {
- DirectorySize = strlen(DirectoryA) + 1;
- }
- Status = SwAppendPath(DirectoryA,
- DirectorySize,
- FileA->Name,
- strlen(FileA->Name) + 1,
- &AppendedPathA,
- &AppendedPathASize);
- if (Status == FALSE) {
- Status = ENOMEM;
- goto CompareFilesEnd;
- }
- DirectorySize = 0;
- if (DirectoryB != NULL) {
- DirectorySize = strlen(DirectoryB) + 1;
- }
- Status = SwAppendPath(DirectoryB,
- DirectorySize,
- FileB->Name,
- strlen(FileB->Name) + 1,
- &AppendedPathB,
- &AppendedPathBSize);
- if (Status == FALSE) {
- Status = ENOMEM;
- goto CompareFilesEnd;
- }
- Status = DiffCompareDirectories(Context,
- AppendedPathA,
- AppendedPathB,
- RecursionLevel);
- }
- }
- CompareFilesEnd:
- if (AppendedPathA != NULL) {
- free(AppendedPathA);
- }
- if (AppendedPathB != NULL) {
- free(AppendedPathB);
- }
- return Status;
- }
- INT
- DiffCompareDirectories (
- PDIFF_CONTEXT Context,
- PSTR PathA,
- PSTR PathB,
- ULONG RecursionLevel
- )
- /*++
- Routine Description:
- This routine compares the contents of two directories, printing out the
- differences.
- Arguments:
- Context - Supplies a pointer to the diff application context.
- PathA - Supplies a pointer to a null terminated string containing the first
- directory to compare.
- PathB - Supplies a pointer to a null terminated string containing the
- directory path to compare.
- RecursionLevel - Supplies the current recursion depth.
- Return Value:
- Returns an integer exit code. 0 for success, nonzero otherwise.
- --*/
- {
- PDIFF_DIRECTORY DirectoryA;
- PDIFF_DIRECTORY DirectoryB;
- PDIFF_FILE FileA;
- UINTN FileAIndex;
- PDIFF_FILE FileB;
- UINTN FileBIndex;
- INT NameComparison;
- INT Status;
- INT TotalStatus;
- Status = 0;
- TotalStatus = 0;
- //
- // Enumerate both directories.
- //
- DirectoryA = NULL;
- DirectoryB = NULL;
- Status = DiffGetDirectoryListing(Context, PathA, &DirectoryA);
- if (Status != 0) {
- SwPrintError(Status, PathA, "Unable to enumerate directory");
- goto DiffCompareDirectoriesEnd;
- }
- Status = DiffGetDirectoryListing(Context, PathB, &DirectoryB);
- if (Status != 0) {
- SwPrintError(Status, PathB, "Unable to enumerate directory");
- goto DiffCompareDirectoriesEnd;
- }
- //
- // Loop through until all files have been dealt with.
- //
- FileAIndex = 0;
- FileBIndex = 0;
- while ((FileAIndex < DirectoryA->FileCount) ||
- (FileBIndex < DirectoryB->FileCount)) {
- FileA = NULL;
- FileB = NULL;
- //
- // If A is at the end, then just print that B exists.
- //
- if ((FileAIndex == DirectoryA->FileCount) &&
- ((Context->Options & DIFF_OPTION_ABSENT_EMPTY) == 0)) {
- assert(FileBIndex < DirectoryB->FileCount);
- FileB = DirectoryB->Files[FileBIndex];
- printf("Only in %s: %s\n", PathB, FileB->Name);
- FileBIndex += 1;
- if (TotalStatus == 0) {
- TotalStatus = 1;
- }
- //
- // If B is at the end, then just print that A exists.
- //
- } else if ((FileBIndex == DirectoryB->FileCount) &&
- ((Context->Options & DIFF_OPTION_ABSENT_EMPTY) == 0)) {
- assert(FileAIndex < DirectoryA->FileCount);
- FileA = DirectoryA->Files[FileAIndex];
- printf("Only in %s: %s\n", PathA, FileA->Name);
- FileAIndex += 1;
- if (TotalStatus == 0) {
- TotalStatus = 1;
- }
- //
- // They both exist (or absent files are treated as empty.
- //
- } else {
- if (FileAIndex < DirectoryA->FileCount) {
- FileA = DirectoryA->Files[FileAIndex];
- }
- if (FileBIndex < DirectoryB->FileCount) {
- FileB = DirectoryB->Files[FileBIndex];
- }
- //
- // Use the empty file if either file came up NULL.
- //
- if (FileA == NULL) {
- FileA = &(Context->EmptyFile);
- FileA->Type = FileB->Type;
- FileA->Name = FileB->Name;
- } else if (FileB == NULL) {
- FileB = &(Context->EmptyFile);
- FileB->Type = FileA->Type;
- FileB->Name = FileA->Name;
- }
- //
- // They both exist. If they're the same file, then actually compare
- // them.
- //
- NameComparison = strcmp(FileA->Name, FileB->Name);
- if (NameComparison == 0) {
- if ((FileA->Type == DiffFileDirectory) &&
- (FileB->Type == DiffFileDirectory) &&
- ((Context->Options & DIFF_OPTION_RECURSIVE) == 0)) {
- printf("Common subdirectories: %s/%s and %s/%s\n",
- PathA,
- FileA->Name,
- PathB,
- FileB->Name);
- }
- Status = DiffCompareFiles(Context,
- PathA,
- FileA,
- PathB,
- FileB,
- RecursionLevel + 1);
- if (Status != 0) {
- if (TotalStatus == 0) {
- TotalStatus = Status;
- }
- }
- //
- // Destroy files now to conserve memory and file descriptors.
- //
- DiffDestroyFile(Context, FileA);
- if (FileAIndex < DirectoryA->FileCount) {
- DirectoryA->Files[FileAIndex] = NULL;
- }
- DiffDestroyFile(Context, FileB);
- if (FileBIndex < DirectoryB->FileCount) {
- DirectoryB->Files[FileBIndex] = NULL;
- }
- FileAIndex += 1;
- FileBIndex += 1;
- //
- // Name whichever file is lower as the oddball in the directory.
- //
- } else if (NameComparison < 0) {
- if ((Context->Options & DIFF_OPTION_ABSENT_EMPTY) != 0) {
- FileB = &(Context->EmptyFile);
- FileB->Type = FileA->Type;
- FileB->Name = FileA->Name;
- Status = DiffCompareFiles(Context,
- PathA,
- FileA,
- PathB,
- FileB,
- RecursionLevel + 1);
- if (Status != 0) {
- if (TotalStatus == 0) {
- TotalStatus = Status;
- }
- }
- } else {
- printf("Only in %s: %s\n", PathA, FileA->Name);
- }
- DiffDestroyFile(Context, FileA);
- DirectoryA->Files[FileAIndex] = NULL;
- FileAIndex += 1;
- } else {
- if ((Context->Options & DIFF_OPTION_ABSENT_EMPTY) != 0) {
- FileA = &(Context->EmptyFile);
- FileA->Type = FileB->Type;
- FileA->Name = FileB->Name;
- Status = DiffCompareFiles(Context,
- PathA,
- FileA,
- PathB,
- FileB,
- RecursionLevel + 1);
- if (Status != 0) {
- if (TotalStatus == 0) {
- TotalStatus = Status;
- }
- }
- } else {
- printf("Only in %s: %s\n", PathB, FileB->Name);
- }
- DiffDestroyFile(Context, FileB);
- DirectoryB->Files[FileBIndex] = NULL;
- FileBIndex += 1;
- }
- }
- }
- DiffCompareDirectoriesEnd:
- if (DirectoryA != NULL) {
- DiffDestroyDirectory(Context, DirectoryA);
- }
- if (DirectoryB != NULL) {
- DiffDestroyDirectory(Context, DirectoryB);
- }
- if (Status != 0) {
- TotalStatus = Status;
- }
- return TotalStatus;
- }
- INT
- DiffGetDirectoryListing (
- PDIFF_CONTEXT Context,
- PSTR Path,
- PDIFF_DIRECTORY *NewDirectory
- )
- /*++
- Routine Description:
- This routine enumerates the contents of a directory.
- Arguments:
- Context - Supplies a pointer to the application context.
- Path - Supplies a pointer to the null terminated path of the directory to
- enumerate.
- NewDirectory - Supplies a pointer where a pointer to the new directory
- structure will be returned on success.
- Return Value:
- 0 on success.
- Returns a non-zero error code on failure.
- --*/
- {
- PDIFF_DIRECTORY Directory;
- DIR *DirectoryFile;
- struct dirent Entry;
- struct dirent *EntryPointer;
- PVOID NewBuffer;
- UINTN NewCapacity;
- PDIFF_FILE NewFile;
- INT Status;
- NewFile = NULL;
- Directory = NULL;
- DirectoryFile = opendir(Path);
- if ((DirectoryFile == NULL) && (errno != ENOENT)) {
- Status = errno;
- SwPrintError(Status, Path, "Failed to open directory");
- goto GetDirectoryListingEnd;
- }
- Directory = malloc(sizeof(DIFF_DIRECTORY));
- if (Directory == NULL) {
- Status = ENOMEM;
- goto GetDirectoryListingEnd;
- }
- memset(Directory, 0, sizeof(DIFF_DIRECTORY));
- if (DirectoryFile == NULL) {
- Status = 0;
- goto GetDirectoryListingEnd;
- }
- while (TRUE) {
- Status = SwReadDirectory(DirectoryFile, &Entry, &EntryPointer);
- if (Status != 0) {
- SwPrintError(Status, Path, "Unable to read directory");
- goto GetDirectoryListingEnd;
- }
- if (EntryPointer == NULL) {
- break;
- }
- if ((strcmp(Entry.d_name, ".") == 0) ||
- (strcmp(Entry.d_name, "..") == 0)) {
- continue;
- }
- //
- // Skip the file if it's excluded.
- //
- if (DiffIsFileNameExcluded(Context, Entry.d_name) != 0) {
- continue;
- }
- //
- // Expand the array if there's not space.
- //
- if (Directory->FileCount == Directory->ArrayCapacity) {
- NewCapacity = Directory->ArrayCapacity * 2;
- if (NewCapacity == 0) {
- NewCapacity = DIFF_INITIAL_ARRAY_CAPACITY;
- }
- NewBuffer = realloc(Directory->Files,
- NewCapacity * sizeof(PDIFF_FILE));
- if (NewBuffer == NULL) {
- Status = ENOMEM;
- goto GetDirectoryListingEnd;
- }
- Directory->Files = NewBuffer;
- Directory->ArrayCapacity = NewCapacity;
- }
- //
- // Create the file and add it to the array.
- //
- Status = DiffCreateFile(Context, Path, Entry.d_name, &NewFile);
- if (Status != 0) {
- goto GetDirectoryListingEnd;
- }
- Directory->Files[Directory->FileCount] = NewFile;
- Directory->FileCount += 1;
- NewFile = NULL;
- }
- //
- // Sort the files.
- //
- qsort(Directory->Files,
- Directory->FileCount,
- sizeof(PDIFF_FILE),
- DiffCompareFileNames);
- GetDirectoryListingEnd:
- if (DirectoryFile != NULL) {
- closedir(DirectoryFile);
- }
- if (NewFile != NULL) {
- DiffDestroyFile(Context, NewFile);
- }
- if (Status != 0) {
- if (Directory != NULL) {
- DiffDestroyDirectory(Context, Directory);
- Directory = NULL;
- }
- }
- *NewDirectory = Directory;
- return Status;
- }
- BOOL
- DiffIsFileNameExcluded (
- PDIFF_CONTEXT Context,
- PSTR Name
- )
- /*++
- Routine Description:
- This routine determines if a file name should be excluded because it
- matches one of the specified exclusion patterns.
- Arguments:
- Context - Supplies a pointer to the application context.
- Name - Supplies a pointer to the name to compare.
- Return Value:
- TRUE if the file is excluded.
- FALSE if the file is included.
- --*/
- {
- UINTN ExclusionIndex;
- BOOL Match;
- UINTN NameSize;
- PSTR Pattern;
- UINTN PatternSize;
- if (Context->FileExclusionCount == 0) {
- return FALSE;
- }
- NameSize = strlen(Name) + 1;
- for (ExclusionIndex = 0;
- ExclusionIndex < Context->FileExclusionCount;
- ExclusionIndex += 1) {
- Pattern = Context->FileExclusions[ExclusionIndex];
- PatternSize = strlen(Pattern) + 1;
- Match = SwDoesPathPatternMatch(Name, NameSize, Pattern, PatternSize);
- if (Match != FALSE) {
- return TRUE;
- }
- }
- return FALSE;
- }
- VOID
- DiffDestroyDirectory (
- PDIFF_CONTEXT Context,
- PDIFF_DIRECTORY Directory
- )
- /*++
- Routine Description:
- This routine destroys a diff directory structure.
- Arguments:
- Context - Supplies a pointer to the application context.
- Directory - Supplies a pointer to the directory structure to destroy.
- Return Value:
- None.
- --*/
- {
- PDIFF_FILE File;
- UINTN Index;
- for (Index = 0; Index < Directory->FileCount; Index += 1) {
- File = Directory->Files[Index];
- if (File != NULL) {
- DiffDestroyFile(Context, File);
- }
- }
- if (Directory->Files != NULL) {
- free(Directory->Files);
- }
- free(Directory);
- return;
- }
- INT
- DiffCreateFile (
- PDIFF_CONTEXT Context,
- PSTR Directory,
- PSTR Path,
- PDIFF_FILE *File
- )
- /*++
- Routine Description:
- This routine creates a diff file structure based on the given path.
- Arguments:
- Context - Supplies a pointer to the application context.
- Directory - Supplies an optional pointer to a directory prefix of the path.
- Path - Supplies a pointer to the null terminated path of the file to
- create an entry for.
- File - Supplies a pointer where a pointer to the new file structure will be
- returned on success.
- Return Value:
- 0 on success.
- Returns a non-zero error code on failure.
- --*/
- {
- PSTR AppendedPath;
- ULONG AppendedPathSize;
- ULONG DirectorySize;
- PDIFF_FILE NewFile;
- struct stat Stat;
- INT Status;
- AppendedPath = NULL;
- NewFile = malloc(sizeof(DIFF_FILE));
- if (NewFile == NULL) {
- Status = ENOMEM;
- goto CreateFileEnd;
- }
- memset(NewFile, 0, sizeof(DIFF_FILE));
- NewFile->Name = strdup(Path);
- if (NewFile->Name == NULL) {
- Status = ENOMEM;
- goto CreateFileEnd;
- }
- if ((Directory == NULL) && (strcmp(Path, "-") == 0)) {
- NewFile->File = stdin;
- NewFile->ModificationTime = time(NULL);
- NewFile->Type = DiffFileRegularFile;
- } else {
- DirectorySize = 0;
- if (Directory != NULL) {
- DirectorySize = strlen(Directory) + 1;
- }
- Status = SwAppendPath(Directory,
- DirectorySize,
- Path,
- strlen(Path) + 1,
- &AppendedPath,
- &AppendedPathSize);
- if (Status == FALSE) {
- Status = ENOMEM;
- goto CreateFileEnd;
- }
- Status = SwStat(AppendedPath, TRUE, &Stat);
- if (Status != 0) {
- SwPrintError(errno, AppendedPath, "Unable to stat");
- Status = errno;
- goto CreateFileEnd;
- }
- NewFile->ModificationTime = Stat.st_mtime;
- NewFile->Type = DiffGetFileType(Stat.st_mode);
- }
- Status = 0;
- CreateFileEnd:
- if (AppendedPath != NULL) {
- free(AppendedPath);
- }
- if (Status != 0) {
- if (NewFile != NULL) {
- DiffDestroyFile(Context, NewFile);
- NewFile = NULL;
- }
- }
- *File = NewFile;
- return Status;
- }
- VOID
- DiffDestroyFile (
- PDIFF_CONTEXT Context,
- PDIFF_FILE File
- )
- /*++
- Routine Description:
- This routine destroys a diff file structure.
- Arguments:
- Context - Supplies the application context.
- File - Supplies a pointer to the file to destroy.
- Return Value:
- None.
- --*/
- {
- UINTN LineIndex;
- if (File == &(Context->EmptyFile)) {
- File->Name = NULL;
- return;
- }
- if (File->Name != NULL) {
- free(File->Name);
- }
- if ((File->File != NULL) && (File->File != stdin)) {
- fclose(File->File);
- }
- for (LineIndex = 0; LineIndex < File->LineCount; LineIndex += 1) {
- DiffDestroyLine(File->Lines[LineIndex]);
- }
- if (File->Lines != NULL) {
- free(File->Lines);
- }
- free(File);
- return;
- }
- VOID
- DiffDestroyLine (
- PDIFF_LINE Line
- )
- /*++
- Routine Description:
- This routine destroys a diff line structure.
- Arguments:
- Line - Supplies a pointer to the line to destroy.
- Return Value:
- None.
- --*/
- {
- if (Line->Data != NULL) {
- free(Line->Data);
- }
- free(Line);
- return;
- }
- DIFF_FILE_TYPE
- DiffGetFileType (
- mode_t Mode
- )
- /*++
- Routine Description:
- This routine returns the diff file type for the given mode.
- Arguments:
- Mode - Supplies the mode bits coming back from stat.
- Return Value:
- Returns the diff file type.
- --*/
- {
- if (S_ISBLK(Mode)) {
- return DiffFileBlockDevice;
- } else if (S_ISCHR(Mode)) {
- return DiffFileCharacterDevice;
- } else if (S_ISDIR(Mode)) {
- return DiffFileDirectory;
- } else if (S_ISREG(Mode)) {
- return DiffFileRegularFile;
- } else if (S_ISFIFO(Mode)) {
- return DiffFileFifo;
- } else if (S_ISLNK(Mode)) {
- return DiffFileLink;
- } else if (S_ISSOCK(Mode)) {
- return DiffFileSocket;
- }
- return DiffFileUnknown;
- }
- int
- DiffCompareFileNames (
- const void *LeftPointer,
- const void *RightPointer
- )
- /*++
- Routine Description:
- This routine compares the names of two diff files.
- Arguments:
- LeftPointer - Supplies a pointer to a pointer to the left diff file of the
- comparison.
- RightPointer - Supplies a pointer to a pointer to the right diff file of
- the comparison.
- Return Value:
- <0 if the left is less than the right.
- 0 if the two are equal.
- >0 if the left is greater than the right.
- --*/
- {
- PDIFF_FILE LeftFile;
- PDIFF_FILE RightFile;
- LeftFile = *((PDIFF_FILE *)LeftPointer);
- RightFile = *((PDIFF_FILE *)RightPointer);
- return strcmp(LeftFile->Name, RightFile->Name);
- }
- VOID
- DiffPrintCommandLine (
- PDIFF_CONTEXT Context,
- PSTR PathA,
- PDIFF_FILE FileA,
- PSTR PathB,
- PDIFF_FILE FileB
- )
- /*++
- Routine Description:
- This routine prints the diff command line corresponding to the given
- context and files.
- Arguments:
- Context - Supplies a pointer to the application context.
- PathA - Supplies the directory path of the left file.
- FileA - Supplies a pointer to the left file information.
- PathB - Supplies the directory path of the right file.
- FileB - Supplies a pointer to the right file information.
- Return Value:
- None.
- --*/
- {
- printf("diff ");
- if ((Context->Options & DIFF_OPTION_IGNORE_BLANKS) != 0) {
- printf("-b ");
- }
- if ((Context->Options & DIFF_OPTION_RECURSIVE) != 0) {
- printf("-r ");
- }
- if ((Context->Options & DIFF_OPTION_ABSENT_EMPTY) != 0) {
- printf("-N ");
- }
- if (Context->OutputType == DiffOutputUnified) {
- printf("-u %d ", Context->ContextLines);
- } else if (Context->OutputType == DiffOutputEd) {
- printf("-e ");
- } else if (Context->ContextLines != 0) {
- printf("-C %d ");
- }
- printf("%s/%s %s/%s\n", PathA, FileA->Name, PathB, FileB->Name);
- return;
- }
- INT
- DiffLoadFile (
- PDIFF_CONTEXT Context,
- PSTR Directory,
- PDIFF_FILE File
- )
- /*++
- Routine Description:
- This routine loads the contents of a file into lines.
- Arguments:
- Context - Supplies a pointer to the application context.
- Directory - Supplies an optional pointer to a null terminated string
- containing the directory the file is in.
- File - Supplies a pointer to the file to load.
- Return Value:
- 0 on success.
- Returns an error code on failure.
- --*/
- {
- PSTR AppendedPath;
- ULONG AppendedPathSize;
- INT Character;
- ULONG DirectorySize;
- PDIFF_LINE Line;
- UINTN LineArrayCapacity;
- PSTR LineBuffer;
- UINTN LineBufferCapacity;
- UINTN LineBufferSize;
- ULONG LineHash;
- PVOID NewBuffer;
- UINTN NewCapacity;
- INT Status;
- AppendedPath = NULL;
- Line = NULL;
- LineArrayCapacity = 0;
- LineBuffer = NULL;
- LineBufferCapacity = 0;
- LineBufferSize = 0;
- if (File == &(Context->EmptyFile)) {
- Status = 0;
- goto LoadFileEnd;
- }
- if (File->File == NULL) {
- DirectorySize = 0;
- if (Directory != NULL) {
- DirectorySize = strlen(Directory) + 1;
- }
- Status = SwAppendPath(Directory,
- DirectorySize,
- File->Name,
- strlen(File->Name) + 1,
- &AppendedPath,
- &AppendedPathSize);
- if (Status == FALSE) {
- Status = ENOMEM;
- goto LoadFileEnd;
- }
- File->File = fopen(AppendedPath, "r");
- if (File->File == NULL) {
- Status = errno;
- SwPrintError(Status, AppendedPath, "Failed to open");
- goto LoadFileEnd;
- }
- }
- Status = ENOMEM;
- //
- // Loop loading lines.
- //
- LineBufferCapacity = DIFF_INITIAL_LINE_BUFFER;
- LineBuffer = malloc(LineBufferCapacity);
- if (LineBuffer == NULL) {
- goto LoadFileEnd;
- }
- while (TRUE) {
- LineBufferSize = 0;
- LineHash = 0;
- //
- // Loop adding characters to the line.
- //
- while (TRUE) {
- Character = fgetc(File->File);
- if ((Character == EOF) || (Character == '\n')) {
- break;
- }
- if (Character == '\0') {
- File->Binary = TRUE;
- break;
- }
- //
- // Expand the line buffer if it's not big enough to hold the
- // character.
- //
- if (LineBufferSize + 1 >= LineBufferCapacity) {
- NewCapacity = LineBufferCapacity * 2;
- assert(NewCapacity > LineBufferSize + 1);
- NewBuffer = realloc(LineBuffer, NewCapacity);
- if (NewBuffer == NULL) {
- goto LoadFileEnd;
- }
- LineBuffer = NewBuffer;
- LineBufferCapacity = NewCapacity;
- }
- //
- // Add the character to the line buffer.
- //
- LineBuffer[LineBufferSize] = (CHAR)Character;
- LineBufferSize += 1;
- //
- // The poor man's hash is really just the sum of all the
- // characters. Don't count this character as part of the hash if
- // it's blank and blanks are being ignored.
- //
- if (((Context->Options & DIFF_OPTION_IGNORE_BLANKS) == 0) ||
- (isspace(Character) == 0)) {
- LineHash += Character;
- }
- }
- //
- // If the file ended with a newline, that's normal, just stop before
- // adding another empty line. The newline really belongs to the the
- // previous line.
- //
- if ((Character == EOF) && (LineBufferSize == 0)) {
- break;
- }
- //
- // Allocate a line structure.
- //
- Line = malloc(sizeof(DIFF_LINE));
- if (Line == NULL) {
- goto LoadFileEnd;
- }
- memset(Line, 0, sizeof(DIFF_LINE));
- //
- // Allocate a line of exactly the right size and copy it in.
- //
- Line->Data = malloc(LineBufferSize + 1);
- if (Line->Data == NULL) {
- goto LoadFileEnd;
- }
- memcpy(Line->Data, LineBuffer, LineBufferSize);
- Line->Data[LineBufferSize] = '\0';
- Line->Size = LineBufferSize + 1;
- Line->Hash = LineHash;
- //
- // Expand the line array if it is not big enough to hold this line.
- //
- if (File->LineCount >= LineArrayCapacity) {
- NewCapacity = LineArrayCapacity * 2;
- if (NewCapacity == 0) {
- NewCapacity = DIFF_INITIAL_ARRAY_CAPACITY;
- }
- assert(NewCapacity > File->LineCount);
- NewBuffer = realloc(File->Lines, NewCapacity * sizeof(PDIFF_LINE));
- if (NewBuffer == NULL) {
- goto LoadFileEnd;
- }
- File->Lines = NewBuffer;
- LineArrayCapacity = NewCapacity;
- }
- File->Lines[File->LineCount] = Line;
- File->LineCount += 1;
- Line = NULL;
- //
- // If this was the last line, stop.
- //
- if (Character == EOF) {
- File->NoNewlineAtEnd = TRUE;
- break;
- }
- }
- //
- // If the file has a problem, fail.
- //
- if (ferror(File->File) != 0) {
- Status = errno;
- SwPrintError(Status, File->Name, "Failed to read");
- goto LoadFileEnd;
- }
- Status = 0;
- LoadFileEnd:
- if (Line != NULL) {
- DiffDestroyLine(Line);
- }
- if (AppendedPath != NULL) {
- free(AppendedPath);
- }
- if (LineBuffer != NULL) {
- free(LineBuffer);
- }
- return Status;
- }
- //
- // Routines for computing the difference between two files
- //
- INT
- DiffCompareRegularFiles (
- PDIFF_CONTEXT Context,
- PSTR DirectoryA,
- PDIFF_FILE FileA,
- PSTR DirectoryB,
- PDIFF_FILE FileB,
- ULONG RecursionLevel
- )
- /*++
- Routine Description:
- This routine compares two regular files, printing out the difference.
- Arguments:
- Context - Supplies a pointer to the diff application context.
- DirectoryA - Supplies a pointer to the directory prefix for file A.
- FileA - Supplies a pointer to the first file object to compare.
- DirectoryB - Supplies a pointer to the directory prefix for file B.
- FileB - Supplies a pointer to the second file object to compare.
- RecursionLevel - Supplies the level of recursion this function is operating
- in.
- Return Value:
- 0 if the files are equal.
- 1 if there are differences.
- Other values on error.
- --*/
- {
- PINTN DownVector;
- UINTN Maximum;
- INT Status;
- PINTN UpVector;
- UINTN VectorSize;
- //
- // Load up the two files.
- //
- Status = DiffLoadFile(Context, DirectoryA, FileA);
- if (Status != 0) {
- SwPrintError(Status,
- NULL,
- "Failed to load file '%s/%s'",
- DirectoryA,
- FileA->Name);
- goto CompareRegularFilesEnd;
- }
- Status = DiffLoadFile(Context, DirectoryB, FileB);
- if (Status != 0) {
- SwPrintError(Status,
- NULL,
- "Failed to load file '%s/%s'",
- DirectoryB,
- FileB->Name);
- goto CompareRegularFilesEnd;
- }
- //
- // If either file is binary, just perform a binary comparison and report
- // whether or not they're the same.
- //
- if ((FileA->Binary != FALSE) || (FileB->Binary != FALSE)) {
- Status = DiffCompareBinaryFiles(Context, FileA, FileB);
- if (Status == 1) {
- if (RecursionLevel != 0) {
- DiffPrintCommandLine(Context,
- DirectoryA,
- FileA,
- DirectoryB,
- FileB);
- }
- printf("Binary files '");
- if (DirectoryA != NULL) {
- printf("%s/%s", DirectoryA, FileA->Name);
- } else {
- printf("%s", FileA->Name);
- }
- printf("' and '");
- if (DirectoryB != NULL) {
- printf("%s/%s", DirectoryB, FileB->Name);
- } else {
- printf("%s", FileB->Name);
- }
- printf("' differ.\n");
- }
- return Status;
- }
- //
- // Allocate vectors (V in the paper) for computing the shortest middle
- // snake from both directions (forward and reverse). The vectors are
- // indexed by k-line, which is the distance from the diagonal. The maximum
- // possible distance is the sum of the two lengths. This goes in either
- // direction (times two), plus two extra.
- //
- Maximum = FileA->LineCount + FileB->LineCount + 1;
- VectorSize = (2 * Maximum) + 2;
- DownVector = malloc(sizeof(INTN) * VectorSize);
- if (DownVector == NULL) {
- Status = ENOMEM;
- goto CompareRegularFilesEnd;
- }
- UpVector = malloc(sizeof(INTN) * VectorSize);
- if (UpVector == NULL) {
- Status = ENOMEM;
- goto CompareRegularFilesEnd;
- }
- //
- // Find the longest common subsequence, which marks the different lines
- // as modified.
- //
- Status = DiffComputeLongestCommonSubsequence(Context,
- FileA,
- FileB,
- 0,
- FileA->LineCount,
- 0,
- FileB->LineCount,
- DownVector,
- UpVector);
- if (Status != 1) {
- goto CompareRegularFilesEnd;
- }
- if (RecursionLevel != 0) {
- DiffPrintCommandLine(Context, DirectoryA, FileA, DirectoryB, FileB);
- }
- //
- // Print the diffs in the desired format.
- //
- switch (Context->OutputType) {
- case DiffOutputDefault:
- DiffPrintContextDiffs(Context, DirectoryA, FileA, DirectoryB, FileB);
- break;
- case DiffOutputEd:
- DiffPrintEdDiffs(Context, DirectoryA, FileA, DirectoryB, FileB);
- break;
- case DiffOutputUnified:
- DiffPrintUnifiedDiffs(Context, DirectoryA, FileA, DirectoryB, FileB);
- break;
- default:
- assert(FALSE);
- Status = EINVAL;
- goto CompareRegularFilesEnd;
- }
- CompareRegularFilesEnd:
- return Status;
- }
- INT
- DiffCompareBinaryFiles (
- PDIFF_CONTEXT Context,
- PDIFF_FILE FileA,
- PDIFF_FILE FileB
- )
- /*++
- Routine Description:
- This routine compares two binary files for equality. The ignore blanks
- flag is ignored here.
- Arguments:
- Context - Supplies a pointer to the diff application context.
- FileA - Supplies a pointer to the first file.
- FileB - Supplies a pointer to the second file.
- Return Value:
- 0 if the files are equal in the compared region.
- 1 if the files differ somewhere.
- Other codes on error.
- --*/
- {
- INT CharacterA;
- INT CharacterB;
- INT Status;
- //
- // If one file is not there but the other is, they're different. This
- // happens when non-existant files are treated as empty.
- //
- if (((FileA->File == NULL) && (FileB->File != NULL)) ||
- ((FileA->File != NULL) && (FileB->File == NULL))) {
- Status = 1;
- goto CompareBinaryFilesEnd;
- }
- assert((FileA->File != NULL) && (FileB->File != NULL));
- rewind(FileA->File);
- rewind(FileB->File);
- Status = 0;
- while (TRUE) {
- CharacterA = fgetc(FileA->File);
- if (ferror(FileA->File)) {
- Status = errno;
- SwPrintError(Status, FileA->Name, "Failed to read");
- goto CompareBinaryFilesEnd;
- }
- CharacterB = fgetc(FileB->File);
- if (ferror(FileB->File)) {
- Status = errno;
- SwPrintError(Status, FileB->Name, "Failed to read");
- goto CompareBinaryFilesEnd;
- }
- if (CharacterA != CharacterB) {
- Status = 1;
- goto CompareBinaryFilesEnd;
- }
- if (CharacterA == EOF) {
- break;
- }
- }
- CompareBinaryFilesEnd:
- return Status;
- }
- INT
- DiffComputeLongestCommonSubsequence (
- PDIFF_CONTEXT Context,
- PDIFF_FILE FileA,
- PDIFF_FILE FileB,
- INTN LowerA,
- INTN UpperA,
- INTN LowerB,
- INTN UpperB,
- PINTN DownVector,
- PINTN UpVector
- )
- /*++
- Routine Description:
- This routine implements the Myers' algorithm for computing the longest
- common subsequence in linear space (but with recursion). The algorithm is
- a divide-and-conquer algorithm, finding an element of the correct path
- in the middle and then recursing on each of the slightly smaller split
- pieces.
- Arguments:
- Context - Supplies a pointer to the diff application context.
- FileA - Supplies a pointer to the first file.
- FileB - Supplies a pointer to the second file.
- LowerA - Supplies the starting index within file A to work on.
- UpperA - Supplies the ending index within file A to work on, exclusive.
- LowerB - Supplies the starting index within file B to work on.
- UpperB - Supplies the ending index within file B to work on, exclusive.
- DownVector - Supplies the k-indexed vector for computing the shortest
- middle snake from the top down.
- UpVector - Supplies the k-indexed vector for computing the shortest middle
- snake from the bottom up.
- Return Value:
- 0 if the files are equal in the compared region.
- 1 if the files differ somewhere.
- --*/
- {
- INTN MiddleSnakeX;
- INTN MiddleSnakeY;
- INT Status;
- INT TotalStatus;
- Status = 0;
- TotalStatus = 0;
- //
- // As a basic no-brainer, get past any lines at the beginning and the
- // end that match.
- //
- while ((LowerA < UpperA) && (LowerB < UpperB)) {
- Status = DiffCompareLines(Context, FileA, FileB, LowerA, LowerB);
- if (Status != 0) {
- break;
- }
- LowerA += 1;
- LowerB += 1;
- }
- if (Status != 0) {
- TotalStatus = Status;
- }
- while ((LowerA < UpperA) && (LowerB < UpperB)) {
- Status = DiffCompareLines(Context,
- FileA,
- FileB,
- UpperA - 1,
- UpperB - 1);
- if (Status != 0) {
- break;
- }
- UpperA -= 1;
- UpperB -= 1;
- }
- if (Status != 0) {
- TotalStatus = Status;
- }
- //
- // If file A ended, then mark everything in file B as an insertion.
- //
- if (LowerA == UpperA) {
- if (LowerB < UpperB) {
- TotalStatus = 1;
- }
- while (LowerB < UpperB) {
- FileB->Lines[LowerB]->Modified = TRUE;
- LowerB += 1;
- }
- //
- // If file B ended, then mark everything in file A as a deletion.
- //
- } else if (LowerB == UpperB) {
- if (LowerA < UpperA) {
- TotalStatus = 1;
- }
- while (LowerA < UpperA) {
- FileA->Lines[LowerA]->Modified = TRUE;
- LowerA += 1;
- }
- //
- // Run the real crux of the diff algorithm.
- //
- } else {
- //
- // Find the shortest middle snake, which returns a k index into the
- // down vector array. This index contains the x value of the shortest
- // middle snake. The y value is then x - k.
- //
- DiffComputeShortestMiddleSnake(Context,
- FileA,
- FileB,
- LowerA,
- UpperA,
- LowerB,
- UpperB,
- DownVector,
- UpVector,
- &MiddleSnakeX,
- &MiddleSnakeY);
- //
- // Now that a middle value in the longest common subsequence is known,
- // recurse down to find the longest common subsequences of the upper
- // left box and lower right box that remains.
- //
- Status = DiffComputeLongestCommonSubsequence(Context,
- FileA,
- FileB,
- LowerA,
- MiddleSnakeX,
- LowerB,
- MiddleSnakeY,
- DownVector,
- UpVector);
- if (Status != 0) {
- TotalStatus = Status;
- }
- Status = DiffComputeLongestCommonSubsequence(Context,
- FileA,
- FileB,
- MiddleSnakeX,
- UpperA,
- MiddleSnakeY,
- UpperB,
- DownVector,
- UpVector);
- if (Status != 0) {
- TotalStatus = Status;
- }
- }
- return TotalStatus;
- }
- VOID
- DiffComputeShortestMiddleSnake (
- PDIFF_CONTEXT Context,
- PDIFF_FILE FileA,
- PDIFF_FILE FileB,
- INTN LowerA,
- INTN UpperA,
- INTN LowerB,
- INTN UpperB,
- PINTN DownVector,
- PINTN UpVector,
- PINTN MiddleSnakeX,
- PINTN MiddleSnakeY
- )
- /*++
- Routine Description:
- This routine implements the crux of the Myers' algorithm for computing the
- longest common subsequence in linear space, which is computing the shortest
- middle snake. Let's explore the algorithm a bit.
- Introduction
- Computing the difference between two files is equivalent to asking minimum
- set of steps it would take to transform one file into the other. Minimum
- being the tricky part (as in it's not good enough to say "delete all the
- lines from file A and replace them with all the lines from file B"). It
- turns out this problem is simple with the Longest Common Subsequence of the
- two sequences. For example, the longest common subsequence of BANANA and
- CATANA is AANA. Everything not in the longest common subsequence is the
- diff. The Myers' diff paper sets out to solve the problem of finding an
- LCS efficiently.
- Visualization:
- Arrange the two sequences with one along the X axis and one along the Y
- axis. Moving horizontally along the grid represents a single deletion, and
- moving vertically represents an addition. Diagonal moves can be made when
- the sequences are equal. Finding the longest common subsequence is then a
- matter of tracing a path from the top left to the bottom right using as few
- horizontal and vertical moves as possible (and therefore as many diagonals
- as possible). Below is an example trace through the sequences ABCABBA and
- CBABAC.
- A B C A B B A
- --------\ . . . . .
- \
- C . . \ . . . .
- |
- B . . \ . . . .
- \
- A . . . \ . . .
- \
- B . . . . ----\ .
- \
- A . . . . . . \
- |
- C . . . . . . |
- Terms:
- Snake - A snake is a single horizontal or vertical move followed by zero
- or more diagonals. The algorithm is greedy, and does as many diagonal
- moves as possible.
- D Path - A D path is a path with D non-diagonal moves. So a 0 path moves
- only diagonally, a 1 path has one horizontal or vertical move and then
- a bunch of diagonals, etc. The maximum D path would be the maximum X
- plus the maximum Y (deleting all of the old stuff and replacing with
- all the new).
- k Line - This is the tricky one. K-lines run parallel to the 0 path (so
- they're diagonal). The value of k is the distance from the 0 path.
- So making one horizontal move and then travelling diagonally puts one
- on the k=1 line. Making a vertical move lands on the k=-1 line. Two
- horizontal moves is the k=2 line, etc. In fact, k = x - y. K lines
- really are just plots of y = x - k.
- V - V is a vector indexed by k (weird, right). At any given D, the vector V
- saves the coordinate of the farthest reaching snake for that k line.
- For early D values like 1, the only reachable k lines are 1 and -1. As
- D increases, the number of accessible k lines increases by two (going
- all the way right gets to a new k line as does going all the way down).
- The maximum reachable k lines ever are +/-D (going all the way right or
- all the way down), so this is the size of V.
- Quadratic space algorithm:
- A naive algorithm woulc simply explore every possible path from the top
- left to the bottom right (moving only right, down, and diagonally). This
- algorithm isn't too far off from there, it's just greedy and at every step
- it takes as many diagonals as it can. The quadratic space algorithm
- computes the vector V for successive D values until some index of V hits
- the lower right corner. It basically says, "try going both right and down,
- then take as many diagonals as possible. Recurse on the two new endpoints
- created." One interesting thing to note is that computing a new value in V
- can be done by taking its adjacent values (for one k below and one k above,
- ie one move left and one move up), and running snakes off those values. V
- is computed in steps of 2 (since it's not possible to get to a k=2 line
- from a D=1 path). So computing V only requires the previous value of V. If
- all that's desired is the length of the LCS, then just a single V array is
- needed. The only reason all the old V arrays are kept around after each
- step is to trace the path when finished.
- Linear space optimization:
- One observation that should be easy to understand is that the algorithm can
- also be run in reverse; start at the lower right corner and works towards
- the upper left. The resulting path may be different as different snakes are
- investigated. The linear space optimization involves running the algorithm
- forward and reverse at the same time until they overlap somewhere in the
- middle. Only the point at which they collide is needed, so only the last
- V is saved while running forward and reverse (this is what makes it linear).
- The magic is that this single overlapping snake somewhere in the middle is
- part of the optimal solution. So the algorithm found one piece of the
- solution in the middle. Then it's simply a matter of solving the two new
- smaller rectangles created in the upper left and lower right corners
- recursively until the solution is trivial.
- Arguments:
- Context - Supplies a pointer to the diff application context.
- FileA - Supplies a pointer to the first file.
- FileB - Supplies a pointer to the second file.
- LowerA - Supplies the starting index within file A to work on.
- UpperA - Supplies the ending index within file A to work on, exclusive.
- LowerB - Supplies the starting index within file B to work on.
- UpperB - Supplies the ending index within file B to work on, exclusive.
- DownVector - Supplies the k-indexed vector for computing the shortest
- middle snake from the top down.
- UpVector - Supplies the k-indexed vector for computing the shortest middle
- snake from the bottom up.
- MiddleSnakeX - Supplies a pointer where the X coordinate (line index of
- file A) of the shortest middle snake will be returned.
- MiddleSnakeY - Supplies a pointer where the Y coordinate (line index of
- file B) of the shortest middle snake will be returned.
- Return Value:
- None.
- --*/
- {
- INTN Delta;
- BOOL DeltaIsOdd;
- INTN DIndex;
- INTN DownK;
- INTN DownOffset;
- INTN KIndex;
- INTN Maximum;
- INTN MaximumD;
- INTN SnakeX;
- INTN SnakeY;
- INT Status;
- INTN UpK;
- INTN UpOffset;
- //
- // The maximum D value would be going all the way right and all the way
- // down (the files are entirely different).
- //
- Maximum = FileA->LineCount + FileB->LineCount + 1;
- //
- // Compute the K lines to start the forward (down) and reverse (up)
- // searches.
- //
- DownK = LowerA - LowerB;
- UpK = UpperA - UpperB;
- //
- // Delta is the difference in k between the start point and the end point.
- // This is needed to know which indices of the vector to check for overlap.
- //
- Delta = (UpperA - LowerA) - (UpperB - LowerB);
- DeltaIsOdd = FALSE;
- if ((Delta & 0x1) != 0) {
- DeltaIsOdd = TRUE;
- }
- //
- // In the paper, k values can go from -D to D. Use offsets to avoid
- // actually accessing negative array values.
- //
- DownOffset = Maximum - DownK;
- UpOffset = Maximum - UpK;
- //
- // Running the algorithm forward and reverse is guaranteed to cross
- // somewhere before D / 2.
- //
- MaximumD = (((UpperA - LowerA) + (UpperB - LowerB)) / 2) + 1;
- //
- // Initialize the vectors.
- //
- DownVector[DownOffset + DownK + 1] = LowerA;
- UpVector[UpOffset + UpK - 1] = UpperA;
- //
- // Iterate through successive D values until an overlap is found. This is
- // guaranteed to be the shortest path because it has the lowest D value.
- //
- for (DIndex = 0; DIndex <= MaximumD; DIndex += 1) {
- //
- // Run the algorithm forward. Compute all the coordinates for each
- // k line between -D and D in steps of two.
- //
- for (KIndex = DownK - DIndex; KIndex <= DownK + DIndex; KIndex += 2) {
- //
- // Use the better of the two x coordinates of the adjacent k lines,
- // being careful at the edges to avoid comparing against
- // impossible (unreachable) k lines.
- //
- if (KIndex == DownK - DIndex) {
- //
- // Take the same x coordinate as the k line above (meaning go
- // down).
- //
- SnakeX = DownVector[DownOffset + KIndex + 1];
- } else {
- //
- // Take the 1 + the x coordinate below, meaning go right.
- // Switch to going down if it is possible and better (starts
- // further). In a tie, go down.
- //
- SnakeX = DownVector[DownOffset + KIndex - 1] + 1;
- if ((KIndex < DownK + DIndex) &&
- (DownVector[DownOffset + KIndex + 1] >= SnakeX)) {
- SnakeX = DownVector[DownOffset + KIndex + 1];
- }
- }
- SnakeY = SnakeX - KIndex;
- //
- // Take as many diagonals as possible.
- //
- while ((SnakeX < UpperA) && (SnakeY < UpperB)) {
- Status = DiffCompareLines(Context,
- FileA,
- FileB,
- SnakeX,
- SnakeY);
- if (Status != 0) {
- break;
- }
- SnakeX += 1;
- SnakeY += 1;
- }
- DownVector[DownOffset + KIndex] = SnakeX;
- //
- // Check for overlap.
- //
- if ((DeltaIsOdd != FALSE) &&
- (KIndex > UpK - DIndex) && (KIndex < UpK + DIndex)) {
- if (UpVector[UpOffset + KIndex] <=
- DownVector[DownOffset + KIndex]) {
- *MiddleSnakeX = DownVector[DownOffset + KIndex];
- *MiddleSnakeY = *MiddleSnakeX - KIndex;
- return;
- }
- }
- }
- //
- // Run the algorithm in reverse. Compute all the coordinates for each k
- // line between -D and D in steps of two.
- //
- for (KIndex = UpK - DIndex; KIndex <= UpK + DIndex; KIndex += 2) {
- //
- // Decide whether to take the path from the bottom or right.
- //
- if (KIndex == UpK + DIndex) {
- //
- // Take the x position from the lower k line, meaning go up.
- //
- SnakeX = UpVector[UpOffset + KIndex - 1];
- } else {
- //
- // Go right, unless going up is better.
- //
- SnakeX = UpVector[UpOffset + KIndex + 1] - 1;
- if ((KIndex > UpK - DIndex) &&
- (UpVector[UpOffset + KIndex - 1] < SnakeX)) {
- SnakeX = UpVector[UpOffset + KIndex - 1];
- }
- }
- SnakeY = SnakeX - KIndex;
- //
- // Take as many diagonals as possible.
- //
- while ((SnakeX > LowerA) && (SnakeY > LowerB)) {
- Status = DiffCompareLines(Context,
- FileA,
- FileB,
- SnakeX - 1,
- SnakeY - 1);
- if (Status != 0) {
- break;
- }
- SnakeX -= 1;
- SnakeY -= 1;
- }
- UpVector[UpOffset + KIndex] = SnakeX;
- //
- // Check for overlap.
- //
- if ((DeltaIsOdd == FALSE) &&
- (KIndex >= DownK - DIndex) && (KIndex <= DownK + DIndex)) {
- if (UpVector[UpOffset + KIndex] <=
- DownVector[DownOffset + KIndex]) {
- *MiddleSnakeX = DownVector[DownOffset + KIndex];
- *MiddleSnakeY = *MiddleSnakeX - KIndex;
- return;
- }
- }
- }
- }
- //
- // A middle snake should always be found.
- //
- assert(FALSE);
- return;
- }
- INT
- DiffCompareLines (
- PDIFF_CONTEXT Context,
- PDIFF_FILE FileA,
- PDIFF_FILE FileB,
- UINTN LineIndexA,
- UINTN LineIndexB
- )
- /*++
- Routine Description:
- This routine compares two diff lines for equality.
- Arguments:
- Context - Supplies a pointer to the diff application context.
- FileA - Supplies a pointer to the first file.
- FileB - Supplies a pointer to the second file.
- LineIndexA - Supplies the index of the line in the first file.
- LineIndexB - Supplies the index of the line in the second file.
- Return Value:
- 0 if the lines are equal.
- 1 if the linse are not equal.
- --*/
- {
- UINTN IndexA;
- UINTN IndexB;
- PDIFF_LINE LineA;
- PDIFF_LINE LineB;
- assert(LineIndexA < FileA->LineCount);
- assert(LineIndexB < FileB->LineCount);
- LineA = FileA->Lines[LineIndexA];
- LineB = FileB->Lines[LineIndexB];
- //
- // If the hashes are not equal, then the lines are definitely not equal.
- // Easy peasy.
- //
- if (LineA->Hash != LineB->Hash) {
- return 1;
- }
- //
- // If this is the last line for either of them and there's not a newline at
- // the end, then it's probably different.
- //
- if ((LineIndexA == FileA->LineCount - 1) &&
- (FileA->NoNewlineAtEnd != FALSE)) {
- if ((LineIndexB != FileB->LineCount - 1) ||
- (FileB->NoNewlineAtEnd == FALSE)) {
- return 1;
- }
- }
- if ((LineIndexB == FileB->LineCount - 1) &&
- (FileB->NoNewlineAtEnd != FALSE)) {
- if ((LineIndexA != FileA->LineCount - 1) ||
- (FileA->NoNewlineAtEnd == FALSE)) {
- return 1;
- }
- }
- //
- // If not ignoring blanks, then use strcmp, as it's probably a bit more
- // optimized.
- //
- if ((Context->Options & DIFF_OPTION_IGNORE_BLANKS) == 0) {
- //
- // The sizes being different is also a dead giveaway.
- //
- if (LineA->Size != LineB->Size) {
- return 1;
- }
- if (strcmp(LineA->Data, LineB->Data) == 0) {
- return 0;
- }
- return 1;
- }
- //
- // Loop through comparing everything except whitespace.
- //
- IndexA = 0;
- IndexB = 0;
- while (TRUE) {
- while (isspace(LineA->Data[IndexA]) != 0) {
- IndexA += 1;
- }
- while (isspace(LineB->Data[IndexB]) != 0) {
- IndexB += 1;
- }
- if (LineA->Data[IndexA] != LineB->Data[IndexB]) {
- return 1;
- }
- //
- // If either one of them is at the end, it's time to stop the loop.
- //
- if ((IndexA == LineA->Size - 1) || (IndexB == LineB->Size - 1)) {
- //
- // They're not equal if they're not both at the end. But zoom past
- // any whitespace first.
- //
- while (isspace(LineA->Data[IndexA]) != 0) {
- IndexA += 1;
- }
- while (isspace(LineB->Data[IndexB]) != 0) {
- IndexB += 1;
- }
- if ((IndexA != LineA->Size - 1) || (IndexB != LineB->Size - 1)) {
- return 1;
- }
- break;
- }
- IndexA += 1;
- IndexB += 1;
- }
- return 0;
- }
- //
- // Routines for displaying diffs.
- //
- VOID
- DiffPrintEdDiffs (
- PDIFF_CONTEXT Context,
- PSTR DirectoryA,
- PDIFF_FILE FileA,
- PSTR DirectoryB,
- PDIFF_FILE FileB
- )
- /*++
- Routine Description:
- This routine prints the precomputed differences of two files using the ed
- output format.
- Arguments:
- Context - Supplies a pointer to the diff application context.
- DirectoryA - Supplies a pointer to the directory prefix for file A.
- FileA - Supplies a pointer to the first file object to compare.
- DirectoryB - Supplies a pointer to the directory prefix for file B.
- FileB - Supplies a pointer to the second file object to compare.
- Return Value:
- None.
- --*/
- {
- INTN LineA;
- INTN LineB;
- PSTR LineData;
- INTN LineIndex;
- INTN SizeA;
- INTN SizeB;
- assert(Context->ContextLines == 0);
- LineA = 0;
- LineB = 0;
- SizeA = 0;
- SizeB = 0;
- while ((LineA < FileA->LineCount) || (LineB < FileB->LineCount)) {
- DiffFindNextHunk(Context, FileA, FileB, &LineA, &LineB, &SizeA, &SizeB);
- if ((SizeA == 0) && (SizeB == 0)) {
- assert((LineA == FileA->LineCount) && (LineB == FileB->LineCount));
- break;
- }
- //
- // If both files are modified, then it's a change.
- //
- if ((LineA < FileA->LineCount) &&
- (FileA->Lines[LineA]->Modified != FALSE) &&
- (LineB < FileB->LineCount) &&
- (FileB->Lines[LineB]->Modified != FALSE)) {
- assert((SizeA != 0) && (SizeB != 0));
- if (SizeA == 1) {
- printf("%d", LineA + 1);
- } else {
- printf("%d,%d", LineA + 1, LineA + SizeA);
- }
- if (SizeB == 1) {
- printf("c%d\n", LineB + 1);
- } else {
- printf("c%d,%d\n", LineB + 1, LineB + SizeB);
- }
- //
- // If only files A is modified, then it's a deletion.
- //
- } else if ((LineA < FileA->LineCount) &&
- (FileA->Lines[LineA]->Modified != FALSE)) {
- assert((SizeA != 0) && (SizeB == 0));
- if (SizeA == 1) {
- printf("%dd%d\n", LineA + 1, LineB);
- } else {
- printf("%d,%dd%d\n", LineA + 1, LineA + SizeA, LineB);
- }
- //
- // It must be an insertion.
- //
- } else {
- assert((LineB < FileB->LineCount) &&
- (FileB->Lines[LineB]->Modified != FALSE));
- assert((SizeB != 0) && (SizeA == 0));
- if (SizeB == 1) {
- printf("%da%d\n", LineA, LineB + 1);
- } else {
- printf("%da%d,%d\n", LineA, LineB + 1, LineB + SizeB);
- }
- }
- //
- // Print the contents.
- //
- for (LineIndex = 0; LineIndex < SizeA; LineIndex += 1) {
- assert(FileA->Lines[LineA + LineIndex]->Modified != FALSE);
- LineData = FileA->Lines[LineA + LineIndex]->Data;
- if ((Context->Options & DIFF_OPTION_COLOR) != 0) {
- SwPrintInColor(ConsoleColorDefault,
- DIFF_DELETION_COLOR,
- "< %s\n",
- LineData);
- } else {
- printf("< %s\n", LineData);
- }
- }
- if ((SizeA != 0) &&
- (LineA + SizeA == FileA->LineCount) &&
- (FileA->NoNewlineAtEnd != FALSE)) {
- printf("\\ No newline at end of file\n");
- }
- if ((SizeA != 0) && (SizeB != 0)) {
- printf("---\n");
- }
- for (LineIndex = 0; LineIndex < SizeB; LineIndex += 1) {
- assert(FileB->Lines[LineB + LineIndex]->Modified != FALSE);
- LineData = FileB->Lines[LineB + LineIndex]->Data;
- if ((Context->Options & DIFF_OPTION_COLOR) != 0) {
- SwPrintInColor(ConsoleColorDefault,
- DIFF_INSERTION_COLOR,
- "> %s\n",
- LineData);
- } else {
- printf("> %s\n", LineData);
- }
- }
- if ((SizeB != 0) &&
- (LineB + SizeB == FileB->LineCount) &&
- (FileB->NoNewlineAtEnd != FALSE)) {
- printf("\\ No newline at end of file\n");
- }
- //
- // Advance beyond this hunk.
- //
- LineA += SizeA;
- LineB += SizeB;
- }
- return;
- }
- VOID
- DiffPrintContextDiffs (
- PDIFF_CONTEXT Context,
- PSTR DirectoryA,
- PDIFF_FILE FileA,
- PSTR DirectoryB,
- PDIFF_FILE FileB
- )
- /*++
- Routine Description:
- This routine prints the precomputed differences of two files using the
- context output format.
- Arguments:
- Context - Supplies a pointer to the diff application context.
- DirectoryA - Supplies a pointer to the directory prefix for file A.
- FileA - Supplies a pointer to the first file object to compare.
- DirectoryB - Supplies a pointer to the directory prefix for file B.
- FileB - Supplies a pointer to the second file object to compare.
- Return Value:
- None.
- --*/
- {
- BOOL ChangesPresent;
- INTN IndexA;
- INTN IndexB;
- INTN LineA;
- INTN LineB;
- PSTR LineData;
- CHAR Marker;
- struct tm *ModificationTime;
- INTN SizeA;
- INTN SizeB;
- CHAR Timestamp[CONTEXT_DIFF_TIMESTAMP_SIZE];
- //
- // Print the heading.
- //
- ModificationTime = localtime(&(FileA->ModificationTime));
- strftime(Timestamp,
- sizeof(Timestamp),
- CONTEXT_DIFF_TIMESTAMP_FORMAT,
- ModificationTime);
- if (DirectoryA != NULL) {
- printf("*** %s/%s\t%s\n", DirectoryA, FileA->Name, Timestamp);
- } else {
- printf("*** %s\t%s\n", FileA->Name, Timestamp);
- }
- ModificationTime = localtime(&(FileB->ModificationTime));
- strftime(Timestamp,
- sizeof(Timestamp),
- CONTEXT_DIFF_TIMESTAMP_FORMAT,
- ModificationTime);
- if (DirectoryB != NULL) {
- printf("--- %s/%s\t%s\n", DirectoryB, FileB->Name, Timestamp);
- } else {
- printf("--- %s\t%s\n", FileB->Name, Timestamp);
- }
- LineA = 0;
- LineB = 0;
- SizeA = 0;
- SizeB = 0;
- while ((LineA < FileA->LineCount) || (LineB < FileB->LineCount)) {
- DiffFindNextHunk(Context, FileA, FileB, &LineA, &LineB, &SizeA, &SizeB);
- if ((SizeA == 0) && (SizeB == 0)) {
- assert((LineA == FileA->LineCount) && (LineB == FileB->LineCount));
- break;
- }
- printf("***************\n");
- //
- // Print the top half of the change, the deletions with context.
- //
- if (SizeA <= 1) {
- printf("*** %d ***\n", LineA + SizeA);
- } else {
- printf("*** %d,%d ***\n", LineA + 1, LineA + SizeA);
- }
- ChangesPresent = FALSE;
- for (IndexA = LineA; IndexA < LineA + SizeA; IndexA += 1) {
- if (FileA->Lines[IndexA]->Modified != FALSE) {
- ChangesPresent = TRUE;
- break;
- }
- }
- if (ChangesPresent != FALSE) {
- IndexB = LineB;
- for (IndexA = LineA; IndexA < LineA + SizeA; IndexA += 1) {
- LineData = FileA->Lines[IndexA]->Data;
- //
- // If the first file is not modified, it's context.
- //
- if (FileA->Lines[IndexA]->Modified == FALSE) {
- Marker = ' ';
- if ((IndexB < FileB->LineCount) &&
- (FileB->Lines[IndexB]->Modified == FALSE)) {
- IndexB += 1;
- }
- //
- // The line in file A is modified, so it's a deletion. Look at
- // file B to figure out if it's a change or pure deletion.
- //
- } else {
- if ((IndexB >= FileB->LineCount) ||
- (FileB->Lines[IndexB]->Modified == FALSE)) {
- Marker = '-';
- if (IndexB < FileB->LineCount) {
- IndexB += 1;
- }
- } else {
- Marker = '!';
- IndexB += 1;
- }
- }
- if (((Context->Options & DIFF_OPTION_COLOR) != 0) &&
- (Marker != ' ')) {
- SwPrintInColor(ConsoleColorDefault,
- DIFF_DELETION_COLOR,
- "%c %s\n",
- Marker,
- LineData);
- } else {
- printf("%c %s\n", Marker, LineData);
- }
- }
- if ((SizeA != 0) &&
- (IndexA == FileA->LineCount) &&
- (FileA->NoNewlineAtEnd != FALSE) &&
- (Marker != ' ')) {
- printf("\\ No newline at end of file\n");
- }
- }
- //
- // Print the bottom half of the change, the additions with context.
- //
- if (SizeB <= 1) {
- printf("--- %d ---\n", LineB + SizeB);
- } else {
- printf("--- %d,%d ---\n", LineB + 1, LineB + SizeB);
- }
- ChangesPresent = FALSE;
- for (IndexB = LineB; IndexB < LineB + SizeB; IndexB += 1) {
- if (FileB->Lines[IndexB]->Modified != FALSE) {
- ChangesPresent = TRUE;
- break;
- }
- }
- if (ChangesPresent != FALSE) {
- IndexA = LineA;
- for (IndexB = LineB; IndexB < LineB + SizeB; IndexB += 1) {
- LineData = FileB->Lines[IndexB]->Data;
- //
- // If the first file is not modified, it's context.
- //
- if (FileB->Lines[IndexB]->Modified == FALSE) {
- Marker = ' ';
- if ((IndexA < FileA->LineCount) &&
- (FileA->Lines[IndexA]->Modified == FALSE)) {
- IndexA += 1;
- }
- //
- // The line in file B is modified, so it's an insertion. Look at
- // file A to figure out if it's a change or pure insertion.
- //
- } else {
- if ((IndexA >= FileA->LineCount) ||
- (FileA->Lines[IndexA]->Modified == FALSE)) {
- Marker = '+';
- if (IndexA < FileA->LineCount) {
- IndexA += 1;
- }
- } else {
- Marker = '!';
- IndexA += 1;
- }
- }
- if (((Context->Options & DIFF_OPTION_COLOR) != 0) &&
- (Marker != ' ')) {
- SwPrintInColor(ConsoleColorDefault,
- DIFF_INSERTION_COLOR,
- "%c %s\n",
- Marker,
- LineData);
- } else {
- printf("%c %s\n", Marker, LineData);
- }
- }
- if ((SizeB != 0) &&
- (IndexB == FileB->LineCount) &&
- (FileB->NoNewlineAtEnd != FALSE) &&
- (Marker != ' ')) {
- printf("\\ No newline at end of file\n");
- }
- }
- //
- // Advance beyond this hunk.
- //
- LineA += SizeA;
- LineB += SizeB;
- }
- return;
- }
- VOID
- DiffPrintUnifiedDiffs (
- PDIFF_CONTEXT Context,
- PSTR DirectoryA,
- PDIFF_FILE FileA,
- PSTR DirectoryB,
- PDIFF_FILE FileB
- )
- /*++
- Routine Description:
- This routine prints the precomputed differences of two files using the
- unified output format.
- Arguments:
- Context - Supplies a pointer to the diff application context.
- DirectoryA - Supplies a pointer to the directory prefix for file A.
- FileA - Supplies a pointer to the first file object to compare.
- DirectoryB - Supplies a pointer to the directory prefix for file B.
- FileB - Supplies a pointer to the second file object to compare.
- Return Value:
- None.
- --*/
- {
- INTN IndexA;
- INTN IndexB;
- INTN LineA;
- INTN LineB;
- PSTR LineData;
- struct tm *ModificationTime;
- INTN SizeA;
- INTN SizeB;
- CHAR Timestamp[CONTEXT_DIFF_TIMESTAMP_SIZE];
- //
- // Print the heading.
- //
- ModificationTime = localtime(&(FileA->ModificationTime));
- strftime(Timestamp,
- sizeof(Timestamp),
- CONTEXT_DIFF_TIMESTAMP_FORMAT,
- ModificationTime);
- if (DirectoryA != NULL) {
- printf("--- %s/%s\t%s\n", DirectoryA, FileA->Name, Timestamp);
- } else {
- printf("--- %s\t%s\n", FileA->Name, Timestamp);
- }
- ModificationTime = localtime(&(FileB->ModificationTime));
- strftime(Timestamp,
- sizeof(Timestamp),
- CONTEXT_DIFF_TIMESTAMP_FORMAT,
- ModificationTime);
- if (DirectoryB != NULL) {
- printf("+++ %s/%s\t%s\n", DirectoryB, FileB->Name, Timestamp);
- } else {
- printf("+++ %s\t%s\n", FileB->Name, Timestamp);
- }
- LineA = 0;
- LineB = 0;
- SizeA = 0;
- SizeB = 0;
- while ((LineA < FileA->LineCount) || (LineB < FileB->LineCount)) {
- DiffFindNextHunk(Context, FileA, FileB, &LineA, &LineB, &SizeA, &SizeB);
- if ((SizeA == 0) && (SizeB == 0)) {
- assert((LineA == FileA->LineCount) && (LineB == FileB->LineCount));
- break;
- }
- //
- // Print the hunk marker.
- //
- if (SizeA == 0) {
- printf("@@ -%d,0 ", LineA);
- } else if (SizeA == 1) {
- printf("@@ -%d ", LineA + SizeA);
- } else {
- printf("@@ -%d,%d ", LineA + 1, SizeA);
- }
- if (SizeB == 0) {
- printf("+%d,0 @@\n", LineB);
- } else if (SizeB == 1) {
- printf("+%d @@\n", LineB + SizeB);
- } else {
- printf("+%d,%d @@\n", LineB + 1, SizeB);
- }
- IndexA = LineA;
- IndexB = LineB;
- while ((IndexA < LineA + SizeA) || (IndexB < LineB + SizeB)) {
- //
- // Print any context lines.
- //
- while ((IndexA < LineA + SizeA) &&
- (FileA->Lines[IndexA]->Modified == FALSE) &&
- (IndexB < LineB + SizeB) &&
- (FileB->Lines[IndexB]->Modified == FALSE)) {
- LineData = FileA->Lines[IndexA]->Data;
- IndexA += 1;
- IndexB += 1;
- printf(" %s\n", LineData);
- }
- //
- // Print all deletion lines together.
- //
- while ((IndexA < LineA + SizeA) &&
- (FileA->Lines[IndexA]->Modified != FALSE)) {
- LineData = FileA->Lines[IndexA]->Data;
- IndexA += 1;
- if ((Context->Options & DIFF_OPTION_COLOR) != 0) {
- SwPrintInColor(ConsoleColorDefault,
- DIFF_DELETION_COLOR,
- "-%s\n",
- LineData);
- } else {
- printf("-%s\n", LineData);
- }
- }
- if ((SizeA != 0) &&
- (IndexA == FileA->LineCount) &&
- (FileA->NoNewlineAtEnd != FALSE)) {
- printf("\\ No newline at end of file\n");
- }
- //
- // Print all insertion lines together.
- //
- while ((IndexB < LineB + SizeB) &&
- (FileB->Lines[IndexB]->Modified != FALSE)) {
- LineData = FileB->Lines[IndexB]->Data;
- IndexB += 1;
- if ((Context->Options & DIFF_OPTION_COLOR) != 0) {
- SwPrintInColor(ConsoleColorDefault,
- DIFF_INSERTION_COLOR,
- "+%s\n",
- LineData);
- } else {
- printf("+%s\n", LineData);
- }
- }
- if ((SizeB != 0) &&
- (IndexB == FileB->LineCount) &&
- (FileB->NoNewlineAtEnd != FALSE)) {
- printf("\\ No newline at end of file\n");
- }
- }
- //
- // Advance beyond this hunk.
- //
- LineA += SizeA;
- LineB += SizeB;
- }
- return;
- }
- VOID
- DiffFindNextHunk (
- PDIFF_CONTEXT Context,
- PDIFF_FILE FileA,
- PDIFF_FILE FileB,
- PINTN LineA,
- PINTN LineB,
- PINTN SizeA,
- PINTN SizeB
- )
- /*++
- Routine Description:
- This routine finds the next diff hunk of two precomputed diffs.
- Arguments:
- Context - Supplies a pointer to the diff application context.
- FileA - Supplies a pointer to the first file object.
- FileB - Supplies a pointer to the second file object.
- LineA - Supplies a pointer that on input contains the zero-based line of
- A to start from. On output, this will contain the line index of the
- next hunk.
- LineB - Supplies a pointer that on input contains the zero-based line of
- B to start from. On output, this will contain the line index of the
- next hunk.
- SizeA - Supplies a pointer where the size of the next hunk for A in lines
- will be returned.
- SizeB - Supplies a pointer where the size of the next hunk for B in lines
- will be returned.
- Return Value:
- None.
- --*/
- {
- INTN ContextA;
- INTN ContextB;
- INTN ContextLines;
- INTN OriginalLineA;
- INTN OriginalLineB;
- *SizeA = 0;
- *SizeB = 0;
- OriginalLineA = *LineA;
- OriginalLineB = *LineB;
- while ((*LineA < FileA->LineCount) || (*LineB < FileB->LineCount)) {
- //
- // If file A ended, then the rest of file B is a hunk.
- //
- if (*LineA == FileA->LineCount) {
- *SizeB = FileB->LineCount - *LineB;
- return;
- //
- // If file B ended, then the rest of file A is a hunk.
- //
- } else if (*LineB == FileB->LineCount) {
- *SizeA = FileA->LineCount - *LineA;
- return;
- //
- // If either line is modified, then a hunk has been found.
- //
- } else if ((FileA->Lines[*LineA]->Modified != FALSE) ||
- (FileB->Lines[*LineB]->Modified != FALSE)) {
- break;
- }
- *LineA += 1;
- *LineB += 1;
- }
- //
- // Loop advancing past the diff and lines of context.
- //
- while (TRUE) {
- //
- // Advance through modified lines in A.
- //
- while ((*LineA + *SizeA < FileA->LineCount) &&
- (FileA->Lines[*LineA + *SizeA]->Modified != FALSE)) {
- *SizeA += 1;
- }
- //
- // Advance through modified lines in B.
- //
- while ((*LineB + *SizeB < FileB->LineCount) &&
- (FileB->Lines[*LineB + *SizeB]->Modified != FALSE)) {
- *SizeB += 1;
- }
- //
- // Now try to advance through the lines of context as well. If a
- // modification is found in either line, then the next hunk blurs into
- // this one. Look past two sets of context lines because any fewer and
- // the next hunk will bleed back up into this one.
- //
- ContextLines = 0;
- ContextA = 0;
- ContextB = 0;
- while (ContextLines < (Context->ContextLines * 2) + 1) {
- if ((*LineA + *SizeA + ContextA < FileA->LineCount) &&
- (FileA->Lines[*LineA + *SizeA + ContextA]->Modified != FALSE)) {
- break;
- }
- if ((*LineB + *SizeB + ContextB < FileB->LineCount) &&
- (FileB->Lines[*LineB + *SizeB + ContextB]->Modified != FALSE)) {
- break;
- }
- if (*LineA + *SizeA + ContextA < FileA->LineCount) {
- ContextA += 1;
- }
- if (*LineB + *SizeB + ContextB < FileB->LineCount) {
- ContextB += 1;
- }
- ContextLines += 1;
- }
- assert(*LineA + *SizeA + ContextA <= FileA->LineCount);
- assert(*LineB + *SizeB + ContextB <= FileB->LineCount);
- if ((ContextLines == (Context->ContextLines * 2) + 1) ||
- ((ContextA == 0) && (ContextB == 0))) {
- //
- // Add up to the requested amount of context lines to the size.
- //
- if (ContextA > Context->ContextLines) {
- ContextA = Context->ContextLines;
- }
- if (ContextB > Context->ContextLines) {
- ContextB = Context->ContextLines;
- }
- *SizeA += ContextA;
- *SizeB += ContextB;
- break;
- //
- // Otherwise, add all the context lines consumed to the hunk.
- //
- } else {
- *SizeA += ContextA;
- *SizeB += ContextB;
- }
- }
- //
- // If both hunks are of size zero, then there were no more diffs.
- //
- if ((*SizeA == 0) && (*SizeB == 0)) {
- return;
- }
- //
- // Also back up to provide the context lines at the beginning.
- //
- ContextLines = Context->ContextLines;
- if (ContextLines > *LineA) {
- ContextLines = *LineA;
- }
- if (*LineA - ContextLines < OriginalLineA) {
- ContextLines = *LineA - OriginalLineA;
- }
- *LineA -= ContextLines;
- *SizeA += ContextLines;
- ContextLines = Context->ContextLines;
- if (ContextLines > *LineB) {
- ContextLines = *LineB;
- }
- if (*LineB - ContextLines < OriginalLineB) {
- ContextLines = *LineB - OriginalLineB;
- }
- *LineB -= ContextLines;
- *SizeB += ContextLines;
- return;
- }
|