1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #ifdef HAVE_CONFIG_H
30 #include "../config.h"
31 #endif
32
33 #include "file.h"
34 #include "filter.h"
35 #include "textBuf.h"
36 #include "text.h"
37 #include "window.h"
38 #include "preferences.h"
39 #include "undo.h"
40 #include "menu.h"
41 #include "tags.h"
42 #include "server.h"
43 #include "interpret.h"
44 #include "editorconfig.h"
45 #include "../util/misc.h"
46 #include "../util/DialogF.h"
47 #include "../util/fileUtils.h"
48 #include "../util/getfiles.h"
49 #include "../util/printUtils.h"
50 #include "../util/utils.h"
51 #include "../util/nedit_malloc.h"
52 #include "../util/libxattr.h"
53
54 #include <errno.h>
55 #include <limits.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60 #include <ctype.h>
61 #include <iconv.h>
62 #include <locale.h>
63
64 #include <sys/types.h>
65 #include <sys/stat.h>
66 #ifndef __MVS__
67 #include <sys/param.h>
68 #endif
69 #include <fcntl.h>
70
71 #include <Xm/Xm.h>
72 #include <Xm/ToggleB.h>
73 #include <Xm/FileSB.h>
74 #include <Xm/RowColumn.h>
75 #include <Xm/Form.h>
76 #include <Xm/Label.h>
77
78 #ifdef HAVE_DEBUG_H
79 #include "../debug.h"
80 #endif
81
82 #include <inttypes.h>
83
84 #define ENC_ERROR_LIST_LEN 8
85
86 typedef struct Locale {
87 char *locale;
88 char *encoding;
89 } Locale;
90
91 static Locale locales[] = {
92 {
"aa_DJ",
"ISO8859-1"},
93 {
"af_ZA",
"ISO8859-1"},
94 {
"an_ES",
"ISO8859-15"},
95 {
"ar_AE",
"ISO8859-6"},
96 {
"ar_BH",
"ISO8859-6"},
97 {
"ar_DZ",
"ISO8859-6"},
98 {
"ar_EG",
"ISO8859-6"},
99 {
"ar_IQ",
"ISO8859-6"},
100 {
"ar_JO",
"ISO8859-6"},
101 {
"ar_KW",
"ISO8859-6"},
102 {
"ar_LB",
"ISO8859-6"},
103 {
"ar_LY",
"ISO8859-6"},
104 {
"ar_MA",
"ISO8859-6"},
105 {
"ar_OM",
"ISO8859-6"},
106 {
"ar_QA",
"ISO8859-6"},
107 {
"ar_SA",
"ISO8859-6"},
108 {
"ar_SD",
"ISO8859-6"},
109 {
"ar_SY",
"ISO8859-6"},
110 {
"ar_TN",
"ISO8859-6"},
111 {
"ar_YE",
"ISO8859-6"},
112 {
"ast_ES",
"ISO8859-15"},
113 {
"be_BY",
"CP1251"},
114 {
"bg_BG",
"CP1251"},
115 {
"br_FR",
"ISO8859-15"},
116 {
"br_FR@euro",
"ISO8859-15"},
117 {
"bs_BA",
"ISO8859-2"},
118 {
"ca_AD",
"ISO8859-15"},
119 {
"ca_ES",
"ISO8859-15"},
120 {
"ca_ES@euro",
"ISO8859-15"},
121 {
"ca_FR",
"ISO8859-15"},
122 {
"ca_IT",
"ISO8859-15"},
123 {
"cs_CZ",
"ISO8859-2"},
124 {
"cy_GB",
"ISO8859-14"},
125 {
"da_DK",
"ISO8859-1"},
126 {
"de_AT",
"ISO8859-15"},
127 {
"de_AT@euro",
"ISO8859-15"},
128 {
"de_BE",
"ISO8859-15"},
129 {
"de_BE@euro",
"ISO8859-15"},
130 {
"de_CH",
"ISO8859-1"},
131 {
"de_DE",
"ISO8859-15"},
132 {
"de_DE@euro",
"ISO8859-15"},
133 {
"de_LU",
"ISO8859-15"},
134 {
"de_LU@euro",
"ISO8859-15"},
135 {
"el_GR",
"ISO8859-7"},
136 {
"el_CY",
"ISO8859-7"},
137 {
"en_AU",
"ISO8859-1"},
138 {
"en_BW",
"ISO8859-1"},
139 {
"en_CA",
"ISO8859-1"},
140 {
"en_DK",
"ISO8859-1"},
141 {
"en_GB",
"ISO8859-1"},
142 {
"en_HK",
"ISO8859-1"},
143 {
"en_IE",
"ISO8859-15"},
144 {
"en_IE@euro",
"ISO8859-15"},
145 {
"en_NZ",
"ISO8859-1"},
146 {
"en_PH",
"ISO8859-1"},
147 {
"en_SG",
"ISO8859-1"},
148 {
"en_US",
"ISO8859-1"},
149 {
"en_ZA",
"ISO8859-1"},
150 {
"en_ZW",
"ISO8859-1"},
151 {
"es_AR",
"ISO8859-1"},
152 {
"es_BO",
"ISO8859-1"},
153 {
"es_CL",
"ISO8859-1"},
154 {
"es_CO",
"ISO8859-1"},
155 {
"es_CR",
"ISO8859-1"},
156 {
"es_DO",
"ISO8859-1"},
157 {
"es_EC",
"ISO8859-1"},
158 {
"es_ES",
"ISO8859-15"},
159 {
"es_ES@euro",
"ISO8859-15"},
160 {
"es_GT",
"ISO8859-1"},
161 {
"es_HN",
"ISO8859-1"},
162 {
"es_MX",
"ISO8859-1"},
163 {
"es_NI",
"ISO8859-1"},
164 {
"es_PA",
"ISO8859-1"},
165 {
"es_PE",
"ISO8859-1"},
166 {
"es_PR",
"ISO8859-1"},
167 {
"es_PY",
"ISO8859-1"},
168 {
"es_SV",
"ISO8859-1"},
169 {
"es_US",
"ISO8859-1"},
170 {
"es_UY",
"ISO8859-1"},
171 {
"es_VE",
"ISO8859-1"},
172 {
"et_EE",
"ISO8859-15"},
173 {
"et_EE.ISO8859-15",
"ISO8859-15"},
174 {
"eu_ES",
"ISO8859-15"},
175 {
"eu_ES@euro",
"ISO8859-15"},
176 {
"fi_FI",
"ISO8859-15"},
177 {
"fi_FI@euro",
"ISO8859-15"},
178 {
"fo_FO",
"ISO8859-1"},
179 {
"fr_BE",
"ISO8859-15"},
180 {
"fr_BE@euro",
"ISO8859-15"},
181 {
"fr_CA",
"ISO8859-15"},
182 {
"fr_CH",
"ISO8859-1"},
183 {
"fr_FR",
"ISO8859-15"},
184 {
"fr_FR@euro",
"ISO8859-15"},
185 {
"fr_LU",
"ISO8859-15"},
186 {
"fr_LU@euro",
"ISO8859-15"},
187 {
"ga_IE",
"ISO8859-15"},
188 {
"ga_IE@euro",
"ISO8859-15"},
189 {
"gd_GB",
"ISO8859-15"},
190 {
"gl_ES",
"ISO8859-15"},
191 {
"gl_ES@euro",
"ISO8859-15"},
192 {
"gv_GB",
"ISO8859-1"},
193 {
"he_IL",
"ISO8859-8"},
194 {
"hr_HR",
"ISO8859-2"},
195 {
"hsb_DE",
"ISO8859-2"},
196 {
"hu_HU",
"ISO8859-2"},
197 {
"hy_AM.ARMSCII-8",
"ARMSCII-8"},
198 {
"id_ID",
"ISO8859-1"},
199 {
"is_IS",
"ISO8859-1"},
200 {
"it_CH",
"ISO8859-1"},
201 {
"it_IT",
"ISO8859-15"},
202 {
"it_IT@euro",
"ISO8859-15"},
203 {
"iw_IL",
"ISO8859-8"},
204 {
"ja_JP.EUC-JP",
"EUC-JP"},
205 {
"ka_GE",
"GEORGIAN-PS"},
206 {
"kk_KZ",
"PT154"},
207 {
"kl_GL",
"ISO8859-1"},
208 {
"ko_KR.EUC-KR",
"EUC-KR"},
209 {
"ku_TR",
"ISO8859-9"},
210 {
"kw_GB",
"ISO8859-1"},
211 {
"lg_UG",
"ISO8859-10"},
212 {
"lt_LT",
"ISO8859-13"},
213 {
"lv_LV",
"ISO8859-13"},
214 {
"mg_MG",
"ISO8859-15"},
215 {
"mi_NZ",
"ISO8859-13"},
216 {
"mk_MK",
"ISO8859-5"},
217 {
"ms_MY",
"ISO8859-1"},
218 {
"mt_MT",
"ISO8859-3"},
219 {
"nb_NO",
"ISO8859-1"},
220 {
"nl_BE",
"ISO8859-15"},
221 {
"nl_BE@euro",
"ISO8859-15"},
222 {
"nl_NL",
"ISO8859-15"},
223 {
"nl_NL@euro",
"ISO8859-15"},
224 {
"nn_NO",
"ISO8859-1"},
225 {
"oc_FR",
"ISO8859-1"},
226 {
"om_KE",
"ISO8859-1"},
227 {
"pl_PL",
"ISO8859-2"},
228 {
"pt_BR",
"ISO8859-1"},
229 {
"pt_PT",
"ISO8859-15"},
230 {
"pt_PT@euro",
"ISO8859-15"},
231 {
"ro_RO",
"ISO8859-2"},
232 {
"ru_RU.KOI8-R",
"KOI8-R"},
233 {
"ru_RU",
"ISO8859-5"},
234 {
"ru_UA",
"KOI8-U"},
235 {
"sk_SK",
"ISO8859-2"},
236 {
"sl_SI",
"ISO8859-2"},
237 {
"so_DJ",
"ISO8859-1"},
238 {
"so_KE",
"ISO8859-1"},
239 {
"so_SO",
"ISO8859-1"},
240 {
"sq_AL",
"ISO8859-1"},
241 {
"st_ZA",
"ISO8859-1"},
242 {
"sv_FI",
"ISO8859-15"},
243 {
"sv_FI@euro",
"ISO8859-15"},
244 {
"sv_SE",
"ISO8859-1"},
245 {
"tg_TJ",
"KOI8-T"},
246 {
"th_TH",
"TIS-620"},
247 {
"tl_PH",
"ISO8859-1"},
248 {
"tr_CY",
"ISO8859-9"},
249 {
"tr_TR",
"ISO8859-9"},
250 {
"uk_UA",
"KOI8-U"},
251 {
"uz_UZ",
"ISO8859-1"},
252 {
"wa_BE",
"ISO8859-15"},
253 {
"wa_BE@euro",
"ISO8859-15"},
254 {
"xh_ZA",
"ISO8859-1"},
255 {
"yi_US",
"CP1255"},
256 {
"zh_CN.GB18030",
"GB18030"},
257 {
"zh_CN.GBK",
"GBK"},
258 {
"zh_CN",
"GB2312"},
259 {
"zh_HK",
"BIG5-HKSCS"},
260 {
"zh_SG.GBK",
"GBK"},
261 {
"zh_SG",
"GB2312"},
262 {
"zh_TW.EUC-TW",
"EUC-TW"},
263 {
"zh_TW",
"BIG5"},
264 {
"zu_ZA",
"ISO8859-1"},
265 {
NULL,
NULL}
266 };
267
268
269
270
271
272 #define MOD_CHECK_INTERVAL 3000
273
274 static int doSave(WindowInfo *window, Boolean setEncAttr);
275 static void safeClose(WindowInfo *window);
276 static int doOpen(WindowInfo *window,
const char *name,
const char *path,
277 const char *encoding,
const char *filter_name,
int flags);
278 static void backupFileName(WindowInfo *window,
char *name,
size_t len);
279 static int writeBckVersion(WindowInfo *window);
280 static int bckError(WindowInfo *window,
const char *errString,
const char *file);
281 static int fileWasModifiedExternally(WindowInfo *window);
282 static const char *errorString(
void);
283 static void addWrapNewlines(WindowInfo *window);
284 static int cmpWinAgainstFile(WindowInfo *window,
const char *fileName);
285 static int min(
int i1,
int i2);
286 static void modifiedWindowDestroyedCB(Widget w, XtPointer clientData,
287 XtPointer callData);
288 static void forceShowLineNumbers(WindowInfo *window);
289
290 static char* getEncodingAttribute(
const char *path);
291
292
293 WindowInfo *EditNewFile(WindowInfo *inWindow,
char *geometry,
int iconic,
294 const char *languageMode,
const char *defaultPath)
295 {
296 char name[
MAXPATHLEN];
297 WindowInfo *window;
298 size_t pathlen;
299 char *path;
300
301
302
303
304 UniqueUntitledName(name);
305
306
307 if (inWindow)
308 window = CreateDocument(inWindow, name);
309 else
310 window = CreateWindow(name, geometry, iconic);
311
312 path = window->path;
313 strcpy(window->filename, name);
314 strcpy(path, (defaultPath && *defaultPath) ? defaultPath : GetCurrentDir());
315 pathlen = strlen(window->path);
316
317
318 if (
0 < pathlen && path[pathlen -
1] !=
'/' && pathlen <
MAXPATHLEN -
1) {
319 strcpy(&path[pathlen],
"/");
320 }
321
322 SetWindowModified(window,
FALSE);
323 CLEAR_ALL_LOCKS(window->lockReasons);
324 UpdateWindowReadOnly(window);
325 UpdateStatsLine(window);
326 UpdateWindowTitle(window);
327 RefreshTabState(window);
328
329 if (languageMode ==
NULL)
330 DetermineLanguageMode(window, True);
331 else
332 SetLanguageMode(window, FindLanguageMode(languageMode), True);
333
334 ShowTabBar(window, GetShowTabBar(window));
335
336 if (iconic && IsIconic(window))
337 RaiseDocument(window);
338 else
339 RaiseDocumentWindow(window);
340
341 SortTabBar(window);
342 return window;
343 }
344
345 static void ApplyEditorConfig(WindowInfo *window, EditorConfig ec) {
346
347 char *params[
1];
348 char numStr[
25];
349
350 if(ec.indent_size >
0 && ec.tab_width ==
0) {
351 ec.tab_width = ec.indent_size;
352 }
353
354
355 int indent_size_set =
0;
356 if(ec.indent_style ==
EC_TAB) {
357 params[
0] = numStr;
358 snprintf(numStr,
24,
"%d",
1);
359 XtCallActionProc(window->textArea,
"set_use_tabs",
NULL, params,
1);
360 }
else if(ec.indent_style ==
EC_SPACE) {
361 int emTabDist = ec.indent_size;
362 if(emTabDist ==
0) {
363 XtVaGetValues(window->textArea, textNemulateTabs, &emTabDist,
NULL);
364 if(emTabDist ==
0) {
365 emTabDist = BufGetTabDistance(window->buffer);
366 }
367 }
368
369 params[
0] = numStr;
370 snprintf(numStr,
24,
"%d",
0);
371 XtCallActionProc(window->textArea,
"set_use_tabs",
NULL, params,
1);
372
373 params[
0] = numStr;
374 snprintf(numStr,
24,
"%d", emTabDist);
375 XtCallActionProc(window->textArea,
"set_em_tab_dist",
NULL, params,
1);
376 indent_size_set =
1;
377 }
378
379 if(ec.tab_width >
0) {
380 params[
0] = numStr;
381 snprintf(numStr,
24,
"%d", ec.tab_width);
382 XtCallActionProc(window->textArea,
"set_tab_dist",
NULL, params,
1);
383 }
384
385 if(ec.indent_size >
0 && !indent_size_set) {
386 params[
0] = numStr;
387 snprintf(numStr,
24,
"%d", ec.indent_size);
388 XtCallActionProc(window->textArea,
"set_em_tab_dist",
NULL, params,
1);
389 }
390
391
392 switch(ec.end_of_line) {
393 default:
break;
394 case EC_LF: {
395 window->fileFormat =
UNIX_FILE_FORMAT;
396 break;
397 }
398 case EC_CR: {
399 window->fileFormat =
MAC_FILE_FORMAT;
400 break;
401 }
402 case EC_CRLF: {
403 window->fileFormat =
DOS_FILE_FORMAT;
404 break;
405 }
406 }
407 }
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428 WindowInfo *EditExistingFile(WindowInfo *inWindow,
const char *name,
429 const char *path,
const char *encoding,
const char *filter,
int flags,
430 char *geometry,
int iconic,
const char *languageMode,
int tabbed,
431 int bgOpen)
432 {
433 WindowInfo *window;
434 char fullname[
MAXPATHLEN];
435
436
437 window = FindWindowWithFile(name, path);
438 if (window !=
NULL) {
439 if (!bgOpen) {
440 if (iconic)
441 RaiseDocument(window);
442 else
443 RaiseDocumentWindow(window);
444 }
445 return window;
446 }
447
448
449
450
451 if (inWindow ==
NULL) {
452 window = CreateWindow(name, geometry, iconic);
453 }
454 else if (inWindow->filenameSet || inWindow->fileChanged ||
455 inWindow->macroCmdData !=
NULL) {
456 if (tabbed) {
457 window = CreateDocument(inWindow, name);
458 }
459 else {
460 window = CreateWindow(name, geometry, iconic);
461 }
462 }
463 else {
464
465 window = inWindow;
466 strcpy(window->path, path);
467 strcpy(window->filename, name);
468 if(encoding) {
469 SetEncoding(window, encoding);
470 }
else {
471 window->encoding[
0] =
'\0';
472 }
473
474 if (!iconic && !bgOpen) {
475 RaiseDocumentWindow(window);
476 }
477 }
478
479
480 EditorConfig ec;
481 if(GetEditorConfig()) {
482 ec = EditorConfigGet(path, name);
483 }
else {
484 memset(&ec,
0,
sizeof(EditorConfig));
485 }
486 if(ec.charset && !encoding) {
487 encoding = ec.charset;
488 if(ec.bom !=
EC_BOM_UNSET) {
489 window->bom = True;
490 }
491 }
492
493
494 if (!doOpen(window, name, path, encoding, filter, flags)) {
495
496
497 safeClose(window);
498
499 if(ec.charset) {
500 free(ec.charset);
501 }
502 return NULL;
503 }
504 forceShowLineNumbers(window);
505
506
507 if(window->buffer->length <
DISABLE_LANG_THRESHOLD) {
508 if (languageMode ==
NULL) {
509 DetermineLanguageMode(window, True);
510 }
else {
511 SetLanguageMode(window, FindLanguageMode(languageMode), True);
512 }
513 }
else {
514 SetLanguageMode(window,
PLAIN_LANGUAGE_MODE, True);
515 }
516
517
518
519
520 window->wrapModeNoneForced = False;
521
522
523 RefreshTabState(window);
524 SortTabBar(window);
525 ShowTabBar(window, GetShowTabBar(window));
526
527 if (!bgOpen)
528 RaiseDocument(window);
529
530
531
532 UpdateWindowTitle(window);
533 UpdateWindowReadOnly(window);
534 UpdateStatsLine(window);
535
536
537 strcpy(fullname, path);
538 strcat(fullname, name);
539 if(GetPrefAlwaysCheckRelTagsSpecs())
540 AddRelTagsFile(GetPrefTagFile(), path,
TAG);
541 AddToPrevOpenMenu(fullname);
542
543 if(ec.found) {
544 ApplyEditorConfig(window, ec);
545 }
546
547 if(ec.charset) {
548 free(ec.charset);
549 }
550
551 return window;
552 }
553
554 void RevertToSaved(WindowInfo *window,
char *newEncoding)
555 {
556 char name[
MAXPATHLEN], path[
MAXPATHLEN];
557 char *encoding;
558 int i;
559 int insertPositions[
MAX_PANES], topLines[
MAX_PANES];
560 int horizOffsets[
MAX_PANES];
561 int openFlags =
0;
562 Widget text;
563
564
565 if (!window->filenameSet)
566 {
567 DialogF(
DF_WARN, window->shell,
1,
"Error",
568 "Window ''%s'' was never saved, can''t re-read",
"OK",
569 window->filename);
570 return;
571 }
572
573
574 for (i=
0; i<=window->nPanes; i++) {
575 text = i==
0 ? window->textArea : window->textPanes[i-
1];
576 insertPositions[i] = TextGetCursorPos(text);
577 TextGetScroll(text, &topLines[i], &horizOffsets[i]);
578 }
579
580
581 strcpy(name, window->filename);
582 strcpy(path, window->path);
583
584 if(newEncoding) {
585 encoding = newEncoding;
586 }
else if(window->encoding[
0] !=
'\0') {
587 encoding = window->encoding;
588 }
else {
589 encoding =
NULL;
590 }
591
592
593 RemoveBackupFile(window);
594 ClearUndoList(window);
595 openFlags |=
IS_USER_LOCKED(window->lockReasons) && !
IS_ENCODING_LOCKED(window->lockReasons) ?
PREF_READ_ONLY :
0;
596 if (!doOpen(window, name, path, encoding, window->filter, openFlags)) {
597
598
599
600
601 if (!window->fileMissing)
602 safeClose(window);
603 else {
604
605 window->lastModTime=
0;
606 window->fileMissing=
FALSE;
607 }
608 return;
609 }
610 forceShowLineNumbers(window);
611 UpdateWindowTitle(window);
612 UpdateWindowReadOnly(window);
613
614
615 for (i=
0; i<=window->nPanes; i++) {
616 text = i==
0 ? window->textArea : window->textPanes[i-
1];
617 TextSetCursorPos(text, insertPositions[i]);
618 TextSetScroll(text, topLines[i], horizOffsets[i]);
619 }
620 }
621
622
623
624
625
626
627
628
629 static void safeClose(WindowInfo *window)
630 {
631 WindowInfo* p = WindowList;
632 while(p) {
633 if (p == window) {
634 CloseWindow(window);
635 return;
636 }
637 p = p->next;
638 }
639 }
640
641 static char bom_utf8[
3] = { (
char)0xEF, (
char)0xBB, (
char)0xBF };
642 static char bom_utf16be[
2] = { (
char)0xFE, (
char)0xFF };
643 static char bom_utf16le[
2] = { (
char)0xFF, (
char)0xFE };
644 static char bom_utf32be[
4] = { (
char)
0, (
char)
0, (
char)0xFE, (
char)0xFF };
645 static char bom_utf32le[
4] = { (
char)0xFF, (
char)0xFE, (
char)
0, (
char)
0 };
646 static char bom_gb18030[
4] = { (
char)0x84, (
char)0x31, (
char)0x95, (
char)0x33 };
647 static char bom_utfebcdic[
4] = { (
char)0xDD, (
char)0x73, (
char)0x66, (
char)0x73 };
648
649 typedef size_t(*ConvertFunc)(
iconv_t,
char **,
size_t *,
char **,
size_t *);
650
651
652
653
654 size_t copyBytes(
655 iconv_t cd,
656 char **inbuf,
657 size_t *inbytesleft,
658 char **outbuf,
659 size_t *outbytesleft)
660 {
661 if(!inbuf || !outbuf) {
662 return 0;
663 }
664 size_t in = *inbytesleft;
665 size_t out = *outbytesleft;
666 size_t cp = in > out ? out : in;
667 memcpy(*outbuf, *inbuf, cp);
668 *inbuf = (*inbuf) + cp;
669 *outbuf = (*outbuf) + cp;
670 *inbytesleft = in - cp;
671 *outbytesleft = out - cp;
672 return 0;
673 }
674
675
676 #define IO_BUFSIZE 2048
677
678 static int doOpen(WindowInfo *window,
const char *name,
const char *path,
679 const char *encoding,
const char *filter_name,
int flags)
680 {
681 char fullname[
MAXPATHLEN];
682 int resp;
683
684
685 char encoding_buffer[
MAX_ENCODING_LENGTH];
686 if(encoding == window->encoding) {
687 size_t enclen = strlen(window->encoding);
688 if(enclen >
MAX_ENCODING_LENGTH) {
689 enclen =
MAX_ENCODING_LENGTH-
1;
690 }
691 memcpy(encoding_buffer, window->encoding, enclen);
692 encoding_buffer[enclen] =
0;
693 encoding = encoding_buffer;
694 }
695
696
697 CLEAR_ALL_LOCKS(window->lockReasons);
698
699
700 size_t name_len = strlen(name);
701 size_t path_len = strlen(path);
702 if(name_len + path_len >=
MAXPATHLEN-
1) {
703 window->filenameSet =
FALSE;
704 DialogF(
DF_ERR, window->shell,
1,
"Error opening File",
705 "Path too long",
"OK");
706 window->filenameSet =
TRUE;
707 return False;
708 }
709
710 memcpy(window->filename, name, name_len+
1);
711 memcpy(window->path, path, path_len+
1);
712 window->encoding[
0] =
'\0';
713 window->filenameSet =
TRUE;
714 window->fileMissing =
TRUE;
715
716
717 memcpy(fullname, path, path_len);
718 memcpy(fullname+path_len, name, name_len+
1);
719
720 FileContent content;
721 if(GetFileContent(window->shell, fullname, encoding, filter_name, &content)) {
722 if(content.err ==
ENOENT && flags &
CREATE) {
723
724 if (!(flags &
SUPPRESS_CREATE_WARN)) {
725
726
727 RaiseShellWindow(window->shell, False);
728
729
730 if (WindowList == window && window->next ==
NULL) {
731 resp = DialogF(
DF_WARN, window->shell,
3,
"New File",
732 "Can''t open %s:\n%s",
"New File",
"Cancel",
733 "Exit NEdit", fullname, strerror(content.err));
734 }
else {
735 resp = DialogF(
DF_WARN, window->shell,
2,
"New File",
736 "Can''t open %s:\n%s",
"New File",
"Cancel", fullname,
737 strerror(content.err));
738 }
739
740 if (resp ==
2) {
741 return FALSE;
742 }
else if (resp ==
3) {
743 exit(
EXIT_SUCCESS);
744 }
745 }
746
747
748 int fd;
749 if ((fd = creat(fullname,
0666)) == -
1) {
750 DialogF(
DF_ERR, window->shell,
1,
"Error creating File",
751 "Can''t create %s:\n%s",
"OK", fullname, errorString());
752 return FALSE;
753 }
else {
754 close(fd);
755 remove(fullname);
756 }
757
758 SetWindowModified(window,
FALSE);
759 if ((flags &
PREF_READ_ONLY) !=
0) {
760 SET_USER_LOCKED(window->lockReasons,
TRUE);
761 }
762 UpdateWindowReadOnly(window);
763 return TRUE;
764 }
else if(content.isdir) {
765 window->filenameSet =
FALSE;
766 DialogF(
DF_ERR, window->shell,
1,
"Error opening File",
767 "Can''t open directory %s",
"OK", name);
768 window->filenameSet =
TRUE;
769 }
else if(content.isblk) {
770 window->filenameSet =
FALSE;
771 DialogF(
DF_ERR, window->shell,
1,
"Error opening File",
772 "Can''t open block device %s",
"OK", name);
773 window->filenameSet =
TRUE;
774 }
else if(content.allocerror) {
775 window->filenameSet =
FALSE;
776 DialogF(
DF_ERR, window->shell,
1,
"Error while opening File",
777 "File is too large to edit",
"OK");
778 window->filenameSet =
TRUE;
779 }
else if(content.iconverror) {
780 window->filenameSet =
FALSE;
781 char *format =
"File cannot be converted from %s to UTF8";
782 size_t msglen = strlen(format) + strlen(encoding) +
4;
783 char *msgbuf = NEditMalloc(msglen);
784 snprintf(msgbuf, msglen, format, encoding);
785 DialogF(
DF_ERR, window->shell,
1,
"Error while opening File",
786 msgbuf,
"OK");
787 NEditFree(msgbuf);
788 window->filenameSet =
TRUE;
789 }
else {
790
791 DialogF(
DF_ERR, window->shell,
1,
"Error opening File",
792 "Could not open %s%s:\n%s",
"OK", path, name,
793 strerror(content.err));
794 return FALSE;
795 }
796 return 0;
797 }
798 SetEncoding(window, content.encoding);
799
800 SET_ENCODING_LOCKED(window->lockReasons,
FALSE);
801 if(content.readonly) {
802 SET_PERM_LOCKED(window->lockReasons,
TRUE);
803 }
804
805 int show_infobar =
FALSE;
806 int lock_enc_error =
FALSE;
807 if(content.skipped >
0) {
808 char *lockmsg =
"";
809 if(GetPrefLockEncodingError()) {
810 lockmsg =
": file locked to prevent accidental changes";
811 flags = flags |
PREF_READ_ONLY;
812 lock_enc_error =
TRUE;
813 }
814
815 char msgbuf[
256];
816 snprintf(msgbuf,
256,
"%d non-convertible characters skipped%s", content.skipped, lockmsg);
817
818 show_infobar =
TRUE;
819 SetEncodingInfoBarLabel(window, msgbuf);
820 SetEncErrors(window, content.enc_errors, content.num_enc_errors);
821 }
else {
822 SetEncodingInfoBarLabel(window,
"No conversion errors");
823 SetEncErrors(window,
NULL,
0);
824 NEditFree(content.enc_errors);
825 }
826
827 SetFilter(window, filter_name);
828
829 window->bom = content.hasBOM;
830 window->fileFormat = content.fileFormat;
831
832
833
834
835 window->fileMode = content.statbuf.st_mode;
836 window->fileUid = content.statbuf.st_uid;
837 window->fileGid = content.statbuf.st_gid;
838 window->lastModTime = content.statbuf.st_mtime;
839 window->device = content.statbuf.st_dev;
840 window->inode = content.statbuf.st_ino;
841 window->fileMissing =
FALSE;
842
843
844 if(content.length >
DISABLE_WRAPPING_THRESHOLD) {
845 SetAutoWrap(window,
NO_WRAP);
846 window->wrapModeNoneForced = True;
847 }
848
849
850 window->ignoreModify = True;
851 BufSetAll(window->buffer, content.content);
852 window->ignoreModify = False;
853
854
855
856
857
858 if (window->buffer->length != content.length) {
859 if (!BufSubstituteNullChars(content.content, content.length, window->buffer)) {
860 resp = DialogF(
DF_ERR, window->shell,
2,
"Error while opening File",
861 "Too much binary data in file. You may view\n"
862 "it, but not modify or re-save its contents.",
"View",
863 "Cancel");
864 if (resp ==
2) {
865 return FALSE;
866 }
867
868 SET_TMBD_LOCKED(window->lockReasons,
TRUE);
869 for (
char *c = content.content; c < &content.content[content.length]; c++) {
870 if (*c ==
'\0') {
871 *c = (
char) 0xfe;
872 }
873 }
874 window->buffer->nullSubsChar = (
char) 0xfe;
875 }
876 window->ignoreModify = True;
877 BufSetAll(window->buffer, content.content);
878 window->ignoreModify = False;
879 }
880
881
882 NEditFree(content.content);
883
884
885 if ((flags &
PREF_READ_ONLY) !=
0) {
886 SET_USER_LOCKED(window->lockReasons,
TRUE);
887 SET_ENCODING_LOCKED(window->lockReasons, lock_enc_error);
888 }
889 if (
IS_PERM_LOCKED(window->lockReasons)) {
890 window->fileChanged =
FALSE;
891 UpdateWindowTitle(window);
892 }
else {
893 SetWindowModified(window,
FALSE);
894 if (
IS_ANY_LOCKED(window->lockReasons)) {
895 UpdateWindowTitle(window);
896 }
897 }
898 UpdateWindowReadOnly(window);
899
900
901 if(show_infobar) {
902 ShowEncodingInfoBar(window,
1);
903 }
904
905 return TRUE;
906 }
907
908 int GetFileContent(Widget shell,
const char *path,
const char *encoding,
const char *filter_name, FileContent *content)
909 {
910 memset(content,
0,
sizeof(FileContent));
911
912 off_t fileLen, readLen;
913 char *fileString, *c;
914 char buf[
IO_BUFSIZE];
915 FILE *fp =
NULL;
916 FileStream *stream =
NULL;;
917 int err;
918
919 char encoding_buffer[
MAX_ENCODING_LENGTH];
920 if(encoding) {
921 size_t enclen = strlen(encoding);
922 if(enclen >=
MAX_ENCODING_LENGTH) {
923 enclen =
MAX_ENCODING_LENGTH-
1;
924 }
925 memcpy(encoding_buffer, encoding, enclen);
926 encoding_buffer[enclen] =
0;
927 encoding = encoding_buffer;
928 }
else {
929 encoding_buffer[
0] =
0;
930 }
931 encoding = encoding_buffer[
0] !=
'\0' ? encoding_buffer :
NULL;
932
933
934
935 fp = fopen(path,
"rb+");
936 if(!fp) {
937 fp = fopen(path,
"rb");
938 if(fp) {
939
940 content->readonly =
1;
941 }
else {
942 content->err = errno;
943 return 1;
944 }
945 }
946
947
948
949 if (fstat(fileno(fp), &content->statbuf) !=
0) {
950 fclose(fp);
951 content->err = errno;
952 return 1;
953 }
954
955 if (
S_ISDIR(content->statbuf.st_mode)) {
956 fclose(fp);
957 content->isdir =
1;
958 return 1;
959 }
960
961 #ifdef S_ISBLK
962 if (
S_ISBLK(content->statbuf.st_mode)) {
963 fclose(fp);
964 content->isblk =
1;
965 return 1;
966 }
967 #endif
968
969 fileLen = content->statbuf.st_size;
970
971
972 IOFilter* filter = GetFilterFromName(filter_name);
973 char *filter_cmd =
NULL;
974 if(filter && filter->cmdin && strlen(filter->cmdin) >
0) {
975 filter_cmd = filter->cmdin;
976 }
977 stream = filestream_open_r(shell, fp, filter_cmd);
978
979
980 if(!encoding) {
981 char *xattr_charset = getEncodingAttribute(path);
982 if(xattr_charset) {
983 size_t enclen = strlen(xattr_charset);
984 if(enclen >=
MAX_ENCODING_LENGTH) {
985 enclen =
MAX_ENCODING_LENGTH-
1;
986 }
987 memcpy(encoding_buffer, xattr_charset, enclen);
988 encoding_buffer[enclen] =
0;
989 encoding = encoding_buffer;
990 NEditFree(xattr_charset);
991 }
992 }
993
994 int checkBOM =
1;
995 int checkEncoding =
0;
996 if(encoding) {
997
998 if(strlen(encoding) <
3) {
999
1000 checkBOM =
0;
1001 }
else {
1002 char encpre[
4];
1003 encpre[
0] = encoding[
0];
1004 encpre[
1] = encoding[
1];
1005 encpre[
2] = encoding[
2];
1006 encpre[
3] =
0;
1007 if(strcasecmp(encpre,
"UTF")) {
1008
1009 checkBOM =
0;
1010 }
1011 }
1012 if(!strcasecmp(encoding,
"GB18030")) {
1013 checkBOM =
1;
1014 }
1015 }
else {
1016
1017 encoding = GetPrefDefaultCharset();
1018 checkEncoding =
1;
1019 }
1020
1021 char *setEncoding =
NULL;
1022 int hasBOM =
0;
1023 if(checkBOM) {
1024
1025 int bom =
0;
1026 size_t r = filestream_read(buf,
4, stream);
1027 do {
1028 if(r >=
4) {
1029 bom =
4;
1030 if(!memcmp(buf, bom_utf32be,
4)) {
1031 setEncoding =
"UTF-32BE";
1032 hasBOM =
TRUE;
1033 break;
1034 }
else if(!memcmp(buf, bom_utf32le,
4)) {
1035 setEncoding =
"UTF-32LE";
1036 hasBOM =
TRUE;
1037 break;
1038 }
else if(!memcmp(buf, bom_gb18030,
4)) {
1039 setEncoding =
"GB18030";
1040 hasBOM =
TRUE;
1041 break;
1042 }
else if(!memcmp(buf, bom_utfebcdic,
4)) {
1043 setEncoding =
"UTF-EBCDIC";
1044 hasBOM =
TRUE;
1045 break;
1046 }
1047 }
1048 if(r >=
3) {
1049 bom =
3;
1050 if(!memcmp(buf, bom_utf8,
3)) {
1051 setEncoding =
"UTF-8";
1052 hasBOM =
TRUE;
1053 break;
1054 }
1055 }
1056 if(r >=
2) {
1057 bom =
2;
1058 if(!memcmp(buf, bom_utf16be,
2)) {
1059 setEncoding =
"UTF-16BE";
1060 hasBOM =
TRUE;
1061 break;
1062 }
else if(!memcmp(buf, bom_utf16le,
2)) {
1063 setEncoding =
"UTF-16LE";
1064 hasBOM =
TRUE;
1065 break;
1066 }
1067 }
1068 bom =
0;
1069 }
while (
0);
1070 filestream_reset(stream, bom);
1071 }
1072 if(setEncoding) {
1073 encoding = setEncoding;
1074 checkEncoding =
0;
1075 }
1076
1077 if(checkEncoding) {
1078 size_t r = filestream_read(buf,
IO_BUFSIZE, stream);
1079 const char *newEnc = DetectEncoding(buf, r, encoding);
1080 if(newEnc && newEnc != encoding) {
1081 encoding = newEnc;
1082 }
1083 filestream_reset(stream,
0);
1084 }
1085
1086
1087 size_t strAlloc = fileLen;
1088 fileString = malloc(strAlloc +
1);
1089 if (fileString ==
NULL) {
1090 filestream_close(stream);
1091 content->allocerror =
1;
1092 return 1;
1093 }
1094
1095 iconv_t ic =
NULL;
1096 ConvertFunc strconv = copyBytes;
1097 if(encoding) {
1098 ic = iconv_open(
"UTF-8", encoding);
1099 if(ic == (
iconv_t) -
1) {
1100 filestream_close(stream);
1101 free(fileString);
1102 return 1;
1103 }
1104 strconv = (ConvertFunc)iconv;
1105
1106
1107 size_t len = strlen(encoding);
1108 if(len+
1 >=
MAX_ENCODING_LENGTH) {
1109 fprintf(stderr,
"Error: Encoding string too large\n");
1110 len =
MAX_ENCODING_LENGTH-
1;
1111 }
1112 memcpy(content->encoding, encoding, len);
1113 content->encoding[len] =
0;
1114 }
1115
1116 EncError *encErrors = NEditCalloc(
ENC_ERROR_LIST_LEN,
sizeof(EncError));
1117 size_t numEncErrors =
0;
1118 size_t allocEncErrors =
ENC_ERROR_LIST_LEN;
1119
1120 err =
0;
1121 int skipped =
0;
1122 size_t r =
0;
1123 readLen =
0;
1124 char *outStr = fileString;
1125 size_t prev =
0;
1126 while((r = filestream_read(buf+prev,
IO_BUFSIZE-prev, stream)) >
0 && !err) {
1127 char *str = buf;
1128 size_t inleft = prev + r;
1129 size_t outleft = strAlloc - readLen;
1130 prev =
0;
1131 while(inleft >
0) {
1132 size_t w = outleft;
1133 size_t rc = strconv(ic, &str, &inleft, &outStr, &outleft);
1134 w = w - outleft;
1135 readLen += w;
1136
1137 if(rc == (
size_t)-
1) {
1138
1139 int extendBuf =
0;
1140 switch(errno) {
1141 default: err =
1; content->iconverror =
1;
break;
1142 case EILSEQ: {
1143 if(inleft >
0) {
1144
1145 if(outleft <
3) {
1146
1147
1148
1149
1150
1151 extendBuf =
1;
1152 break;
1153 }
1154
1155 outStr[
0] = 0xEF;
1156 outStr[
1] = 0xBF;
1157 outStr[
2] = 0xBD;
1158
1159
1160 if(numEncErrors >= allocEncErrors) {
1161 allocEncErrors +=
16;
1162 encErrors = NEditRealloc(encErrors, allocEncErrors *
sizeof(EncError));
1163 }
1164 encErrors[numEncErrors].c = (
unsigned char)*str;
1165 encErrors[numEncErrors].pos = outStr - fileString;
1166 numEncErrors++;
1167
1168
1169 outStr +=
3;
1170 outleft -=
3;
1171 readLen +=
3;
1172
1173 str++;
1174 inleft--;
1175
1176 skipped++;
1177 }
1178 break;
1179 }
1180 case EINVAL: {
1181 memcpy(buf, str, inleft);
1182 prev = inleft;
1183 inleft =
0;
1184 break;
1185 }
1186 case E2BIG: {
1187 extendBuf =
1;
1188 break;
1189 }
1190 }
1191
1192 if(extendBuf) {
1193
1194
1195
1196 strAlloc +=
512;
1197 size_t outpos = outStr - fileString;
1198 fileString = realloc(fileString, strAlloc +
1);
1199 if(!fileString) {
1200 err =
1;
1201 break;
1202 }
1203 outStr = fileString + outpos;
1204 outleft = strAlloc - readLen;
1205 }
1206
1207 if(err) {
1208 break;
1209 }
1210 }
1211 }
1212 }
1213
1214 if (filestream_close(stream) !=
0) {
1215 content->closeerror =
1;
1216 content->err = errno;
1217 }
1218 fileString[readLen] =
0;
1219
1220 if(ic) {
1221 iconv_close(ic);
1222 }
1223
1224 content->hasBOM = hasBOM;
1225 content->skipped = skipped;
1226
1227
1228 if (GetPrefForceOSConversion()) {
1229 content->fileFormat = FormatOfFile(fileString);
1230 int rLen = readLen;
1231 if (content->fileFormat ==
DOS_FILE_FORMAT) {
1232 ConvertFromDosFileString(fileString, &rLen,
NULL);
1233 }
else if (content->fileFormat ==
MAC_FILE_FORMAT) {
1234 ConvertFromMacFileString(fileString, rLen);
1235 }
1236 readLen = rLen;
1237 }
1238
1239 if(err) {
1240 free(fileString);
1241 free(encErrors);
1242 }
else {
1243 content->content = fileString;
1244 content->length = readLen;
1245 content->enc_errors = encErrors;
1246 content->num_enc_errors = numEncErrors;
1247 }
1248
1249 return err;
1250 }
1251
1252 int IncludeFile(WindowInfo *window,
const char *name,
const char *encoding,
const char *filter_name)
1253 {
1254 int err =
0;
1255
1256
1257 FileContent content;
1258 if(GetFileContent(window->shell, name, encoding, filter_name, &content)) {
1259 int filenameSet = window->filenameSet;
1260 if(content.isdir) {
1261 window->filenameSet =
FALSE;
1262 DialogF(
DF_ERR, window->shell,
1,
"Error opening File",
1263 "Can''t open directory %s",
"OK", name);
1264 window->filenameSet = filenameSet;
1265 }
else if(content.isblk) {
1266 window->filenameSet =
FALSE;
1267 DialogF(
DF_ERR, window->shell,
1,
"Error opening File",
1268 "Can''t open block device %s",
"OK", name);
1269 window->filenameSet = filenameSet;
1270 }
else if(content.allocerror) {
1271 window->filenameSet =
FALSE;
1272 DialogF(
DF_ERR, window->shell,
1,
"Error while opening File",
1273 "File is too large to include",
"OK");
1274 window->filenameSet = filenameSet;
1275 }
else if(content.iconverror) {
1276 window->filenameSet =
FALSE;
1277 char *format =
"File cannot be converted from %s to UTF8";
1278 size_t msglen = strlen(format) + strlen(encoding) +
4;
1279 char *msgbuf = NEditMalloc(msglen);
1280 snprintf(msgbuf, msglen, format, encoding);
1281 DialogF(
DF_ERR, window->shell,
1,
"Error while opening File",
1282 msgbuf,
"OK");
1283 NEditFree(msgbuf);
1284 window->filenameSet = filenameSet;
1285 }
else {
1286 window->filenameSet =
FALSE;
1287 DialogF(
DF_ERR, window->shell,
1,
"Error while opening File",
1288 "Unknown error",
"OK");
1289 window->filenameSet = filenameSet;
1290 }
1291 return 0;
1292 }
1293
1294 if(content.skipped >
0) {
1295 int btn = DialogF(
DF_WARN, window->shell,
2,
"Encoding warning",
1296 "%d non-convertible characters skipped\n"
1297 "Include anyway?",
"NO",
"YES",
1298 content.skipped);
1299 if(btn ==
1) {
1300 err =
TRUE;
1301 }
1302 }
1303
1304 if(!err) {
1305
1306 if (!BufSubstituteNullChars(content.content, content.length, window->buffer))
1307 {
1308 DialogF(
DF_ERR, window->shell,
1,
"Error opening File",
1309 "Too much binary data in file",
"OK");
1310 }
else {
1311
1312
1313 if (window->buffer->primary.selected) {
1314 BufReplaceSelected(window->buffer, content.content);
1315 }
else {
1316 BufInsert(window->buffer, TextGetCursorPos(window->lastFocus), content.content);
1317 }
1318 }
1319 }
1320
1321 NEditFree(content.content);
1322 NEditFree(content.enc_errors);
1323
1324 return TRUE;
1325 }
1326
1327
1328
1329
1330 int CloseAllFilesAndWindows(
void)
1331 {
1332 while (WindowList->next !=
NULL ||
1333 WindowList->filenameSet || WindowList->fileChanged) {
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343 if (WindowList == MacroRunWindow() && WindowList->next !=
NULL) {
1344 if (!CloseAllDocumentInWindow(WindowList->next)) {
1345 return False;
1346 }
1347 }
1348 else {
1349 if (!CloseAllDocumentInWindow(WindowList)) {
1350 return False;
1351 }
1352 }
1353 }
1354
1355 return TRUE;
1356 }
1357
1358 int CloseFileAndWindow(WindowInfo *window,
int preResponse)
1359 {
1360 int response, stat;
1361
1362
1363 if (window->fileChanged)
1364 RaiseDocumentWindow(window);
1365
1366
1367
1368
1369 if (!window->fileChanged &&
1370
1371 ((!window->fileMissing && window->lastModTime >
0) ||
1372
1373 (window->fileMissing && window->lastModTime ==
0) ||
1374
1375 !GetPrefWarnFileMods()))
1376 {
1377 CloseWindow(window);
1378
1379 }
else
1380 {
1381 if (preResponse ==
PROMPT_SBC_DIALOG_RESPONSE)
1382 {
1383 response = DialogF(
DF_WARN, window->shell,
3,
"Save File",
1384 "Save %s before closing?",
"Yes",
"No",
"Cancel", window->filename);
1385 }
else
1386 {
1387 response = preResponse;
1388 }
1389
1390 if (response ==
YES_SBC_DIALOG_RESPONSE)
1391 {
1392
1393 stat = SaveWindow(window);
1394 if (stat)
1395 {
1396 CloseWindow(window);
1397 }
else
1398 {
1399 return FALSE;
1400 }
1401 }
else if (response ==
NO_SBC_DIALOG_RESPONSE)
1402 {
1403
1404 RemoveBackupFile(window);
1405 CloseWindow(window);
1406 }
else
1407 {
1408 return FALSE;
1409 }
1410 }
1411 return TRUE;
1412 }
1413
1414 int SaveWindow(WindowInfo *window)
1415 {
1416 int stat;
1417
1418
1419 CheckForChangesToFile(window);
1420
1421
1422
1423 if ( (!window->fileChanged && !window->fileMissing &&
1424 window->lastModTime >
0) ||
1425 IS_ANY_LOCKED_IGNORING_PERM(window->lockReasons))
1426 return TRUE;
1427
1428 if (!window->filenameSet)
1429 return SaveWindowAs(window,
NULL);
1430
1431
1432 if (GetPrefWarnFileMods() && fileWasModifiedExternally(window))
1433 {
1434 stat = DialogF(
DF_WARN, window->shell,
2,
"Save File",
1435 "%s has been modified by another program.\n\n"
1436 "Continuing this operation will overwrite any external\n"
1437 "modifications to the file since it was opened in NEdit,\n"
1438 "and your work or someone else''s may potentially be lost.\n\n"
1439 "To preserve the modified file, cancel this operation and\n"
1440 "use Save As... to save this file under a different name,\n"
1441 "or Revert to Saved to revert to the modified version.",
1442 "Continue",
"Cancel", window->filename);
1443 if (stat ==
2)
1444 {
1445
1446 window->lastModTime =
0;
1447 window->fileMissing =
FALSE;
1448 return FALSE;
1449 }
1450 }
1451
1452 if (writeBckVersion(window))
1453 return FALSE;
1454 stat = doSave(window,
0);
1455 if (stat)
1456 RemoveBackupFile(window);
1457
1458 return stat;
1459 }
1460
1461 int SaveWindowAs(WindowInfo *window, FileSelection *file)
1462 {
1463 int response, retVal, fileFormat;
1464 char filename[
MAXPATHLEN], pathname[
MAXPATHLEN];
1465 WindowInfo *otherWindow;
1466 char fullname[
MAXPATHLEN];
1467
1468
1469 FileSelection newFile;
1470 if (!file) {
1471 memset(&newFile,
0,
sizeof(FileSelection));
1472 newFile.extraoptions = True;
1473 newFile.encoding = strlen(window->encoding) >
0 ? window->encoding :
NULL;
1474 newFile.format = window->fileFormat;
1475 newFile.writebom = window->bom;
1476
1477 response = PromptForNewFile(window,
"Save File As", &newFile, &fileFormat);
1478 if (response !=
GFN_OK)
1479 return FALSE;
1480 window->bom = newFile.writebom;
1481 window->fileFormat = newFile.format;
1482 SetFilter(window, newFile.filter);
1483 size_t pathlen = strlen(newFile.path);
1484 if(pathlen >=
MAXPATHLEN) {
1485 fprintf(stderr,
"Error: Path too long\n");
1486 NEditFree(newFile.path);
1487 return FALSE;
1488 }
1489 memcpy(fullname, newFile.path, pathlen);
1490 fullname[pathlen] =
'\0';
1491 NEditFree(newFile.path);
1492
1493 if(newFile.encoding) {
1494 if(!strcmp(newFile.encoding,
"UTF-8")) {
1495 window->encoding[
0] =
'\0';
1496 }
else {
1497 SetEncoding(window, newFile.encoding);
1498 }
1499 }
1500
1501 file = &newFile;
1502 }
else
1503 {
1504 strcpy(fullname, file->path);
1505 if(!strcmp(file->encoding,
"UTF-8")) {
1506 window->encoding[
0] =
'\0';
1507 }
else {
1508 SetEncoding(window, file->encoding);
1509 }
1510 window->bom = file->writebom;
1511 window->fileFormat = file->format;
1512 SetFilter(window, file->filter);
1513 }
1514
1515 if (
1 == NormalizePathname(fullname))
1516 {
1517 return False;
1518 }
1519
1520
1521 if (file->addwrap)
1522 addWrapNewlines(window);
1523
1524 if (ParseFilename(fullname, filename, pathname) !=
0) {
1525 return FALSE;
1526 }
1527
1528
1529 if (!strcmp(window->filename, filename) &&
1530 !strcmp(window->path, pathname)) {
1531 if (writeBckVersion(window))
1532 return FALSE;
1533 return doSave(window, file->setxattr);
1534 }
1535
1536
1537
1538
1539
1540 otherWindow = FindWindowWithFile(filename, pathname);
1541 if (otherWindow !=
NULL)
1542 {
1543 response = DialogF(
DF_WARN, window->shell,
2,
"File open",
1544 "%s is open in another XNEdit window",
"Cancel",
1545 "Close Other Window", filename);
1546
1547 if (response ==
1)
1548 {
1549 return FALSE;
1550 }
1551 if (otherWindow == FindWindowWithFile(filename, pathname))
1552 {
1553 if (!CloseFileAndWindow(otherWindow,
PROMPT_SBC_DIALOG_RESPONSE))
1554 {
1555 return FALSE;
1556 }
1557 }
1558 }
1559
1560
1561 DeleteFileClosedProperty(window);
1562
1563
1564 RemoveBackupFile(window);
1565 strcpy(window->filename, filename);
1566 strcpy(window->path, pathname);
1567 window->fileMode =
0;
1568 window->fileUid =
0;
1569 window->fileGid =
0;
1570 CLEAR_ALL_LOCKS(window->lockReasons);
1571 retVal = doSave(window, file->setxattr);
1572 UpdateWindowReadOnly(window);
1573 RefreshTabState(window);
1574
1575
1576 AddToPrevOpenMenu(fullname);
1577
1578
1579
1580
1581 if (
PLAIN_LANGUAGE_MODE == window->languageMode || window->filenameSet) {
1582 DetermineLanguageMode(window, False);
1583 }
1584 window->filenameSet = True;
1585
1586
1587 UpdateWindowTitle(window);
1588 UpdateStatsLine(window);
1589
1590 SortTabBar(window);
1591 return retVal;
1592 }
1593
1594 static int getBOM(
char *encoding,
char **bom)
1595 {
1596 int len =
0;
1597 *bom =
NULL;
1598 if(!encoding || strlen(encoding) ==
0 || !strcasecmp(encoding,
"UTF-8")) {
1599 *bom = bom_utf8;
1600 len =
3;
1601 }
else if(!strcasecmp(encoding,
"UTF-16BE")) {
1602 *bom = bom_utf16be;
1603 len =
2;
1604 }
else if(!strcasecmp(encoding,
"UTF-16LE")) {
1605 *bom = bom_utf16le;
1606 len =
2;
1607 }
else if(!strcasecmp(encoding,
"UTF-32BE")) {
1608 *bom = bom_utf32be;
1609 len =
4;
1610 }
else if(!strcasecmp(encoding,
"UTF-32LE")) {
1611 *bom = bom_utf32le;
1612 len =
4;
1613 }
else if(!strcasecmp(encoding,
"GB18030")) {
1614 *bom = bom_gb18030;
1615 len =
4;
1616 }
else if(!strcasecmp(encoding,
"UTF-EBCDIC")) {
1617 *bom = bom_utfebcdic;
1618 len =
4;
1619 }
1620 return len;
1621 }
1622
1623 static int doSave(WindowInfo *window, Boolean setEncAttr)
1624 {
1625 char *fileString =
NULL;
1626 char fullname[
MAXPATHLEN];
1627 struct stat statbuf;
1628 FILE *fp;
1629 int fileLen, result;
1630
1631 iconv_t ic =
NULL;
1632 ConvertFunc strconv = copyBytes;
1633 if(strlen(window->encoding) >
0) {
1634 ic = iconv_open(window->encoding,
"UTF-8");
1635 if(ic == (
iconv_t) -
1) {
1636 DialogF(
DF_ERR, window->shell,
1,
"Error saving File",
1637 "The text cannot be converted to %s",
"OK", window->encoding);
1638 return FALSE;
1639 }
1640 strconv = (ConvertFunc)iconv;
1641 }
1642
1643
1644 strcpy(fullname, window->path);
1645 strcat(fullname, window->filename);
1646
1647
1648
1649 if ((
0 == getuid())
1650 && (
0 == stat(fullname, &statbuf))
1651 && !(statbuf.st_mode & (
S_IWUSR |
S_IWGRP |
S_IWOTH)))
1652 {
1653 result = DialogF(
DF_WARN, window->shell,
2,
"Writing Read-only File",
1654 "File ''%s'' is marked as read-only.\n"
1655 "Do you want to save anyway?",
1656 "Save",
"Cancel", window->filename);
1657 if (
1 != result)
1658 {
1659 return True;
1660 }
1661 }
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671 if (BufGetCharacter(window->buffer, window->buffer->length -
1) !=
'\n'
1672 && window->buffer->length !=
0
1673 && GetPrefAppendLF())
1674 {
1675 BufInsert(window->buffer, window->buffer->length,
"\n");
1676 }
1677
1678
1679 fp = fopen(fullname,
"wb");
1680 if (fp ==
NULL)
1681 {
1682 result = DialogF(
DF_WARN, window->shell,
2,
"Error saving File",
1683 "Unable to save %s:\n%s\n\nSave as a new file?",
1684 "Save As...",
"Cancel",
1685 window->filename, errorString());
1686
1687 if (result ==
1)
1688 {
1689 return SaveWindowAs(window,
NULL);
1690 }
1691 return FALSE;
1692 }
1693
1694
1695 fileString = BufGetAll(window->buffer);
1696 fileLen = window->buffer->length;
1697
1698
1699 BufUnsubstituteNullChars(fileString, window->buffer);
1700
1701
1702 if (window->fileFormat ==
DOS_FILE_FORMAT)
1703 {
1704 if (!ConvertToDosFileString(&fileString, &fileLen))
1705 {
1706 DialogF(
DF_ERR, window->shell,
1,
"Out of Memory",
1707 "Out of memory! Try\nsaving in Unix format",
"OK");
1708 fclose(fp);
1709 return FALSE;
1710 }
1711 }
else if (window->fileFormat ==
MAC_FILE_FORMAT)
1712 {
1713 ConvertToMacFileString(fileString, fileLen);
1714 }
1715
1716
1717 IOFilter *filter = GetFilterFromName(window->filter);
1718 char *filter_cmd =
NULL;
1719 if(filter && filter->cmdout && strlen(filter->cmdout) >
0) {
1720 filter_cmd = filter->cmdout;
1721 }
1722 FileStream *stream = filestream_open_w(window->shell, fp, filter_cmd);
1723
1724
1725 if(window->bom) {
1726 char *bom;
1727 int bomLen = getBOM(window->encoding, &bom);
1728 if(bomLen >
0) {
1729 if(filestream_write(bom, bomLen, stream) != bomLen) {
1730 fileLen =
0;
1731 }
1732 }
1733 }
1734
1735
1736 int skipped =
0;
1737 int nonreversible =
0;
1738 int unerr =
0;
1739 char buf[
IO_BUFSIZE];
1740 char *in = fileString;
1741 size_t inleft = fileLen;
1742 while(in) {
1743 char *out = buf;
1744 size_t outleft =
IO_BUFSIZE;
1745 size_t w = outleft;
1746 if (inleft ==
0) {
1747
1748 in =
NULL;
1749 }
1750 size_t rc = strconv(ic, &in, &inleft, &out, &outleft);
1751 w -= outleft;
1752
1753 if(w >
0) {
1754 filestream_write(buf, w, stream);
1755 }
1756
1757 if(rc == (
size_t)-
1) {
1758 size_t skip;
1759 switch (errno) {
1760 case EILSEQ:
1761 case EINVAL:
1762
1763 skip = Utf8CharLen((
const unsigned char*)in);
1764 ++skipped;
1765 in += skip;
1766 if(inleft >= skip) {
1767 inleft -= skip;
1768 }
else {
1769 inleft =
0;
1770 }
1771 break;
1772 case E2BIG:
1773
1774 break;
1775 default:
1776
1777 ++unerr;
1778 }
1779 }
1780
1781 if (inleft ==
0) {
1782
1783 nonreversible += rc;
1784 }
1785 }
1786
1787 unsigned int eresp =
0;
1788 if (skipped >
0 || nonreversible >
0 || unerr >
0) {
1789 eresp = DialogF(
DF_WARN, window->shell,
2,
"Encoding warning",
1790 "%d non-convertible characters skipped\n"
1791 "%d non-reversible characters encountered\n"
1792 "%d unknown errors occurred\n"
1793 "Save anyway?",
"YES",
"NO",
1794 skipped, nonreversible, unerr);
1795 }
1796
1797 if(ic) {
1798 iconv_close(ic);
1799 }
1800
1801 if (ferror(fp))
1802 {
1803 DialogF(
DF_ERR, window->shell,
1,
"Error saving File",
1804 "%s not saved:\n%s",
"OK", window->filename, errorString());
1805 }
1806
1807 if (ferror(fp) || eresp ==
2) {
1808 filestream_close(stream);
1809 remove(fullname);
1810 NEditFree(fileString);
1811 return FALSE;
1812 }
1813
1814 if(setEncAttr) {
1815 size_t encLen = strlen(window->encoding);
1816 char *encStr;
1817 char *encCopy =
NULL;
1818 if(encLen ==
0) {
1819 encStr =
"utf-8";
1820 encLen =
5;
1821 }
else {
1822 encCopy = NEditStrdup(window->encoding);
1823 for(
int i=
0;i<encLen;i++) {
1824 encCopy[i] = tolower(encCopy[i]);
1825 }
1826 encStr = encCopy;
1827 }
1828
1829 if(xattr_set(fullname,
"charset", encStr, encLen)) {
1830 perror(
"xattr_set failed");
1831 }
1832
1833 if(encCopy) {
1834 NEditFree(encCopy);
1835 }
1836 }
else {
1837 ssize_t len =
0;
1838 char *fileAttr = xattr_get(fullname,
"charset", &len);
1839 if(fileAttr) {
1840 size_t winEncLen = strlen(window->encoding);
1841 size_t cmpLen = winEncLen > len ? len : winEncLen;
1842 if(len != winEncLen || memcmp(fileAttr, window->encoding, cmpLen)) {
1843 if(xattr_remove(fullname,
"charset")) {
1844 DialogF(
DF_ERR, window->shell,
1,
"Error saving File",
1845 "Cannot remove previous charset attribute");
1846 }
1847 }
1848 free(fileAttr);
1849 }
1850 }
1851
1852
1853 if (filestream_close(stream) !=
0)
1854 {
1855 DialogF(
DF_ERR, window->shell,
1,
"Error closing File",
1856 "Error closing file:\n%s",
"OK", errorString());
1857 NEditFree(fileString);
1858 return FALSE;
1859 }
1860
1861
1862 NEditFree(fileString);
1863
1864
1865 SetWindowModified(window,
FALSE);
1866
1867
1868 if (stat(fullname, &statbuf) ==
0) {
1869 window->lastModTime = statbuf.st_mtime;
1870 window->fileMissing =
FALSE;
1871 window->device = statbuf.st_dev;
1872 window->inode = statbuf.st_ino;
1873 }
else {
1874
1875
1876 window->lastModTime =
0;
1877 window->fileMissing =
TRUE;
1878 window->device =
0;
1879 window->inode =
0;
1880 }
1881
1882 return TRUE;
1883 }
1884
1885
1886
1887
1888
1889
1890 int WriteBackupFile(WindowInfo *window)
1891 {
1892 char *fileString =
NULL;
1893 char name[
MAXPATHLEN];
1894 FILE *fp;
1895 int fd, fileLen;
1896
1897
1898 backupFileName(window, name,
sizeof(name));
1899
1900
1901
1902 remove(name);
1903
1904
1905
1906
1907 if ((fd = open(name,
O_CREAT|
O_EXCL|
O_WRONLY,
S_IRUSR |
S_IWUSR)) <
0
1908 || (fp = fdopen(fd,
"w")) ==
NULL)
1909 {
1910 DialogF(
DF_WARN, window->shell,
1,
"Error writing Backup",
1911 "Unable to save backup for %s:\n%s\n"
1912 "Automatic backup is now off",
"OK", window->filename,
1913 errorString());
1914 window->autoSave =
FALSE;
1915 SetToggleButtonState(window, window->autoSaveItem,
FALSE,
FALSE);
1916 return FALSE;
1917 }
1918
1919
1920 fileString = BufGetAll(window->buffer);
1921 fileLen = window->buffer->length;
1922
1923
1924 BufUnsubstituteNullChars(fileString, window->buffer);
1925
1926
1927 if (fileLen !=
0 && fileString[fileLen-
1] !=
'\n')
1928 fileString[fileLen++] =
'\n';
1929
1930
1931 fwrite(fileString,
sizeof(
char), fileLen, fp);
1932 if (ferror(fp))
1933 {
1934 DialogF(
DF_ERR, window->shell,
1,
"Error saving Backup",
1935 "Error while saving backup for %s:\n%s\n"
1936 "Automatic backup is now off",
"OK", window->filename,
1937 errorString());
1938 fclose(fp);
1939 remove(name);
1940 NEditFree(fileString);
1941 window->autoSave =
FALSE;
1942 return FALSE;
1943 }
1944
1945
1946 if (fclose(fp) !=
0) {
1947 NEditFree(fileString);
1948 return FALSE;
1949 }
1950
1951
1952 NEditFree(fileString);
1953
1954 return TRUE;
1955 }
1956
1957
1958
1959
1960 void RemoveBackupFile(WindowInfo *window)
1961 {
1962 char name[
MAXPATHLEN];
1963
1964
1965 if (window->autoSave ==
FALSE)
1966 return;
1967
1968 backupFileName(window, name,
sizeof(name));
1969 remove(name);
1970 }
1971
1972
1973
1974
1975
1976 static void backupFileName(WindowInfo *window,
char *name,
size_t len)
1977 {
1978 char bckname[
MAXPATHLEN];
1979 if (window->filenameSet)
1980 {
1981 sprintf(name,
"%s~%s", window->path, window->filename);
1982 }
else
1983 {
1984 strcpy(bckname,
"~");
1985 strncat(bckname, window->filename,
MAXPATHLEN -
1);
1986 PrependHome(bckname, name, len);
1987 }
1988 }
1989
1990
1991
1992
1993
1994
1995 static int writeBckVersion(WindowInfo *window)
1996 {
1997 char fullname[
MAXPATHLEN], bckname[
MAXPATHLEN];
1998 struct stat statbuf;
1999 int in_fd, out_fd;
2000 char *io_buffer;
2001 #define IO_BUFFER_SIZE ((
size_t)(
1024*
1024))
2002
2003
2004 if (!window->saveOldVersion) {
2005 return False;
2006 }
2007
2008
2009 strcpy(fullname, window->path);
2010 strcat(fullname, window->filename);
2011
2012
2013 if ((strlen(fullname) +
5) > (
size_t)
MAXPATHLEN) {
2014 return bckError(window,
"file name too long", window->filename);
2015 }
2016 if(snprintf(bckname,
MAXPATHLEN,
"%s.bck", fullname) >=
MAXPATHLEN) {
2017 return FALSE;
2018 }
2019
2020
2021
2022 remove(bckname);
2023
2024
2025
2026 in_fd = open(fullname,
O_RDONLY);
2027 if (in_fd<
0) {
2028 return FALSE;
2029 }
2030
2031
2032
2033
2034 if (fstat(in_fd, &statbuf) !=
0) {
2035 close(in_fd);
2036 return FALSE;
2037 }
2038
2039
2040 out_fd = open(bckname,
O_CREAT|
O_EXCL|
O_TRUNC|
O_WRONLY,
S_IRUSR |
S_IWUSR);
2041 if (out_fd <
0) {
2042 close(in_fd);
2043 return bckError(window,
"Error open backup file", bckname);
2044 }
2045
2046
2047 if (fchmod(out_fd, statbuf.st_mode) !=
0) {
2048 close(in_fd);
2049 close(out_fd);
2050 remove(bckname);
2051 return bckError(window,
"fchmod() failed", bckname);
2052 }
2053
2054
2055 io_buffer = (
char*) malloc(
IO_BUFFER_SIZE);
2056 if (
NULL == io_buffer) {
2057 close(in_fd);
2058 close(out_fd);
2059 remove(bckname);
2060 return bckError(window,
"out of memory", bckname);
2061 }
2062
2063
2064 for(;;) {
2065 ssize_t bytes_read;
2066 ssize_t bytes_written;
2067 bytes_read = read(in_fd, io_buffer,
IO_BUFFER_SIZE);
2068
2069 if (bytes_read <
0) {
2070 close(in_fd);
2071 close(out_fd);
2072 remove(bckname);
2073 free(io_buffer);
2074 return bckError(window,
"read() error", window->filename);
2075 }
2076
2077 if (
0 == bytes_read) {
2078 break;
2079 }
2080
2081
2082 bytes_written = write(out_fd, io_buffer, (
size_t) bytes_read);
2083 if (bytes_written != bytes_read) {
2084 close(in_fd);
2085 close(out_fd);
2086 remove(bckname);
2087 free(io_buffer);
2088 return bckError(window, errorString(), bckname);
2089 }
2090 }
2091
2092
2093 close(in_fd);
2094 close(out_fd);
2095
2096 free(io_buffer);
2097
2098 return FALSE;
2099 }
2100
2101
2102
2103
2104
2105 static int bckError(WindowInfo *window,
const char *errString,
const char *file)
2106 {
2107 int resp;
2108
2109 resp = DialogF(
DF_ERR, window->shell,
3,
"Error writing Backup",
2110 "Couldn''t write .bck (last version) file.\n%s: %s",
"Cancel Save",
2111 "Turn off Backups",
"Continue", file, errString);
2112 if (resp ==
1)
2113 return TRUE;
2114 if (resp ==
2) {
2115 window->saveOldVersion =
FALSE;
2116 SetToggleButtonState(window, window->saveLastItem,
FALSE,
FALSE);
2117 }
2118 return FALSE;
2119 }
2120
2121 void PrintWindow(WindowInfo *window,
int selectedOnly)
2122 {
2123 textBuffer *buf = window->buffer;
2124 selection *sel = &buf->primary;
2125 char *fileString =
NULL;
2126 int fileLen;
2127
2128
2129
2130 if (selectedOnly) {
2131 if (!sel->selected) {
2132 XBell(TheDisplay,
0);
2133 return;
2134 }
2135 if (sel->rectangular) {
2136 fileString = BufGetSelectionText(buf);
2137 fileLen = strlen(fileString);
2138 }
else
2139 fileString = TextGetWrapped(window->textArea, sel->start, sel->end,
2140 &fileLen);
2141 }
else
2142 fileString = TextGetWrapped(window->textArea,
0, buf->length, &fileLen);
2143
2144
2145 BufUnsubstituteNullChars(fileString, buf);
2146
2147
2148 if (fileLen !=
0 && fileString[fileLen-
1] !=
'\n')
2149 fileString[fileLen++] =
'\n';
2150
2151
2152 PrintString(fileString, fileLen, window->shell, window->filename);
2153
2154
2155 NEditFree(fileString);
2156 }
2157
2158
2159
2160
2161
2162 void PrintString(
const char *string,
int length, Widget parent,
const char *jobName)
2163 {
2164 char tmpFileName[L_tmpnam];
2165 FILE *fp;
2166 int fd;
2167
2168
2169
2170
2171
2172
2173
2174
2175 #ifdef __GLIBC__
2176 mkstemp(tmpFileName);
2177 #else
2178 tmpnam(tmpFileName);
2179 #endif
2180
2181
2182 if ((fd = open(tmpFileName,
O_CREAT|
O_EXCL|
O_WRONLY,
S_IRUSR |
S_IWUSR)) <
0 || (fp = fdopen(fd,
"w")) ==
NULL)
2183 {
2184 DialogF(
DF_WARN, parent,
1,
"Error while Printing",
2185 "Unable to write file for printing:\n%s",
"OK",
2186 errorString());
2187 return;
2188 }
2189
2190
2191 fwrite(string,
sizeof(
char), length, fp);
2192 if (ferror(fp))
2193 {
2194 DialogF(
DF_ERR, parent,
1,
"Error while Printing",
2195 "%s not printed:\n%s",
"OK", jobName, errorString());
2196 fclose(fp);
2197 remove(tmpFileName);
2198 return;
2199 }
2200
2201
2202 if (fclose(fp) !=
0)
2203 {
2204 DialogF(
DF_ERR, parent,
1,
"Error while Printing",
2205 "Error closing temp. print file:\n%s",
"OK",
2206 errorString());
2207 remove(tmpFileName);
2208 return;
2209 }
2210
2211
2212 PrintFile(parent, tmpFileName, jobName);
2213 remove(tmpFileName);
2214 return;
2215 }
2216
2217
2218
2219
2220
2221 int PromptForExistingFile(WindowInfo *window,
char *prompt, FileSelection *file)
2222 {
2223 char *savedDefaultDir;
2224 int retVal;
2225
2226
2227
2228
2229 savedDefaultDir = GetFileDialogDefaultDirectory();
2230 if (*window->path !=
'\0')
2231 SetFileDialogDefaultDirectory(window->path);
2232 retVal = GetExistingFilename(window->shell, prompt, file);
2233 if (retVal !=
GFN_OK)
2234 SetFileDialogDefaultDirectory(savedDefaultDir);
2235
2236 NEditFree(savedDefaultDir);
2237
2238 return retVal;
2239 }
2240
2241
2242
2243
2244
2245
2246 int PromptForNewFile(WindowInfo *window,
char *prompt, FileSelection *file,
2247 int *fileFormat)
2248 {
2249 int retVal;
2250 char *savedDefaultDir;
2251
2252 *fileFormat = window->fileFormat;
2253
2254
2255
2256
2257 savedDefaultDir = GetFileDialogDefaultDirectory();
2258 if (*window->path !=
'\0')
2259 SetFileDialogDefaultDirectory(window->path);
2260
2261 char *prevPath =
NULL;
2262 if(window->path[
0] !=
'\0' && window->filename[
0] !=
'\0' && window->filenameSet) {
2263 size_t plen = strlen(window->path);
2264 size_t nlen = strlen(window->filename);
2265 prevPath = NEditMalloc(plen + nlen +
2);
2266 memcpy(prevPath, window->path, plen);
2267 if(window->path[plen-
1] !=
'/') {
2268 prevPath[plen] =
'/';
2269 plen++;
2270 }
2271 memcpy(prevPath+plen, window->filename, nlen);
2272 prevPath[plen+nlen] =
'\0';
2273 }
2274
2275 file->path = prevPath;
2276 retVal = GetNewFilename(window->shell, prompt, file,
"");
2277 if(prevPath) {
2278 NEditFree(prevPath);
2279 }
2280
2281 if (retVal !=
GFN_OK)
2282 SetFileDialogDefaultDirectory(savedDefaultDir);
2283
2284 NEditFree(savedDefaultDir);
2285
2286 return retVal;
2287 }
2288
2289
2290
2291
2292
2293
2294 void UniqueUntitledName(
char *name)
2295 {
2296 WindowInfo *w;
2297 int i;
2298
2299 for (i=
0; i<
INT_MAX; i++) {
2300 if (i ==
0)
2301 sprintf(name,
"Untitled");
2302 else
2303 sprintf(name,
"Untitled_%d", i);
2304 for (w=WindowList; w!=
NULL; w=w->next)
2305 if (!strcmp(w->filename, name))
2306 break;
2307 if (w ==
NULL)
2308 break;
2309 }
2310 }
2311
2312
2313
2314
2315
2316 static void modifiedWindowDestroyedCB(Widget w, XtPointer clientData,
2317 XtPointer callData)
2318 {
2319 *(Bool*)clientData =
TRUE;
2320 }
2321
2322
2323
2324
2325
2326 void CheckForChangesToFile(WindowInfo *window)
2327 {
2328 static WindowInfo* lastCheckWindow =
NULL;
2329 static Time lastCheckTime =
0;
2330 char fullname[
MAXPATHLEN];
2331 struct stat statbuf;
2332 Time timestamp;
2333 FILE *fp;
2334 int resp, silent =
0;
2335 XWindowAttributes winAttr;
2336 Boolean windowIsDestroyed = False;
2337
2338 if(!window->filenameSet)
2339 return;
2340
2341
2342 timestamp = XtLastTimestampProcessed(XtDisplay(window->shell));
2343 if (window == lastCheckWindow &&
2344 timestamp - lastCheckTime <
MOD_CHECK_INTERVAL)
2345 return;
2346 lastCheckWindow = window;
2347 lastCheckTime = timestamp;
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360 if (!IsTopDocument(window))
2361 silent =
1;
2362 else {
2363 XGetWindowAttributes(XtDisplay(window->shell),
2364 XtWindow(window->shell),
2365 &winAttr);
2366
2367 if (winAttr.map_state != IsViewable)
2368 silent =
1;
2369 }
2370
2371
2372 strcpy(fullname, window->path);
2373 strcat(fullname, window->filename);
2374 if (stat(fullname, &statbuf) !=
0) {
2375
2376 if (window->fileMissing || silent) {
2377 return;
2378 }
2379
2380
2381
2382 window->fileMissing =
TRUE;
2383 window->lastModTime =
1;
2384 window->device =
0;
2385 window->inode =
0;
2386
2387
2388
2389 if (GetPrefWarnFileMods()) {
2390 char* title;
2391 char* body;
2392
2393
2394 XUngrabPointer(XtDisplay(window->shell), timestamp);
2395
2396
2397
2398
2399 XtAddCallback(window->shell, XmNdestroyCallback,
2400 modifiedWindowDestroyedCB, &windowIsDestroyed);
2401
2402
2403 switch (errno) {
2404 case ENOENT:
2405
2406 title =
"File not Found";
2407 body =
"File ''%s'' (or directory in its path)\n"
2408 "no longer exists.\n"
2409 "Another program may have deleted or moved it.";
2410 resp = DialogF(
DF_ERR, window->shell,
2, title, body,
2411 "Save",
"Cancel", window->filename);
2412 break;
2413 case EACCES:
2414
2415
2416
2417 title =
"Permission Denied";
2418 body =
"You no longer have access to file ''%s''.\n"
2419 "Another program may have changed the permissions of\n"
2420 "one of its parent directories.";
2421 resp =
1 + DialogF(
DF_ERR, window->shell,
1, title, body,
2422 "Cancel", window->filename);
2423 break;
2424 default:
2425
2426
2427 title =
"File not Accessible";
2428 body =
"Error while checking the status of file ''%s'':\n"
2429 " ''%s''\n"
2430 "Please make sure that no data is lost before closing\n"
2431 "this window.";
2432 resp = DialogF(
DF_ERR, window->shell,
2, title, body,
2433 "Save",
"Cancel", window->filename,
2434 errorString());
2435 break;
2436 }
2437
2438 if (!windowIsDestroyed) {
2439 XtRemoveCallback(window->shell, XmNdestroyCallback,
2440 modifiedWindowDestroyedCB, &windowIsDestroyed);
2441 }
2442
2443 switch (resp) {
2444 case 1:
2445 SaveWindow(window);
2446 break;
2447
2448
2449
2450
2451
2452
2453
2454
2455 }
2456 }
2457
2458
2459
2460
2461 if (!windowIsDestroyed) {
2462 SET_PERM_LOCKED(window->lockReasons, False);
2463 UpdateWindowTitle(window);
2464 UpdateWindowReadOnly(window);
2465 }
2466 return;
2467 }
2468
2469
2470
2471 if (window->fileMode != statbuf.st_mode ||
2472 window->fileUid != statbuf.st_uid ||
2473 window->fileGid != statbuf.st_gid) {
2474 window->fileMode = statbuf.st_mode;
2475 window->fileUid = statbuf.st_uid;
2476 window->fileGid = statbuf.st_gid;
2477 if ((fp = fopen(fullname,
"r")) !=
NULL) {
2478 int readOnly;
2479 fclose(fp);
2480 #ifndef DONT_USE_ACCESS
2481 readOnly = access(fullname,
W_OK) !=
0;
2482 #else
2483 if (((fp = fopen(fullname,
"r+")) !=
NULL)) {
2484 readOnly =
FALSE;
2485 fclose(fp);
2486 }
else
2487 readOnly =
TRUE;
2488 #endif
2489 if (
IS_PERM_LOCKED(window->lockReasons) != readOnly) {
2490 SET_PERM_LOCKED(window->lockReasons, readOnly);
2491 UpdateWindowTitle(window);
2492 UpdateWindowReadOnly(window);
2493 }
2494 }
2495 }
2496
2497
2498
2499
2500
2501
2502
2503
2504 if (!silent &&
2505 ((window->lastModTime !=
0 &&
2506 window->lastModTime != statbuf.st_mtime) ||
2507 window->fileMissing) ){
2508 window->lastModTime =
0;
2509 window->fileMissing =
FALSE;
2510 if (!GetPrefWarnFileMods())
2511 return;
2512 if (GetPrefWarnRealFileMods() &&
2513 !cmpWinAgainstFile(window, fullname)) {
2514
2515 window->lastModTime = statbuf.st_mtime;
2516 return;
2517 }
2518 XUngrabPointer(XtDisplay(window->shell), timestamp);
2519 if (window->fileChanged)
2520 resp = DialogF(
DF_WARN, window->shell,
2,
2521 "File modified externally",
2522 "%s has been modified by another program. Reload?\n\n"
2523 "WARNING: Reloading will discard changes made in this\n"
2524 "editing session!",
"Reload",
"Cancel", window->filename);
2525 else
2526 resp = DialogF(
DF_WARN, window->shell,
2,
2527 "File modified externally",
2528 "%s has been modified by another\nprogram. Reload?",
2529 "Reload",
"Cancel", window->filename);
2530 if (resp ==
1)
2531 RevertToSaved(window,
NULL);
2532 }
2533 }
2534
2535
2536
2537
2538
2539
2540 static int fileWasModifiedExternally(WindowInfo *window)
2541 {
2542 char fullname[
MAXPATHLEN];
2543 struct stat statbuf;
2544
2545 if(!window->filenameSet)
2546 return FALSE;
2547
2548
2549 strcpy(fullname, window->path);
2550 strcat(fullname, window->filename);
2551 if (stat(fullname, &statbuf) !=
0)
2552 return FALSE;
2553 if (window->lastModTime == statbuf.st_mtime)
2554 return FALSE;
2555 if (GetPrefWarnRealFileMods() &&
2556 !cmpWinAgainstFile(window, fullname)) {
2557 return FALSE;
2558 }
2559 return TRUE;
2560 }
2561
2562
2563
2564
2565
2566 int CheckReadOnly(WindowInfo *window)
2567 {
2568 if (
IS_ANY_LOCKED(window->lockReasons)) {
2569 XBell(TheDisplay,
0);
2570 return True;
2571 }
2572 return False;
2573 }
2574
2575
2576
2577
2578 static const char *errorString(
void)
2579 {
2580 return strerror(errno);
2581 }
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625 static void addWrapNewlines(WindowInfo *window)
2626 {
2627 int fileLen, i, insertPositions[
MAX_PANES], topLines[
MAX_PANES];
2628 int horizOffset;
2629 Widget text;
2630 char *fileString;
2631
2632
2633 for (i=
0; i<=window->nPanes; i++) {
2634 text = i==
0 ? window->textArea : window->textPanes[i-
1];
2635 insertPositions[i] = TextGetCursorPos(text);
2636 TextGetScroll(text, &topLines[i], &horizOffset);
2637 }
2638
2639
2640 fileString = TextGetWrapped(window->textArea,
0,
2641 window->buffer->length, &fileLen);
2642 BufSetAll(window->buffer, fileString);
2643 NEditFree(fileString);
2644
2645
2646 for (i=
0; i<=window->nPanes; i++) {
2647 text = i==
0 ? window->textArea : window->textPanes[i-
1];
2648 TextSetCursorPos(text, insertPositions[i]);
2649 TextSetScroll(text, topLines[i],
0);
2650 }
2651
2652
2653
2654 SetToggleButtonState(window, window->continuousWrapItem, False, True);
2655 }
2656
2657
2658
2659
2660 #define PREFERRED_CMPBUF_LEN 32768
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671 static int cmpWinAgainstFile(WindowInfo *window,
const char *fileName)
2672 {
2673 char fileString[
PREFERRED_CMPBUF_LEN +
2];
2674 struct stat statbuf;
2675 int fileLen, restLen, nRead, bufPos, rv, offset, filePos;
2676 char pendingCR =
0;
2677 int fileFormat = window->fileFormat;
2678 char message[
MAXPATHLEN+
50];
2679 textBuffer *buf = window->buffer;
2680 FILE *fp;
2681
2682 fp = fopen(fileName,
"r");
2683 if (!fp)
2684 return (
1);
2685 if (fstat(fileno(fp), &statbuf) !=
0) {
2686 fclose(fp);
2687 return (
1);
2688 }
2689
2690 fileLen = statbuf.st_size;
2691
2692 if (fileFormat !=
DOS_FILE_FORMAT) {
2693 if (fileLen != buf->length) {
2694 fclose(fp);
2695 return (
1);
2696 }
2697 }
else {
2698
2699 if (fileLen < buf->length) {
2700 fclose(fp);
2701 return (
1);
2702 }
2703 }
2704
2705
2706
2707 sprintf(message,
"Comparing externally modified %s ...", window->filename);
2708 restLen = min(
PREFERRED_CMPBUF_LEN, fileLen);
2709 bufPos =
0;
2710 filePos =
0;
2711 while (restLen >
0) {
2712 AllWindowsBusy(message);
2713 if (pendingCR) {
2714 fileString[
0] = pendingCR;
2715 offset =
1;
2716 }
else {
2717 offset =
0;
2718 }
2719
2720 nRead = fread(fileString+offset,
sizeof(
char), restLen, fp);
2721 if (nRead != restLen) {
2722 fclose(fp);
2723 AllWindowsUnbusy();
2724 return (
1);
2725 }
2726 filePos += nRead;
2727
2728 nRead += offset;
2729
2730
2731 if (bufPos ==
0 && fileFormat != FormatOfFile(fileString)) {
2732 fclose(fp);
2733 AllWindowsUnbusy();
2734 return (
1);
2735 }
2736
2737 if (fileFormat ==
MAC_FILE_FORMAT)
2738 ConvertFromMacFileString(fileString, nRead);
2739 else if (fileFormat ==
DOS_FILE_FORMAT)
2740 ConvertFromDosFileString(fileString, &nRead, &pendingCR);
2741
2742
2743 BufSubstituteNullChars(fileString, nRead, buf);
2744 rv = BufCmp(buf, bufPos, nRead, fileString);
2745 if (rv) {
2746 fclose(fp);
2747 AllWindowsUnbusy();
2748 return (rv);
2749 }
2750 bufPos += nRead;
2751 restLen = min(fileLen - filePos,
PREFERRED_CMPBUF_LEN);
2752 }
2753 AllWindowsUnbusy();
2754 fclose(fp);
2755 if (pendingCR) {
2756 rv = BufCmp(buf, bufPos,
1, &pendingCR);
2757 if (rv) {
2758 return (rv);
2759 }
2760 bufPos +=
1;
2761 }
2762 if (bufPos != buf->length) {
2763 return (
1);
2764 }
2765 return (
0);
2766 }
2767
2768
2769
2770
2771
2772 static void forceShowLineNumbers(WindowInfo *window)
2773 {
2774 Boolean showLineNum = window->showLineNumbers;
2775 if (showLineNum) {
2776 window->showLineNumbers = False;
2777 ShowLineNumbers(window, showLineNum);
2778 }
2779 }
2780
2781 static int min(
int i1,
int i2)
2782 {
2783 return i1 <= i2 ? i1 : i2;
2784 }
2785
2786 static const char * GetDefaultEncoding(
void) {
2787 const char *fallback = GetPrefFallbackCharset();
2788 if(strcmp(fallback,
"locale")) {
2789 return fallback;
2790 }
2791
2792 char *lc = setlocale (
LC_ALL,
"");
2793 char *d = strchr(lc,
'.');
2794 if(d) {
2795 *d =
0;
2796 }
2797
2798 int i =
0;
2799 while(locales[i].locale) {
2800 if(!strcmp(locales[i].locale, lc)) {
2801 return locales[i].encoding;
2802 }
2803 i++;
2804 }
2805
2806 return "ISO8859-1";
2807 }
2808
2809 const char * DetectEncoding(
const char *buf,
size_t len,
const char *def) {
2810 int utf8Err =
0;
2811 int utf8Mb =
0;
2812
2813 const unsigned char *u = (
const unsigned char*)buf;
2814 int charLen =
0;
2815 for(
size_t i=
0;i<len;i++) {
2816 unsigned char c = u[i];
2817 if(charLen ==
0) {
2818 if(c >=
240) {
2819 charLen =
3;
2820 }
else if(c >=
224) {
2821 charLen =
2;
2822 }
else if(c >
192) {
2823 charLen =
1;
2824 }
2825 }
else {
2826 if((c &
192) ==
128) {
2827 if(--charLen ==
0) {
2828 utf8Mb++;
2829 }
2830 }
else {
2831 utf8Err++;
2832 charLen =
0;
2833 }
2834 }
2835 }
2836
2837 if(utf8Err ==
0 || utf8Mb - utf8Err >
2) {
2838 return "UTF-8";
2839 }
2840
2841 char defbuf[
4];
2842 memset(defbuf,
0,
4);
2843 if(def && strlen(def) >
3) {
2844 memcpy(defbuf, def,
3);
2845 }
2846 if(!strcasecmp(defbuf,
"utf")) {
2847 return GetDefaultEncoding();
2848 }
2849
2850 return def;
2851 }
2852
2853
2854
2855
2856 static char* getEncodingAttribute(
const char *path)
2857 {
2858 char *enc_attr =
NULL;
2859
2860 ssize_t attrlen =
0;
2861 enc_attr = xattr_get(path,
"charset", &attrlen);
2862
2863 if(enc_attr) {
2864 if(attrlen ==
0) {
2865 free(enc_attr);
2866 return NULL;
2867 }
2868
2869
2870 if(isspace(enc_attr[
0]) || isspace(enc_attr[attrlen-
1])) {
2871 char *enc_trim = enc_attr;
2872 size_t etlen = attrlen;
2873 while(etlen >
0 && isspace(*enc_trim)) {
2874 enc_trim++;
2875 etlen--;
2876 }
2877 while(etlen >
0 && isspace(enc_trim[etlen-
1])) {
2878 etlen--;
2879 }
2880 enc_trim[etlen] =
'\0';
2881
2882 if(etlen ==
0) {
2883 free(enc_attr);
2884 return NULL;
2885 }
2886
2887 char *enc_str = malloc(attrlen +
1);
2888 if(enc_str) {
2889 memcpy(enc_str, enc_trim, etlen+
1);
2890 }
2891 free(enc_attr);
2892 enc_attr = enc_str;
2893 }
2894 }
2895
2896 return enc_attr;
2897 }
2898