1
0

diff.c 96 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. diff.c
  5. Abstract:
  6. This module implements the diff utility.
  7. Author:
  8. Evan Green 3-Jul-2013
  9. Environment:
  10. POSIX
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include <minoca/lib/types.h>
  16. #include <assert.h>
  17. #include <ctype.h>
  18. #include <errno.h>
  19. #include <getopt.h>
  20. #include <libgen.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <unistd.h>
  24. #include "swlib.h"
  25. //
  26. // ---------------------------------------------------------------- Definitions
  27. //
  28. #define DIFF_VERSION_MAJOR 1
  29. #define DIFF_VERSION_MINOR 0
  30. #define DIFF_USAGE \
  31. "usage: diff [-c | -e | -f | -C n][-br] file1 file2\n" \
  32. "The diff utility compares the contents of two paths and reports the \n" \
  33. "differences to standard out. Options are:\n" \
  34. " -b, --ignore-space-change -- Ignore whitespace changes.\n" \
  35. " -c -- Produce three lines of context around every diff.\n" \
  36. " -C, --context=n -- Produce n lines of context around every diff, \n" \
  37. " where n is a decimal integer.\n" \
  38. " --color=value -- Turn on or off color printing. Valid values are \n" \
  39. " always, never, and auto." \
  40. " -e, --ed -- Output an ed script.\n" \
  41. " -N, --new-file -- Treat absent files as empty.\n" \
  42. " -r, --recursive -- Recursively compare any subdirectories found.\n" \
  43. " -u, --unified=n -- Produce a unified diff format, with n lines of \n" \
  44. " context.\n" \
  45. " -x, --exclude=pattern -- exclude file that match the given pattern.\n" \
  46. " --help -- Show this help text and exit.\n" \
  47. " --version -- Show the application version information and exit.\n" \
  48. #define DIFF_OPTIONS_STRING "bcC:eNru::x:"
  49. //
  50. // Define the diff option flags.
  51. //
  52. #define DIFF_OPTION_IGNORE_BLANKS 0x00000001
  53. #define DIFF_OPTION_RECURSIVE 0x00000002
  54. #define DIFF_OPTION_COLOR 0x00000004
  55. #define DIFF_OPTION_ABSENT_EMPTY 0x00000008
  56. //
  57. // Define the default number of context lines when they're asked for.
  58. //
  59. #define DIFF_DEFAULT_CONTEXT_LINES 3
  60. //
  61. // Define the maximum depth of directories that diff will crawl down.
  62. //
  63. #define DIFF_MAX_RECURSION_DEPTH 100
  64. //
  65. // Define the initial size of a directory listing array in elements.
  66. //
  67. #define DIFF_INITIAL_ARRAY_CAPACITY 32
  68. //
  69. // Define the initial size of the line buffer in bytes.
  70. //
  71. #define DIFF_INITIAL_LINE_BUFFER 256
  72. //
  73. // Define the colors used for insertion and deletion.
  74. //
  75. #define DIFF_INSERTION_COLOR ConsoleColorDarkGreen
  76. #define DIFF_DELETION_COLOR ConsoleColorDarkRed
  77. //
  78. // Define the time format string used by context diffs.
  79. //
  80. #define CONTEXT_DIFF_TIMESTAMP_FORMAT "%a %b %d %H:%M:%S %Y"
  81. #define CONTEXT_DIFF_TIMESTAMP_SIZE 26
  82. //
  83. // ------------------------------------------------------ Data Type Definitions
  84. //
  85. typedef enum _DIFF_OUTPUT_TYPE {
  86. DiffOutputInvalid,
  87. DiffOutputDefault,
  88. DiffOutputEd,
  89. DiffOutputUnified,
  90. } DIFF_OUTPUT_TYPE, *PDIFF_OUTPUT_TYPE;
  91. typedef enum _DIFF_FILE_TYPE {
  92. DiffFileUnknown,
  93. DiffFileBlockDevice,
  94. DiffFileCharacterDevice,
  95. DiffFileDirectory,
  96. DiffFileRegularFile,
  97. DiffFileFifo,
  98. DiffFileLink,
  99. DiffFileSocket,
  100. DiffFileTypeCount
  101. } DIFF_FILE_TYPE, *PDIFF_FILE_TYPE;
  102. /*++
  103. Structure Description:
  104. This structure defines a diff input line.
  105. Members:
  106. Data - Stores a pointer to the allocated null terminated line.
  107. Size - Stores the size of the line data in bytes.
  108. Hash - Stores the hash of the file, which is used to quickly determine if
  109. two files are not equal (but not necessarily if they're equal).
  110. Modified - Stores a boolean indicating that this line is part of the diff.
  111. --*/
  112. typedef struct _DIFF_LINE {
  113. PSTR Data;
  114. UINTN Size;
  115. ULONG Hash;
  116. BOOL Modified;
  117. } DIFF_LINE, *PDIFF_LINE;
  118. /*++
  119. Structure Description:
  120. This structure defines an input file of diff.
  121. Members:
  122. Name - Stores a pointer to a string containing the name of the file.
  123. ModificationTime - Stores the files modification date.
  124. Type - Stores the type of file this thing is.
  125. Binary - Stores a boolean indicating if this file is binary data or text.
  126. NoNewlineAtEnd - Stores a boolean indicating if there is no newline at the
  127. end of the file.
  128. File - Stores a pointer to the file input handle.
  129. LineCount - Stores the number of lines in the file.
  130. Lines - Stores an array of pointers to the lines of the file.
  131. --*/
  132. typedef struct _DIFF_FILE {
  133. PSTR Name;
  134. time_t ModificationTime;
  135. DIFF_FILE_TYPE Type;
  136. BOOL Binary;
  137. BOOL NoNewlineAtEnd;
  138. FILE *File;
  139. INTN LineCount;
  140. PDIFF_LINE *Lines;
  141. } DIFF_FILE, *PDIFF_FILE;
  142. /*++
  143. Structure Description:
  144. This structure defines the contents of a directory.
  145. Members:
  146. FileCount - Stores the number of files in the directory.
  147. ArrayCapacity - Stores the size of the array allocation in elements.
  148. Files - Stores the array of pointers to diff files.
  149. --*/
  150. typedef struct _DIFF_DIRECTORY {
  151. UINTN FileCount;
  152. UINTN ArrayCapacity;
  153. PDIFF_FILE *Files;
  154. } DIFF_DIRECTORY, *PDIFF_DIRECTORY;
  155. /*++
  156. Structure Description:
  157. This structure defines the context for an instantiation of the diff
  158. application.
  159. Members:
  160. Options - Stores the bitfield of diff options.
  161. OutputType - Stores the type of output to produce.
  162. ContextLines - Stores the number of lines of context to produce around
  163. each diff.
  164. EmptyFile - Stores a pointer to a dummy file that can be used for
  165. comparisons.
  166. FileExclusions - Stores a pointer to an array of string containing patterns
  167. of file names to exclude.
  168. FileExclusionCount - Stores the number of elements in the file exclusions
  169. pattern.
  170. --*/
  171. typedef struct _DIFF_CONTEXT {
  172. ULONG Options;
  173. DIFF_OUTPUT_TYPE OutputType;
  174. ULONG ContextLines;
  175. DIFF_FILE EmptyFile;
  176. PSTR *FileExclusions;
  177. UINTN FileExclusionCount;
  178. } DIFF_CONTEXT, *PDIFF_CONTEXT;
  179. //
  180. // ----------------------------------------------- Internal Function Prototypes
  181. //
  182. INT
  183. DiffComparePaths (
  184. PDIFF_CONTEXT Context,
  185. PSTR PathA,
  186. PSTR PathB
  187. );
  188. INT
  189. DiffCompareFiles (
  190. PDIFF_CONTEXT Context,
  191. PSTR DirectoryA,
  192. PDIFF_FILE FileA,
  193. PSTR DirectoryB,
  194. PDIFF_FILE FileB,
  195. ULONG RecursionLevel
  196. );
  197. INT
  198. DiffCompareDirectories (
  199. PDIFF_CONTEXT Context,
  200. PSTR PathA,
  201. PSTR PathB,
  202. ULONG RecursionLevel
  203. );
  204. INT
  205. DiffGetDirectoryListing (
  206. PDIFF_CONTEXT Context,
  207. PSTR Path,
  208. PDIFF_DIRECTORY *NewDirectory
  209. );
  210. BOOL
  211. DiffIsFileNameExcluded (
  212. PDIFF_CONTEXT Context,
  213. PSTR Name
  214. );
  215. VOID
  216. DiffDestroyDirectory (
  217. PDIFF_CONTEXT Context,
  218. PDIFF_DIRECTORY Directory
  219. );
  220. INT
  221. DiffCreateFile (
  222. PDIFF_CONTEXT Context,
  223. PSTR Directory,
  224. PSTR Path,
  225. PDIFF_FILE *File
  226. );
  227. VOID
  228. DiffDestroyFile (
  229. PDIFF_CONTEXT Context,
  230. PDIFF_FILE File
  231. );
  232. VOID
  233. DiffDestroyLine (
  234. PDIFF_LINE Line
  235. );
  236. DIFF_FILE_TYPE
  237. DiffGetFileType (
  238. mode_t Mode
  239. );
  240. int
  241. DiffCompareFileNames (
  242. const void *LeftPointer,
  243. const void *RightPointer
  244. );
  245. VOID
  246. DiffPrintCommandLine (
  247. PDIFF_CONTEXT Context,
  248. PSTR PathA,
  249. PDIFF_FILE FileA,
  250. PSTR PathB,
  251. PDIFF_FILE FileB
  252. );
  253. INT
  254. DiffLoadFile (
  255. PDIFF_CONTEXT Context,
  256. PSTR Directory,
  257. PDIFF_FILE File
  258. );
  259. INT
  260. DiffCompareRegularFiles (
  261. PDIFF_CONTEXT Context,
  262. PSTR DirectoryA,
  263. PDIFF_FILE FileA,
  264. PSTR DirectoryB,
  265. PDIFF_FILE FileB,
  266. ULONG RecursionLevel
  267. );
  268. INT
  269. DiffCompareBinaryFiles (
  270. PDIFF_CONTEXT Context,
  271. PDIFF_FILE FileA,
  272. PDIFF_FILE FileB
  273. );
  274. INT
  275. DiffComputeLongestCommonSubsequence (
  276. PDIFF_CONTEXT Context,
  277. PDIFF_FILE FileA,
  278. PDIFF_FILE FileB,
  279. INTN LowerA,
  280. INTN UpperA,
  281. INTN LowerB,
  282. INTN UpperB,
  283. PINTN DownVector,
  284. PINTN UpVector
  285. );
  286. VOID
  287. DiffComputeShortestMiddleSnake (
  288. PDIFF_CONTEXT Context,
  289. PDIFF_FILE FileA,
  290. PDIFF_FILE FileB,
  291. INTN LowerA,
  292. INTN UpperA,
  293. INTN LowerB,
  294. INTN UpperB,
  295. PINTN DownVector,
  296. PINTN UpVector,
  297. PINTN MiddleSnakeX,
  298. PINTN MiddleSnakeY
  299. );
  300. INT
  301. DiffCompareLines (
  302. PDIFF_CONTEXT Context,
  303. PDIFF_FILE FileA,
  304. PDIFF_FILE FileB,
  305. UINTN LineIndexA,
  306. UINTN LineIndexB
  307. );
  308. VOID
  309. DiffPrintEdDiffs (
  310. PDIFF_CONTEXT Context,
  311. PSTR DirectoryA,
  312. PDIFF_FILE FileA,
  313. PSTR DirectoryB,
  314. PDIFF_FILE FileB
  315. );
  316. VOID
  317. DiffPrintContextDiffs (
  318. PDIFF_CONTEXT Context,
  319. PSTR DirectoryA,
  320. PDIFF_FILE FileA,
  321. PSTR DirectoryB,
  322. PDIFF_FILE FileB
  323. );
  324. VOID
  325. DiffPrintUnifiedDiffs (
  326. PDIFF_CONTEXT Context,
  327. PSTR DirectoryA,
  328. PDIFF_FILE FileA,
  329. PSTR DirectoryB,
  330. PDIFF_FILE FileB
  331. );
  332. VOID
  333. DiffFindNextHunk (
  334. PDIFF_CONTEXT Context,
  335. PDIFF_FILE FileA,
  336. PDIFF_FILE FileB,
  337. PINTN LineA,
  338. PINTN LineB,
  339. PINTN SizeA,
  340. PINTN SizeB
  341. );
  342. //
  343. // -------------------------------------------------------------------- Globals
  344. //
  345. struct option DiffLongOptions[] = {
  346. {"ignore-space-change", no_argument, 0, 'b'},
  347. {"context", optional_argument, 0, 'C'},
  348. {"color", required_argument, 0, '1'},
  349. {"ed", no_argument, 0, 'e'},
  350. {"new-file", no_argument, 0, 'N'},
  351. {"recursive", no_argument, 0, 'r'},
  352. {"unified", optional_argument, 0, 'u'},
  353. {"help", no_argument, 0, 'h'},
  354. {"version", no_argument, 0, 'V'},
  355. {NULL, 0, 0, 0},
  356. };
  357. PSTR DiffFileTypeNames[DiffFileTypeCount] = {
  358. "funky thing",
  359. "block device",
  360. "character device",
  361. "directory",
  362. "file",
  363. "fifo",
  364. "symbolic link",
  365. "socket",
  366. };
  367. //
  368. // ------------------------------------------------------------------ Functions
  369. //
  370. INT
  371. DiffMain (
  372. INT ArgumentCount,
  373. CHAR **Arguments
  374. )
  375. /*++
  376. Routine Description:
  377. This routine is the main entry point for the diff utility.
  378. Arguments:
  379. ArgumentCount - Supplies the number of command line arguments the program
  380. was invoked with.
  381. Arguments - Supplies a tokenized array of command line arguments.
  382. Return Value:
  383. Returns an integer exit code. 0 for success, nonzero otherwise.
  384. --*/
  385. {
  386. PSTR AfterScan;
  387. ULONG ArgumentIndex;
  388. DIFF_CONTEXT Context;
  389. BOOL ContextLinesSpecified;
  390. PVOID NewBuffer;
  391. INT Option;
  392. BOOL OutputIsTerminal;
  393. int Status;
  394. memset(&Context, 0, sizeof(DIFF_CONTEXT));
  395. ContextLinesSpecified = FALSE;
  396. Context.OutputType = DiffOutputDefault;
  397. OutputIsTerminal = FALSE;
  398. if (isatty(STDOUT_FILENO) != 0) {
  399. OutputIsTerminal = TRUE;
  400. Context.Options |= DIFF_OPTION_COLOR;
  401. }
  402. //
  403. // Process the control arguments.
  404. //
  405. while (TRUE) {
  406. Option = getopt_long(ArgumentCount,
  407. Arguments,
  408. DIFF_OPTIONS_STRING,
  409. DiffLongOptions,
  410. NULL);
  411. if (Option == -1) {
  412. break;
  413. }
  414. if ((Option == '?') || (Option == ':')) {
  415. Status = 2;
  416. goto MainEnd;
  417. }
  418. switch (Option) {
  419. case 'b':
  420. Context.Options |= DIFF_OPTION_IGNORE_BLANKS;
  421. break;
  422. case 'u':
  423. Context.OutputType = DiffOutputUnified;
  424. //
  425. // Fall through.
  426. //
  427. case 'c':
  428. case 'C':
  429. ContextLinesSpecified = TRUE;
  430. if (Context.OutputType == DiffOutputEd) {
  431. SwPrintError(0, NULL, "Conflicting output style options");
  432. Status = EINVAL;
  433. goto MainEnd;
  434. }
  435. Context.ContextLines = DIFF_DEFAULT_CONTEXT_LINES;
  436. if (optarg != NULL) {
  437. Context.ContextLines = strtol(optarg, &AfterScan, 10);
  438. if ((Context.ContextLines < 0) || (AfterScan == optarg)) {
  439. SwPrintError(0, optarg, "Expected an integer");
  440. Status = EINVAL;
  441. goto MainEnd;
  442. }
  443. }
  444. break;
  445. case '1':
  446. assert(optarg != NULL);
  447. if (strcasecmp(optarg, "always") == 0) {
  448. Context.Options |= DIFF_OPTION_COLOR;
  449. } else if (strcasecmp(optarg, "never") == 0) {
  450. Context.Options &= ~DIFF_OPTION_COLOR;
  451. } else if (strcasecmp(optarg, "auto") == 0) {
  452. Context.Options &= ~DIFF_OPTION_COLOR;
  453. if (OutputIsTerminal != FALSE) {
  454. Context.Options |= DIFF_OPTION_COLOR;
  455. }
  456. } else {
  457. SwPrintError(0, optarg, "Invalid color argument");
  458. Status = EINVAL;
  459. goto MainEnd;
  460. }
  461. break;
  462. case 'e':
  463. if (Context.ContextLines != 0) {
  464. SwPrintError(0, NULL, "Conflicting output style options");
  465. Status = EINVAL;
  466. goto MainEnd;
  467. }
  468. Context.OutputType = DiffOutputEd;
  469. break;
  470. case 'N':
  471. Context.Options |= DIFF_OPTION_ABSENT_EMPTY;
  472. break;
  473. case 'r':
  474. Context.Options |= DIFF_OPTION_RECURSIVE;
  475. break;
  476. case 'x':
  477. assert(optarg != NULL);
  478. NewBuffer = realloc(
  479. Context.FileExclusions,
  480. (Context.FileExclusionCount + 1) * sizeof(PSTR));
  481. if (NewBuffer == NULL) {
  482. Status = ENOMEM;
  483. goto MainEnd;
  484. }
  485. Context.FileExclusions = NewBuffer;
  486. Context.FileExclusions[Context.FileExclusionCount] = optarg;
  487. Context.FileExclusionCount += 1;
  488. break;
  489. case 'V':
  490. SwPrintVersion(DIFF_VERSION_MAJOR, DIFF_VERSION_MINOR);
  491. return 1;
  492. case 'h':
  493. printf(DIFF_USAGE);
  494. return 1;
  495. default:
  496. assert(FALSE);
  497. Status = 1;
  498. goto MainEnd;
  499. }
  500. }
  501. //
  502. // If context was not specified and the format is still default, use the
  503. // ed format.
  504. //
  505. if ((Context.OutputType == DiffOutputDefault) &&
  506. (ContextLinesSpecified == FALSE)) {
  507. Context.OutputType = DiffOutputEd;
  508. }
  509. ArgumentIndex = optind;
  510. if (ArgumentIndex > ArgumentCount) {
  511. ArgumentIndex = ArgumentCount;
  512. }
  513. //
  514. // Fail if there were not enough arguments.
  515. //
  516. if (ArgumentIndex >= ArgumentCount) {
  517. SwPrintError(0,
  518. NULL,
  519. "Diff needs two things to compare. Try --help for usage");
  520. Status = 2;
  521. goto MainEnd;
  522. }
  523. if (ArgumentIndex + 2 != ArgumentCount) {
  524. SwPrintError(0,
  525. NULL,
  526. "Diff needs exactly two arguments. Try --help for usage");
  527. Status = 2;
  528. goto MainEnd;
  529. }
  530. Status = DiffComparePaths(&Context,
  531. Arguments[ArgumentIndex],
  532. Arguments[ArgumentIndex + 1]);
  533. MainEnd:
  534. if (Context.FileExclusions != NULL) {
  535. free(Context.FileExclusions);
  536. }
  537. return Status;
  538. }
  539. //
  540. // --------------------------------------------------------- Internal Functions
  541. //
  542. INT
  543. DiffComparePaths (
  544. PDIFF_CONTEXT Context,
  545. PSTR PathA,
  546. PSTR PathB
  547. )
  548. /*++
  549. Routine Description:
  550. This routine compares two paths, printing out the differences.
  551. Arguments:
  552. Context - Supplies a pointer to the diff application context.
  553. PathA - Supplies a pointer to a null terminated string containing the first
  554. path to compare.
  555. PathB - Supplies a pointer to a null terminated string containing the
  556. second path to compare.
  557. Return Value:
  558. 0 if the files are equal.
  559. 1 if there are differences.
  560. Other values on error.
  561. --*/
  562. {
  563. PSTR AppendedPath;
  564. ULONG AppendedPathSize;
  565. PSTR BaseName;
  566. PDIFF_FILE FileA;
  567. PDIFF_FILE FileB;
  568. INT Status;
  569. AppendedPath = NULL;
  570. FileA = NULL;
  571. FileB = NULL;
  572. Status = DiffCreateFile(Context, NULL, PathA, &FileA);
  573. if (Status != 0) {
  574. goto ComparePathsEnd;
  575. }
  576. Status = DiffCreateFile(Context, NULL, PathB, &FileB);
  577. if (Status != 0) {
  578. goto ComparePathsEnd;
  579. }
  580. //
  581. // If only one of A and B is a directory, then the diff is between the file
  582. // and directory/file (or basename of file).
  583. //
  584. if ((FileA->Type == DiffFileDirectory) &&
  585. (FileB->Type != DiffFileDirectory)) {
  586. BaseName = basename(PathB);
  587. Status = SwAppendPath(PathA,
  588. strlen(PathA) + 1,
  589. BaseName,
  590. strlen(BaseName) + 1,
  591. &AppendedPath,
  592. &AppendedPathSize);
  593. if (Status != 0) {
  594. SwPrintError(Status, NULL, "Could not append paths");
  595. goto ComparePathsEnd;
  596. }
  597. DiffDestroyFile(Context, FileB);
  598. Status = DiffCreateFile(Context, NULL, AppendedPath, &FileB);
  599. if (Status != 0) {
  600. goto ComparePathsEnd;
  601. }
  602. } else if ((FileA->Type != DiffFileDirectory) &&
  603. (FileB->Type == DiffFileDirectory)) {
  604. BaseName = basename(PathA);
  605. Status = SwAppendPath(PathB,
  606. strlen(PathB) + 1,
  607. BaseName,
  608. strlen(BaseName) + 1,
  609. &AppendedPath,
  610. &AppendedPathSize);
  611. if (Status != 0) {
  612. SwPrintError(Status, NULL, "Could not append paths");
  613. goto ComparePathsEnd;
  614. }
  615. DiffDestroyFile(Context, FileA);
  616. Status = DiffCreateFile(Context, NULL, AppendedPath, &FileA);
  617. if (Status != 0) {
  618. goto ComparePathsEnd;
  619. }
  620. }
  621. Status = DiffCompareFiles(Context, NULL, FileA, NULL, FileB, 0);
  622. ComparePathsEnd:
  623. if (AppendedPath != NULL) {
  624. free(AppendedPath);
  625. }
  626. if (FileA != NULL) {
  627. DiffDestroyFile(Context, FileA);
  628. }
  629. if (FileB != NULL) {
  630. DiffDestroyFile(Context, FileB);
  631. }
  632. return Status;
  633. }
  634. INT
  635. DiffCompareFiles (
  636. PDIFF_CONTEXT Context,
  637. PSTR DirectoryA,
  638. PDIFF_FILE FileA,
  639. PSTR DirectoryB,
  640. PDIFF_FILE FileB,
  641. ULONG RecursionLevel
  642. )
  643. /*++
  644. Routine Description:
  645. This routine compares two file structures, printing out the differences.
  646. Arguments:
  647. Context - Supplies a pointer to the diff application context.
  648. DirectoryA - Supplies a pointer to the directory prefix for file A.
  649. FileA - Supplies a pointer to the first file object to compare.
  650. DirectoryB - Supplies a pointer to the directory prefix for file B.
  651. FileB - Supplies a pointer to the second file object to compare.
  652. RecursionLevel - Supplies the level of recursion this function is operating
  653. in.
  654. Return Value:
  655. 0 if the files are equal.
  656. 1 if there are differences.
  657. Other values on error.
  658. --*/
  659. {
  660. PSTR AppendedPathA;
  661. ULONG AppendedPathASize;
  662. PSTR AppendedPathB;
  663. ULONG AppendedPathBSize;
  664. ULONG DirectorySize;
  665. INT Status;
  666. AppendedPathA = NULL;
  667. AppendedPathB = NULL;
  668. //
  669. // If the types are not equal, simply report that.
  670. //
  671. if ((DirectoryA != NULL) && (DirectoryB != NULL)) {
  672. if (FileA->Type != FileB->Type) {
  673. printf("File %s is a %s while file %s is a %s.\n",
  674. FileA->Name,
  675. DiffFileTypeNames[FileA->Type],
  676. FileB->Name,
  677. DiffFileTypeNames[FileB->Type]);
  678. Status = 1;
  679. goto CompareFilesEnd;
  680. }
  681. }
  682. Status = 0;
  683. //
  684. // The file types are equal. If they're files or directories, compare them.
  685. //
  686. if (FileA->Type != DiffFileDirectory) {
  687. Status = DiffCompareRegularFiles(Context,
  688. DirectoryA,
  689. FileA,
  690. DirectoryB,
  691. FileB,
  692. RecursionLevel);
  693. } else {
  694. //
  695. // Compare the contents of the directories if either 1) this is the
  696. // entry directly from the command line or 2) The recursion option is
  697. // enabled and the current recursion level is below the maximum
  698. // depth.
  699. //
  700. if ((RecursionLevel == 0) ||
  701. ((Context->Options & DIFF_OPTION_RECURSIVE) != 0)) {
  702. if (RecursionLevel >= DIFF_MAX_RECURSION_DEPTH) {
  703. SwPrintError(0, FileA->Name, "Max recursion depth reached");
  704. Status = ELOOP;
  705. goto CompareFilesEnd;
  706. }
  707. DirectorySize = 0;
  708. if (DirectoryA != NULL) {
  709. DirectorySize = strlen(DirectoryA) + 1;
  710. }
  711. Status = SwAppendPath(DirectoryA,
  712. DirectorySize,
  713. FileA->Name,
  714. strlen(FileA->Name) + 1,
  715. &AppendedPathA,
  716. &AppendedPathASize);
  717. if (Status == FALSE) {
  718. Status = ENOMEM;
  719. goto CompareFilesEnd;
  720. }
  721. DirectorySize = 0;
  722. if (DirectoryB != NULL) {
  723. DirectorySize = strlen(DirectoryB) + 1;
  724. }
  725. Status = SwAppendPath(DirectoryB,
  726. DirectorySize,
  727. FileB->Name,
  728. strlen(FileB->Name) + 1,
  729. &AppendedPathB,
  730. &AppendedPathBSize);
  731. if (Status == FALSE) {
  732. Status = ENOMEM;
  733. goto CompareFilesEnd;
  734. }
  735. Status = DiffCompareDirectories(Context,
  736. AppendedPathA,
  737. AppendedPathB,
  738. RecursionLevel);
  739. }
  740. }
  741. CompareFilesEnd:
  742. if (AppendedPathA != NULL) {
  743. free(AppendedPathA);
  744. }
  745. if (AppendedPathB != NULL) {
  746. free(AppendedPathB);
  747. }
  748. return Status;
  749. }
  750. INT
  751. DiffCompareDirectories (
  752. PDIFF_CONTEXT Context,
  753. PSTR PathA,
  754. PSTR PathB,
  755. ULONG RecursionLevel
  756. )
  757. /*++
  758. Routine Description:
  759. This routine compares the contents of two directories, printing out the
  760. differences.
  761. Arguments:
  762. Context - Supplies a pointer to the diff application context.
  763. PathA - Supplies a pointer to a null terminated string containing the first
  764. directory to compare.
  765. PathB - Supplies a pointer to a null terminated string containing the
  766. directory path to compare.
  767. RecursionLevel - Supplies the current recursion depth.
  768. Return Value:
  769. Returns an integer exit code. 0 for success, nonzero otherwise.
  770. --*/
  771. {
  772. PDIFF_DIRECTORY DirectoryA;
  773. PDIFF_DIRECTORY DirectoryB;
  774. PDIFF_FILE FileA;
  775. UINTN FileAIndex;
  776. PDIFF_FILE FileB;
  777. UINTN FileBIndex;
  778. INT NameComparison;
  779. INT Status;
  780. INT TotalStatus;
  781. Status = 0;
  782. TotalStatus = 0;
  783. //
  784. // Enumerate both directories.
  785. //
  786. DirectoryA = NULL;
  787. DirectoryB = NULL;
  788. Status = DiffGetDirectoryListing(Context, PathA, &DirectoryA);
  789. if (Status != 0) {
  790. SwPrintError(Status, PathA, "Unable to enumerate directory");
  791. goto DiffCompareDirectoriesEnd;
  792. }
  793. Status = DiffGetDirectoryListing(Context, PathB, &DirectoryB);
  794. if (Status != 0) {
  795. SwPrintError(Status, PathB, "Unable to enumerate directory");
  796. goto DiffCompareDirectoriesEnd;
  797. }
  798. //
  799. // Loop through until all files have been dealt with.
  800. //
  801. FileAIndex = 0;
  802. FileBIndex = 0;
  803. while ((FileAIndex < DirectoryA->FileCount) ||
  804. (FileBIndex < DirectoryB->FileCount)) {
  805. FileA = NULL;
  806. FileB = NULL;
  807. //
  808. // If A is at the end, then just print that B exists.
  809. //
  810. if ((FileAIndex == DirectoryA->FileCount) &&
  811. ((Context->Options & DIFF_OPTION_ABSENT_EMPTY) == 0)) {
  812. assert(FileBIndex < DirectoryB->FileCount);
  813. FileB = DirectoryB->Files[FileBIndex];
  814. printf("Only in %s: %s\n", PathB, FileB->Name);
  815. FileBIndex += 1;
  816. if (TotalStatus == 0) {
  817. TotalStatus = 1;
  818. }
  819. //
  820. // If B is at the end, then just print that A exists.
  821. //
  822. } else if ((FileBIndex == DirectoryB->FileCount) &&
  823. ((Context->Options & DIFF_OPTION_ABSENT_EMPTY) == 0)) {
  824. assert(FileAIndex < DirectoryA->FileCount);
  825. FileA = DirectoryA->Files[FileAIndex];
  826. printf("Only in %s: %s\n", PathA, FileA->Name);
  827. FileAIndex += 1;
  828. if (TotalStatus == 0) {
  829. TotalStatus = 1;
  830. }
  831. //
  832. // They both exist (or absent files are treated as empty.
  833. //
  834. } else {
  835. if (FileAIndex < DirectoryA->FileCount) {
  836. FileA = DirectoryA->Files[FileAIndex];
  837. }
  838. if (FileBIndex < DirectoryB->FileCount) {
  839. FileB = DirectoryB->Files[FileBIndex];
  840. }
  841. //
  842. // Use the empty file if either file came up NULL.
  843. //
  844. if (FileA == NULL) {
  845. FileA = &(Context->EmptyFile);
  846. FileA->Type = FileB->Type;
  847. FileA->Name = FileB->Name;
  848. } else if (FileB == NULL) {
  849. FileB = &(Context->EmptyFile);
  850. FileB->Type = FileA->Type;
  851. FileB->Name = FileA->Name;
  852. }
  853. //
  854. // They both exist. If they're the same file, then actually compare
  855. // them.
  856. //
  857. NameComparison = strcmp(FileA->Name, FileB->Name);
  858. if (NameComparison == 0) {
  859. if ((FileA->Type == DiffFileDirectory) &&
  860. (FileB->Type == DiffFileDirectory) &&
  861. ((Context->Options & DIFF_OPTION_RECURSIVE) == 0)) {
  862. printf("Common subdirectories: %s/%s and %s/%s\n",
  863. PathA,
  864. FileA->Name,
  865. PathB,
  866. FileB->Name);
  867. }
  868. Status = DiffCompareFiles(Context,
  869. PathA,
  870. FileA,
  871. PathB,
  872. FileB,
  873. RecursionLevel + 1);
  874. if (Status != 0) {
  875. if (TotalStatus == 0) {
  876. TotalStatus = Status;
  877. }
  878. }
  879. //
  880. // Destroy files now to conserve memory and file descriptors.
  881. //
  882. DiffDestroyFile(Context, FileA);
  883. if (FileAIndex < DirectoryA->FileCount) {
  884. DirectoryA->Files[FileAIndex] = NULL;
  885. }
  886. DiffDestroyFile(Context, FileB);
  887. if (FileBIndex < DirectoryB->FileCount) {
  888. DirectoryB->Files[FileBIndex] = NULL;
  889. }
  890. FileAIndex += 1;
  891. FileBIndex += 1;
  892. //
  893. // Name whichever file is lower as the oddball in the directory.
  894. //
  895. } else if (NameComparison < 0) {
  896. if ((Context->Options & DIFF_OPTION_ABSENT_EMPTY) != 0) {
  897. FileB = &(Context->EmptyFile);
  898. FileB->Type = FileA->Type;
  899. FileB->Name = FileA->Name;
  900. Status = DiffCompareFiles(Context,
  901. PathA,
  902. FileA,
  903. PathB,
  904. FileB,
  905. RecursionLevel + 1);
  906. if (Status != 0) {
  907. if (TotalStatus == 0) {
  908. TotalStatus = Status;
  909. }
  910. }
  911. } else {
  912. printf("Only in %s: %s\n", PathA, FileA->Name);
  913. }
  914. DiffDestroyFile(Context, FileA);
  915. DirectoryA->Files[FileAIndex] = NULL;
  916. FileAIndex += 1;
  917. } else {
  918. if ((Context->Options & DIFF_OPTION_ABSENT_EMPTY) != 0) {
  919. FileA = &(Context->EmptyFile);
  920. FileA->Type = FileB->Type;
  921. FileA->Name = FileB->Name;
  922. Status = DiffCompareFiles(Context,
  923. PathA,
  924. FileA,
  925. PathB,
  926. FileB,
  927. RecursionLevel + 1);
  928. if (Status != 0) {
  929. if (TotalStatus == 0) {
  930. TotalStatus = Status;
  931. }
  932. }
  933. } else {
  934. printf("Only in %s: %s\n", PathB, FileB->Name);
  935. }
  936. DiffDestroyFile(Context, FileB);
  937. DirectoryB->Files[FileBIndex] = NULL;
  938. FileBIndex += 1;
  939. }
  940. }
  941. }
  942. DiffCompareDirectoriesEnd:
  943. if (DirectoryA != NULL) {
  944. DiffDestroyDirectory(Context, DirectoryA);
  945. }
  946. if (DirectoryB != NULL) {
  947. DiffDestroyDirectory(Context, DirectoryB);
  948. }
  949. if (Status != 0) {
  950. TotalStatus = Status;
  951. }
  952. return TotalStatus;
  953. }
  954. INT
  955. DiffGetDirectoryListing (
  956. PDIFF_CONTEXT Context,
  957. PSTR Path,
  958. PDIFF_DIRECTORY *NewDirectory
  959. )
  960. /*++
  961. Routine Description:
  962. This routine enumerates the contents of a directory.
  963. Arguments:
  964. Context - Supplies a pointer to the application context.
  965. Path - Supplies a pointer to the null terminated path of the directory to
  966. enumerate.
  967. NewDirectory - Supplies a pointer where a pointer to the new directory
  968. structure will be returned on success.
  969. Return Value:
  970. 0 on success.
  971. Returns a non-zero error code on failure.
  972. --*/
  973. {
  974. PDIFF_DIRECTORY Directory;
  975. DIR *DirectoryFile;
  976. struct dirent Entry;
  977. struct dirent *EntryPointer;
  978. PVOID NewBuffer;
  979. UINTN NewCapacity;
  980. PDIFF_FILE NewFile;
  981. INT Status;
  982. NewFile = NULL;
  983. Directory = NULL;
  984. DirectoryFile = opendir(Path);
  985. if ((DirectoryFile == NULL) && (errno != ENOENT)) {
  986. Status = errno;
  987. SwPrintError(Status, Path, "Failed to open directory");
  988. goto GetDirectoryListingEnd;
  989. }
  990. Directory = malloc(sizeof(DIFF_DIRECTORY));
  991. if (Directory == NULL) {
  992. Status = ENOMEM;
  993. goto GetDirectoryListingEnd;
  994. }
  995. memset(Directory, 0, sizeof(DIFF_DIRECTORY));
  996. if (DirectoryFile == NULL) {
  997. Status = 0;
  998. goto GetDirectoryListingEnd;
  999. }
  1000. while (TRUE) {
  1001. Status = SwReadDirectory(DirectoryFile, &Entry, &EntryPointer);
  1002. if (Status != 0) {
  1003. SwPrintError(Status, Path, "Unable to read directory");
  1004. goto GetDirectoryListingEnd;
  1005. }
  1006. if (EntryPointer == NULL) {
  1007. break;
  1008. }
  1009. if ((strcmp(Entry.d_name, ".") == 0) ||
  1010. (strcmp(Entry.d_name, "..") == 0)) {
  1011. continue;
  1012. }
  1013. //
  1014. // Skip the file if it's excluded.
  1015. //
  1016. if (DiffIsFileNameExcluded(Context, Entry.d_name) != 0) {
  1017. continue;
  1018. }
  1019. //
  1020. // Expand the array if there's not space.
  1021. //
  1022. if (Directory->FileCount == Directory->ArrayCapacity) {
  1023. NewCapacity = Directory->ArrayCapacity * 2;
  1024. if (NewCapacity == 0) {
  1025. NewCapacity = DIFF_INITIAL_ARRAY_CAPACITY;
  1026. }
  1027. NewBuffer = realloc(Directory->Files,
  1028. NewCapacity * sizeof(PDIFF_FILE));
  1029. if (NewBuffer == NULL) {
  1030. Status = ENOMEM;
  1031. goto GetDirectoryListingEnd;
  1032. }
  1033. Directory->Files = NewBuffer;
  1034. Directory->ArrayCapacity = NewCapacity;
  1035. }
  1036. //
  1037. // Create the file and add it to the array.
  1038. //
  1039. Status = DiffCreateFile(Context, Path, Entry.d_name, &NewFile);
  1040. if (Status != 0) {
  1041. goto GetDirectoryListingEnd;
  1042. }
  1043. Directory->Files[Directory->FileCount] = NewFile;
  1044. Directory->FileCount += 1;
  1045. NewFile = NULL;
  1046. }
  1047. //
  1048. // Sort the files.
  1049. //
  1050. qsort(Directory->Files,
  1051. Directory->FileCount,
  1052. sizeof(PDIFF_FILE),
  1053. DiffCompareFileNames);
  1054. GetDirectoryListingEnd:
  1055. if (DirectoryFile != NULL) {
  1056. closedir(DirectoryFile);
  1057. }
  1058. if (NewFile != NULL) {
  1059. DiffDestroyFile(Context, NewFile);
  1060. }
  1061. if (Status != 0) {
  1062. if (Directory != NULL) {
  1063. DiffDestroyDirectory(Context, Directory);
  1064. Directory = NULL;
  1065. }
  1066. }
  1067. *NewDirectory = Directory;
  1068. return Status;
  1069. }
  1070. BOOL
  1071. DiffIsFileNameExcluded (
  1072. PDIFF_CONTEXT Context,
  1073. PSTR Name
  1074. )
  1075. /*++
  1076. Routine Description:
  1077. This routine determines if a file name should be excluded because it
  1078. matches one of the specified exclusion patterns.
  1079. Arguments:
  1080. Context - Supplies a pointer to the application context.
  1081. Name - Supplies a pointer to the name to compare.
  1082. Return Value:
  1083. TRUE if the file is excluded.
  1084. FALSE if the file is included.
  1085. --*/
  1086. {
  1087. UINTN ExclusionIndex;
  1088. BOOL Match;
  1089. UINTN NameSize;
  1090. PSTR Pattern;
  1091. UINTN PatternSize;
  1092. if (Context->FileExclusionCount == 0) {
  1093. return FALSE;
  1094. }
  1095. NameSize = strlen(Name) + 1;
  1096. for (ExclusionIndex = 0;
  1097. ExclusionIndex < Context->FileExclusionCount;
  1098. ExclusionIndex += 1) {
  1099. Pattern = Context->FileExclusions[ExclusionIndex];
  1100. PatternSize = strlen(Pattern) + 1;
  1101. Match = SwDoesPathPatternMatch(Name, NameSize, Pattern, PatternSize);
  1102. if (Match != FALSE) {
  1103. return TRUE;
  1104. }
  1105. }
  1106. return FALSE;
  1107. }
  1108. VOID
  1109. DiffDestroyDirectory (
  1110. PDIFF_CONTEXT Context,
  1111. PDIFF_DIRECTORY Directory
  1112. )
  1113. /*++
  1114. Routine Description:
  1115. This routine destroys a diff directory structure.
  1116. Arguments:
  1117. Context - Supplies a pointer to the application context.
  1118. Directory - Supplies a pointer to the directory structure to destroy.
  1119. Return Value:
  1120. None.
  1121. --*/
  1122. {
  1123. PDIFF_FILE File;
  1124. UINTN Index;
  1125. for (Index = 0; Index < Directory->FileCount; Index += 1) {
  1126. File = Directory->Files[Index];
  1127. if (File != NULL) {
  1128. DiffDestroyFile(Context, File);
  1129. }
  1130. }
  1131. if (Directory->Files != NULL) {
  1132. free(Directory->Files);
  1133. }
  1134. free(Directory);
  1135. return;
  1136. }
  1137. INT
  1138. DiffCreateFile (
  1139. PDIFF_CONTEXT Context,
  1140. PSTR Directory,
  1141. PSTR Path,
  1142. PDIFF_FILE *File
  1143. )
  1144. /*++
  1145. Routine Description:
  1146. This routine creates a diff file structure based on the given path.
  1147. Arguments:
  1148. Context - Supplies a pointer to the application context.
  1149. Directory - Supplies an optional pointer to a directory prefix of the path.
  1150. Path - Supplies a pointer to the null terminated path of the file to
  1151. create an entry for.
  1152. File - Supplies a pointer where a pointer to the new file structure will be
  1153. returned on success.
  1154. Return Value:
  1155. 0 on success.
  1156. Returns a non-zero error code on failure.
  1157. --*/
  1158. {
  1159. PSTR AppendedPath;
  1160. ULONG AppendedPathSize;
  1161. ULONG DirectorySize;
  1162. PDIFF_FILE NewFile;
  1163. struct stat Stat;
  1164. INT Status;
  1165. AppendedPath = NULL;
  1166. NewFile = malloc(sizeof(DIFF_FILE));
  1167. if (NewFile == NULL) {
  1168. Status = ENOMEM;
  1169. goto CreateFileEnd;
  1170. }
  1171. memset(NewFile, 0, sizeof(DIFF_FILE));
  1172. NewFile->Name = strdup(Path);
  1173. if (NewFile->Name == NULL) {
  1174. Status = ENOMEM;
  1175. goto CreateFileEnd;
  1176. }
  1177. if ((Directory == NULL) && (strcmp(Path, "-") == 0)) {
  1178. NewFile->File = stdin;
  1179. NewFile->ModificationTime = time(NULL);
  1180. NewFile->Type = DiffFileRegularFile;
  1181. } else {
  1182. DirectorySize = 0;
  1183. if (Directory != NULL) {
  1184. DirectorySize = strlen(Directory) + 1;
  1185. }
  1186. Status = SwAppendPath(Directory,
  1187. DirectorySize,
  1188. Path,
  1189. strlen(Path) + 1,
  1190. &AppendedPath,
  1191. &AppendedPathSize);
  1192. if (Status == FALSE) {
  1193. Status = ENOMEM;
  1194. goto CreateFileEnd;
  1195. }
  1196. Status = SwStat(AppendedPath, TRUE, &Stat);
  1197. if (Status != 0) {
  1198. SwPrintError(errno, AppendedPath, "Unable to stat");
  1199. Status = errno;
  1200. goto CreateFileEnd;
  1201. }
  1202. NewFile->ModificationTime = Stat.st_mtime;
  1203. NewFile->Type = DiffGetFileType(Stat.st_mode);
  1204. }
  1205. Status = 0;
  1206. CreateFileEnd:
  1207. if (AppendedPath != NULL) {
  1208. free(AppendedPath);
  1209. }
  1210. if (Status != 0) {
  1211. if (NewFile != NULL) {
  1212. DiffDestroyFile(Context, NewFile);
  1213. NewFile = NULL;
  1214. }
  1215. }
  1216. *File = NewFile;
  1217. return Status;
  1218. }
  1219. VOID
  1220. DiffDestroyFile (
  1221. PDIFF_CONTEXT Context,
  1222. PDIFF_FILE File
  1223. )
  1224. /*++
  1225. Routine Description:
  1226. This routine destroys a diff file structure.
  1227. Arguments:
  1228. Context - Supplies the application context.
  1229. File - Supplies a pointer to the file to destroy.
  1230. Return Value:
  1231. None.
  1232. --*/
  1233. {
  1234. UINTN LineIndex;
  1235. if (File == &(Context->EmptyFile)) {
  1236. File->Name = NULL;
  1237. return;
  1238. }
  1239. if (File->Name != NULL) {
  1240. free(File->Name);
  1241. }
  1242. if ((File->File != NULL) && (File->File != stdin)) {
  1243. fclose(File->File);
  1244. }
  1245. for (LineIndex = 0; LineIndex < File->LineCount; LineIndex += 1) {
  1246. DiffDestroyLine(File->Lines[LineIndex]);
  1247. }
  1248. if (File->Lines != NULL) {
  1249. free(File->Lines);
  1250. }
  1251. free(File);
  1252. return;
  1253. }
  1254. VOID
  1255. DiffDestroyLine (
  1256. PDIFF_LINE Line
  1257. )
  1258. /*++
  1259. Routine Description:
  1260. This routine destroys a diff line structure.
  1261. Arguments:
  1262. Line - Supplies a pointer to the line to destroy.
  1263. Return Value:
  1264. None.
  1265. --*/
  1266. {
  1267. if (Line->Data != NULL) {
  1268. free(Line->Data);
  1269. }
  1270. free(Line);
  1271. return;
  1272. }
  1273. DIFF_FILE_TYPE
  1274. DiffGetFileType (
  1275. mode_t Mode
  1276. )
  1277. /*++
  1278. Routine Description:
  1279. This routine returns the diff file type for the given mode.
  1280. Arguments:
  1281. Mode - Supplies the mode bits coming back from stat.
  1282. Return Value:
  1283. Returns the diff file type.
  1284. --*/
  1285. {
  1286. if (S_ISBLK(Mode)) {
  1287. return DiffFileBlockDevice;
  1288. } else if (S_ISCHR(Mode)) {
  1289. return DiffFileCharacterDevice;
  1290. } else if (S_ISDIR(Mode)) {
  1291. return DiffFileDirectory;
  1292. } else if (S_ISREG(Mode)) {
  1293. return DiffFileRegularFile;
  1294. } else if (S_ISFIFO(Mode)) {
  1295. return DiffFileFifo;
  1296. } else if (S_ISLNK(Mode)) {
  1297. return DiffFileLink;
  1298. } else if (S_ISSOCK(Mode)) {
  1299. return DiffFileSocket;
  1300. }
  1301. return DiffFileUnknown;
  1302. }
  1303. int
  1304. DiffCompareFileNames (
  1305. const void *LeftPointer,
  1306. const void *RightPointer
  1307. )
  1308. /*++
  1309. Routine Description:
  1310. This routine compares the names of two diff files.
  1311. Arguments:
  1312. LeftPointer - Supplies a pointer to a pointer to the left diff file of the
  1313. comparison.
  1314. RightPointer - Supplies a pointer to a pointer to the right diff file of
  1315. the comparison.
  1316. Return Value:
  1317. <0 if the left is less than the right.
  1318. 0 if the two are equal.
  1319. >0 if the left is greater than the right.
  1320. --*/
  1321. {
  1322. PDIFF_FILE LeftFile;
  1323. PDIFF_FILE RightFile;
  1324. LeftFile = *((PDIFF_FILE *)LeftPointer);
  1325. RightFile = *((PDIFF_FILE *)RightPointer);
  1326. return strcmp(LeftFile->Name, RightFile->Name);
  1327. }
  1328. VOID
  1329. DiffPrintCommandLine (
  1330. PDIFF_CONTEXT Context,
  1331. PSTR PathA,
  1332. PDIFF_FILE FileA,
  1333. PSTR PathB,
  1334. PDIFF_FILE FileB
  1335. )
  1336. /*++
  1337. Routine Description:
  1338. This routine prints the diff command line corresponding to the given
  1339. context and files.
  1340. Arguments:
  1341. Context - Supplies a pointer to the application context.
  1342. PathA - Supplies the directory path of the left file.
  1343. FileA - Supplies a pointer to the left file information.
  1344. PathB - Supplies the directory path of the right file.
  1345. FileB - Supplies a pointer to the right file information.
  1346. Return Value:
  1347. None.
  1348. --*/
  1349. {
  1350. printf("diff ");
  1351. if ((Context->Options & DIFF_OPTION_IGNORE_BLANKS) != 0) {
  1352. printf("-b ");
  1353. }
  1354. if ((Context->Options & DIFF_OPTION_RECURSIVE) != 0) {
  1355. printf("-r ");
  1356. }
  1357. if ((Context->Options & DIFF_OPTION_ABSENT_EMPTY) != 0) {
  1358. printf("-N ");
  1359. }
  1360. if (Context->OutputType == DiffOutputUnified) {
  1361. printf("-u %d ", Context->ContextLines);
  1362. } else if (Context->OutputType == DiffOutputEd) {
  1363. printf("-e ");
  1364. } else if (Context->ContextLines != 0) {
  1365. printf("-C %d ");
  1366. }
  1367. printf("%s/%s %s/%s\n", PathA, FileA->Name, PathB, FileB->Name);
  1368. return;
  1369. }
  1370. INT
  1371. DiffLoadFile (
  1372. PDIFF_CONTEXT Context,
  1373. PSTR Directory,
  1374. PDIFF_FILE File
  1375. )
  1376. /*++
  1377. Routine Description:
  1378. This routine loads the contents of a file into lines.
  1379. Arguments:
  1380. Context - Supplies a pointer to the application context.
  1381. Directory - Supplies an optional pointer to a null terminated string
  1382. containing the directory the file is in.
  1383. File - Supplies a pointer to the file to load.
  1384. Return Value:
  1385. 0 on success.
  1386. Returns an error code on failure.
  1387. --*/
  1388. {
  1389. PSTR AppendedPath;
  1390. ULONG AppendedPathSize;
  1391. INT Character;
  1392. ULONG DirectorySize;
  1393. PDIFF_LINE Line;
  1394. UINTN LineArrayCapacity;
  1395. PSTR LineBuffer;
  1396. UINTN LineBufferCapacity;
  1397. UINTN LineBufferSize;
  1398. ULONG LineHash;
  1399. PVOID NewBuffer;
  1400. UINTN NewCapacity;
  1401. INT Status;
  1402. AppendedPath = NULL;
  1403. Line = NULL;
  1404. LineArrayCapacity = 0;
  1405. LineBuffer = NULL;
  1406. LineBufferCapacity = 0;
  1407. LineBufferSize = 0;
  1408. if (File == &(Context->EmptyFile)) {
  1409. Status = 0;
  1410. goto LoadFileEnd;
  1411. }
  1412. if (File->File == NULL) {
  1413. DirectorySize = 0;
  1414. if (Directory != NULL) {
  1415. DirectorySize = strlen(Directory) + 1;
  1416. }
  1417. Status = SwAppendPath(Directory,
  1418. DirectorySize,
  1419. File->Name,
  1420. strlen(File->Name) + 1,
  1421. &AppendedPath,
  1422. &AppendedPathSize);
  1423. if (Status == FALSE) {
  1424. Status = ENOMEM;
  1425. goto LoadFileEnd;
  1426. }
  1427. File->File = fopen(AppendedPath, "r");
  1428. if (File->File == NULL) {
  1429. Status = errno;
  1430. SwPrintError(Status, AppendedPath, "Failed to open");
  1431. goto LoadFileEnd;
  1432. }
  1433. }
  1434. Status = ENOMEM;
  1435. //
  1436. // Loop loading lines.
  1437. //
  1438. LineBufferCapacity = DIFF_INITIAL_LINE_BUFFER;
  1439. LineBuffer = malloc(LineBufferCapacity);
  1440. if (LineBuffer == NULL) {
  1441. goto LoadFileEnd;
  1442. }
  1443. while (TRUE) {
  1444. LineBufferSize = 0;
  1445. LineHash = 0;
  1446. //
  1447. // Loop adding characters to the line.
  1448. //
  1449. while (TRUE) {
  1450. Character = fgetc(File->File);
  1451. if ((Character == EOF) || (Character == '\n')) {
  1452. break;
  1453. }
  1454. if (Character == '\0') {
  1455. File->Binary = TRUE;
  1456. break;
  1457. }
  1458. //
  1459. // Expand the line buffer if it's not big enough to hold the
  1460. // character.
  1461. //
  1462. if (LineBufferSize + 1 >= LineBufferCapacity) {
  1463. NewCapacity = LineBufferCapacity * 2;
  1464. assert(NewCapacity > LineBufferSize + 1);
  1465. NewBuffer = realloc(LineBuffer, NewCapacity);
  1466. if (NewBuffer == NULL) {
  1467. goto LoadFileEnd;
  1468. }
  1469. LineBuffer = NewBuffer;
  1470. LineBufferCapacity = NewCapacity;
  1471. }
  1472. //
  1473. // Add the character to the line buffer.
  1474. //
  1475. LineBuffer[LineBufferSize] = (CHAR)Character;
  1476. LineBufferSize += 1;
  1477. //
  1478. // The poor man's hash is really just the sum of all the
  1479. // characters. Don't count this character as part of the hash if
  1480. // it's blank and blanks are being ignored.
  1481. //
  1482. if (((Context->Options & DIFF_OPTION_IGNORE_BLANKS) == 0) ||
  1483. (isspace(Character) == 0)) {
  1484. LineHash += Character;
  1485. }
  1486. }
  1487. //
  1488. // If the file ended with a newline, that's normal, just stop before
  1489. // adding another empty line. The newline really belongs to the the
  1490. // previous line.
  1491. //
  1492. if ((Character == EOF) && (LineBufferSize == 0)) {
  1493. break;
  1494. }
  1495. //
  1496. // Allocate a line structure.
  1497. //
  1498. Line = malloc(sizeof(DIFF_LINE));
  1499. if (Line == NULL) {
  1500. goto LoadFileEnd;
  1501. }
  1502. memset(Line, 0, sizeof(DIFF_LINE));
  1503. //
  1504. // Allocate a line of exactly the right size and copy it in.
  1505. //
  1506. Line->Data = malloc(LineBufferSize + 1);
  1507. if (Line->Data == NULL) {
  1508. goto LoadFileEnd;
  1509. }
  1510. memcpy(Line->Data, LineBuffer, LineBufferSize);
  1511. Line->Data[LineBufferSize] = '\0';
  1512. Line->Size = LineBufferSize + 1;
  1513. Line->Hash = LineHash;
  1514. //
  1515. // Expand the line array if it is not big enough to hold this line.
  1516. //
  1517. if (File->LineCount >= LineArrayCapacity) {
  1518. NewCapacity = LineArrayCapacity * 2;
  1519. if (NewCapacity == 0) {
  1520. NewCapacity = DIFF_INITIAL_ARRAY_CAPACITY;
  1521. }
  1522. assert(NewCapacity > File->LineCount);
  1523. NewBuffer = realloc(File->Lines, NewCapacity * sizeof(PDIFF_LINE));
  1524. if (NewBuffer == NULL) {
  1525. goto LoadFileEnd;
  1526. }
  1527. File->Lines = NewBuffer;
  1528. LineArrayCapacity = NewCapacity;
  1529. }
  1530. File->Lines[File->LineCount] = Line;
  1531. File->LineCount += 1;
  1532. Line = NULL;
  1533. //
  1534. // If this was the last line, stop.
  1535. //
  1536. if (Character == EOF) {
  1537. File->NoNewlineAtEnd = TRUE;
  1538. break;
  1539. }
  1540. }
  1541. //
  1542. // If the file has a problem, fail.
  1543. //
  1544. if (ferror(File->File) != 0) {
  1545. Status = errno;
  1546. SwPrintError(Status, File->Name, "Failed to read");
  1547. goto LoadFileEnd;
  1548. }
  1549. Status = 0;
  1550. LoadFileEnd:
  1551. if (Line != NULL) {
  1552. DiffDestroyLine(Line);
  1553. }
  1554. if (AppendedPath != NULL) {
  1555. free(AppendedPath);
  1556. }
  1557. if (LineBuffer != NULL) {
  1558. free(LineBuffer);
  1559. }
  1560. return Status;
  1561. }
  1562. //
  1563. // Routines for computing the difference between two files
  1564. //
  1565. INT
  1566. DiffCompareRegularFiles (
  1567. PDIFF_CONTEXT Context,
  1568. PSTR DirectoryA,
  1569. PDIFF_FILE FileA,
  1570. PSTR DirectoryB,
  1571. PDIFF_FILE FileB,
  1572. ULONG RecursionLevel
  1573. )
  1574. /*++
  1575. Routine Description:
  1576. This routine compares two regular files, printing out the difference.
  1577. Arguments:
  1578. Context - Supplies a pointer to the diff application context.
  1579. DirectoryA - Supplies a pointer to the directory prefix for file A.
  1580. FileA - Supplies a pointer to the first file object to compare.
  1581. DirectoryB - Supplies a pointer to the directory prefix for file B.
  1582. FileB - Supplies a pointer to the second file object to compare.
  1583. RecursionLevel - Supplies the level of recursion this function is operating
  1584. in.
  1585. Return Value:
  1586. 0 if the files are equal.
  1587. 1 if there are differences.
  1588. Other values on error.
  1589. --*/
  1590. {
  1591. PINTN DownVector;
  1592. UINTN Maximum;
  1593. INT Status;
  1594. PINTN UpVector;
  1595. UINTN VectorSize;
  1596. //
  1597. // Load up the two files.
  1598. //
  1599. Status = DiffLoadFile(Context, DirectoryA, FileA);
  1600. if (Status != 0) {
  1601. SwPrintError(Status,
  1602. NULL,
  1603. "Failed to load file '%s/%s'",
  1604. DirectoryA,
  1605. FileA->Name);
  1606. goto CompareRegularFilesEnd;
  1607. }
  1608. Status = DiffLoadFile(Context, DirectoryB, FileB);
  1609. if (Status != 0) {
  1610. SwPrintError(Status,
  1611. NULL,
  1612. "Failed to load file '%s/%s'",
  1613. DirectoryB,
  1614. FileB->Name);
  1615. goto CompareRegularFilesEnd;
  1616. }
  1617. //
  1618. // If either file is binary, just perform a binary comparison and report
  1619. // whether or not they're the same.
  1620. //
  1621. if ((FileA->Binary != FALSE) || (FileB->Binary != FALSE)) {
  1622. Status = DiffCompareBinaryFiles(Context, FileA, FileB);
  1623. if (Status == 1) {
  1624. if (RecursionLevel != 0) {
  1625. DiffPrintCommandLine(Context,
  1626. DirectoryA,
  1627. FileA,
  1628. DirectoryB,
  1629. FileB);
  1630. }
  1631. printf("Binary files '");
  1632. if (DirectoryA != NULL) {
  1633. printf("%s/%s", DirectoryA, FileA->Name);
  1634. } else {
  1635. printf("%s", FileA->Name);
  1636. }
  1637. printf("' and '");
  1638. if (DirectoryB != NULL) {
  1639. printf("%s/%s", DirectoryB, FileB->Name);
  1640. } else {
  1641. printf("%s", FileB->Name);
  1642. }
  1643. printf("' differ.\n");
  1644. }
  1645. return Status;
  1646. }
  1647. //
  1648. // Allocate vectors (V in the paper) for computing the shortest middle
  1649. // snake from both directions (forward and reverse). The vectors are
  1650. // indexed by k-line, which is the distance from the diagonal. The maximum
  1651. // possible distance is the sum of the two lengths. This goes in either
  1652. // direction (times two), plus two extra.
  1653. //
  1654. Maximum = FileA->LineCount + FileB->LineCount + 1;
  1655. VectorSize = (2 * Maximum) + 2;
  1656. DownVector = malloc(sizeof(INTN) * VectorSize);
  1657. if (DownVector == NULL) {
  1658. Status = ENOMEM;
  1659. goto CompareRegularFilesEnd;
  1660. }
  1661. UpVector = malloc(sizeof(INTN) * VectorSize);
  1662. if (UpVector == NULL) {
  1663. Status = ENOMEM;
  1664. goto CompareRegularFilesEnd;
  1665. }
  1666. //
  1667. // Find the longest common subsequence, which marks the different lines
  1668. // as modified.
  1669. //
  1670. Status = DiffComputeLongestCommonSubsequence(Context,
  1671. FileA,
  1672. FileB,
  1673. 0,
  1674. FileA->LineCount,
  1675. 0,
  1676. FileB->LineCount,
  1677. DownVector,
  1678. UpVector);
  1679. if (Status != 1) {
  1680. goto CompareRegularFilesEnd;
  1681. }
  1682. if (RecursionLevel != 0) {
  1683. DiffPrintCommandLine(Context, DirectoryA, FileA, DirectoryB, FileB);
  1684. }
  1685. //
  1686. // Print the diffs in the desired format.
  1687. //
  1688. switch (Context->OutputType) {
  1689. case DiffOutputDefault:
  1690. DiffPrintContextDiffs(Context, DirectoryA, FileA, DirectoryB, FileB);
  1691. break;
  1692. case DiffOutputEd:
  1693. DiffPrintEdDiffs(Context, DirectoryA, FileA, DirectoryB, FileB);
  1694. break;
  1695. case DiffOutputUnified:
  1696. DiffPrintUnifiedDiffs(Context, DirectoryA, FileA, DirectoryB, FileB);
  1697. break;
  1698. default:
  1699. assert(FALSE);
  1700. Status = EINVAL;
  1701. goto CompareRegularFilesEnd;
  1702. }
  1703. CompareRegularFilesEnd:
  1704. return Status;
  1705. }
  1706. INT
  1707. DiffCompareBinaryFiles (
  1708. PDIFF_CONTEXT Context,
  1709. PDIFF_FILE FileA,
  1710. PDIFF_FILE FileB
  1711. )
  1712. /*++
  1713. Routine Description:
  1714. This routine compares two binary files for equality. The ignore blanks
  1715. flag is ignored here.
  1716. Arguments:
  1717. Context - Supplies a pointer to the diff application context.
  1718. FileA - Supplies a pointer to the first file.
  1719. FileB - Supplies a pointer to the second file.
  1720. Return Value:
  1721. 0 if the files are equal in the compared region.
  1722. 1 if the files differ somewhere.
  1723. Other codes on error.
  1724. --*/
  1725. {
  1726. INT CharacterA;
  1727. INT CharacterB;
  1728. INT Status;
  1729. //
  1730. // If one file is not there but the other is, they're different. This
  1731. // happens when non-existant files are treated as empty.
  1732. //
  1733. if (((FileA->File == NULL) && (FileB->File != NULL)) ||
  1734. ((FileA->File != NULL) && (FileB->File == NULL))) {
  1735. Status = 1;
  1736. goto CompareBinaryFilesEnd;
  1737. }
  1738. assert((FileA->File != NULL) && (FileB->File != NULL));
  1739. rewind(FileA->File);
  1740. rewind(FileB->File);
  1741. Status = 0;
  1742. while (TRUE) {
  1743. CharacterA = fgetc(FileA->File);
  1744. if (ferror(FileA->File)) {
  1745. Status = errno;
  1746. SwPrintError(Status, FileA->Name, "Failed to read");
  1747. goto CompareBinaryFilesEnd;
  1748. }
  1749. CharacterB = fgetc(FileB->File);
  1750. if (ferror(FileB->File)) {
  1751. Status = errno;
  1752. SwPrintError(Status, FileB->Name, "Failed to read");
  1753. goto CompareBinaryFilesEnd;
  1754. }
  1755. if (CharacterA != CharacterB) {
  1756. Status = 1;
  1757. goto CompareBinaryFilesEnd;
  1758. }
  1759. if (CharacterA == EOF) {
  1760. break;
  1761. }
  1762. }
  1763. CompareBinaryFilesEnd:
  1764. return Status;
  1765. }
  1766. INT
  1767. DiffComputeLongestCommonSubsequence (
  1768. PDIFF_CONTEXT Context,
  1769. PDIFF_FILE FileA,
  1770. PDIFF_FILE FileB,
  1771. INTN LowerA,
  1772. INTN UpperA,
  1773. INTN LowerB,
  1774. INTN UpperB,
  1775. PINTN DownVector,
  1776. PINTN UpVector
  1777. )
  1778. /*++
  1779. Routine Description:
  1780. This routine implements the Myers' algorithm for computing the longest
  1781. common subsequence in linear space (but with recursion). The algorithm is
  1782. a divide-and-conquer algorithm, finding an element of the correct path
  1783. in the middle and then recursing on each of the slightly smaller split
  1784. pieces.
  1785. Arguments:
  1786. Context - Supplies a pointer to the diff application context.
  1787. FileA - Supplies a pointer to the first file.
  1788. FileB - Supplies a pointer to the second file.
  1789. LowerA - Supplies the starting index within file A to work on.
  1790. UpperA - Supplies the ending index within file A to work on, exclusive.
  1791. LowerB - Supplies the starting index within file B to work on.
  1792. UpperB - Supplies the ending index within file B to work on, exclusive.
  1793. DownVector - Supplies the k-indexed vector for computing the shortest
  1794. middle snake from the top down.
  1795. UpVector - Supplies the k-indexed vector for computing the shortest middle
  1796. snake from the bottom up.
  1797. Return Value:
  1798. 0 if the files are equal in the compared region.
  1799. 1 if the files differ somewhere.
  1800. --*/
  1801. {
  1802. INTN MiddleSnakeX;
  1803. INTN MiddleSnakeY;
  1804. INT Status;
  1805. INT TotalStatus;
  1806. Status = 0;
  1807. TotalStatus = 0;
  1808. //
  1809. // As a basic no-brainer, get past any lines at the beginning and the
  1810. // end that match.
  1811. //
  1812. while ((LowerA < UpperA) && (LowerB < UpperB)) {
  1813. Status = DiffCompareLines(Context, FileA, FileB, LowerA, LowerB);
  1814. if (Status != 0) {
  1815. break;
  1816. }
  1817. LowerA += 1;
  1818. LowerB += 1;
  1819. }
  1820. if (Status != 0) {
  1821. TotalStatus = Status;
  1822. }
  1823. while ((LowerA < UpperA) && (LowerB < UpperB)) {
  1824. Status = DiffCompareLines(Context,
  1825. FileA,
  1826. FileB,
  1827. UpperA - 1,
  1828. UpperB - 1);
  1829. if (Status != 0) {
  1830. break;
  1831. }
  1832. UpperA -= 1;
  1833. UpperB -= 1;
  1834. }
  1835. if (Status != 0) {
  1836. TotalStatus = Status;
  1837. }
  1838. //
  1839. // If file A ended, then mark everything in file B as an insertion.
  1840. //
  1841. if (LowerA == UpperA) {
  1842. if (LowerB < UpperB) {
  1843. TotalStatus = 1;
  1844. }
  1845. while (LowerB < UpperB) {
  1846. FileB->Lines[LowerB]->Modified = TRUE;
  1847. LowerB += 1;
  1848. }
  1849. //
  1850. // If file B ended, then mark everything in file A as a deletion.
  1851. //
  1852. } else if (LowerB == UpperB) {
  1853. if (LowerA < UpperA) {
  1854. TotalStatus = 1;
  1855. }
  1856. while (LowerA < UpperA) {
  1857. FileA->Lines[LowerA]->Modified = TRUE;
  1858. LowerA += 1;
  1859. }
  1860. //
  1861. // Run the real crux of the diff algorithm.
  1862. //
  1863. } else {
  1864. //
  1865. // Find the shortest middle snake, which returns a k index into the
  1866. // down vector array. This index contains the x value of the shortest
  1867. // middle snake. The y value is then x - k.
  1868. //
  1869. DiffComputeShortestMiddleSnake(Context,
  1870. FileA,
  1871. FileB,
  1872. LowerA,
  1873. UpperA,
  1874. LowerB,
  1875. UpperB,
  1876. DownVector,
  1877. UpVector,
  1878. &MiddleSnakeX,
  1879. &MiddleSnakeY);
  1880. //
  1881. // Now that a middle value in the longest common subsequence is known,
  1882. // recurse down to find the longest common subsequences of the upper
  1883. // left box and lower right box that remains.
  1884. //
  1885. Status = DiffComputeLongestCommonSubsequence(Context,
  1886. FileA,
  1887. FileB,
  1888. LowerA,
  1889. MiddleSnakeX,
  1890. LowerB,
  1891. MiddleSnakeY,
  1892. DownVector,
  1893. UpVector);
  1894. if (Status != 0) {
  1895. TotalStatus = Status;
  1896. }
  1897. Status = DiffComputeLongestCommonSubsequence(Context,
  1898. FileA,
  1899. FileB,
  1900. MiddleSnakeX,
  1901. UpperA,
  1902. MiddleSnakeY,
  1903. UpperB,
  1904. DownVector,
  1905. UpVector);
  1906. if (Status != 0) {
  1907. TotalStatus = Status;
  1908. }
  1909. }
  1910. return TotalStatus;
  1911. }
  1912. VOID
  1913. DiffComputeShortestMiddleSnake (
  1914. PDIFF_CONTEXT Context,
  1915. PDIFF_FILE FileA,
  1916. PDIFF_FILE FileB,
  1917. INTN LowerA,
  1918. INTN UpperA,
  1919. INTN LowerB,
  1920. INTN UpperB,
  1921. PINTN DownVector,
  1922. PINTN UpVector,
  1923. PINTN MiddleSnakeX,
  1924. PINTN MiddleSnakeY
  1925. )
  1926. /*++
  1927. Routine Description:
  1928. This routine implements the crux of the Myers' algorithm for computing the
  1929. longest common subsequence in linear space, which is computing the shortest
  1930. middle snake. Let's explore the algorithm a bit.
  1931. Introduction
  1932. Computing the difference between two files is equivalent to asking minimum
  1933. set of steps it would take to transform one file into the other. Minimum
  1934. being the tricky part (as in it's not good enough to say "delete all the
  1935. lines from file A and replace them with all the lines from file B"). It
  1936. turns out this problem is simple with the Longest Common Subsequence of the
  1937. two sequences. For example, the longest common subsequence of BANANA and
  1938. CATANA is AANA. Everything not in the longest common subsequence is the
  1939. diff. The Myers' diff paper sets out to solve the problem of finding an
  1940. LCS efficiently.
  1941. Visualization:
  1942. Arrange the two sequences with one along the X axis and one along the Y
  1943. axis. Moving horizontally along the grid represents a single deletion, and
  1944. moving vertically represents an addition. Diagonal moves can be made when
  1945. the sequences are equal. Finding the longest common subsequence is then a
  1946. matter of tracing a path from the top left to the bottom right using as few
  1947. horizontal and vertical moves as possible (and therefore as many diagonals
  1948. as possible). Below is an example trace through the sequences ABCABBA and
  1949. CBABAC.
  1950. A B C A B B A
  1951. --------\ . . . . .
  1952. \
  1953. C . . \ . . . .
  1954. |
  1955. B . . \ . . . .
  1956. \
  1957. A . . . \ . . .
  1958. \
  1959. B . . . . ----\ .
  1960. \
  1961. A . . . . . . \
  1962. |
  1963. C . . . . . . |
  1964. Terms:
  1965. Snake - A snake is a single horizontal or vertical move followed by zero
  1966. or more diagonals. The algorithm is greedy, and does as many diagonal
  1967. moves as possible.
  1968. D Path - A D path is a path with D non-diagonal moves. So a 0 path moves
  1969. only diagonally, a 1 path has one horizontal or vertical move and then
  1970. a bunch of diagonals, etc. The maximum D path would be the maximum X
  1971. plus the maximum Y (deleting all of the old stuff and replacing with
  1972. all the new).
  1973. k Line - This is the tricky one. K-lines run parallel to the 0 path (so
  1974. they're diagonal). The value of k is the distance from the 0 path.
  1975. So making one horizontal move and then travelling diagonally puts one
  1976. on the k=1 line. Making a vertical move lands on the k=-1 line. Two
  1977. horizontal moves is the k=2 line, etc. In fact, k = x - y. K lines
  1978. really are just plots of y = x - k.
  1979. V - V is a vector indexed by k (weird, right). At any given D, the vector V
  1980. saves the coordinate of the farthest reaching snake for that k line.
  1981. For early D values like 1, the only reachable k lines are 1 and -1. As
  1982. D increases, the number of accessible k lines increases by two (going
  1983. all the way right gets to a new k line as does going all the way down).
  1984. The maximum reachable k lines ever are +/-D (going all the way right or
  1985. all the way down), so this is the size of V.
  1986. Quadratic space algorithm:
  1987. A naive algorithm woulc simply explore every possible path from the top
  1988. left to the bottom right (moving only right, down, and diagonally). This
  1989. algorithm isn't too far off from there, it's just greedy and at every step
  1990. it takes as many diagonals as it can. The quadratic space algorithm
  1991. computes the vector V for successive D values until some index of V hits
  1992. the lower right corner. It basically says, "try going both right and down,
  1993. then take as many diagonals as possible. Recurse on the two new endpoints
  1994. created." One interesting thing to note is that computing a new value in V
  1995. can be done by taking its adjacent values (for one k below and one k above,
  1996. ie one move left and one move up), and running snakes off those values. V
  1997. is computed in steps of 2 (since it's not possible to get to a k=2 line
  1998. from a D=1 path). So computing V only requires the previous value of V. If
  1999. all that's desired is the length of the LCS, then just a single V array is
  2000. needed. The only reason all the old V arrays are kept around after each
  2001. step is to trace the path when finished.
  2002. Linear space optimization:
  2003. One observation that should be easy to understand is that the algorithm can
  2004. also be run in reverse; start at the lower right corner and works towards
  2005. the upper left. The resulting path may be different as different snakes are
  2006. investigated. The linear space optimization involves running the algorithm
  2007. forward and reverse at the same time until they overlap somewhere in the
  2008. middle. Only the point at which they collide is needed, so only the last
  2009. V is saved while running forward and reverse (this is what makes it linear).
  2010. The magic is that this single overlapping snake somewhere in the middle is
  2011. part of the optimal solution. So the algorithm found one piece of the
  2012. solution in the middle. Then it's simply a matter of solving the two new
  2013. smaller rectangles created in the upper left and lower right corners
  2014. recursively until the solution is trivial.
  2015. Arguments:
  2016. Context - Supplies a pointer to the diff application context.
  2017. FileA - Supplies a pointer to the first file.
  2018. FileB - Supplies a pointer to the second file.
  2019. LowerA - Supplies the starting index within file A to work on.
  2020. UpperA - Supplies the ending index within file A to work on, exclusive.
  2021. LowerB - Supplies the starting index within file B to work on.
  2022. UpperB - Supplies the ending index within file B to work on, exclusive.
  2023. DownVector - Supplies the k-indexed vector for computing the shortest
  2024. middle snake from the top down.
  2025. UpVector - Supplies the k-indexed vector for computing the shortest middle
  2026. snake from the bottom up.
  2027. MiddleSnakeX - Supplies a pointer where the X coordinate (line index of
  2028. file A) of the shortest middle snake will be returned.
  2029. MiddleSnakeY - Supplies a pointer where the Y coordinate (line index of
  2030. file B) of the shortest middle snake will be returned.
  2031. Return Value:
  2032. None.
  2033. --*/
  2034. {
  2035. INTN Delta;
  2036. BOOL DeltaIsOdd;
  2037. INTN DIndex;
  2038. INTN DownK;
  2039. INTN DownOffset;
  2040. INTN KIndex;
  2041. INTN Maximum;
  2042. INTN MaximumD;
  2043. INTN SnakeX;
  2044. INTN SnakeY;
  2045. INT Status;
  2046. INTN UpK;
  2047. INTN UpOffset;
  2048. //
  2049. // The maximum D value would be going all the way right and all the way
  2050. // down (the files are entirely different).
  2051. //
  2052. Maximum = FileA->LineCount + FileB->LineCount + 1;
  2053. //
  2054. // Compute the K lines to start the forward (down) and reverse (up)
  2055. // searches.
  2056. //
  2057. DownK = LowerA - LowerB;
  2058. UpK = UpperA - UpperB;
  2059. //
  2060. // Delta is the difference in k between the start point and the end point.
  2061. // This is needed to know which indices of the vector to check for overlap.
  2062. //
  2063. Delta = (UpperA - LowerA) - (UpperB - LowerB);
  2064. DeltaIsOdd = FALSE;
  2065. if ((Delta & 0x1) != 0) {
  2066. DeltaIsOdd = TRUE;
  2067. }
  2068. //
  2069. // In the paper, k values can go from -D to D. Use offsets to avoid
  2070. // actually accessing negative array values.
  2071. //
  2072. DownOffset = Maximum - DownK;
  2073. UpOffset = Maximum - UpK;
  2074. //
  2075. // Running the algorithm forward and reverse is guaranteed to cross
  2076. // somewhere before D / 2.
  2077. //
  2078. MaximumD = (((UpperA - LowerA) + (UpperB - LowerB)) / 2) + 1;
  2079. //
  2080. // Initialize the vectors.
  2081. //
  2082. DownVector[DownOffset + DownK + 1] = LowerA;
  2083. UpVector[UpOffset + UpK - 1] = UpperA;
  2084. //
  2085. // Iterate through successive D values until an overlap is found. This is
  2086. // guaranteed to be the shortest path because it has the lowest D value.
  2087. //
  2088. for (DIndex = 0; DIndex <= MaximumD; DIndex += 1) {
  2089. //
  2090. // Run the algorithm forward. Compute all the coordinates for each
  2091. // k line between -D and D in steps of two.
  2092. //
  2093. for (KIndex = DownK - DIndex; KIndex <= DownK + DIndex; KIndex += 2) {
  2094. //
  2095. // Use the better of the two x coordinates of the adjacent k lines,
  2096. // being careful at the edges to avoid comparing against
  2097. // impossible (unreachable) k lines.
  2098. //
  2099. if (KIndex == DownK - DIndex) {
  2100. //
  2101. // Take the same x coordinate as the k line above (meaning go
  2102. // down).
  2103. //
  2104. SnakeX = DownVector[DownOffset + KIndex + 1];
  2105. } else {
  2106. //
  2107. // Take the 1 + the x coordinate below, meaning go right.
  2108. // Switch to going down if it is possible and better (starts
  2109. // further). In a tie, go down.
  2110. //
  2111. SnakeX = DownVector[DownOffset + KIndex - 1] + 1;
  2112. if ((KIndex < DownK + DIndex) &&
  2113. (DownVector[DownOffset + KIndex + 1] >= SnakeX)) {
  2114. SnakeX = DownVector[DownOffset + KIndex + 1];
  2115. }
  2116. }
  2117. SnakeY = SnakeX - KIndex;
  2118. //
  2119. // Take as many diagonals as possible.
  2120. //
  2121. while ((SnakeX < UpperA) && (SnakeY < UpperB)) {
  2122. Status = DiffCompareLines(Context,
  2123. FileA,
  2124. FileB,
  2125. SnakeX,
  2126. SnakeY);
  2127. if (Status != 0) {
  2128. break;
  2129. }
  2130. SnakeX += 1;
  2131. SnakeY += 1;
  2132. }
  2133. DownVector[DownOffset + KIndex] = SnakeX;
  2134. //
  2135. // Check for overlap.
  2136. //
  2137. if ((DeltaIsOdd != FALSE) &&
  2138. (KIndex > UpK - DIndex) && (KIndex < UpK + DIndex)) {
  2139. if (UpVector[UpOffset + KIndex] <=
  2140. DownVector[DownOffset + KIndex]) {
  2141. *MiddleSnakeX = DownVector[DownOffset + KIndex];
  2142. *MiddleSnakeY = *MiddleSnakeX - KIndex;
  2143. return;
  2144. }
  2145. }
  2146. }
  2147. //
  2148. // Run the algorithm in reverse. Compute all the coordinates for each k
  2149. // line between -D and D in steps of two.
  2150. //
  2151. for (KIndex = UpK - DIndex; KIndex <= UpK + DIndex; KIndex += 2) {
  2152. //
  2153. // Decide whether to take the path from the bottom or right.
  2154. //
  2155. if (KIndex == UpK + DIndex) {
  2156. //
  2157. // Take the x position from the lower k line, meaning go up.
  2158. //
  2159. SnakeX = UpVector[UpOffset + KIndex - 1];
  2160. } else {
  2161. //
  2162. // Go right, unless going up is better.
  2163. //
  2164. SnakeX = UpVector[UpOffset + KIndex + 1] - 1;
  2165. if ((KIndex > UpK - DIndex) &&
  2166. (UpVector[UpOffset + KIndex - 1] < SnakeX)) {
  2167. SnakeX = UpVector[UpOffset + KIndex - 1];
  2168. }
  2169. }
  2170. SnakeY = SnakeX - KIndex;
  2171. //
  2172. // Take as many diagonals as possible.
  2173. //
  2174. while ((SnakeX > LowerA) && (SnakeY > LowerB)) {
  2175. Status = DiffCompareLines(Context,
  2176. FileA,
  2177. FileB,
  2178. SnakeX - 1,
  2179. SnakeY - 1);
  2180. if (Status != 0) {
  2181. break;
  2182. }
  2183. SnakeX -= 1;
  2184. SnakeY -= 1;
  2185. }
  2186. UpVector[UpOffset + KIndex] = SnakeX;
  2187. //
  2188. // Check for overlap.
  2189. //
  2190. if ((DeltaIsOdd == FALSE) &&
  2191. (KIndex >= DownK - DIndex) && (KIndex <= DownK + DIndex)) {
  2192. if (UpVector[UpOffset + KIndex] <=
  2193. DownVector[DownOffset + KIndex]) {
  2194. *MiddleSnakeX = DownVector[DownOffset + KIndex];
  2195. *MiddleSnakeY = *MiddleSnakeX - KIndex;
  2196. return;
  2197. }
  2198. }
  2199. }
  2200. }
  2201. //
  2202. // A middle snake should always be found.
  2203. //
  2204. assert(FALSE);
  2205. return;
  2206. }
  2207. INT
  2208. DiffCompareLines (
  2209. PDIFF_CONTEXT Context,
  2210. PDIFF_FILE FileA,
  2211. PDIFF_FILE FileB,
  2212. UINTN LineIndexA,
  2213. UINTN LineIndexB
  2214. )
  2215. /*++
  2216. Routine Description:
  2217. This routine compares two diff lines for equality.
  2218. Arguments:
  2219. Context - Supplies a pointer to the diff application context.
  2220. FileA - Supplies a pointer to the first file.
  2221. FileB - Supplies a pointer to the second file.
  2222. LineIndexA - Supplies the index of the line in the first file.
  2223. LineIndexB - Supplies the index of the line in the second file.
  2224. Return Value:
  2225. 0 if the lines are equal.
  2226. 1 if the linse are not equal.
  2227. --*/
  2228. {
  2229. UINTN IndexA;
  2230. UINTN IndexB;
  2231. PDIFF_LINE LineA;
  2232. PDIFF_LINE LineB;
  2233. assert(LineIndexA < FileA->LineCount);
  2234. assert(LineIndexB < FileB->LineCount);
  2235. LineA = FileA->Lines[LineIndexA];
  2236. LineB = FileB->Lines[LineIndexB];
  2237. //
  2238. // If the hashes are not equal, then the lines are definitely not equal.
  2239. // Easy peasy.
  2240. //
  2241. if (LineA->Hash != LineB->Hash) {
  2242. return 1;
  2243. }
  2244. //
  2245. // If this is the last line for either of them and there's not a newline at
  2246. // the end, then it's probably different.
  2247. //
  2248. if ((LineIndexA == FileA->LineCount - 1) &&
  2249. (FileA->NoNewlineAtEnd != FALSE)) {
  2250. if ((LineIndexB != FileB->LineCount - 1) ||
  2251. (FileB->NoNewlineAtEnd == FALSE)) {
  2252. return 1;
  2253. }
  2254. }
  2255. if ((LineIndexB == FileB->LineCount - 1) &&
  2256. (FileB->NoNewlineAtEnd != FALSE)) {
  2257. if ((LineIndexA != FileA->LineCount - 1) ||
  2258. (FileA->NoNewlineAtEnd == FALSE)) {
  2259. return 1;
  2260. }
  2261. }
  2262. //
  2263. // If not ignoring blanks, then use strcmp, as it's probably a bit more
  2264. // optimized.
  2265. //
  2266. if ((Context->Options & DIFF_OPTION_IGNORE_BLANKS) == 0) {
  2267. //
  2268. // The sizes being different is also a dead giveaway.
  2269. //
  2270. if (LineA->Size != LineB->Size) {
  2271. return 1;
  2272. }
  2273. if (strcmp(LineA->Data, LineB->Data) == 0) {
  2274. return 0;
  2275. }
  2276. return 1;
  2277. }
  2278. //
  2279. // Loop through comparing everything except whitespace.
  2280. //
  2281. IndexA = 0;
  2282. IndexB = 0;
  2283. while (TRUE) {
  2284. while (isspace(LineA->Data[IndexA]) != 0) {
  2285. IndexA += 1;
  2286. }
  2287. while (isspace(LineB->Data[IndexB]) != 0) {
  2288. IndexB += 1;
  2289. }
  2290. if (LineA->Data[IndexA] != LineB->Data[IndexB]) {
  2291. return 1;
  2292. }
  2293. //
  2294. // If either one of them is at the end, it's time to stop the loop.
  2295. //
  2296. if ((IndexA == LineA->Size - 1) || (IndexB == LineB->Size - 1)) {
  2297. //
  2298. // They're not equal if they're not both at the end. But zoom past
  2299. // any whitespace first.
  2300. //
  2301. while (isspace(LineA->Data[IndexA]) != 0) {
  2302. IndexA += 1;
  2303. }
  2304. while (isspace(LineB->Data[IndexB]) != 0) {
  2305. IndexB += 1;
  2306. }
  2307. if ((IndexA != LineA->Size - 1) || (IndexB != LineB->Size - 1)) {
  2308. return 1;
  2309. }
  2310. break;
  2311. }
  2312. IndexA += 1;
  2313. IndexB += 1;
  2314. }
  2315. return 0;
  2316. }
  2317. //
  2318. // Routines for displaying diffs.
  2319. //
  2320. VOID
  2321. DiffPrintEdDiffs (
  2322. PDIFF_CONTEXT Context,
  2323. PSTR DirectoryA,
  2324. PDIFF_FILE FileA,
  2325. PSTR DirectoryB,
  2326. PDIFF_FILE FileB
  2327. )
  2328. /*++
  2329. Routine Description:
  2330. This routine prints the precomputed differences of two files using the ed
  2331. output format.
  2332. Arguments:
  2333. Context - Supplies a pointer to the diff application context.
  2334. DirectoryA - Supplies a pointer to the directory prefix for file A.
  2335. FileA - Supplies a pointer to the first file object to compare.
  2336. DirectoryB - Supplies a pointer to the directory prefix for file B.
  2337. FileB - Supplies a pointer to the second file object to compare.
  2338. Return Value:
  2339. None.
  2340. --*/
  2341. {
  2342. INTN LineA;
  2343. INTN LineB;
  2344. PSTR LineData;
  2345. INTN LineIndex;
  2346. INTN SizeA;
  2347. INTN SizeB;
  2348. assert(Context->ContextLines == 0);
  2349. LineA = 0;
  2350. LineB = 0;
  2351. SizeA = 0;
  2352. SizeB = 0;
  2353. while ((LineA < FileA->LineCount) || (LineB < FileB->LineCount)) {
  2354. DiffFindNextHunk(Context, FileA, FileB, &LineA, &LineB, &SizeA, &SizeB);
  2355. if ((SizeA == 0) && (SizeB == 0)) {
  2356. assert((LineA == FileA->LineCount) && (LineB == FileB->LineCount));
  2357. break;
  2358. }
  2359. //
  2360. // If both files are modified, then it's a change.
  2361. //
  2362. if ((LineA < FileA->LineCount) &&
  2363. (FileA->Lines[LineA]->Modified != FALSE) &&
  2364. (LineB < FileB->LineCount) &&
  2365. (FileB->Lines[LineB]->Modified != FALSE)) {
  2366. assert((SizeA != 0) && (SizeB != 0));
  2367. if (SizeA == 1) {
  2368. printf("%d", LineA + 1);
  2369. } else {
  2370. printf("%d,%d", LineA + 1, LineA + SizeA);
  2371. }
  2372. if (SizeB == 1) {
  2373. printf("c%d\n", LineB + 1);
  2374. } else {
  2375. printf("c%d,%d\n", LineB + 1, LineB + SizeB);
  2376. }
  2377. //
  2378. // If only files A is modified, then it's a deletion.
  2379. //
  2380. } else if ((LineA < FileA->LineCount) &&
  2381. (FileA->Lines[LineA]->Modified != FALSE)) {
  2382. assert((SizeA != 0) && (SizeB == 0));
  2383. if (SizeA == 1) {
  2384. printf("%dd%d\n", LineA + 1, LineB);
  2385. } else {
  2386. printf("%d,%dd%d\n", LineA + 1, LineA + SizeA, LineB);
  2387. }
  2388. //
  2389. // It must be an insertion.
  2390. //
  2391. } else {
  2392. assert((LineB < FileB->LineCount) &&
  2393. (FileB->Lines[LineB]->Modified != FALSE));
  2394. assert((SizeB != 0) && (SizeA == 0));
  2395. if (SizeB == 1) {
  2396. printf("%da%d\n", LineA, LineB + 1);
  2397. } else {
  2398. printf("%da%d,%d\n", LineA, LineB + 1, LineB + SizeB);
  2399. }
  2400. }
  2401. //
  2402. // Print the contents.
  2403. //
  2404. for (LineIndex = 0; LineIndex < SizeA; LineIndex += 1) {
  2405. assert(FileA->Lines[LineA + LineIndex]->Modified != FALSE);
  2406. LineData = FileA->Lines[LineA + LineIndex]->Data;
  2407. if ((Context->Options & DIFF_OPTION_COLOR) != 0) {
  2408. SwPrintInColor(ConsoleColorDefault,
  2409. DIFF_DELETION_COLOR,
  2410. "< %s\n",
  2411. LineData);
  2412. } else {
  2413. printf("< %s\n", LineData);
  2414. }
  2415. }
  2416. if ((SizeA != 0) &&
  2417. (LineA + SizeA == FileA->LineCount) &&
  2418. (FileA->NoNewlineAtEnd != FALSE)) {
  2419. printf("\\ No newline at end of file\n");
  2420. }
  2421. if ((SizeA != 0) && (SizeB != 0)) {
  2422. printf("---\n");
  2423. }
  2424. for (LineIndex = 0; LineIndex < SizeB; LineIndex += 1) {
  2425. assert(FileB->Lines[LineB + LineIndex]->Modified != FALSE);
  2426. LineData = FileB->Lines[LineB + LineIndex]->Data;
  2427. if ((Context->Options & DIFF_OPTION_COLOR) != 0) {
  2428. SwPrintInColor(ConsoleColorDefault,
  2429. DIFF_INSERTION_COLOR,
  2430. "> %s\n",
  2431. LineData);
  2432. } else {
  2433. printf("> %s\n", LineData);
  2434. }
  2435. }
  2436. if ((SizeB != 0) &&
  2437. (LineB + SizeB == FileB->LineCount) &&
  2438. (FileB->NoNewlineAtEnd != FALSE)) {
  2439. printf("\\ No newline at end of file\n");
  2440. }
  2441. //
  2442. // Advance beyond this hunk.
  2443. //
  2444. LineA += SizeA;
  2445. LineB += SizeB;
  2446. }
  2447. return;
  2448. }
  2449. VOID
  2450. DiffPrintContextDiffs (
  2451. PDIFF_CONTEXT Context,
  2452. PSTR DirectoryA,
  2453. PDIFF_FILE FileA,
  2454. PSTR DirectoryB,
  2455. PDIFF_FILE FileB
  2456. )
  2457. /*++
  2458. Routine Description:
  2459. This routine prints the precomputed differences of two files using the
  2460. context output format.
  2461. Arguments:
  2462. Context - Supplies a pointer to the diff application context.
  2463. DirectoryA - Supplies a pointer to the directory prefix for file A.
  2464. FileA - Supplies a pointer to the first file object to compare.
  2465. DirectoryB - Supplies a pointer to the directory prefix for file B.
  2466. FileB - Supplies a pointer to the second file object to compare.
  2467. Return Value:
  2468. None.
  2469. --*/
  2470. {
  2471. BOOL ChangesPresent;
  2472. INTN IndexA;
  2473. INTN IndexB;
  2474. INTN LineA;
  2475. INTN LineB;
  2476. PSTR LineData;
  2477. CHAR Marker;
  2478. struct tm *ModificationTime;
  2479. INTN SizeA;
  2480. INTN SizeB;
  2481. CHAR Timestamp[CONTEXT_DIFF_TIMESTAMP_SIZE];
  2482. //
  2483. // Print the heading.
  2484. //
  2485. ModificationTime = localtime(&(FileA->ModificationTime));
  2486. strftime(Timestamp,
  2487. sizeof(Timestamp),
  2488. CONTEXT_DIFF_TIMESTAMP_FORMAT,
  2489. ModificationTime);
  2490. if (DirectoryA != NULL) {
  2491. printf("*** %s/%s\t%s\n", DirectoryA, FileA->Name, Timestamp);
  2492. } else {
  2493. printf("*** %s\t%s\n", FileA->Name, Timestamp);
  2494. }
  2495. ModificationTime = localtime(&(FileB->ModificationTime));
  2496. strftime(Timestamp,
  2497. sizeof(Timestamp),
  2498. CONTEXT_DIFF_TIMESTAMP_FORMAT,
  2499. ModificationTime);
  2500. if (DirectoryB != NULL) {
  2501. printf("--- %s/%s\t%s\n", DirectoryB, FileB->Name, Timestamp);
  2502. } else {
  2503. printf("--- %s\t%s\n", FileB->Name, Timestamp);
  2504. }
  2505. LineA = 0;
  2506. LineB = 0;
  2507. SizeA = 0;
  2508. SizeB = 0;
  2509. while ((LineA < FileA->LineCount) || (LineB < FileB->LineCount)) {
  2510. DiffFindNextHunk(Context, FileA, FileB, &LineA, &LineB, &SizeA, &SizeB);
  2511. if ((SizeA == 0) && (SizeB == 0)) {
  2512. assert((LineA == FileA->LineCount) && (LineB == FileB->LineCount));
  2513. break;
  2514. }
  2515. printf("***************\n");
  2516. //
  2517. // Print the top half of the change, the deletions with context.
  2518. //
  2519. if (SizeA <= 1) {
  2520. printf("*** %d ***\n", LineA + SizeA);
  2521. } else {
  2522. printf("*** %d,%d ***\n", LineA + 1, LineA + SizeA);
  2523. }
  2524. ChangesPresent = FALSE;
  2525. for (IndexA = LineA; IndexA < LineA + SizeA; IndexA += 1) {
  2526. if (FileA->Lines[IndexA]->Modified != FALSE) {
  2527. ChangesPresent = TRUE;
  2528. break;
  2529. }
  2530. }
  2531. if (ChangesPresent != FALSE) {
  2532. IndexB = LineB;
  2533. for (IndexA = LineA; IndexA < LineA + SizeA; IndexA += 1) {
  2534. LineData = FileA->Lines[IndexA]->Data;
  2535. //
  2536. // If the first file is not modified, it's context.
  2537. //
  2538. if (FileA->Lines[IndexA]->Modified == FALSE) {
  2539. Marker = ' ';
  2540. if ((IndexB < FileB->LineCount) &&
  2541. (FileB->Lines[IndexB]->Modified == FALSE)) {
  2542. IndexB += 1;
  2543. }
  2544. //
  2545. // The line in file A is modified, so it's a deletion. Look at
  2546. // file B to figure out if it's a change or pure deletion.
  2547. //
  2548. } else {
  2549. if ((IndexB >= FileB->LineCount) ||
  2550. (FileB->Lines[IndexB]->Modified == FALSE)) {
  2551. Marker = '-';
  2552. if (IndexB < FileB->LineCount) {
  2553. IndexB += 1;
  2554. }
  2555. } else {
  2556. Marker = '!';
  2557. IndexB += 1;
  2558. }
  2559. }
  2560. if (((Context->Options & DIFF_OPTION_COLOR) != 0) &&
  2561. (Marker != ' ')) {
  2562. SwPrintInColor(ConsoleColorDefault,
  2563. DIFF_DELETION_COLOR,
  2564. "%c %s\n",
  2565. Marker,
  2566. LineData);
  2567. } else {
  2568. printf("%c %s\n", Marker, LineData);
  2569. }
  2570. }
  2571. if ((SizeA != 0) &&
  2572. (IndexA == FileA->LineCount) &&
  2573. (FileA->NoNewlineAtEnd != FALSE) &&
  2574. (Marker != ' ')) {
  2575. printf("\\ No newline at end of file\n");
  2576. }
  2577. }
  2578. //
  2579. // Print the bottom half of the change, the additions with context.
  2580. //
  2581. if (SizeB <= 1) {
  2582. printf("--- %d ---\n", LineB + SizeB);
  2583. } else {
  2584. printf("--- %d,%d ---\n", LineB + 1, LineB + SizeB);
  2585. }
  2586. ChangesPresent = FALSE;
  2587. for (IndexB = LineB; IndexB < LineB + SizeB; IndexB += 1) {
  2588. if (FileB->Lines[IndexB]->Modified != FALSE) {
  2589. ChangesPresent = TRUE;
  2590. break;
  2591. }
  2592. }
  2593. if (ChangesPresent != FALSE) {
  2594. IndexA = LineA;
  2595. for (IndexB = LineB; IndexB < LineB + SizeB; IndexB += 1) {
  2596. LineData = FileB->Lines[IndexB]->Data;
  2597. //
  2598. // If the first file is not modified, it's context.
  2599. //
  2600. if (FileB->Lines[IndexB]->Modified == FALSE) {
  2601. Marker = ' ';
  2602. if ((IndexA < FileA->LineCount) &&
  2603. (FileA->Lines[IndexA]->Modified == FALSE)) {
  2604. IndexA += 1;
  2605. }
  2606. //
  2607. // The line in file B is modified, so it's an insertion. Look at
  2608. // file A to figure out if it's a change or pure insertion.
  2609. //
  2610. } else {
  2611. if ((IndexA >= FileA->LineCount) ||
  2612. (FileA->Lines[IndexA]->Modified == FALSE)) {
  2613. Marker = '+';
  2614. if (IndexA < FileA->LineCount) {
  2615. IndexA += 1;
  2616. }
  2617. } else {
  2618. Marker = '!';
  2619. IndexA += 1;
  2620. }
  2621. }
  2622. if (((Context->Options & DIFF_OPTION_COLOR) != 0) &&
  2623. (Marker != ' ')) {
  2624. SwPrintInColor(ConsoleColorDefault,
  2625. DIFF_INSERTION_COLOR,
  2626. "%c %s\n",
  2627. Marker,
  2628. LineData);
  2629. } else {
  2630. printf("%c %s\n", Marker, LineData);
  2631. }
  2632. }
  2633. if ((SizeB != 0) &&
  2634. (IndexB == FileB->LineCount) &&
  2635. (FileB->NoNewlineAtEnd != FALSE) &&
  2636. (Marker != ' ')) {
  2637. printf("\\ No newline at end of file\n");
  2638. }
  2639. }
  2640. //
  2641. // Advance beyond this hunk.
  2642. //
  2643. LineA += SizeA;
  2644. LineB += SizeB;
  2645. }
  2646. return;
  2647. }
  2648. VOID
  2649. DiffPrintUnifiedDiffs (
  2650. PDIFF_CONTEXT Context,
  2651. PSTR DirectoryA,
  2652. PDIFF_FILE FileA,
  2653. PSTR DirectoryB,
  2654. PDIFF_FILE FileB
  2655. )
  2656. /*++
  2657. Routine Description:
  2658. This routine prints the precomputed differences of two files using the
  2659. unified output format.
  2660. Arguments:
  2661. Context - Supplies a pointer to the diff application context.
  2662. DirectoryA - Supplies a pointer to the directory prefix for file A.
  2663. FileA - Supplies a pointer to the first file object to compare.
  2664. DirectoryB - Supplies a pointer to the directory prefix for file B.
  2665. FileB - Supplies a pointer to the second file object to compare.
  2666. Return Value:
  2667. None.
  2668. --*/
  2669. {
  2670. INTN IndexA;
  2671. INTN IndexB;
  2672. INTN LineA;
  2673. INTN LineB;
  2674. PSTR LineData;
  2675. struct tm *ModificationTime;
  2676. INTN SizeA;
  2677. INTN SizeB;
  2678. CHAR Timestamp[CONTEXT_DIFF_TIMESTAMP_SIZE];
  2679. //
  2680. // Print the heading.
  2681. //
  2682. ModificationTime = localtime(&(FileA->ModificationTime));
  2683. strftime(Timestamp,
  2684. sizeof(Timestamp),
  2685. CONTEXT_DIFF_TIMESTAMP_FORMAT,
  2686. ModificationTime);
  2687. if (DirectoryA != NULL) {
  2688. printf("--- %s/%s\t%s\n", DirectoryA, FileA->Name, Timestamp);
  2689. } else {
  2690. printf("--- %s\t%s\n", FileA->Name, Timestamp);
  2691. }
  2692. ModificationTime = localtime(&(FileB->ModificationTime));
  2693. strftime(Timestamp,
  2694. sizeof(Timestamp),
  2695. CONTEXT_DIFF_TIMESTAMP_FORMAT,
  2696. ModificationTime);
  2697. if (DirectoryB != NULL) {
  2698. printf("+++ %s/%s\t%s\n", DirectoryB, FileB->Name, Timestamp);
  2699. } else {
  2700. printf("+++ %s\t%s\n", FileB->Name, Timestamp);
  2701. }
  2702. LineA = 0;
  2703. LineB = 0;
  2704. SizeA = 0;
  2705. SizeB = 0;
  2706. while ((LineA < FileA->LineCount) || (LineB < FileB->LineCount)) {
  2707. DiffFindNextHunk(Context, FileA, FileB, &LineA, &LineB, &SizeA, &SizeB);
  2708. if ((SizeA == 0) && (SizeB == 0)) {
  2709. assert((LineA == FileA->LineCount) && (LineB == FileB->LineCount));
  2710. break;
  2711. }
  2712. //
  2713. // Print the hunk marker.
  2714. //
  2715. if (SizeA == 0) {
  2716. printf("@@ -%d,0 ", LineA);
  2717. } else if (SizeA == 1) {
  2718. printf("@@ -%d ", LineA + SizeA);
  2719. } else {
  2720. printf("@@ -%d,%d ", LineA + 1, SizeA);
  2721. }
  2722. if (SizeB == 0) {
  2723. printf("+%d,0 @@\n", LineB);
  2724. } else if (SizeB == 1) {
  2725. printf("+%d @@\n", LineB + SizeB);
  2726. } else {
  2727. printf("+%d,%d @@\n", LineB + 1, SizeB);
  2728. }
  2729. IndexA = LineA;
  2730. IndexB = LineB;
  2731. while ((IndexA < LineA + SizeA) || (IndexB < LineB + SizeB)) {
  2732. //
  2733. // Print any context lines.
  2734. //
  2735. while ((IndexA < LineA + SizeA) &&
  2736. (FileA->Lines[IndexA]->Modified == FALSE) &&
  2737. (IndexB < LineB + SizeB) &&
  2738. (FileB->Lines[IndexB]->Modified == FALSE)) {
  2739. LineData = FileA->Lines[IndexA]->Data;
  2740. IndexA += 1;
  2741. IndexB += 1;
  2742. printf(" %s\n", LineData);
  2743. }
  2744. //
  2745. // Print all deletion lines together.
  2746. //
  2747. while ((IndexA < LineA + SizeA) &&
  2748. (FileA->Lines[IndexA]->Modified != FALSE)) {
  2749. LineData = FileA->Lines[IndexA]->Data;
  2750. IndexA += 1;
  2751. if ((Context->Options & DIFF_OPTION_COLOR) != 0) {
  2752. SwPrintInColor(ConsoleColorDefault,
  2753. DIFF_DELETION_COLOR,
  2754. "-%s\n",
  2755. LineData);
  2756. } else {
  2757. printf("-%s\n", LineData);
  2758. }
  2759. }
  2760. if ((SizeA != 0) &&
  2761. (IndexA == FileA->LineCount) &&
  2762. (FileA->NoNewlineAtEnd != FALSE)) {
  2763. printf("\\ No newline at end of file\n");
  2764. }
  2765. //
  2766. // Print all insertion lines together.
  2767. //
  2768. while ((IndexB < LineB + SizeB) &&
  2769. (FileB->Lines[IndexB]->Modified != FALSE)) {
  2770. LineData = FileB->Lines[IndexB]->Data;
  2771. IndexB += 1;
  2772. if ((Context->Options & DIFF_OPTION_COLOR) != 0) {
  2773. SwPrintInColor(ConsoleColorDefault,
  2774. DIFF_INSERTION_COLOR,
  2775. "+%s\n",
  2776. LineData);
  2777. } else {
  2778. printf("+%s\n", LineData);
  2779. }
  2780. }
  2781. if ((SizeB != 0) &&
  2782. (IndexB == FileB->LineCount) &&
  2783. (FileB->NoNewlineAtEnd != FALSE)) {
  2784. printf("\\ No newline at end of file\n");
  2785. }
  2786. }
  2787. //
  2788. // Advance beyond this hunk.
  2789. //
  2790. LineA += SizeA;
  2791. LineB += SizeB;
  2792. }
  2793. return;
  2794. }
  2795. VOID
  2796. DiffFindNextHunk (
  2797. PDIFF_CONTEXT Context,
  2798. PDIFF_FILE FileA,
  2799. PDIFF_FILE FileB,
  2800. PINTN LineA,
  2801. PINTN LineB,
  2802. PINTN SizeA,
  2803. PINTN SizeB
  2804. )
  2805. /*++
  2806. Routine Description:
  2807. This routine finds the next diff hunk of two precomputed diffs.
  2808. Arguments:
  2809. Context - Supplies a pointer to the diff application context.
  2810. FileA - Supplies a pointer to the first file object.
  2811. FileB - Supplies a pointer to the second file object.
  2812. LineA - Supplies a pointer that on input contains the zero-based line of
  2813. A to start from. On output, this will contain the line index of the
  2814. next hunk.
  2815. LineB - Supplies a pointer that on input contains the zero-based line of
  2816. B to start from. On output, this will contain the line index of the
  2817. next hunk.
  2818. SizeA - Supplies a pointer where the size of the next hunk for A in lines
  2819. will be returned.
  2820. SizeB - Supplies a pointer where the size of the next hunk for B in lines
  2821. will be returned.
  2822. Return Value:
  2823. None.
  2824. --*/
  2825. {
  2826. INTN ContextA;
  2827. INTN ContextB;
  2828. INTN ContextLines;
  2829. INTN OriginalLineA;
  2830. INTN OriginalLineB;
  2831. *SizeA = 0;
  2832. *SizeB = 0;
  2833. OriginalLineA = *LineA;
  2834. OriginalLineB = *LineB;
  2835. while ((*LineA < FileA->LineCount) || (*LineB < FileB->LineCount)) {
  2836. //
  2837. // If file A ended, then the rest of file B is a hunk.
  2838. //
  2839. if (*LineA == FileA->LineCount) {
  2840. *SizeB = FileB->LineCount - *LineB;
  2841. return;
  2842. //
  2843. // If file B ended, then the rest of file A is a hunk.
  2844. //
  2845. } else if (*LineB == FileB->LineCount) {
  2846. *SizeA = FileA->LineCount - *LineA;
  2847. return;
  2848. //
  2849. // If either line is modified, then a hunk has been found.
  2850. //
  2851. } else if ((FileA->Lines[*LineA]->Modified != FALSE) ||
  2852. (FileB->Lines[*LineB]->Modified != FALSE)) {
  2853. break;
  2854. }
  2855. *LineA += 1;
  2856. *LineB += 1;
  2857. }
  2858. //
  2859. // Loop advancing past the diff and lines of context.
  2860. //
  2861. while (TRUE) {
  2862. //
  2863. // Advance through modified lines in A.
  2864. //
  2865. while ((*LineA + *SizeA < FileA->LineCount) &&
  2866. (FileA->Lines[*LineA + *SizeA]->Modified != FALSE)) {
  2867. *SizeA += 1;
  2868. }
  2869. //
  2870. // Advance through modified lines in B.
  2871. //
  2872. while ((*LineB + *SizeB < FileB->LineCount) &&
  2873. (FileB->Lines[*LineB + *SizeB]->Modified != FALSE)) {
  2874. *SizeB += 1;
  2875. }
  2876. //
  2877. // Now try to advance through the lines of context as well. If a
  2878. // modification is found in either line, then the next hunk blurs into
  2879. // this one. Look past two sets of context lines because any fewer and
  2880. // the next hunk will bleed back up into this one.
  2881. //
  2882. ContextLines = 0;
  2883. ContextA = 0;
  2884. ContextB = 0;
  2885. while (ContextLines < (Context->ContextLines * 2) + 1) {
  2886. if ((*LineA + *SizeA + ContextA < FileA->LineCount) &&
  2887. (FileA->Lines[*LineA + *SizeA + ContextA]->Modified != FALSE)) {
  2888. break;
  2889. }
  2890. if ((*LineB + *SizeB + ContextB < FileB->LineCount) &&
  2891. (FileB->Lines[*LineB + *SizeB + ContextB]->Modified != FALSE)) {
  2892. break;
  2893. }
  2894. if (*LineA + *SizeA + ContextA < FileA->LineCount) {
  2895. ContextA += 1;
  2896. }
  2897. if (*LineB + *SizeB + ContextB < FileB->LineCount) {
  2898. ContextB += 1;
  2899. }
  2900. ContextLines += 1;
  2901. }
  2902. assert(*LineA + *SizeA + ContextA <= FileA->LineCount);
  2903. assert(*LineB + *SizeB + ContextB <= FileB->LineCount);
  2904. if ((ContextLines == (Context->ContextLines * 2) + 1) ||
  2905. ((ContextA == 0) && (ContextB == 0))) {
  2906. //
  2907. // Add up to the requested amount of context lines to the size.
  2908. //
  2909. if (ContextA > Context->ContextLines) {
  2910. ContextA = Context->ContextLines;
  2911. }
  2912. if (ContextB > Context->ContextLines) {
  2913. ContextB = Context->ContextLines;
  2914. }
  2915. *SizeA += ContextA;
  2916. *SizeB += ContextB;
  2917. break;
  2918. //
  2919. // Otherwise, add all the context lines consumed to the hunk.
  2920. //
  2921. } else {
  2922. *SizeA += ContextA;
  2923. *SizeB += ContextB;
  2924. }
  2925. }
  2926. //
  2927. // If both hunks are of size zero, then there were no more diffs.
  2928. //
  2929. if ((*SizeA == 0) && (*SizeB == 0)) {
  2930. return;
  2931. }
  2932. //
  2933. // Also back up to provide the context lines at the beginning.
  2934. //
  2935. ContextLines = Context->ContextLines;
  2936. if (ContextLines > *LineA) {
  2937. ContextLines = *LineA;
  2938. }
  2939. if (*LineA - ContextLines < OriginalLineA) {
  2940. ContextLines = *LineA - OriginalLineA;
  2941. }
  2942. *LineA -= ContextLines;
  2943. *SizeA += ContextLines;
  2944. ContextLines = Context->ContextLines;
  2945. if (ContextLines > *LineB) {
  2946. ContextLines = *LineB;
  2947. }
  2948. if (*LineB - ContextLines < OriginalLineB) {
  2949. ContextLines = *LineB - OriginalLineB;
  2950. }
  2951. *LineB -= ContextLines;
  2952. *SizeB += ContextLines;
  2953. return;
  2954. }