ydiff.c 88 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490
  1. /*
  2. Copyright (C) 2007, 2010 Free Software Foundation, Inc.
  3. Written by:
  4. 2007 Daniel Borca <dborca@yahoo.com>
  5. 2010 Slava Zanko <slavazanko@gmail.com>
  6. 2010 Andrew Borodin <aborodin@vmail.ru>
  7. 2010 Ilia Maslakov <il.smind@gmail.com>
  8. This program is free software; you can redistribute it and/or modify
  9. it under the terms of the GNU General Public License as published by
  10. the Free Software Foundation; either version 2 of the License, or
  11. (at your option) any later version.
  12. This program is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. GNU General Public License for more details.
  16. You should have received a copy of the GNU General Public License
  17. along with this program; if not, write to the Free Software
  18. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. #include <config.h>
  21. #include <ctype.h>
  22. #include <errno.h>
  23. #include <fcntl.h>
  24. #include <stdlib.h>
  25. #include <sys/stat.h>
  26. #include <sys/types.h>
  27. #include <sys/wait.h>
  28. #include "lib/global.h"
  29. #include "lib/tty/tty.h"
  30. #include "lib/tty/color.h"
  31. #include "lib/tty/key.h"
  32. #include "lib/skin.h" /* EDITOR_NORMAL_COLOR */
  33. #include "lib/vfs/mc-vfs/vfs.h" /* mc_opendir, mc_readdir, mc_closedir, */
  34. #include "lib/util.h"
  35. #include "lib/widget.h"
  36. #include "lib/charsets.h"
  37. #include "src/filemanager/cmd.h"
  38. #include "src/filemanager/midnight.h" /* Needed for current_panel and other_panel */
  39. #include "src/filemanager/layout.h" /* Needed for get_current_index and get_other_panel */
  40. #include "src/keybind-defaults.h"
  41. #include "src/help.h"
  42. #include "src/history.h"
  43. #include "src/main.h" /* mc_run_mode, midnight_shutdown */
  44. #include "src/selcodepage.h"
  45. #include "ydiff.h"
  46. #include "internal.h"
  47. /*** global variables ****************************************************************************/
  48. /*** file scope macro definitions ****************************************************************/
  49. #define g_array_foreach(a, TP, cbf) \
  50. do { \
  51. size_t g_array_foreach_i;\
  52. TP *g_array_foreach_var=NULL; \
  53. for (g_array_foreach_i=0;g_array_foreach_i < a->len; g_array_foreach_i++) \
  54. { \
  55. g_array_foreach_var = &g_array_index(a,TP,g_array_foreach_i); \
  56. (*cbf) (g_array_foreach_var); \
  57. } \
  58. } while (0)
  59. #define FILE_READ_BUF 4096
  60. #define FILE_FLAG_TEMP (1 << 0)
  61. #define OPTX 56
  62. #define OPTY 17
  63. #define ADD_CH '+'
  64. #define DEL_CH '-'
  65. #define CHG_CH '*'
  66. #define EQU_CH ' '
  67. #define HDIFF_ENABLE 1
  68. #define HDIFF_MINCTX 5
  69. #define HDIFF_DEPTH 10
  70. #define FILE_DIRTY(fs) \
  71. do \
  72. { \
  73. (fs)->pos = 0; \
  74. (fs)->len = 0; \
  75. } \
  76. while (0)
  77. /*** file scope type declarations ****************************************************************/
  78. /*** file scope variables ************************************************************************/
  79. /*** file scope functions ************************************************************************/
  80. /* --------------------------------------------------------------------------------------------- */
  81. static inline int
  82. TAB_SKIP (int ts, int pos)
  83. {
  84. if (ts > 0 && ts < 9)
  85. return ts - pos % ts;
  86. else
  87. return 8 - pos % 8;
  88. }
  89. /* --------------------------------------------------------------------------------------------- */
  90. static void
  91. dview_set_codeset (WDiff * dview)
  92. {
  93. #ifdef HAVE_CHARSET
  94. const char *encoding_id = NULL;
  95. dview->utf8 = TRUE;
  96. encoding_id = get_codepage_id (source_codepage >= 0 ? source_codepage : display_codepage);
  97. if (encoding_id != NULL)
  98. {
  99. GIConv conv;
  100. conv = str_crt_conv_from (encoding_id);
  101. if (conv != INVALID_CONV)
  102. {
  103. if (dview->converter != str_cnv_from_term)
  104. str_close_conv (dview->converter);
  105. dview->converter = conv;
  106. }
  107. dview->utf8 = (gboolean) str_isutf8 (encoding_id);
  108. }
  109. #else
  110. (void) dview;
  111. #endif
  112. }
  113. /* --------------------------------------------------------------------------------------------- */
  114. static void
  115. dview_select_encoding (WDiff * dview)
  116. {
  117. #ifdef HAVE_CHARSET
  118. if (do_select_codepage ())
  119. dview_set_codeset (dview);
  120. #else
  121. (void) dview;
  122. #endif
  123. }
  124. static gboolean
  125. rewrite_backup_content (const char *from_file_name, const char *to_file_name)
  126. {
  127. FILE *backup_fd;
  128. char *contents;
  129. gsize length;
  130. if (!g_file_get_contents (from_file_name, &contents, &length, NULL))
  131. return FALSE;
  132. backup_fd = fopen (to_file_name, "w");
  133. if (backup_fd == NULL)
  134. {
  135. g_free (contents);
  136. return FALSE;
  137. }
  138. length = fwrite ((const void *) contents, length, 1, backup_fd);
  139. fflush (backup_fd);
  140. fclose (backup_fd);
  141. g_free (contents);
  142. return TRUE;
  143. }
  144. /* buffered I/O ************************************************************* */
  145. /**
  146. * Try to open a temporary file.
  147. *
  148. * \param[out] name address of a pointer to store the temporary name
  149. *
  150. * \return file descriptor on success, negative on error
  151. *
  152. * \note the name is not altered if this function fails
  153. */
  154. static int
  155. open_temp (void **name)
  156. {
  157. int fd;
  158. char *diff_file_name = NULL;
  159. fd = mc_mkstemps (&diff_file_name, "mcdiff", NULL);
  160. if (fd == -1)
  161. {
  162. message (D_ERROR, MSG_ERROR,
  163. _("Cannot create temporary diff file\n%s"), unix_error_string (errno));
  164. return -1;
  165. }
  166. *name = diff_file_name;
  167. return fd;
  168. }
  169. /* --------------------------------------------------------------------------------------------- */
  170. /**
  171. * Alocate file structure and associate file descriptor to it.
  172. *
  173. * \param fd file descriptor
  174. *
  175. * \return file structure
  176. */
  177. static FBUF *
  178. f_dopen (int fd)
  179. {
  180. FBUF *fs;
  181. if (fd < 0)
  182. return NULL;
  183. fs = g_try_malloc (sizeof (FBUF));
  184. if (fs == NULL)
  185. return NULL;
  186. fs->buf = g_try_malloc (FILE_READ_BUF);
  187. if (fs->buf == NULL)
  188. {
  189. g_free (fs);
  190. return NULL;
  191. }
  192. fs->fd = fd;
  193. FILE_DIRTY (fs);
  194. fs->flags = 0;
  195. fs->data = NULL;
  196. return fs;
  197. }
  198. /* --------------------------------------------------------------------------------------------- */
  199. /**
  200. * Free file structure without closing the file.
  201. *
  202. * \param fs file structure
  203. *
  204. * \return 0 on success, non-zero on error
  205. */
  206. static int
  207. f_free (FBUF * fs)
  208. {
  209. int rv = 0;
  210. if (fs->flags & FILE_FLAG_TEMP)
  211. {
  212. rv = unlink (fs->data);
  213. g_free (fs->data);
  214. }
  215. g_free (fs->buf);
  216. g_free (fs);
  217. return rv;
  218. }
  219. /* --------------------------------------------------------------------------------------------- */
  220. /**
  221. * Open a binary temporary file in R/W mode.
  222. *
  223. * \return file structure
  224. *
  225. * \note the file will be deleted when closed
  226. */
  227. static FBUF *
  228. f_temp (void)
  229. {
  230. int fd;
  231. FBUF *fs;
  232. fs = f_dopen (0);
  233. if (fs == NULL)
  234. {
  235. return NULL;
  236. }
  237. fd = open_temp (&fs->data);
  238. if (fd < 0)
  239. {
  240. f_free (fs);
  241. return NULL;
  242. }
  243. fs->fd = fd;
  244. fs->flags = FILE_FLAG_TEMP;
  245. return fs;
  246. }
  247. /* --------------------------------------------------------------------------------------------- */
  248. /**
  249. * Open a binary file in specified mode.
  250. *
  251. * \param filename file name
  252. * \param flags open mode, a combination of O_RDONLY, O_WRONLY, O_RDWR
  253. *
  254. * \return file structure
  255. */
  256. static FBUF *
  257. f_open (const char *filename, int flags)
  258. {
  259. int fd;
  260. FBUF *fs;
  261. fs = f_dopen (0);
  262. if (fs == NULL)
  263. {
  264. return NULL;
  265. }
  266. fd = open (filename, flags);
  267. if (fd < 0)
  268. {
  269. f_free (fs);
  270. return NULL;
  271. }
  272. fs->fd = fd;
  273. return fs;
  274. }
  275. /* --------------------------------------------------------------------------------------------- */
  276. /**
  277. * Read a line of bytes from file until newline or EOF.
  278. *
  279. * \param buf destination buffer
  280. * \param size size of buffer
  281. * \param fs file structure
  282. *
  283. * \return number of bytes read
  284. *
  285. * \note does not stop on null-byte
  286. * \note buf will not be null-terminated
  287. */
  288. static size_t
  289. f_gets (char *buf, size_t size, FBUF * fs)
  290. {
  291. size_t j = 0;
  292. do
  293. {
  294. int i;
  295. int stop = 0;
  296. for (i = fs->pos; j < size && i < fs->len && !stop; i++, j++)
  297. {
  298. buf[j] = fs->buf[i];
  299. if (buf[j] == '\n')
  300. {
  301. stop = 1;
  302. }
  303. }
  304. fs->pos = i;
  305. if (j == size || stop)
  306. {
  307. break;
  308. }
  309. fs->pos = 0;
  310. fs->len = read (fs->fd, fs->buf, FILE_READ_BUF);
  311. }
  312. while (fs->len > 0);
  313. return j;
  314. }
  315. /* --------------------------------------------------------------------------------------------- */
  316. /**
  317. * Seek into file.
  318. *
  319. * \param fs file structure
  320. * \param off offset
  321. * \param whence seek directive: SEEK_SET, SEEK_CUR or SEEK_END
  322. *
  323. * \return position in file, starting from begginning
  324. *
  325. * \note avoids thrashing read cache when possible
  326. */
  327. static off_t
  328. f_seek (FBUF * fs, off_t off, int whence)
  329. {
  330. off_t rv;
  331. if (fs->len && whence != SEEK_END)
  332. {
  333. rv = lseek (fs->fd, 0, SEEK_CUR);
  334. if (rv != -1)
  335. {
  336. if (whence == SEEK_CUR)
  337. {
  338. whence = SEEK_SET;
  339. off += rv - fs->len + fs->pos;
  340. }
  341. if (off - rv >= -fs->len && off - rv <= 0)
  342. {
  343. fs->pos = fs->len + off - rv;
  344. return off;
  345. }
  346. }
  347. }
  348. rv = lseek (fs->fd, off, whence);
  349. if (rv != -1)
  350. {
  351. FILE_DIRTY (fs);
  352. }
  353. return rv;
  354. }
  355. /* --------------------------------------------------------------------------------------------- */
  356. /**
  357. * Seek to the beginning of file, thrashing read cache.
  358. *
  359. * \param fs file structure
  360. *
  361. * \return 0 if success, non-zero on error
  362. */
  363. static off_t
  364. f_reset (FBUF * fs)
  365. {
  366. off_t rv = lseek (fs->fd, 0, SEEK_SET);
  367. if (rv != -1)
  368. {
  369. FILE_DIRTY (fs);
  370. }
  371. return rv;
  372. }
  373. /* --------------------------------------------------------------------------------------------- */
  374. /**
  375. * Write bytes to file.
  376. *
  377. * \param fs file structure
  378. * \param buf source buffer
  379. * \param size size of buffer
  380. *
  381. * \return number of written bytes, -1 on error
  382. *
  383. * \note thrashes read cache
  384. */
  385. static ssize_t
  386. f_write (FBUF * fs, const char *buf, size_t size)
  387. {
  388. ssize_t rv = write (fs->fd, buf, size);
  389. if (rv >= 0)
  390. {
  391. FILE_DIRTY (fs);
  392. }
  393. return rv;
  394. }
  395. /* --------------------------------------------------------------------------------------------- */
  396. /**
  397. * Truncate file to the current position.
  398. *
  399. * \param fs file structure
  400. *
  401. * \return current file size on success, negative on error
  402. *
  403. * \note thrashes read cache
  404. */
  405. static off_t
  406. f_trunc (FBUF * fs)
  407. {
  408. off_t off = lseek (fs->fd, 0, SEEK_CUR);
  409. if (off != -1)
  410. {
  411. int rv = ftruncate (fs->fd, off);
  412. if (rv != 0)
  413. {
  414. off = -1;
  415. }
  416. else
  417. {
  418. FILE_DIRTY (fs);
  419. }
  420. }
  421. return off;
  422. }
  423. /* --------------------------------------------------------------------------------------------- */
  424. /**
  425. * Close file.
  426. *
  427. * \param fs file structure
  428. *
  429. * \return 0 on success, non-zero on error
  430. *
  431. * \note if this is temporary file, it is deleted
  432. */
  433. static int
  434. f_close (FBUF * fs)
  435. {
  436. int rv = close (fs->fd);
  437. f_free (fs);
  438. return rv;
  439. }
  440. /* --------------------------------------------------------------------------------------------- */
  441. /**
  442. * Create pipe stream to process.
  443. *
  444. * \param cmd shell command line
  445. * \param flags open mode, either O_RDONLY or O_WRONLY
  446. *
  447. * \return file structure
  448. */
  449. static FBUF *
  450. p_open (const char *cmd, int flags)
  451. {
  452. FILE *f;
  453. FBUF *fs;
  454. const char *type = NULL;
  455. if (flags == O_RDONLY)
  456. {
  457. type = "r";
  458. }
  459. if (flags == O_WRONLY)
  460. {
  461. type = "w";
  462. }
  463. if (type == NULL)
  464. {
  465. return NULL;
  466. }
  467. fs = f_dopen (0);
  468. if (fs == NULL)
  469. {
  470. return NULL;
  471. }
  472. f = popen (cmd, type);
  473. if (f == NULL)
  474. {
  475. f_free (fs);
  476. return NULL;
  477. }
  478. fs->fd = fileno (f);
  479. fs->data = f;
  480. return fs;
  481. }
  482. /* --------------------------------------------------------------------------------------------- */
  483. /**
  484. * Close pipe stream.
  485. *
  486. * \param fs structure
  487. *
  488. * \return 0 on success, non-zero on error
  489. */
  490. static int
  491. p_close (FBUF * fs)
  492. {
  493. int rv = pclose (fs->data);
  494. f_free (fs);
  495. return rv;
  496. }
  497. /**
  498. * Get one char (byte) from string
  499. *
  500. * \param char * str, gboolean * result
  501. *
  502. * \return int as character or 0 and result == FALSE if fail
  503. */
  504. static int
  505. dview_get_byte (char *str, gboolean * result)
  506. {
  507. if (str == NULL)
  508. {
  509. *result = FALSE;
  510. return 0;
  511. }
  512. *result = TRUE;
  513. return (unsigned char) *str;
  514. }
  515. /**
  516. * Get utf multibyte char from string
  517. *
  518. * \param char * str, int * char_width, gboolean * result
  519. *
  520. * \return int as utf character or 0 and result == FALSE if fail
  521. */
  522. static int
  523. dview_get_utf (char *str, int *char_width, gboolean * result)
  524. {
  525. int res = -1;
  526. gunichar ch;
  527. gchar *next_ch = NULL;
  528. int width = 0;
  529. *result = TRUE;
  530. if (str == NULL)
  531. {
  532. *result = FALSE;
  533. width = 0;
  534. return 0;
  535. }
  536. res = g_utf8_get_char_validated (str, -1);
  537. if (res < 0)
  538. {
  539. ch = *str;
  540. width = 0;
  541. }
  542. else
  543. {
  544. ch = res;
  545. /* Calculate UTF-8 char width */
  546. next_ch = g_utf8_next_char (str);
  547. if (next_ch)
  548. {
  549. width = next_ch - str;
  550. }
  551. else
  552. {
  553. ch = 0;
  554. width = 0;
  555. }
  556. }
  557. *char_width = width;
  558. return ch;
  559. }
  560. static int
  561. dview_str_utf8_offset_to_pos (const char *text, size_t length)
  562. {
  563. ptrdiff_t result;
  564. if (text == NULL || text[0] == '\0')
  565. return length;
  566. if (g_utf8_validate (text, -1, NULL))
  567. {
  568. result = g_utf8_offset_to_pointer (text, length) - text;
  569. }
  570. else
  571. {
  572. gunichar uni;
  573. char *tmpbuf, *buffer;
  574. buffer = tmpbuf = g_strdup (text);
  575. while (tmpbuf[0] != '\0')
  576. {
  577. uni = g_utf8_get_char_validated (tmpbuf, -1);
  578. if ((uni != (gunichar) (-1)) && (uni != (gunichar) (-2)))
  579. {
  580. tmpbuf = g_utf8_next_char (tmpbuf);
  581. }
  582. else
  583. {
  584. tmpbuf[0] = '.';
  585. tmpbuf++;
  586. }
  587. }
  588. result = g_utf8_offset_to_pointer (tmpbuf, length) - tmpbuf;
  589. g_free (buffer);
  590. }
  591. return max (length, (size_t) result);
  592. }
  593. /* --------------------------------------------------------------------------------------------- */
  594. /* diff parse *************************************************************** */
  595. /**
  596. * Read decimal number from string.
  597. *
  598. * \param[in,out] str string to parse
  599. * \param[out] n extracted number
  600. *
  601. * \return 0 if success, otherwise non-zero
  602. */
  603. static int
  604. scan_deci (const char **str, int *n)
  605. {
  606. const char *p = *str;
  607. char *q;
  608. errno = 0;
  609. *n = strtol (p, &q, 10);
  610. if (errno || p == q)
  611. {
  612. return -1;
  613. }
  614. *str = q;
  615. return 0;
  616. }
  617. /* --------------------------------------------------------------------------------------------- */
  618. /**
  619. * Parse line for diff statement.
  620. *
  621. * \param p string to parse
  622. * \param ops list of diff statements
  623. *
  624. * \return 0 if success, otherwise non-zero
  625. */
  626. static int
  627. scan_line (const char *p, GArray * ops)
  628. {
  629. DIFFCMD op;
  630. int f1, f2;
  631. int t1, t2;
  632. int cmd;
  633. int range;
  634. /* handle the following cases:
  635. * NUMaNUM[,NUM]
  636. * NUM[,NUM]cNUM[,NUM]
  637. * NUM[,NUM]dNUM
  638. * where NUM is a positive integer
  639. */
  640. if (scan_deci (&p, &f1) != 0 || f1 < 0)
  641. {
  642. return -1;
  643. }
  644. f2 = f1;
  645. range = 0;
  646. if (*p == ',')
  647. {
  648. p++;
  649. if (scan_deci (&p, &f2) != 0 || f2 < f1)
  650. {
  651. return -1;
  652. }
  653. range = 1;
  654. }
  655. cmd = *p++;
  656. if (cmd == 'a')
  657. {
  658. if (range)
  659. {
  660. return -1;
  661. }
  662. }
  663. else if (cmd != 'c' && cmd != 'd')
  664. {
  665. return -1;
  666. }
  667. if (scan_deci (&p, &t1) != 0 || t1 < 0)
  668. {
  669. return -1;
  670. }
  671. t2 = t1;
  672. range = 0;
  673. if (*p == ',')
  674. {
  675. p++;
  676. if (scan_deci (&p, &t2) != 0 || t2 < t1)
  677. {
  678. return -1;
  679. }
  680. range = 1;
  681. }
  682. if (cmd == 'd')
  683. {
  684. if (range)
  685. {
  686. return -1;
  687. }
  688. }
  689. op.a[0][0] = f1;
  690. op.a[0][1] = f2;
  691. op.cmd = cmd;
  692. op.a[1][0] = t1;
  693. op.a[1][1] = t2;
  694. g_array_append_val (ops, op);
  695. return 0;
  696. }
  697. /* --------------------------------------------------------------------------------------------- */
  698. /**
  699. * Parse diff output and extract diff statements.
  700. *
  701. * \param f stream to read from
  702. * \param ops list of diff statements to fill
  703. *
  704. * \return positive number indicating number of hunks, otherwise negative
  705. */
  706. static int
  707. scan_diff (FBUF * f, GArray * ops)
  708. {
  709. int sz;
  710. char buf[BUFSIZ];
  711. while ((sz = f_gets (buf, sizeof (buf) - 1, f)))
  712. {
  713. if (isdigit (buf[0]))
  714. {
  715. if (buf[sz - 1] != '\n')
  716. {
  717. return -1;
  718. }
  719. buf[sz] = '\0';
  720. if (scan_line (buf, ops) != 0)
  721. {
  722. return -1;
  723. }
  724. continue;
  725. }
  726. while (buf[sz - 1] != '\n' && (sz = f_gets (buf, sizeof (buf), f)) != 0)
  727. {
  728. }
  729. }
  730. return ops->len;
  731. }
  732. /* --------------------------------------------------------------------------------------------- */
  733. /**
  734. * Invoke diff and extract diff statements.
  735. *
  736. * \param args extra arguments to be passed to diff
  737. * \param extra more arguments to be passed to diff
  738. * \param file1 first file to compare
  739. * \param file2 second file to compare
  740. * \param ops list of diff statements to fill
  741. *
  742. * \return positive number indicating number of hunks, otherwise negative
  743. */
  744. static int
  745. dff_execute (const char *args, const char *extra, const char *file1, const char *file2,
  746. GArray * ops)
  747. {
  748. static const char *opt =
  749. " --old-group-format='%df%(f=l?:,%dl)d%dE\n'"
  750. " --new-group-format='%dea%dF%(F=L?:,%dL)\n'"
  751. " --changed-group-format='%df%(f=l?:,%dl)c%dF%(F=L?:,%dL)\n'"
  752. " --unchanged-group-format=''";
  753. int rv;
  754. FBUF *f;
  755. char *cmd;
  756. int code;
  757. cmd = g_strdup_printf ("diff %s %s %s \"%s\" \"%s\"", args, extra, opt, file1, file2);
  758. if (cmd == NULL)
  759. return -1;
  760. f = p_open (cmd, O_RDONLY);
  761. g_free (cmd);
  762. if (f == NULL)
  763. return -1;
  764. rv = scan_diff (f, ops);
  765. code = p_close (f);
  766. if (rv < 0 || code == -1 || !WIFEXITED (code) || WEXITSTATUS (code) == 2)
  767. return -1;
  768. return rv;
  769. }
  770. /* --------------------------------------------------------------------------------------------- */
  771. /**
  772. * Reparse and display file according to diff statements.
  773. *
  774. * \param ord 0 if displaying first file, 1 if displaying 2nd file
  775. * \param filename file name to display
  776. * \param ops list of diff statements
  777. * \param printer printf-like function to be used for displaying
  778. * \param ctx printer context
  779. *
  780. * \return 0 if success, otherwise non-zero
  781. */
  782. static int
  783. dff_reparse (int ord, const char *filename, const GArray * ops, DFUNC printer, void *ctx)
  784. {
  785. size_t i;
  786. FBUF *f;
  787. size_t sz;
  788. char buf[BUFSIZ];
  789. int line = 0;
  790. off_t off = 0;
  791. const DIFFCMD *op;
  792. int eff;
  793. int add_cmd;
  794. int del_cmd;
  795. f = f_open (filename, O_RDONLY);
  796. if (f == NULL)
  797. {
  798. return -1;
  799. }
  800. ord &= 1;
  801. eff = ord;
  802. add_cmd = 'a';
  803. del_cmd = 'd';
  804. if (ord)
  805. {
  806. add_cmd = 'd';
  807. del_cmd = 'a';
  808. }
  809. #define F1 a[eff][0]
  810. #define F2 a[eff][1]
  811. #define T1 a[ ord^1 ][0]
  812. #define T2 a[ ord^1 ][1]
  813. for (i = 0; i < ops->len; i++)
  814. {
  815. int n;
  816. op = &g_array_index (ops, DIFFCMD, i);
  817. n = op->F1 - (op->cmd != add_cmd);
  818. while (line < n && (sz = f_gets (buf, sizeof (buf), f)) != 0)
  819. {
  820. line++;
  821. printer (ctx, EQU_CH, line, off, sz, buf);
  822. off += sz;
  823. while (buf[sz - 1] != '\n')
  824. {
  825. sz = f_gets (buf, sizeof (buf), f);
  826. if (sz == 0)
  827. {
  828. printer (ctx, 0, 0, 0, 1, "\n");
  829. break;
  830. }
  831. printer (ctx, 0, 0, 0, sz, buf);
  832. off += sz;
  833. }
  834. }
  835. if (line != n)
  836. {
  837. goto err;
  838. }
  839. if (op->cmd == add_cmd)
  840. {
  841. n = op->T2 - op->T1 + 1;
  842. while (n)
  843. {
  844. printer (ctx, DEL_CH, 0, 0, 1, "\n");
  845. n--;
  846. }
  847. }
  848. if (op->cmd == del_cmd)
  849. {
  850. n = op->F2 - op->F1 + 1;
  851. while (n != 0 && (sz = f_gets (buf, sizeof (buf), f)) != 0)
  852. {
  853. line++;
  854. printer (ctx, ADD_CH, line, off, sz, buf);
  855. off += sz;
  856. while (buf[sz - 1] != '\n')
  857. {
  858. sz = f_gets (buf, sizeof (buf), f);
  859. if (sz == 0)
  860. {
  861. printer (ctx, 0, 0, 0, 1, "\n");
  862. break;
  863. }
  864. printer (ctx, 0, 0, 0, sz, buf);
  865. off += sz;
  866. }
  867. n--;
  868. }
  869. if (n)
  870. {
  871. goto err;
  872. }
  873. }
  874. if (op->cmd == 'c')
  875. {
  876. n = op->F2 - op->F1 + 1;
  877. while (n != 0 && (sz = f_gets (buf, sizeof (buf), f)) != 0)
  878. {
  879. line++;
  880. printer (ctx, CHG_CH, line, off, sz, buf);
  881. off += sz;
  882. while (buf[sz - 1] != '\n')
  883. {
  884. sz = f_gets (buf, sizeof (buf), f);
  885. if (sz == 0)
  886. {
  887. printer (ctx, 0, 0, 0, 1, "\n");
  888. break;
  889. }
  890. printer (ctx, 0, 0, 0, sz, buf);
  891. off += sz;
  892. }
  893. n--;
  894. }
  895. if (n)
  896. {
  897. goto err;
  898. }
  899. n = op->T2 - op->T1 - (op->F2 - op->F1);
  900. while (n > 0)
  901. {
  902. printer (ctx, CHG_CH, 0, 0, 1, "\n");
  903. n--;
  904. }
  905. }
  906. }
  907. #undef T2
  908. #undef T1
  909. #undef F2
  910. #undef F1
  911. while ((sz = f_gets (buf, sizeof (buf), f)) != 0)
  912. {
  913. line++;
  914. printer (ctx, EQU_CH, line, off, sz, buf);
  915. off += sz;
  916. while (buf[sz - 1] != '\n')
  917. {
  918. sz = f_gets (buf, sizeof (buf), f);
  919. if (sz == 0)
  920. {
  921. printer (ctx, 0, 0, 0, 1, "\n");
  922. break;
  923. }
  924. printer (ctx, 0, 0, 0, sz, buf);
  925. off += sz;
  926. }
  927. }
  928. f_close (f);
  929. return 0;
  930. err:
  931. f_close (f);
  932. return -1;
  933. }
  934. /* --------------------------------------------------------------------------------------------- */
  935. /* horizontal diff ********************************************************** */
  936. /**
  937. * Longest common substring.
  938. *
  939. * \param s first string
  940. * \param m length of first string
  941. * \param t second string
  942. * \param n length of second string
  943. * \param ret list of offsets for longest common substrings inside each string
  944. * \param min minimum length of common substrings
  945. *
  946. * \return 0 if success, nonzero otherwise
  947. */
  948. static int
  949. lcsubstr (const char *s, int m, const char *t, int n, GArray * ret, int min)
  950. {
  951. int i, j;
  952. int *Lprev, *Lcurr;
  953. int z = 0;
  954. if (m < min || n < min)
  955. {
  956. /* XXX early culling */
  957. return 0;
  958. }
  959. Lprev = g_new0 (int, n + 1);
  960. Lcurr = g_new0 (int, n + 1);
  961. if (Lprev == NULL || Lcurr == NULL)
  962. {
  963. g_free (Lprev);
  964. g_free (Lcurr);
  965. return -1;
  966. }
  967. for (i = 0; i < m; i++)
  968. {
  969. int *L = Lprev;
  970. Lprev = Lcurr;
  971. Lcurr = L;
  972. #ifdef USE_MEMSET_IN_LCS
  973. memset (Lcurr, 0, (n + 1) * sizeof (int));
  974. #endif
  975. for (j = 0; j < n; j++)
  976. {
  977. #ifndef USE_MEMSET_IN_LCS
  978. Lcurr[j + 1] = 0;
  979. #endif
  980. if (s[i] == t[j])
  981. {
  982. int v = Lprev[j] + 1;
  983. Lcurr[j + 1] = v;
  984. if (z < v)
  985. {
  986. z = v;
  987. g_array_set_size (ret, 0);
  988. }
  989. if (z == v && z >= min)
  990. {
  991. int off0 = i - z + 1;
  992. int off1 = j - z + 1;
  993. size_t k;
  994. for (k = 0; k < ret->len; k++)
  995. {
  996. PAIR *p = (PAIR *) g_array_index (ret, PAIR, k);
  997. if ((*p)[0] == off0)
  998. {
  999. break;
  1000. }
  1001. if ((*p)[1] >= off1)
  1002. {
  1003. break;
  1004. }
  1005. }
  1006. if (k == ret->len)
  1007. {
  1008. PAIR p2;
  1009. p2[0] = off0;
  1010. p2[1] = off1;
  1011. g_array_append_val (ret, p2);
  1012. }
  1013. }
  1014. }
  1015. }
  1016. }
  1017. free (Lcurr);
  1018. free (Lprev);
  1019. return z;
  1020. free (Lcurr);
  1021. free (Lprev);
  1022. return -1;
  1023. }
  1024. /* --------------------------------------------------------------------------------------------- */
  1025. /**
  1026. * Scan recursively for common substrings and build ranges.
  1027. *
  1028. * \param s first string
  1029. * \param t second string
  1030. * \param bracket current limits for both of the strings
  1031. * \param min minimum length of common substrings
  1032. * \param hdiff list of horizontal diff ranges to fill
  1033. * \param depth recursion depth
  1034. *
  1035. * \return 0 if success, nonzero otherwise
  1036. */
  1037. static gboolean
  1038. hdiff_multi (const char *s, const char *t, const BRACKET bracket, int min, GArray * hdiff,
  1039. unsigned int depth)
  1040. {
  1041. BRACKET p;
  1042. if (depth--)
  1043. {
  1044. GArray *ret;
  1045. BRACKET b;
  1046. int len;
  1047. ret = g_array_new (FALSE, TRUE, sizeof (PAIR));
  1048. if (ret == NULL)
  1049. return FALSE;
  1050. len = lcsubstr (s + bracket[0].off, bracket[0].len,
  1051. t + bracket[1].off, bracket[1].len, ret, min);
  1052. if (ret->len != 0)
  1053. {
  1054. size_t k = 0;
  1055. const PAIR *data = (const PAIR *) &g_array_index (ret, PAIR, 0);
  1056. const PAIR *data2;
  1057. b[0].off = bracket[0].off;
  1058. b[0].len = (*data)[0];
  1059. b[1].off = bracket[1].off;
  1060. b[1].len = (*data)[1];
  1061. if (!hdiff_multi (s, t, b, min, hdiff, depth))
  1062. return FALSE;
  1063. for (k = 0; k < ret->len - 1; k++)
  1064. {
  1065. data = (const PAIR *) &g_array_index (ret, PAIR, k);
  1066. data2 = (const PAIR *) &g_array_index (ret, PAIR, k + 1);
  1067. b[0].off = bracket[0].off + (*data)[0] + len;
  1068. b[0].len = (*data2)[0] - (*data)[0] - len;
  1069. b[1].off = bracket[1].off + (*data)[1] + len;
  1070. b[1].len = (*data2)[1] - (*data)[1] - len;
  1071. if (!hdiff_multi (s, t, b, min, hdiff, depth))
  1072. return FALSE;
  1073. }
  1074. data = (const PAIR *) &g_array_index (ret, PAIR, k);
  1075. b[0].off = bracket[0].off + (*data)[0] + len;
  1076. b[0].len = bracket[0].len - (*data)[0] - len;
  1077. b[1].off = bracket[1].off + (*data)[1] + len;
  1078. b[1].len = bracket[1].len - (*data)[1] - len;
  1079. if (!hdiff_multi (s, t, b, min, hdiff, depth))
  1080. return FALSE;
  1081. g_array_free (ret, TRUE);
  1082. return TRUE;
  1083. }
  1084. }
  1085. p[0].off = bracket[0].off;
  1086. p[0].len = bracket[0].len;
  1087. p[1].off = bracket[1].off;
  1088. p[1].len = bracket[1].len;
  1089. g_array_append_val (hdiff, p);
  1090. return TRUE;
  1091. }
  1092. /* --------------------------------------------------------------------------------------------- */
  1093. /**
  1094. * Build list of horizontal diff ranges.
  1095. *
  1096. * \param s first string
  1097. * \param m length of first string
  1098. * \param t second string
  1099. * \param n length of second string
  1100. * \param min minimum length of common substrings
  1101. * \param hdiff list of horizontal diff ranges to fill
  1102. * \param depth recursion depth
  1103. *
  1104. * \return 0 if success, nonzero otherwise
  1105. */
  1106. static gboolean
  1107. hdiff_scan (const char *s, int m, const char *t, int n, int min, GArray * hdiff, unsigned int depth)
  1108. {
  1109. int i;
  1110. BRACKET b;
  1111. /* dumbscan (single horizontal diff) -- does not compress whitespace */
  1112. for (i = 0; i < m && i < n && s[i] == t[i]; i++)
  1113. ;
  1114. for (; m > i && n > i && s[m - 1] == t[n - 1]; m--, n--)
  1115. ;
  1116. b[0].off = i;
  1117. b[0].len = m - i;
  1118. b[1].off = i;
  1119. b[1].len = n - i;
  1120. /* smartscan (multiple horizontal diff) */
  1121. return hdiff_multi (s, t, b, min, hdiff, depth);
  1122. }
  1123. /* --------------------------------------------------------------------------------------------- */
  1124. /* read line **************************************************************** */
  1125. /**
  1126. * Check if character is inside horizontal diff limits.
  1127. *
  1128. * \param k rank of character inside line
  1129. * \param hdiff horizontal diff structure
  1130. * \param ord 0 if reading from first file, 1 if reading from 2nd file
  1131. *
  1132. * \return TRUE if inside hdiff limits, FALSE otherwise
  1133. */
  1134. static int
  1135. is_inside (int k, GArray * hdiff, int ord)
  1136. {
  1137. size_t i;
  1138. BRACKET *b;
  1139. for (i = 0; i < hdiff->len; i++)
  1140. {
  1141. int start, end;
  1142. b = &g_array_index (hdiff, BRACKET, i);
  1143. start = (*b)[ord].off;
  1144. end = start + (*b)[ord].len;
  1145. if (k >= start && k < end)
  1146. {
  1147. return 1;
  1148. }
  1149. }
  1150. return 0;
  1151. }
  1152. /* --------------------------------------------------------------------------------------------- */
  1153. /**
  1154. * Copy `src' to `dst' expanding tabs.
  1155. *
  1156. * \param dst destination buffer
  1157. * \param src source buffer
  1158. * \param srcsize size of src buffer
  1159. * \param base virtual base of this string, needed to calculate tabs
  1160. * \param ts tab size
  1161. *
  1162. * \return new virtual base
  1163. *
  1164. * \note The procedure returns when all bytes are consumed from `src'
  1165. */
  1166. static int
  1167. cvt_cpy (char *dst, const char *src, size_t srcsize, int base, int ts)
  1168. {
  1169. int i;
  1170. for (i = 0; srcsize != 0; i++, src++, dst++, srcsize--)
  1171. {
  1172. *dst = *src;
  1173. if (*src == '\t')
  1174. {
  1175. int j = TAB_SKIP (ts, i + base);
  1176. i += j - 1;
  1177. while (j-- > 0)
  1178. {
  1179. *dst++ = ' ';
  1180. }
  1181. dst--;
  1182. }
  1183. }
  1184. return i + base;
  1185. }
  1186. /* --------------------------------------------------------------------------------------------- */
  1187. /**
  1188. * Copy `src' to `dst' expanding tabs.
  1189. *
  1190. * \param dst destination buffer
  1191. * \param dstsize size of dst buffer
  1192. * \param[in,out] _src source buffer
  1193. * \param srcsize size of src buffer
  1194. * \param base virtual base of this string, needed to calculate tabs
  1195. * \param ts tab size
  1196. *
  1197. * \return new virtual base
  1198. *
  1199. * \note The procedure returns when all bytes are consumed from `src'
  1200. * or `dstsize' bytes are written to `dst'
  1201. * \note Upon return, `src' points to the first unwritten character in source
  1202. */
  1203. static int
  1204. cvt_ncpy (char *dst, int dstsize, const char **_src, size_t srcsize, int base, int ts)
  1205. {
  1206. int i;
  1207. const char *src = *_src;
  1208. for (i = 0; i < dstsize && srcsize != 0; i++, src++, dst++, srcsize--)
  1209. {
  1210. *dst = *src;
  1211. if (*src == '\t')
  1212. {
  1213. int j = TAB_SKIP (ts, i + base);
  1214. if (j > dstsize - i)
  1215. {
  1216. j = dstsize - i;
  1217. }
  1218. i += j - 1;
  1219. while (j-- > 0)
  1220. {
  1221. *dst++ = ' ';
  1222. }
  1223. dst--;
  1224. }
  1225. }
  1226. *_src = src;
  1227. return i + base;
  1228. }
  1229. /* --------------------------------------------------------------------------------------------- */
  1230. /**
  1231. * Read line from memory, converting tabs to spaces and padding with spaces.
  1232. *
  1233. * \param src buffer to read from
  1234. * \param srcsize size of src buffer
  1235. * \param dst buffer to read to
  1236. * \param dstsize size of dst buffer, excluding trailing null
  1237. * \param skip number of characters to skip
  1238. * \param ts tab size
  1239. * \param show_cr show trailing carriage return as ^M
  1240. *
  1241. * \return negative on error, otherwise number of bytes except padding
  1242. */
  1243. static int
  1244. cvt_mget (const char *src, size_t srcsize, char *dst, int dstsize, int skip, int ts, int show_cr)
  1245. {
  1246. int sz = 0;
  1247. if (src != NULL)
  1248. {
  1249. int i;
  1250. char *tmp = dst;
  1251. const int base = 0;
  1252. for (i = 0; dstsize != 0 && srcsize != 0 && *src != '\n'; i++, src++, srcsize--)
  1253. {
  1254. if (*src == '\t')
  1255. {
  1256. int j = TAB_SKIP (ts, i + base);
  1257. i += j - 1;
  1258. while (j-- > 0)
  1259. {
  1260. if (skip > 0)
  1261. {
  1262. skip--;
  1263. }
  1264. else if (dstsize != 0)
  1265. {
  1266. dstsize--;
  1267. *dst++ = ' ';
  1268. }
  1269. }
  1270. }
  1271. else if (src[0] == '\r' && (srcsize == 1 || src[1] == '\n'))
  1272. {
  1273. if (skip == 0 && show_cr)
  1274. {
  1275. if (dstsize > 1)
  1276. {
  1277. dstsize -= 2;
  1278. *dst++ = '^';
  1279. *dst++ = 'M';
  1280. }
  1281. else
  1282. {
  1283. dstsize--;
  1284. *dst++ = '.';
  1285. }
  1286. }
  1287. break;
  1288. }
  1289. else
  1290. {
  1291. if (skip > 0)
  1292. {
  1293. int utf_ch = 0;
  1294. gboolean res;
  1295. int w;
  1296. skip--;
  1297. utf_ch = dview_get_utf ((char *) src, &w, &res);
  1298. if (w > 1)
  1299. skip += w - 1;
  1300. if (!g_unichar_isprint (utf_ch))
  1301. utf_ch = '.';
  1302. }
  1303. else
  1304. {
  1305. dstsize--;
  1306. *dst++ = *src;
  1307. }
  1308. }
  1309. }
  1310. sz = dst - tmp;
  1311. }
  1312. while (dstsize != 0)
  1313. {
  1314. dstsize--;
  1315. *dst++ = ' ';
  1316. }
  1317. *dst = '\0';
  1318. return sz;
  1319. }
  1320. /* --------------------------------------------------------------------------------------------- */
  1321. /**
  1322. * Read line from memory and build attribute array.
  1323. *
  1324. * \param src buffer to read from
  1325. * \param srcsize size of src buffer
  1326. * \param dst buffer to read to
  1327. * \param dstsize size of dst buffer, excluding trailing null
  1328. * \param skip number of characters to skip
  1329. * \param ts tab size
  1330. * \param show_cr show trailing carriage return as ^M
  1331. * \param hdiff horizontal diff structure
  1332. * \param ord 0 if reading from first file, 1 if reading from 2nd file
  1333. * \param att buffer of attributes
  1334. *
  1335. * \return negative on error, otherwise number of bytes except padding
  1336. */
  1337. static int
  1338. cvt_mgeta (const char *src, size_t srcsize, char *dst, int dstsize, int skip, int ts, int show_cr,
  1339. GArray * hdiff, int ord, char *att)
  1340. {
  1341. int sz = 0;
  1342. if (src != NULL)
  1343. {
  1344. int i, k;
  1345. char *tmp = dst;
  1346. const int base = 0;
  1347. for (i = 0, k = 0; dstsize != 0 && srcsize != 0 && *src != '\n'; i++, k++, src++, srcsize--)
  1348. {
  1349. if (*src == '\t')
  1350. {
  1351. int j = TAB_SKIP (ts, i + base);
  1352. i += j - 1;
  1353. while (j-- > 0)
  1354. {
  1355. if (skip != 0)
  1356. {
  1357. skip--;
  1358. }
  1359. else if (dstsize != 0)
  1360. {
  1361. dstsize--;
  1362. *att++ = is_inside (k, hdiff, ord);
  1363. *dst++ = ' ';
  1364. }
  1365. }
  1366. }
  1367. else if (src[0] == '\r' && (srcsize == 1 || src[1] == '\n'))
  1368. {
  1369. if (skip == 0 && show_cr)
  1370. {
  1371. if (dstsize > 1)
  1372. {
  1373. dstsize -= 2;
  1374. *att++ = is_inside (k, hdiff, ord);
  1375. *dst++ = '^';
  1376. *att++ = is_inside (k, hdiff, ord);
  1377. *dst++ = 'M';
  1378. }
  1379. else
  1380. {
  1381. dstsize--;
  1382. *att++ = is_inside (k, hdiff, ord);
  1383. *dst++ = '.';
  1384. }
  1385. }
  1386. break;
  1387. }
  1388. else
  1389. {
  1390. if (skip != 0)
  1391. {
  1392. int utf_ch = 0;
  1393. gboolean res;
  1394. int w;
  1395. skip--;
  1396. utf_ch = dview_get_utf ((char *) src, &w, &res);
  1397. if (w > 1)
  1398. skip += w - 1;
  1399. if (!g_unichar_isprint (utf_ch))
  1400. utf_ch = '.';
  1401. }
  1402. else
  1403. {
  1404. dstsize--;
  1405. *att++ = is_inside (k, hdiff, ord);
  1406. *dst++ = *src;
  1407. }
  1408. }
  1409. }
  1410. sz = dst - tmp;
  1411. }
  1412. while (dstsize != 0)
  1413. {
  1414. dstsize--;
  1415. *att++ = 0;
  1416. *dst++ = ' ';
  1417. }
  1418. *dst = '\0';
  1419. return sz;
  1420. }
  1421. /* --------------------------------------------------------------------------------------------- */
  1422. /**
  1423. * Read line from file, converting tabs to spaces and padding with spaces.
  1424. *
  1425. * \param f file stream to read from
  1426. * \param off offset of line inside file
  1427. * \param dst buffer to read to
  1428. * \param dstsize size of dst buffer, excluding trailing null
  1429. * \param skip number of characters to skip
  1430. * \param ts tab size
  1431. * \param show_cr show trailing carriage return as ^M
  1432. *
  1433. * \return negative on error, otherwise number of bytes except padding
  1434. */
  1435. static int
  1436. cvt_fget (FBUF * f, off_t off, char *dst, size_t dstsize, int skip, int ts, int show_cr)
  1437. {
  1438. int base = 0;
  1439. int old_base = base;
  1440. const int amount = dstsize;
  1441. size_t useful, offset;
  1442. size_t i;
  1443. size_t sz;
  1444. int lastch = '\0';
  1445. const char *q = NULL;
  1446. char tmp[BUFSIZ]; /* XXX capacity must be >= max{dstsize + 1, amount} */
  1447. char cvt[BUFSIZ]; /* XXX capacity must be >= MAX_TAB_WIDTH * amount */
  1448. if ((int) sizeof (tmp) < amount || (int) sizeof (tmp) <= dstsize
  1449. || (int) sizeof (cvt) < 8 * amount)
  1450. {
  1451. /* abnormal, but avoid buffer overflow */
  1452. memset (dst, ' ', dstsize);
  1453. dst[dstsize] = '\0';
  1454. return 0;
  1455. }
  1456. f_seek (f, off, SEEK_SET);
  1457. while (skip > base)
  1458. {
  1459. old_base = base;
  1460. sz = f_gets (tmp, amount, f);
  1461. if (sz == 0)
  1462. break;
  1463. base = cvt_cpy (cvt, tmp, sz, old_base, ts);
  1464. if (cvt[base - old_base - 1] == '\n')
  1465. {
  1466. q = &cvt[base - old_base - 1];
  1467. base = old_base + q - cvt + 1;
  1468. break;
  1469. }
  1470. }
  1471. if (base < skip)
  1472. {
  1473. memset (dst, ' ', dstsize);
  1474. dst[dstsize] = '\0';
  1475. return 0;
  1476. }
  1477. useful = base - skip;
  1478. offset = skip - old_base;
  1479. if (useful <= dstsize)
  1480. {
  1481. if (useful != 0)
  1482. memmove (dst, cvt + offset, useful);
  1483. if (q == NULL)
  1484. {
  1485. sz = f_gets (tmp, dstsize - useful + 1, f);
  1486. if (sz != 0)
  1487. {
  1488. const char *ptr = tmp;
  1489. useful += cvt_ncpy (dst + useful, dstsize - useful, &ptr, sz, base, ts) - base;
  1490. if (ptr < tmp + sz)
  1491. lastch = *ptr;
  1492. }
  1493. }
  1494. sz = useful;
  1495. }
  1496. else
  1497. {
  1498. memmove (dst, cvt + offset, dstsize);
  1499. sz = dstsize;
  1500. lastch = cvt[offset + dstsize];
  1501. }
  1502. dst[sz] = lastch;
  1503. for (i = 0; i < sz && dst[i] != '\n'; i++)
  1504. {
  1505. if (dst[i] == '\r' && dst[i + 1] == '\n')
  1506. {
  1507. if (show_cr)
  1508. {
  1509. if (i + 1 < dstsize)
  1510. {
  1511. dst[i++] = '^';
  1512. dst[i++] = 'M';
  1513. }
  1514. else
  1515. {
  1516. dst[i++] = '*';
  1517. }
  1518. }
  1519. break;
  1520. }
  1521. }
  1522. for (; i < dstsize; i++)
  1523. {
  1524. dst[i] = ' ';
  1525. }
  1526. dst[i] = '\0';
  1527. return sz;
  1528. }
  1529. /* --------------------------------------------------------------------------------------------- */
  1530. /* diff printers et al ****************************************************** */
  1531. static void
  1532. cc_free_elt (void *elt)
  1533. {
  1534. DIFFLN *p = elt;
  1535. if (p != NULL)
  1536. {
  1537. g_free (p->p);
  1538. }
  1539. }
  1540. /* --------------------------------------------------------------------------------------------- */
  1541. static int
  1542. printer (void *ctx, int ch, int line, off_t off, size_t sz, const char *str)
  1543. {
  1544. GArray *a = ((PRINTER_CTX *) ctx)->a;
  1545. DSRC dsrc = ((PRINTER_CTX *) ctx)->dsrc;
  1546. if (ch)
  1547. {
  1548. DIFFLN p;
  1549. p.p = NULL;
  1550. p.ch = ch;
  1551. p.line = line;
  1552. p.u.off = off;
  1553. if (dsrc == DATA_SRC_MEM && line != 0)
  1554. {
  1555. if (sz != 0 && str[sz - 1] == '\n')
  1556. {
  1557. sz--;
  1558. }
  1559. if (sz > 0)
  1560. p.p = g_strndup (str, sz);
  1561. p.u.len = sz;
  1562. }
  1563. g_array_append_val (a, p);
  1564. }
  1565. else if (dsrc == DATA_SRC_MEM)
  1566. {
  1567. DIFFLN *p;
  1568. p = &g_array_index (a, DIFFLN, a->len - 1);
  1569. if (sz != 0 && str[sz - 1] == '\n')
  1570. {
  1571. sz--;
  1572. }
  1573. if (sz != 0)
  1574. {
  1575. size_t new_size = p->u.len + sz;
  1576. char *q = g_realloc (p->p, new_size);
  1577. memcpy (q + p->u.len, str, sz);
  1578. p->p = q;
  1579. }
  1580. p->u.len += sz;
  1581. }
  1582. if (dsrc == DATA_SRC_TMP && (line != 0 || ch == 0))
  1583. {
  1584. FBUF *f = ((PRINTER_CTX *) ctx)->f;
  1585. f_write (f, str, sz);
  1586. }
  1587. return 0;
  1588. }
  1589. /* --------------------------------------------------------------------------------------------- */
  1590. static int
  1591. redo_diff (WDiff * dview)
  1592. {
  1593. FBUF *const *f = dview->f;
  1594. PRINTER_CTX ctx;
  1595. GArray *ops;
  1596. int ndiff;
  1597. int rv;
  1598. char extra[256];
  1599. extra[0] = '\0';
  1600. if (dview->opt.quality == 2)
  1601. {
  1602. strcat (extra, " -d");
  1603. }
  1604. if (dview->opt.quality == 1)
  1605. {
  1606. strcat (extra, " --speed-large-files");
  1607. }
  1608. if (dview->opt.strip_trailing_cr)
  1609. {
  1610. strcat (extra, " --strip-trailing-cr");
  1611. }
  1612. if (dview->opt.ignore_tab_expansion)
  1613. {
  1614. strcat (extra, " -E");
  1615. }
  1616. if (dview->opt.ignore_space_change)
  1617. {
  1618. strcat (extra, " -b");
  1619. }
  1620. if (dview->opt.ignore_all_space)
  1621. {
  1622. strcat (extra, " -w");
  1623. }
  1624. if (dview->opt.ignore_case)
  1625. {
  1626. strcat (extra, " -i");
  1627. }
  1628. if (dview->dsrc != DATA_SRC_MEM)
  1629. {
  1630. f_reset (f[0]);
  1631. f_reset (f[1]);
  1632. }
  1633. ops = g_array_new (FALSE, FALSE, sizeof (DIFFCMD));
  1634. ndiff = dff_execute (dview->args, extra, dview->file[0], dview->file[1], ops);
  1635. if (ndiff < 0)
  1636. {
  1637. if (ops != NULL)
  1638. g_array_free (ops, TRUE);
  1639. return -1;
  1640. }
  1641. ctx.dsrc = dview->dsrc;
  1642. rv = 0;
  1643. ctx.a = dview->a[0];
  1644. ctx.f = f[0];
  1645. rv |= dff_reparse (0, dview->file[0], ops, printer, &ctx);
  1646. ctx.a = dview->a[1];
  1647. ctx.f = f[1];
  1648. rv |= dff_reparse (1, dview->file[1], ops, printer, &ctx);
  1649. if (ops != NULL)
  1650. g_array_free (ops, TRUE);
  1651. if (rv != 0 || dview->a[0]->len != dview->a[1]->len)
  1652. return -1;
  1653. if (dview->dsrc == DATA_SRC_TMP)
  1654. {
  1655. f_trunc (f[0]);
  1656. f_trunc (f[1]);
  1657. }
  1658. if (dview->dsrc == DATA_SRC_MEM && HDIFF_ENABLE)
  1659. {
  1660. dview->hdiff = g_ptr_array_new ();
  1661. if (dview->hdiff != NULL)
  1662. {
  1663. size_t i;
  1664. const DIFFLN *p;
  1665. const DIFFLN *q;
  1666. for (i = 0; i < dview->a[0]->len; i++)
  1667. {
  1668. GArray *h = NULL;
  1669. p = &g_array_index (dview->a[0], DIFFLN, i);
  1670. q = &g_array_index (dview->a[1], DIFFLN, i);
  1671. if (p->line && q->line && p->ch == CHG_CH)
  1672. {
  1673. h = g_array_new (FALSE, FALSE, sizeof (BRACKET));
  1674. if (h != NULL)
  1675. {
  1676. gboolean runresult =
  1677. hdiff_scan (p->p, p->u.len, q->p, q->u.len, HDIFF_MINCTX, h,
  1678. HDIFF_DEPTH);
  1679. if (!runresult)
  1680. {
  1681. g_array_free (h, TRUE);
  1682. h = NULL;
  1683. }
  1684. }
  1685. }
  1686. g_ptr_array_add (dview->hdiff, h);
  1687. }
  1688. }
  1689. }
  1690. return ndiff;
  1691. }
  1692. /* --------------------------------------------------------------------------------------------- */
  1693. static void
  1694. destroy_hdiff (WDiff * dview)
  1695. {
  1696. if (dview->hdiff != NULL)
  1697. {
  1698. int i;
  1699. int len = dview->a[0]->len;
  1700. for (i = 0; i < len; i++)
  1701. {
  1702. GArray *h = (GArray *) g_ptr_array_index (dview->hdiff, i);
  1703. if (h != NULL)
  1704. g_array_free (h, TRUE);
  1705. }
  1706. g_ptr_array_free (dview->hdiff, TRUE);
  1707. dview->hdiff = NULL;
  1708. }
  1709. mc_search_free (dview->search.handle);
  1710. dview->search.handle = NULL;
  1711. g_free (dview->search.last_string);
  1712. dview->search.last_string = NULL;
  1713. }
  1714. /* --------------------------------------------------------------------------------------------- */
  1715. /* stuff ******************************************************************** */
  1716. static int
  1717. get_digits (unsigned int n)
  1718. {
  1719. int d = 1;
  1720. while (n /= 10)
  1721. {
  1722. d++;
  1723. }
  1724. return d;
  1725. }
  1726. /* --------------------------------------------------------------------------------------------- */
  1727. static int
  1728. get_line_numbers (const GArray * a, size_t pos, int *linenum, int *lineofs)
  1729. {
  1730. const DIFFLN *p;
  1731. *linenum = 0;
  1732. *lineofs = 0;
  1733. if (a->len != 0)
  1734. {
  1735. if (pos >= a->len)
  1736. {
  1737. pos = a->len - 1;
  1738. }
  1739. p = &g_array_index (a, DIFFLN, pos);
  1740. if (p->line == 0)
  1741. {
  1742. int n;
  1743. for (n = pos; n > 0; n--)
  1744. {
  1745. p--;
  1746. if (p->line != 0)
  1747. {
  1748. break;
  1749. }
  1750. }
  1751. *lineofs = pos - n + 1;
  1752. }
  1753. *linenum = p->line;
  1754. }
  1755. return 0;
  1756. }
  1757. /* --------------------------------------------------------------------------------------------- */
  1758. static int
  1759. calc_nwidth (const GArray ** const a)
  1760. {
  1761. int l1, o1;
  1762. int l2, o2;
  1763. get_line_numbers (a[0], a[0]->len - 1, &l1, &o1);
  1764. get_line_numbers (a[1], a[1]->len - 1, &l2, &o2);
  1765. if (l1 < l2)
  1766. {
  1767. l1 = l2;
  1768. }
  1769. return get_digits (l1);
  1770. }
  1771. /* --------------------------------------------------------------------------------------------- */
  1772. static int
  1773. find_prev_hunk (const GArray * a, int pos)
  1774. {
  1775. #if 1
  1776. while (pos > 0 && ((DIFFLN *) & g_array_index (a, DIFFLN, pos))->ch != EQU_CH)
  1777. {
  1778. pos--;
  1779. }
  1780. while (pos > 0 && ((DIFFLN *) & g_array_index (a, DIFFLN, pos))->ch == EQU_CH)
  1781. {
  1782. pos--;
  1783. }
  1784. while (pos > 0 && ((DIFFLN *) & g_array_index (a, DIFFLN, pos))->ch != EQU_CH)
  1785. {
  1786. pos--;
  1787. }
  1788. if (pos > 0 && (size_t) pos < a->len)
  1789. pos++;
  1790. #else
  1791. while (pos > 0 && ((DIFFLN *) & g_array_index (a, DIFFLN, pos - 1))->ch == EQU_CH)
  1792. {
  1793. pos--;
  1794. }
  1795. while (pos > 0 && ((DIFFLN *) & g_array_index (a, DIFFLN, pos - 1))->ch != EQU_CH)
  1796. {
  1797. pos--;
  1798. }
  1799. #endif
  1800. return pos;
  1801. }
  1802. /* --------------------------------------------------------------------------------------------- */
  1803. static size_t
  1804. find_next_hunk (const GArray * a, size_t pos)
  1805. {
  1806. while (pos < a->len && ((DIFFLN *) & g_array_index (a, DIFFLN, pos))->ch != EQU_CH)
  1807. {
  1808. pos++;
  1809. }
  1810. while (pos < a->len && ((DIFFLN *) & g_array_index (a, DIFFLN, pos))->ch == EQU_CH)
  1811. {
  1812. pos++;
  1813. }
  1814. return pos;
  1815. }
  1816. /**
  1817. * Find start and end lines of the current hunk.
  1818. *
  1819. * \param dview - widget WDiff
  1820. * \return boolean and
  1821. * start_line1 first line of current hunk (file[0])
  1822. * end_line1 last line of current hunk (file[0])
  1823. * start_line1 first line of current hunk (file[0])
  1824. * end_line1 last line of current hunk (file[0])
  1825. */
  1826. static int
  1827. get_current_hunk (WDiff * dview, int *start_line1, int *end_line1, int *start_line2, int *end_line2)
  1828. {
  1829. const GArray *a0 = dview->a[0];
  1830. const GArray *a1 = dview->a[1];
  1831. size_t pos;
  1832. int ch;
  1833. int res = 0;
  1834. *start_line1 = 1;
  1835. *start_line2 = 1;
  1836. *end_line1 = 1;
  1837. *end_line2 = 1;
  1838. pos = dview->skip_rows;
  1839. ch = ((DIFFLN *) & g_array_index (a0, DIFFLN, pos))->ch;
  1840. if (ch != EQU_CH)
  1841. {
  1842. switch (ch)
  1843. {
  1844. case ADD_CH:
  1845. res = DIFF_DEL;
  1846. break;
  1847. case DEL_CH:
  1848. res = DIFF_ADD;
  1849. break;
  1850. case CHG_CH:
  1851. res = DIFF_CHG;
  1852. break;
  1853. }
  1854. while (pos > 0 && ((DIFFLN *) & g_array_index (a0, DIFFLN, pos))->ch != EQU_CH)
  1855. {
  1856. pos--;
  1857. }
  1858. if (pos > 0)
  1859. {
  1860. *start_line1 = ((DIFFLN *) & g_array_index (a0, DIFFLN, pos))->line + 1;
  1861. *start_line2 = ((DIFFLN *) & g_array_index (a1, DIFFLN, pos))->line + 1;
  1862. }
  1863. pos = dview->skip_rows;
  1864. while (pos < a0->len && ((DIFFLN *) & g_array_index (a0, DIFFLN, pos))->ch != EQU_CH)
  1865. {
  1866. int l0, l1;
  1867. l0 = ((DIFFLN *) & g_array_index (a0, DIFFLN, pos))->line;
  1868. l1 = ((DIFFLN *) & g_array_index (a1, DIFFLN, pos))->line;
  1869. if (l0 > 0)
  1870. *end_line1 = max (*start_line1, l0);
  1871. if (l1 > 0)
  1872. *end_line2 = max (*start_line2, l1);
  1873. pos++;
  1874. }
  1875. }
  1876. return res;
  1877. }
  1878. static void
  1879. dview_remove_hunk (WDiff * dview, FILE * merge_file, int from1, int to1)
  1880. {
  1881. int line;
  1882. char buf[BUF_10K];
  1883. FILE *f0;
  1884. f0 = fopen (dview->file[0], "r");
  1885. line = 0;
  1886. while (fgets (buf, sizeof (buf), f0) != NULL && line < from1 - 1)
  1887. {
  1888. line++;
  1889. fputs (buf, merge_file);
  1890. }
  1891. while (fgets (buf, sizeof (buf), f0) != NULL)
  1892. {
  1893. line++;
  1894. if (line >= to1)
  1895. fputs (buf, merge_file);
  1896. }
  1897. fclose (f0);
  1898. }
  1899. static void
  1900. dview_add_hunk (WDiff * dview, FILE * merge_file, int from1, int from2, int to2)
  1901. {
  1902. int line;
  1903. char buf[BUF_10K];
  1904. FILE *f0;
  1905. FILE *f1;
  1906. f0 = fopen (dview->file[0], "r");
  1907. f1 = fopen (dview->file[1], "r");
  1908. line = 0;
  1909. while (fgets (buf, sizeof (buf), f0) != NULL && line < from1 - 1)
  1910. {
  1911. line++;
  1912. fputs (buf, merge_file);
  1913. }
  1914. line = 0;
  1915. while (fgets (buf, sizeof (buf), f1) != NULL && line <= to2)
  1916. {
  1917. line++;
  1918. if (line >= from2)
  1919. fputs (buf, merge_file);
  1920. }
  1921. while (fgets (buf, sizeof (buf), f0) != NULL)
  1922. {
  1923. fputs (buf, merge_file);
  1924. }
  1925. fclose (f0);
  1926. fclose (f1);
  1927. }
  1928. static void
  1929. dview_replace_hunk (WDiff * dview, FILE * merge_file, int from1, int to1, int from2, int to2)
  1930. {
  1931. int line1, line2;
  1932. char buf[BUF_10K];
  1933. FILE *f0;
  1934. FILE *f1;
  1935. f0 = fopen (dview->file[0], "r");
  1936. f1 = fopen (dview->file[1], "r");
  1937. line1 = 0;
  1938. while (fgets (buf, sizeof (buf), f0) != NULL && line1 < from1 - 1)
  1939. {
  1940. line1++;
  1941. fputs (buf, merge_file);
  1942. }
  1943. line2 = 0;
  1944. while (fgets (buf, sizeof (buf), f1) != NULL && line2 <= to2)
  1945. {
  1946. line2++;
  1947. if (line2 >= from2)
  1948. fputs (buf, merge_file);
  1949. }
  1950. while (fgets (buf, sizeof (buf), f0) != NULL)
  1951. {
  1952. line1++;
  1953. if (line1 > to1)
  1954. fputs (buf, merge_file);
  1955. }
  1956. fclose (f0);
  1957. fclose (f1);
  1958. }
  1959. static void
  1960. do_merge_hunk (WDiff * dview)
  1961. {
  1962. int from1, to1, from2, to2;
  1963. int res;
  1964. int hunk;
  1965. hunk = get_current_hunk (dview, &from1, &to1, &from2, &to2);
  1966. if (hunk > 0)
  1967. {
  1968. int merge_file_fd;
  1969. FILE *merge_file;
  1970. char *merge_file_name = NULL;
  1971. if (!dview->merged)
  1972. {
  1973. dview->merged = mc_util_make_backup_if_possible (dview->file[0], "~~~");
  1974. if (!dview->merged)
  1975. {
  1976. message (D_ERROR, MSG_ERROR,
  1977. _("Cannot create backup file\n%s%s\n%s"),
  1978. dview->file[0], "~~~", unix_error_string (errno));
  1979. return;
  1980. }
  1981. }
  1982. merge_file_fd = mc_mkstemps (&merge_file_name, "mcmerge", NULL);
  1983. if (merge_file_fd == -1)
  1984. {
  1985. message (D_ERROR, MSG_ERROR, _("Cannot create temporary merge file\n%s"),
  1986. unix_error_string (errno));
  1987. return;
  1988. }
  1989. merge_file = fdopen (merge_file_fd, "w");
  1990. switch (hunk)
  1991. {
  1992. case DIFF_DEL:
  1993. dview_remove_hunk (dview, merge_file, from1, to1);
  1994. break;
  1995. case DIFF_ADD:
  1996. dview_add_hunk (dview, merge_file, from1, from2, to2);
  1997. break;
  1998. case DIFF_CHG:
  1999. dview_replace_hunk (dview, merge_file, from1, to1, from2, to2);
  2000. break;
  2001. }
  2002. fflush (merge_file);
  2003. fclose (merge_file);
  2004. res = rewrite_backup_content (merge_file_name, dview->file[0]);
  2005. unlink (merge_file_name);
  2006. g_free (merge_file_name);
  2007. }
  2008. }
  2009. /* --------------------------------------------------------------------------------------------- */
  2010. /* view routines and callbacks ********************************************** */
  2011. static void
  2012. dview_compute_split (WDiff * dview, int i)
  2013. {
  2014. dview->bias += i;
  2015. if (dview->bias < 2 - dview->half1)
  2016. {
  2017. dview->bias = 2 - dview->half1;
  2018. }
  2019. if (dview->bias > dview->half2 - 2)
  2020. {
  2021. dview->bias = dview->half2 - 2;
  2022. }
  2023. }
  2024. /* --------------------------------------------------------------------------------------------- */
  2025. static void
  2026. dview_compute_areas (WDiff * dview)
  2027. {
  2028. dview->height = LINES - 2;
  2029. dview->half1 = COLS / 2;
  2030. dview->half2 = COLS - dview->half1;
  2031. dview_compute_split (dview, 0);
  2032. }
  2033. /* --------------------------------------------------------------------------------------------- */
  2034. static int
  2035. dview_init (WDiff * dview, const char *args, const char *file1, const char *file2,
  2036. const char *label1, const char *label2, DSRC dsrc)
  2037. {
  2038. int ndiff;
  2039. FBUF *f[2];
  2040. f[0] = NULL;
  2041. f[1] = NULL;
  2042. if (dsrc == DATA_SRC_TMP)
  2043. {
  2044. f[0] = f_temp ();
  2045. if (f[0] == NULL)
  2046. return -1;
  2047. f[1] = f_temp ();
  2048. if (f[1] == NULL)
  2049. {
  2050. f_close (f[0]);
  2051. return -1;
  2052. }
  2053. }
  2054. else if (dsrc == DATA_SRC_ORG)
  2055. {
  2056. f[0] = f_open (file1, O_RDONLY);
  2057. if (f[0] == NULL)
  2058. return -1;
  2059. f[1] = f_open (file2, O_RDONLY);
  2060. if (f[1] == NULL)
  2061. {
  2062. f_close (f[0]);
  2063. return -1;
  2064. }
  2065. }
  2066. dview->args = args;
  2067. dview->file[0] = file1;
  2068. dview->file[1] = file2;
  2069. dview->label[0] = g_strdup (label1);
  2070. dview->label[1] = g_strdup (label2);
  2071. dview->f[0] = f[0];
  2072. dview->f[1] = f[1];
  2073. dview->hdiff = NULL;
  2074. dview->dsrc = dsrc;
  2075. dview->converter = str_cnv_from_term;
  2076. dview_set_codeset (dview);
  2077. dview->a[0] = g_array_new (FALSE, FALSE, sizeof (DIFFLN));
  2078. dview->a[1] = g_array_new (FALSE, FALSE, sizeof (DIFFLN));
  2079. ndiff = redo_diff (dview);
  2080. if (ndiff < 0)
  2081. {
  2082. /* goto WIDGET_DESTROY stage: dview_fini() */
  2083. return -1;
  2084. }
  2085. dview->ndiff = ndiff;
  2086. dview->view_quit = 0;
  2087. dview->bias = 0;
  2088. dview->new_frame = 1;
  2089. dview->skip_rows = 0;
  2090. dview->skip_cols = 0;
  2091. dview->display_symbols = 0;
  2092. dview->display_numbers = 0;
  2093. dview->show_cr = 1;
  2094. dview->tab_size = 8;
  2095. dview->ord = 0;
  2096. dview->full = 0;
  2097. dview->search.handle = NULL;
  2098. dview->search.last_string = NULL;
  2099. dview->search.last_found_line = -1;
  2100. dview->search.last_accessed_num_line = -1;
  2101. dview->opt.quality = 0;
  2102. dview->opt.strip_trailing_cr = 0;
  2103. dview->opt.ignore_tab_expansion = 0;
  2104. dview->opt.ignore_space_change = 0;
  2105. dview->opt.ignore_all_space = 0;
  2106. dview->opt.ignore_case = 0;
  2107. dview_compute_areas (dview);
  2108. return 0;
  2109. }
  2110. /* --------------------------------------------------------------------------------------------- */
  2111. static void
  2112. dview_reread (WDiff * dview)
  2113. {
  2114. int ndiff = dview->ndiff;
  2115. destroy_hdiff (dview);
  2116. if (dview->a[0] != NULL)
  2117. {
  2118. g_array_foreach (dview->a[0], DIFFLN, cc_free_elt);
  2119. g_array_free (dview->a[0], TRUE);
  2120. }
  2121. if (dview->a[1] != NULL)
  2122. {
  2123. g_array_foreach (dview->a[1], DIFFLN, cc_free_elt);
  2124. g_array_free (dview->a[1], TRUE);
  2125. }
  2126. dview->a[0] = g_array_new (FALSE, FALSE, sizeof (DIFFLN));
  2127. dview->a[1] = g_array_new (FALSE, FALSE, sizeof (DIFFLN));
  2128. ndiff = redo_diff (dview);
  2129. if (ndiff >= 0)
  2130. dview->ndiff = ndiff;
  2131. }
  2132. /* --------------------------------------------------------------------------------------------- */
  2133. static void
  2134. dview_diff_options (WDiff * dview)
  2135. {
  2136. const char *quality_str[] = {
  2137. N_("&Normal"),
  2138. N_("&Fastest (Assume large files)"),
  2139. N_("&Minimal (Find a smaller set of change)")
  2140. };
  2141. QuickWidget diffopt_widgets[] = {
  2142. QUICK_BUTTON (6, 10, 14, OPTY, N_("&Cancel"), B_CANCEL, NULL),
  2143. QUICK_BUTTON (2, 10, 14, OPTY, N_("&OK"), B_ENTER, NULL),
  2144. QUICK_CHECKBOX (3, OPTX, 12, OPTY,
  2145. N_("Strip &trailing carriage return"), &dview->opt.strip_trailing_cr),
  2146. QUICK_CHECKBOX (3, OPTX, 11, OPTY,
  2147. N_("Ignore all &whitespace"), &dview->opt.ignore_all_space),
  2148. QUICK_CHECKBOX (3, OPTX, 10, OPTY,
  2149. N_("Ignore &space change"), &dview->opt.ignore_space_change),
  2150. QUICK_CHECKBOX (3, OPTX, 9, OPTY,
  2151. N_("Ignore tab &expansion"), &dview->opt.ignore_tab_expansion),
  2152. QUICK_CHECKBOX (3, OPTX, 8, OPTY,
  2153. N_("&Ignore case"), &dview->opt.ignore_case),
  2154. QUICK_LABEL (3, OPTX, 7, OPTY, N_("Diff extra options")),
  2155. QUICK_RADIO (3, OPTX, 3, OPTY,
  2156. 3, (const char **) quality_str, (int *) &dview->opt.quality),
  2157. QUICK_LABEL (3, OPTX, 2, OPTY, N_("Diff algorithm")),
  2158. QUICK_END
  2159. };
  2160. QuickDialog diffopt = {
  2161. OPTX, OPTY, -1, -1,
  2162. N_("Diff Options"), "[Diff Options]",
  2163. diffopt_widgets, NULL, FALSE
  2164. };
  2165. if (quick_dialog (&diffopt) != B_CANCEL)
  2166. {
  2167. dview_reread (dview);
  2168. }
  2169. }
  2170. /* --------------------------------------------------------------------------------------------- */
  2171. static void
  2172. dview_fini (WDiff * dview)
  2173. {
  2174. if (dview->dsrc != DATA_SRC_MEM)
  2175. {
  2176. f_close (dview->f[1]);
  2177. f_close (dview->f[0]);
  2178. }
  2179. if (dview->converter != str_cnv_from_term)
  2180. str_close_conv (dview->converter);
  2181. destroy_hdiff (dview);
  2182. if (dview->a[0] != NULL)
  2183. {
  2184. g_array_foreach (dview->a[0], DIFFLN, cc_free_elt);
  2185. g_array_free (dview->a[0], TRUE);
  2186. dview->a[0] = NULL;
  2187. }
  2188. if (dview->a[1] != NULL)
  2189. {
  2190. g_array_foreach (dview->a[1], DIFFLN, cc_free_elt);
  2191. g_array_free (dview->a[1], TRUE);
  2192. dview->a[1] = NULL;
  2193. }
  2194. g_free (dview->label[0]);
  2195. g_free (dview->label[1]);
  2196. }
  2197. /* --------------------------------------------------------------------------------------------- */
  2198. static int
  2199. dview_display_file (const WDiff * dview, int ord, int r, int c, int height, int width)
  2200. {
  2201. size_t i, k;
  2202. int j;
  2203. char buf[BUFSIZ];
  2204. FBUF *f = dview->f[ord];
  2205. int skip = dview->skip_cols;
  2206. int display_symbols = dview->display_symbols;
  2207. int display_numbers = dview->display_numbers;
  2208. int show_cr = dview->show_cr;
  2209. int tab_size = 8;
  2210. const DIFFLN *p;
  2211. int nwidth = display_numbers;
  2212. int xwidth = display_symbols + display_numbers;
  2213. if (dview->tab_size > 0 && dview->tab_size < 9)
  2214. tab_size = dview->tab_size;
  2215. if (xwidth)
  2216. {
  2217. if (xwidth > width && display_symbols)
  2218. {
  2219. xwidth--;
  2220. display_symbols = 0;
  2221. }
  2222. if (xwidth > width && display_numbers)
  2223. {
  2224. xwidth = width;
  2225. display_numbers = width;
  2226. }
  2227. xwidth++;
  2228. c += xwidth;
  2229. width -= xwidth;
  2230. if (width < 0)
  2231. {
  2232. width = 0;
  2233. }
  2234. }
  2235. if ((int) sizeof (buf) <= width || (int) sizeof (buf) <= nwidth)
  2236. {
  2237. /* abnormal, but avoid buffer overflow */
  2238. return -1;
  2239. }
  2240. for (i = dview->skip_rows, j = 0; i < dview->a[ord]->len && j < height; j++, i++)
  2241. {
  2242. int ch, next_ch, col;
  2243. size_t cnt;
  2244. p = (DIFFLN *) & g_array_index (dview->a[ord], DIFFLN, i);
  2245. ch = p->ch;
  2246. tty_setcolor (NORMAL_COLOR);
  2247. if (display_symbols)
  2248. {
  2249. tty_gotoyx (r + j, c - 2);
  2250. tty_print_char (ch);
  2251. }
  2252. if (p->line != 0)
  2253. {
  2254. if (display_numbers)
  2255. {
  2256. tty_gotoyx (r + j, c - xwidth);
  2257. g_snprintf (buf, display_numbers + 1, "%*d", nwidth, p->line);
  2258. tty_print_string (str_fit_to_term (buf, nwidth, J_LEFT_FIT));
  2259. }
  2260. if (ch == ADD_CH)
  2261. {
  2262. tty_setcolor (DFF_ADD_COLOR);
  2263. }
  2264. if (ch == CHG_CH)
  2265. {
  2266. tty_setcolor (DFF_CHG_COLOR);
  2267. }
  2268. if (f == NULL)
  2269. {
  2270. if (i == (size_t) dview->search.last_found_line)
  2271. {
  2272. tty_setcolor (MARKED_SELECTED_COLOR);
  2273. }
  2274. else
  2275. {
  2276. if (dview->hdiff != NULL && g_ptr_array_index (dview->hdiff, i) != NULL)
  2277. {
  2278. char att[BUFSIZ];
  2279. if (dview->utf8)
  2280. k = dview_str_utf8_offset_to_pos (p->p, width);
  2281. else
  2282. k = width;
  2283. cvt_mgeta (p->p, p->u.len, buf, k, skip, tab_size, show_cr,
  2284. g_ptr_array_index (dview->hdiff, i), ord, att);
  2285. tty_gotoyx (r + j, c);
  2286. col = 0;
  2287. for (cnt = 0; cnt < strlen (buf) && col < width; cnt++)
  2288. {
  2289. int w;
  2290. gboolean ch_res;
  2291. if (dview->utf8)
  2292. {
  2293. next_ch = dview_get_utf (buf + cnt, &w, &ch_res);
  2294. if (w > 1)
  2295. cnt += w - 1;
  2296. if (!g_unichar_isprint (next_ch))
  2297. next_ch = '.';
  2298. }
  2299. else
  2300. next_ch = dview_get_byte (buf + cnt, &ch_res);
  2301. if (ch_res)
  2302. {
  2303. tty_setcolor (att[cnt] ? DFF_CHH_COLOR : DFF_CHG_COLOR);
  2304. #ifdef HAVE_CHARSET
  2305. if (utf8_display)
  2306. {
  2307. if (!dview->utf8)
  2308. {
  2309. next_ch =
  2310. convert_from_8bit_to_utf_c ((unsigned char) next_ch,
  2311. dview->converter);
  2312. }
  2313. }
  2314. else if (dview->utf8)
  2315. next_ch =
  2316. convert_from_utf_to_current_c (next_ch, dview->converter);
  2317. else
  2318. #endif
  2319. next_ch = convert_to_display_c (next_ch);
  2320. tty_print_anychar (next_ch);
  2321. col++;
  2322. }
  2323. }
  2324. continue;
  2325. }
  2326. else if (ch == CHG_CH)
  2327. {
  2328. tty_setcolor (DFF_CHH_COLOR);
  2329. }
  2330. }
  2331. if (dview->utf8)
  2332. k = dview_str_utf8_offset_to_pos (p->p, width);
  2333. else
  2334. k = width;
  2335. cvt_mget (p->p, p->u.len, buf, k, skip, tab_size, show_cr);
  2336. }
  2337. else
  2338. {
  2339. cvt_fget (f, p->u.off, buf, width, skip, tab_size, show_cr);
  2340. }
  2341. }
  2342. else
  2343. {
  2344. if (display_numbers)
  2345. {
  2346. tty_gotoyx (r + j, c - xwidth);
  2347. memset (buf, ' ', display_numbers);
  2348. buf[display_numbers] = '\0';
  2349. tty_print_string (buf);
  2350. }
  2351. if (ch == DEL_CH)
  2352. {
  2353. tty_setcolor (DFF_DEL_COLOR);
  2354. }
  2355. if (ch == CHG_CH)
  2356. {
  2357. tty_setcolor (DFF_CHD_COLOR);
  2358. }
  2359. memset (buf, ' ', width);
  2360. buf[width] = '\0';
  2361. }
  2362. tty_gotoyx (r + j, c);
  2363. /* tty_print_nstring (buf, width); */
  2364. col = 0;
  2365. for (cnt = 0; cnt < strlen (buf) && col < width; cnt++)
  2366. {
  2367. int w;
  2368. gboolean ch_res;
  2369. if (dview->utf8)
  2370. {
  2371. next_ch = dview_get_utf (buf + cnt, &w, &ch_res);
  2372. if (w > 1)
  2373. cnt += w - 1;
  2374. if (!g_unichar_isprint (next_ch))
  2375. next_ch = '.';
  2376. }
  2377. else
  2378. next_ch = dview_get_byte (buf + cnt, &ch_res);
  2379. if (ch_res)
  2380. {
  2381. #ifdef HAVE_CHARSET
  2382. if (utf8_display)
  2383. {
  2384. if (!dview->utf8)
  2385. {
  2386. next_ch =
  2387. convert_from_8bit_to_utf_c ((unsigned char) next_ch, dview->converter);
  2388. }
  2389. }
  2390. else if (dview->utf8)
  2391. next_ch = convert_from_utf_to_current_c (next_ch, dview->converter);
  2392. else
  2393. #endif
  2394. next_ch = convert_to_display_c (next_ch);
  2395. tty_print_anychar (next_ch);
  2396. col++;
  2397. }
  2398. }
  2399. continue;
  2400. }
  2401. tty_setcolor (NORMAL_COLOR);
  2402. k = width;
  2403. if (width < xwidth - 1)
  2404. {
  2405. k = xwidth - 1;
  2406. }
  2407. memset (buf, ' ', k);
  2408. buf[k] = '\0';
  2409. for (; j < height; j++)
  2410. {
  2411. if (xwidth)
  2412. {
  2413. tty_gotoyx (r + j, c - xwidth);
  2414. /* tty_print_nstring (buf, xwidth - 1); */
  2415. tty_print_string (str_fit_to_term (buf, xwidth - 1, J_LEFT_FIT));
  2416. }
  2417. tty_gotoyx (r + j, c);
  2418. /* tty_print_nstring (buf, width); */
  2419. tty_print_string (str_fit_to_term (buf, width, J_LEFT_FIT));
  2420. }
  2421. return 0;
  2422. }
  2423. /* --------------------------------------------------------------------------------------------- */
  2424. static void
  2425. dview_status (const WDiff * dview, int ord, int width, int c)
  2426. {
  2427. const char *buf;
  2428. int filename_width;
  2429. int linenum, lineofs;
  2430. tty_setcolor (STATUSBAR_COLOR);
  2431. tty_gotoyx (0, c);
  2432. get_line_numbers (dview->a[ord], dview->skip_rows, &linenum, &lineofs);
  2433. filename_width = width - 22;
  2434. if (filename_width < 8)
  2435. filename_width = 8;
  2436. buf = str_term_trim (strip_home_and_password (dview->label[ord]), filename_width);
  2437. if (ord == 0)
  2438. tty_printf ("%-*s %6d+%-4d Col %-4d ", filename_width, buf, linenum, lineofs,
  2439. dview->skip_cols);
  2440. else
  2441. tty_printf ("%-*s %6d+%-4d Dif %-4d ", filename_width, buf, linenum, lineofs, dview->ndiff);
  2442. }
  2443. /* --------------------------------------------------------------------------------------------- */
  2444. static void
  2445. dview_redo (WDiff * dview)
  2446. {
  2447. if (dview->display_numbers)
  2448. {
  2449. int old = dview->display_numbers;
  2450. dview->display_numbers = calc_nwidth ((const GArray **) dview->a);
  2451. dview->new_frame = (old != dview->display_numbers);
  2452. }
  2453. dview_reread (dview);
  2454. }
  2455. /* --------------------------------------------------------------------------------------------- */
  2456. static void
  2457. dview_edit (WDiff * dview, int ord)
  2458. {
  2459. Dlg_head *h;
  2460. gboolean h_modal;
  2461. int linenum, lineofs;
  2462. if (dview->dsrc == DATA_SRC_TMP)
  2463. {
  2464. error_dialog (_("Edit"), _("Edit is disabled"));
  2465. return;
  2466. }
  2467. h = ((Widget *) dview)->owner;
  2468. h_modal = h->modal;
  2469. get_line_numbers (dview->a[ord], dview->skip_rows, &linenum, &lineofs);
  2470. h->modal = TRUE; /* not allow edit file in several editors */
  2471. do_edit_at_line (dview->file[ord], use_internal_edit, linenum);
  2472. h->modal = h_modal;
  2473. dview_redo (dview);
  2474. dview_update (dview);
  2475. }
  2476. /* --------------------------------------------------------------------------------------------- */
  2477. static void
  2478. dview_goto_cmd (WDiff * dview, int ord)
  2479. {
  2480. static const char *title[2] = { N_("Goto line (left)"), N_("Goto line (right)") };
  2481. static char prev[256];
  2482. /* XXX some statics here, to be remembered between runs */
  2483. int newline;
  2484. char *input;
  2485. input = input_dialog (_(title[ord]), _("Enter line:"), MC_HISTORY_YDIFF_GOTO_LINE, prev);
  2486. if (input != NULL)
  2487. {
  2488. const char *s = input;
  2489. if (scan_deci (&s, &newline) == 0 && *s == '\0')
  2490. {
  2491. size_t i = 0;
  2492. if (newline > 0)
  2493. {
  2494. const DIFFLN *p;
  2495. for (; i < dview->a[ord]->len; i++)
  2496. {
  2497. p = &g_array_index (dview->a[ord], DIFFLN, i);
  2498. if (p->line == newline)
  2499. {
  2500. break;
  2501. }
  2502. }
  2503. }
  2504. dview->skip_rows = dview->search.last_accessed_num_line = (ssize_t) i;
  2505. g_snprintf (prev, sizeof (prev), "%d", newline);
  2506. }
  2507. g_free (input);
  2508. }
  2509. }
  2510. /* --------------------------------------------------------------------------------------------- */
  2511. static void
  2512. dview_labels (WDiff * dview)
  2513. {
  2514. Dlg_head *h = dview->widget.owner;
  2515. WButtonBar *b = find_buttonbar (h);
  2516. buttonbar_set_label (b, 1, Q_ ("ButtonBar|Help"), diff_map, (Widget *) dview);
  2517. buttonbar_set_label (b, 2, Q_ ("ButtonBar|Save"), diff_map, (Widget *) dview);
  2518. buttonbar_set_label (b, 4, Q_ ("ButtonBar|Edit"), diff_map, (Widget *) dview);
  2519. buttonbar_set_label (b, 5, Q_ ("ButtonBar|Merge"), diff_map, (Widget *) dview);
  2520. buttonbar_set_label (b, 7, Q_ ("ButtonBar|Search"), diff_map, (Widget *) dview);
  2521. buttonbar_set_label (b, 9, Q_ ("ButtonBar|Options"), diff_map, (Widget *) dview);
  2522. buttonbar_set_label (b, 10, Q_ ("ButtonBar|Quit"), diff_map, (Widget *) dview);
  2523. }
  2524. /* --------------------------------------------------------------------------------------------- */
  2525. static int
  2526. dview_event (Gpm_Event * event, void *x)
  2527. {
  2528. WDiff *dview = (WDiff *) x;
  2529. int result = MOU_NORMAL;
  2530. /* We are not interested in the release events */
  2531. if ((event->type & (GPM_DOWN | GPM_DRAG)) == 0)
  2532. {
  2533. return result;
  2534. }
  2535. /* Wheel events */
  2536. if ((event->buttons & GPM_B_UP) != 0 && (event->type & GPM_DOWN) != 0)
  2537. {
  2538. dview->skip_rows -= 2;
  2539. dview->search.last_accessed_num_line = dview->skip_rows;
  2540. dview_update (dview);
  2541. }
  2542. else if ((event->buttons & GPM_B_DOWN) != 0 && (event->type & GPM_DOWN) != 0)
  2543. {
  2544. dview->skip_rows += 2;
  2545. dview->search.last_accessed_num_line = dview->skip_rows;
  2546. dview_update (dview);
  2547. }
  2548. return result;
  2549. }
  2550. static gboolean
  2551. dview_save (WDiff * dview)
  2552. {
  2553. gboolean res = TRUE;
  2554. if (!dview->merged)
  2555. return res;
  2556. res = mc_util_unlink_backup_if_possible (dview->file[0], "~~~");
  2557. dview->merged = !res;
  2558. return res;
  2559. }
  2560. static void
  2561. dview_do_save (WDiff * dview)
  2562. {
  2563. (void) dview_save (dview);
  2564. }
  2565. static void
  2566. dview_save_options (WDiff * dview)
  2567. {
  2568. mc_config_set_bool (mc_main_config, "DiffView", "show_symbols",
  2569. dview->display_symbols != 0 ? TRUE : FALSE);
  2570. mc_config_set_bool (mc_main_config, "DiffView", "show_numbers",
  2571. dview->display_numbers != 0 ? TRUE : FALSE);
  2572. mc_config_set_int (mc_main_config, "DiffView", "tab_size", dview->tab_size);
  2573. mc_config_set_int (mc_main_config, "DiffView", "diff_quality", dview->opt.quality);
  2574. mc_config_set_bool (mc_main_config, "DiffView", "diff_ignore_tws",
  2575. dview->opt.strip_trailing_cr);
  2576. mc_config_set_bool (mc_main_config, "DiffView", "diff_ignore_all_space",
  2577. dview->opt.ignore_all_space);
  2578. mc_config_set_bool (mc_main_config, "DiffView", "diff_ignore_space_change",
  2579. dview->opt.ignore_space_change);
  2580. mc_config_set_bool (mc_main_config, "DiffView", "diff_tab_expansion",
  2581. dview->opt.ignore_tab_expansion);
  2582. mc_config_set_bool (mc_main_config, "DiffView", "diff_ignore_case", dview->opt.ignore_case);
  2583. }
  2584. static void
  2585. dview_load_options (WDiff * dview)
  2586. {
  2587. gboolean show_numbers, show_symbols;
  2588. int tab_size;
  2589. show_symbols = mc_config_get_bool (mc_main_config, "DiffView", "show_symbols", FALSE);
  2590. if (show_symbols)
  2591. dview->display_symbols = 1;
  2592. show_numbers = mc_config_get_bool (mc_main_config, "DiffView", "show_numbers", FALSE);
  2593. if (show_numbers)
  2594. dview->display_numbers = calc_nwidth ((const GArray ** const) dview->a);
  2595. tab_size = mc_config_get_int (mc_main_config, "DiffView", "tab_size", 8);
  2596. if (tab_size > 0 && tab_size < 9)
  2597. dview->tab_size = tab_size;
  2598. else
  2599. dview->tab_size = 8;
  2600. dview->opt.quality = mc_config_get_int (mc_main_config, "DiffView", "diff_quality", 0);
  2601. dview->opt.strip_trailing_cr =
  2602. mc_config_get_bool (mc_main_config, "DiffView", "diff_ignore_tws", FALSE);
  2603. dview->opt.ignore_all_space =
  2604. mc_config_get_bool (mc_main_config, "DiffView", "diff_ignore_all_space", FALSE);
  2605. dview->opt.ignore_space_change =
  2606. mc_config_get_bool (mc_main_config, "DiffView", "diff_ignore_space_change", FALSE);
  2607. dview->opt.ignore_tab_expansion =
  2608. mc_config_get_bool (mc_main_config, "DiffView", "diff_tab_expansion", FALSE);
  2609. dview->opt.ignore_case =
  2610. mc_config_get_bool (mc_main_config, "DiffView", "diff_ignore_case", FALSE);
  2611. dview->new_frame = 1;
  2612. }
  2613. /*
  2614. * Check if it's OK to close the diff viewer. If there are unsaved changes,
  2615. * ask user.
  2616. */
  2617. static gboolean
  2618. dview_ok_to_exit (WDiff * dview)
  2619. {
  2620. gboolean res = TRUE;
  2621. int act;
  2622. if (!dview->merged)
  2623. return res;
  2624. act = query_dialog (_("Quit"), !midnight_shutdown ?
  2625. _("File was modified. Save with exit?") :
  2626. _("Midnight Commander is being shut down.\nSave modified file?"),
  2627. D_NORMAL, 2, _("&Yes"), _("&No"));
  2628. /* Esc is No */
  2629. if (midnight_shutdown || (act == -1))
  2630. act = 1;
  2631. switch (act)
  2632. {
  2633. case -1: /* Esc */
  2634. res = FALSE;
  2635. break;
  2636. case 0: /* Yes */
  2637. (void) dview_save (dview);
  2638. res = TRUE;
  2639. break;
  2640. case 1: /* No */
  2641. if (mc_util_restore_from_backup_if_possible (dview->file[0], "~~~"))
  2642. res = mc_util_unlink_backup_if_possible (dview->file[0], "~~~");
  2643. /* fall through */
  2644. default:
  2645. res = TRUE;
  2646. break;
  2647. }
  2648. return res;
  2649. }
  2650. /* --------------------------------------------------------------------------------------------- */
  2651. static cb_ret_t
  2652. dview_execute_cmd (WDiff * dview, unsigned long command)
  2653. {
  2654. cb_ret_t res = MSG_HANDLED;
  2655. switch (command)
  2656. {
  2657. case CK_Help:
  2658. interactive_display (NULL, "[Diff Viewer]");
  2659. break;
  2660. case CK_ShowSymbols:
  2661. dview->display_symbols ^= 1;
  2662. dview->new_frame = 1;
  2663. break;
  2664. case CK_ShowNumbers:
  2665. dview->display_numbers ^= calc_nwidth ((const GArray ** const) dview->a);
  2666. dview->new_frame = 1;
  2667. break;
  2668. case CK_SplitFull:
  2669. dview->full ^= 1;
  2670. dview->new_frame = 1;
  2671. break;
  2672. case CK_SplitEqual:
  2673. if (!dview->full)
  2674. {
  2675. dview->bias = 0;
  2676. dview->new_frame = 1;
  2677. }
  2678. break;
  2679. case CK_SplitMore:
  2680. if (!dview->full)
  2681. {
  2682. dview_compute_split (dview, 1);
  2683. dview->new_frame = 1;
  2684. }
  2685. break;
  2686. case CK_SplitLess:
  2687. if (!dview->full)
  2688. {
  2689. dview_compute_split (dview, -1);
  2690. dview->new_frame = 1;
  2691. }
  2692. break;
  2693. case CK_Tab2:
  2694. dview->tab_size = 2;
  2695. break;
  2696. case CK_Tab3:
  2697. dview->tab_size = 3;
  2698. break;
  2699. case CK_Tab4:
  2700. dview->tab_size = 4;
  2701. break;
  2702. case CK_Tab8:
  2703. dview->tab_size = 8;
  2704. break;
  2705. case CK_Swap:
  2706. dview->ord ^= 1;
  2707. break;
  2708. case CK_Redo:
  2709. dview_redo (dview);
  2710. break;
  2711. case CK_HunkNext:
  2712. dview->skip_rows = dview->search.last_accessed_num_line =
  2713. find_next_hunk (dview->a[0], dview->skip_rows);
  2714. break;
  2715. case CK_HunkPrev:
  2716. dview->skip_rows = dview->search.last_accessed_num_line =
  2717. find_prev_hunk (dview->a[0], dview->skip_rows);
  2718. break;
  2719. case CK_Goto:
  2720. dview_goto_cmd (dview, TRUE);
  2721. break;
  2722. case CK_Edit:
  2723. dview_edit (dview, dview->ord);
  2724. break;
  2725. case CK_Merge:
  2726. do_merge_hunk (dview);
  2727. dview_redo (dview);
  2728. break;
  2729. case CK_EditOther:
  2730. dview_edit (dview, dview->ord ^ 1);
  2731. break;
  2732. case CK_Search:
  2733. dview_search_cmd (dview);
  2734. break;
  2735. case CK_SearchContinue:
  2736. dview_continue_search_cmd (dview);
  2737. break;
  2738. case CK_Top:
  2739. dview->skip_rows = dview->search.last_accessed_num_line = 0;
  2740. break;
  2741. case CK_Bottom:
  2742. dview->skip_rows = dview->search.last_accessed_num_line = dview->a[0]->len - 1;
  2743. break;
  2744. case CK_Up:
  2745. if (dview->skip_rows > 0)
  2746. {
  2747. dview->skip_rows--;
  2748. dview->search.last_accessed_num_line = dview->skip_rows;
  2749. }
  2750. break;
  2751. case CK_Down:
  2752. dview->skip_rows++;
  2753. dview->search.last_accessed_num_line = dview->skip_rows;
  2754. break;
  2755. case CK_PageDown:
  2756. if (dview->height > 2)
  2757. {
  2758. dview->skip_rows += dview->height - 2;
  2759. dview->search.last_accessed_num_line = dview->skip_rows;
  2760. }
  2761. break;
  2762. case CK_PageUp:
  2763. if (dview->height > 2)
  2764. {
  2765. dview->skip_rows -= dview->height - 2;
  2766. dview->search.last_accessed_num_line = dview->skip_rows;
  2767. }
  2768. break;
  2769. case CK_Left:
  2770. dview->skip_cols--;
  2771. break;
  2772. case CK_Right:
  2773. dview->skip_cols++;
  2774. break;
  2775. case CK_LeftQuick:
  2776. dview->skip_cols -= 8;
  2777. break;
  2778. case CK_RightQuick:
  2779. dview->skip_cols += 8;
  2780. break;
  2781. case CK_Home:
  2782. dview->skip_cols = 0;
  2783. break;
  2784. case CK_Shell:
  2785. view_other_cmd ();
  2786. break;
  2787. case CK_Quit:
  2788. dview->view_quit = 1;
  2789. break;
  2790. case CK_Save:
  2791. dview_do_save (dview);
  2792. break;
  2793. case CK_Options:
  2794. dview_diff_options (dview);
  2795. break;
  2796. #ifdef HAVE_CHARSET
  2797. case CK_SelectCodepage:
  2798. dview_select_encoding (dview);
  2799. dview_reread (dview);
  2800. tty_touch_screen ();
  2801. repaint_screen ();
  2802. break;
  2803. #endif
  2804. default:
  2805. res = MSG_NOT_HANDLED;
  2806. }
  2807. return res;
  2808. }
  2809. /* --------------------------------------------------------------------------------------------- */
  2810. static cb_ret_t
  2811. dview_handle_key (WDiff * dview, int key)
  2812. {
  2813. unsigned long command;
  2814. key = convert_from_input_c (key);
  2815. command = keybind_lookup_keymap_command (diff_map, key);
  2816. if ((command != CK_Ignore_Key) && (dview_execute_cmd (dview, command) == MSG_HANDLED))
  2817. return MSG_HANDLED;
  2818. /* Key not used */
  2819. return MSG_NOT_HANDLED;
  2820. }
  2821. /* --------------------------------------------------------------------------------------------- */
  2822. static cb_ret_t
  2823. dview_callback (Widget * w, widget_msg_t msg, int parm)
  2824. {
  2825. WDiff *dview = (WDiff *) w;
  2826. Dlg_head *h = dview->widget.owner;
  2827. cb_ret_t i;
  2828. switch (msg)
  2829. {
  2830. case WIDGET_INIT:
  2831. dview_labels (dview);
  2832. dview_load_options (dview);
  2833. dview_update (dview);
  2834. return MSG_HANDLED;
  2835. case WIDGET_DRAW:
  2836. dview->new_frame = 1;
  2837. dview_update (dview);
  2838. return MSG_HANDLED;
  2839. case WIDGET_KEY:
  2840. i = dview_handle_key (dview, parm);
  2841. if (dview->view_quit)
  2842. dlg_stop (h);
  2843. else
  2844. dview_update (dview);
  2845. return i;
  2846. case WIDGET_COMMAND:
  2847. i = dview_execute_cmd (dview, parm);
  2848. if (dview->view_quit)
  2849. dlg_stop (h);
  2850. else
  2851. dview_update (dview);
  2852. return i;
  2853. case WIDGET_DESTROY:
  2854. dview_save_options (dview);
  2855. dview_fini (dview);
  2856. return MSG_HANDLED;
  2857. default:
  2858. return default_proc (msg, parm);
  2859. }
  2860. }
  2861. /* --------------------------------------------------------------------------------------------- */
  2862. static void
  2863. dview_adjust_size (Dlg_head * h)
  2864. {
  2865. WDiff *dview;
  2866. WButtonBar *bar;
  2867. /* Look up the viewer and the buttonbar, we assume only two widgets here */
  2868. dview = (WDiff *) find_widget_type (h, dview_callback);
  2869. bar = find_buttonbar (h);
  2870. widget_set_size (&dview->widget, 0, 0, LINES - 1, COLS);
  2871. widget_set_size ((Widget *) bar, LINES - 1, 0, 1, COLS);
  2872. dview_compute_areas (dview);
  2873. }
  2874. /* --------------------------------------------------------------------------------------------- */
  2875. static cb_ret_t
  2876. dview_dialog_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
  2877. {
  2878. WDiff *dview = (WDiff *) data;
  2879. switch (msg)
  2880. {
  2881. case DLG_RESIZE:
  2882. dview_adjust_size (h);
  2883. return MSG_HANDLED;
  2884. case DLG_ACTION:
  2885. /* command from buttonbar */
  2886. return send_message ((Widget *) dview, WIDGET_COMMAND, parm);
  2887. case DLG_VALIDATE:
  2888. dview = (WDiff *) find_widget_type (h, dview_callback);
  2889. h->state = DLG_ACTIVE; /* don't stop the dialog before final decision */
  2890. if (dview_ok_to_exit (dview))
  2891. h->state = DLG_CLOSED;
  2892. return MSG_HANDLED;
  2893. default:
  2894. return default_dlg_callback (h, sender, msg, parm, data);
  2895. }
  2896. }
  2897. /* --------------------------------------------------------------------------------------------- */
  2898. static char *
  2899. dview_get_title (const Dlg_head * h, size_t len)
  2900. {
  2901. const WDiff *dview = (const WDiff *) find_widget_type (h, dview_callback);
  2902. const char *modified = dview->merged ? " (*) " : " ";
  2903. size_t len1;
  2904. GString *title;
  2905. len1 = (len - str_term_width1 (_("Diff:")) - strlen (modified) - 3) / 2;
  2906. title = g_string_sized_new (len);
  2907. g_string_append (title, _("Diff:"));
  2908. g_string_append (title, modified);
  2909. g_string_append (title, str_term_trim (dview->label[0], len1));
  2910. g_string_append (title, " | ");
  2911. g_string_append (title, str_term_trim (dview->label[1], len1));
  2912. return g_string_free (title, FALSE);
  2913. }
  2914. /*** public functions ****************************************************************************/
  2915. /* --------------------------------------------------------------------------------------------- */
  2916. int
  2917. diff_view (const char *file1, const char *file2, const char *label1, const char *label2)
  2918. {
  2919. int error;
  2920. WDiff *dview;
  2921. Dlg_head *dview_dlg;
  2922. /* Create dialog and widgets, put them on the dialog */
  2923. dview_dlg =
  2924. create_dlg (FALSE, 0, 0, LINES, COLS, NULL, dview_dialog_callback,
  2925. "[Diff Viewer]", NULL, DLG_WANT_TAB);
  2926. dview = g_new0 (WDiff, 1);
  2927. init_widget (&dview->widget, 0, 0, LINES - 1, COLS,
  2928. (callback_fn) dview_callback, (mouse_h) dview_event);
  2929. widget_want_cursor (dview->widget, 0);
  2930. add_widget (dview_dlg, dview);
  2931. add_widget (dview_dlg, buttonbar_new (TRUE));
  2932. dview_dlg->get_title = dview_get_title;
  2933. error = dview_init (dview, "-a", file1, file2, label1, label2, DATA_SRC_MEM); /* XXX binary diff? */
  2934. /* Please note that if you add another widget,
  2935. * you have to modify dview_adjust_size to
  2936. * be aware of it
  2937. */
  2938. if (error == 0)
  2939. run_dlg (dview_dlg);
  2940. if ((error != 0) || (dview_dlg->state == DLG_CLOSED))
  2941. destroy_dlg (dview_dlg);
  2942. return error;
  2943. }
  2944. /* --------------------------------------------------------------------------------------------- */
  2945. #define GET_FILE_AND_STAMP(n) \
  2946. do \
  2947. { \
  2948. use_copy##n = 0; \
  2949. real_file##n = file##n; \
  2950. if (!vfs_file_is_local (file##n)) \
  2951. { \
  2952. real_file##n = mc_getlocalcopy (file##n); \
  2953. if (real_file##n != NULL) \
  2954. { \
  2955. use_copy##n = 1; \
  2956. if (mc_stat (real_file##n, &st##n) != 0) \
  2957. use_copy##n = -1; \
  2958. } \
  2959. } \
  2960. } \
  2961. while (0)
  2962. #define UNGET_FILE(n) \
  2963. do \
  2964. { \
  2965. if (use_copy##n) \
  2966. { \
  2967. int changed = 0; \
  2968. if (use_copy##n > 0) \
  2969. { \
  2970. time_t mtime; \
  2971. mtime = st##n.st_mtime; \
  2972. if (mc_stat (real_file##n, &st##n) == 0) \
  2973. changed = (mtime != st##n.st_mtime); \
  2974. } \
  2975. mc_ungetlocalcopy (file##n, real_file##n, changed); \
  2976. g_free (real_file##n); \
  2977. } \
  2978. } \
  2979. while (0)
  2980. void
  2981. dview_diff_cmd (void)
  2982. {
  2983. int rv = 0;
  2984. char *file0 = NULL;
  2985. char *file1 = NULL;
  2986. int is_dir0 = 0;
  2987. int is_dir1 = 0;
  2988. if (mc_run_mode == MC_RUN_FULL)
  2989. {
  2990. const WPanel *panel0 = current_panel;
  2991. const WPanel *panel1 = other_panel;
  2992. if (get_current_index ())
  2993. {
  2994. panel0 = other_panel;
  2995. panel1 = current_panel;
  2996. }
  2997. file0 = concat_dir_and_file (panel0->cwd, selection (panel0)->fname);
  2998. file1 = concat_dir_and_file (panel1->cwd, selection (panel1)->fname);
  2999. is_dir0 = S_ISDIR (selection (panel0)->st.st_mode);
  3000. is_dir1 = S_ISDIR (selection (panel1)->st.st_mode);
  3001. }
  3002. if (rv == 0)
  3003. {
  3004. rv = -1;
  3005. if (file0 != NULL && !is_dir0 && file1 != NULL && !is_dir1)
  3006. {
  3007. int use_copy0;
  3008. int use_copy1;
  3009. struct stat st0;
  3010. struct stat st1;
  3011. char *real_file0;
  3012. char *real_file1;
  3013. GET_FILE_AND_STAMP (0);
  3014. GET_FILE_AND_STAMP (1);
  3015. if (real_file0 != NULL && real_file1 != NULL)
  3016. {
  3017. rv = diff_view (real_file0, real_file1, file0, file1);
  3018. }
  3019. UNGET_FILE (1);
  3020. UNGET_FILE (0);
  3021. }
  3022. }
  3023. g_free (file1);
  3024. g_free (file0);
  3025. if (rv != 0)
  3026. message (1, MSG_ERROR, _("Two files are needed to compare"));
  3027. }
  3028. /* --------------------------------------------------------------------------------------------- */
  3029. void
  3030. dview_update (WDiff * dview)
  3031. {
  3032. int height = dview->height;
  3033. int width1;
  3034. int width2;
  3035. int last = dview->a[0]->len - 1;
  3036. if (dview->skip_rows > last)
  3037. {
  3038. dview->skip_rows = dview->search.last_accessed_num_line = last;
  3039. }
  3040. if (dview->skip_rows < 0)
  3041. {
  3042. dview->skip_rows = dview->search.last_accessed_num_line = 0;
  3043. }
  3044. if (dview->skip_cols < 0)
  3045. {
  3046. dview->skip_cols = 0;
  3047. }
  3048. if (height < 2)
  3049. {
  3050. return;
  3051. }
  3052. width1 = dview->half1 + dview->bias;
  3053. width2 = dview->half2 - dview->bias;
  3054. if (dview->full)
  3055. {
  3056. width1 = COLS;
  3057. width2 = 0;
  3058. }
  3059. if (dview->new_frame)
  3060. {
  3061. int xwidth = dview->display_symbols + dview->display_numbers;
  3062. tty_setcolor (NORMAL_COLOR);
  3063. if (width1 > 1)
  3064. {
  3065. tty_draw_box (1, 0, height, width1, FALSE);
  3066. }
  3067. if (width2 > 1)
  3068. {
  3069. tty_draw_box (1, width1, height, width2, FALSE);
  3070. }
  3071. if (xwidth)
  3072. {
  3073. xwidth++;
  3074. if (xwidth < width1 - 1)
  3075. {
  3076. tty_gotoyx (1, xwidth);
  3077. tty_print_alt_char (mc_tty_frm[MC_TTY_FRM_DTOPMIDDLE], FALSE);
  3078. tty_gotoyx (height, xwidth);
  3079. tty_print_alt_char (mc_tty_frm[MC_TTY_FRM_DBOTTOMMIDDLE], FALSE);
  3080. tty_draw_vline (2, xwidth, mc_tty_frm[MC_TTY_FRM_VERT], height - 2);
  3081. }
  3082. if (xwidth < width2 - 1)
  3083. {
  3084. tty_gotoyx (1, width1 + xwidth);
  3085. tty_print_alt_char (mc_tty_frm[MC_TTY_FRM_DTOPMIDDLE], FALSE);
  3086. tty_gotoyx (height, width1 + xwidth);
  3087. tty_print_alt_char (mc_tty_frm[MC_TTY_FRM_DBOTTOMMIDDLE], FALSE);
  3088. tty_draw_vline (2, width1 + xwidth, mc_tty_frm[MC_TTY_FRM_VERT], height - 2);
  3089. }
  3090. }
  3091. dview->new_frame = 0;
  3092. }
  3093. if (width1 > 2)
  3094. {
  3095. dview_status (dview, dview->ord, width1, 0);
  3096. dview_display_file (dview, dview->ord, 2, 1, height - 2, width1 - 2);
  3097. }
  3098. if (width2 > 2)
  3099. {
  3100. dview_status (dview, dview->ord ^ 1, width2, width1);
  3101. dview_display_file (dview, dview->ord ^ 1, 2, width1 + 1, height - 2, width2 - 2);
  3102. }
  3103. }
  3104. /* --------------------------------------------------------------------------------------------- */