#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
#include "file.h"
#include "filter.h"
#include "textBuf.h"
#include "text.h"
#include "window.h"
#include "preferences.h"
#include "undo.h"
#include "menu.h"
#include "tags.h"
#include "server.h"
#include "interpret.h"
#include "editorconfig.h"
#include "../util/misc.h"
#include "../util/DialogF.h"
#include "../util/fileUtils.h"
#include "../util/getfiles.h"
#include "../util/printUtils.h"
#include "../util/utils.h"
#include "../util/nedit_malloc.h"
#include "../util/libxattr.h"
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <iconv.h>
#include <locale.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef __MVS__
#include <sys/param.h>
#endif
#include <fcntl.h>
#include <Xm/Xm.h>
#include <Xm/ToggleB.h>
#include <Xm/FileSB.h>
#include <Xm/RowColumn.h>
#include <Xm/Form.h>
#include <Xm/Label.h>
#ifdef HAVE_DEBUG_H
#include "../debug.h"
#endif
#include <inttypes.h>
#define ENC_ERROR_LIST_LEN 8
typedef struct Locale {
char *locale;
char *encoding;
} Locale;
static Locale locales[] = {
{
"aa_DJ",
"ISO8859-1"},
{
"af_ZA",
"ISO8859-1"},
{
"an_ES",
"ISO8859-15"},
{
"ar_AE",
"ISO8859-6"},
{
"ar_BH",
"ISO8859-6"},
{
"ar_DZ",
"ISO8859-6"},
{
"ar_EG",
"ISO8859-6"},
{
"ar_IQ",
"ISO8859-6"},
{
"ar_JO",
"ISO8859-6"},
{
"ar_KW",
"ISO8859-6"},
{
"ar_LB",
"ISO8859-6"},
{
"ar_LY",
"ISO8859-6"},
{
"ar_MA",
"ISO8859-6"},
{
"ar_OM",
"ISO8859-6"},
{
"ar_QA",
"ISO8859-6"},
{
"ar_SA",
"ISO8859-6"},
{
"ar_SD",
"ISO8859-6"},
{
"ar_SY",
"ISO8859-6"},
{
"ar_TN",
"ISO8859-6"},
{
"ar_YE",
"ISO8859-6"},
{
"ast_ES",
"ISO8859-15"},
{
"be_BY",
"CP1251"},
{
"bg_BG",
"CP1251"},
{
"br_FR",
"ISO8859-15"},
{
"br_FR@euro",
"ISO8859-15"},
{
"bs_BA",
"ISO8859-2"},
{
"ca_AD",
"ISO8859-15"},
{
"ca_ES",
"ISO8859-15"},
{
"ca_ES@euro",
"ISO8859-15"},
{
"ca_FR",
"ISO8859-15"},
{
"ca_IT",
"ISO8859-15"},
{
"cs_CZ",
"ISO8859-2"},
{
"cy_GB",
"ISO8859-14"},
{
"da_DK",
"ISO8859-1"},
{
"de_AT",
"ISO8859-15"},
{
"de_AT@euro",
"ISO8859-15"},
{
"de_BE",
"ISO8859-15"},
{
"de_BE@euro",
"ISO8859-15"},
{
"de_CH",
"ISO8859-1"},
{
"de_DE",
"ISO8859-15"},
{
"de_DE@euro",
"ISO8859-15"},
{
"de_LU",
"ISO8859-15"},
{
"de_LU@euro",
"ISO8859-15"},
{
"el_GR",
"ISO8859-7"},
{
"el_CY",
"ISO8859-7"},
{
"en_AU",
"ISO8859-1"},
{
"en_BW",
"ISO8859-1"},
{
"en_CA",
"ISO8859-1"},
{
"en_DK",
"ISO8859-1"},
{
"en_GB",
"ISO8859-1"},
{
"en_HK",
"ISO8859-1"},
{
"en_IE",
"ISO8859-15"},
{
"en_IE@euro",
"ISO8859-15"},
{
"en_NZ",
"ISO8859-1"},
{
"en_PH",
"ISO8859-1"},
{
"en_SG",
"ISO8859-1"},
{
"en_US",
"ISO8859-1"},
{
"en_ZA",
"ISO8859-1"},
{
"en_ZW",
"ISO8859-1"},
{
"es_AR",
"ISO8859-1"},
{
"es_BO",
"ISO8859-1"},
{
"es_CL",
"ISO8859-1"},
{
"es_CO",
"ISO8859-1"},
{
"es_CR",
"ISO8859-1"},
{
"es_DO",
"ISO8859-1"},
{
"es_EC",
"ISO8859-1"},
{
"es_ES",
"ISO8859-15"},
{
"es_ES@euro",
"ISO8859-15"},
{
"es_GT",
"ISO8859-1"},
{
"es_HN",
"ISO8859-1"},
{
"es_MX",
"ISO8859-1"},
{
"es_NI",
"ISO8859-1"},
{
"es_PA",
"ISO8859-1"},
{
"es_PE",
"ISO8859-1"},
{
"es_PR",
"ISO8859-1"},
{
"es_PY",
"ISO8859-1"},
{
"es_SV",
"ISO8859-1"},
{
"es_US",
"ISO8859-1"},
{
"es_UY",
"ISO8859-1"},
{
"es_VE",
"ISO8859-1"},
{
"et_EE",
"ISO8859-15"},
{
"et_EE.ISO8859-15",
"ISO8859-15"},
{
"eu_ES",
"ISO8859-15"},
{
"eu_ES@euro",
"ISO8859-15"},
{
"fi_FI",
"ISO8859-15"},
{
"fi_FI@euro",
"ISO8859-15"},
{
"fo_FO",
"ISO8859-1"},
{
"fr_BE",
"ISO8859-15"},
{
"fr_BE@euro",
"ISO8859-15"},
{
"fr_CA",
"ISO8859-15"},
{
"fr_CH",
"ISO8859-1"},
{
"fr_FR",
"ISO8859-15"},
{
"fr_FR@euro",
"ISO8859-15"},
{
"fr_LU",
"ISO8859-15"},
{
"fr_LU@euro",
"ISO8859-15"},
{
"ga_IE",
"ISO8859-15"},
{
"ga_IE@euro",
"ISO8859-15"},
{
"gd_GB",
"ISO8859-15"},
{
"gl_ES",
"ISO8859-15"},
{
"gl_ES@euro",
"ISO8859-15"},
{
"gv_GB",
"ISO8859-1"},
{
"he_IL",
"ISO8859-8"},
{
"hr_HR",
"ISO8859-2"},
{
"hsb_DE",
"ISO8859-2"},
{
"hu_HU",
"ISO8859-2"},
{
"hy_AM.ARMSCII-8",
"ARMSCII-8"},
{
"id_ID",
"ISO8859-1"},
{
"is_IS",
"ISO8859-1"},
{
"it_CH",
"ISO8859-1"},
{
"it_IT",
"ISO8859-15"},
{
"it_IT@euro",
"ISO8859-15"},
{
"iw_IL",
"ISO8859-8"},
{
"ja_JP.EUC-JP",
"EUC-JP"},
{
"ka_GE",
"GEORGIAN-PS"},
{
"kk_KZ",
"PT154"},
{
"kl_GL",
"ISO8859-1"},
{
"ko_KR.EUC-KR",
"EUC-KR"},
{
"ku_TR",
"ISO8859-9"},
{
"kw_GB",
"ISO8859-1"},
{
"lg_UG",
"ISO8859-10"},
{
"lt_LT",
"ISO8859-13"},
{
"lv_LV",
"ISO8859-13"},
{
"mg_MG",
"ISO8859-15"},
{
"mi_NZ",
"ISO8859-13"},
{
"mk_MK",
"ISO8859-5"},
{
"ms_MY",
"ISO8859-1"},
{
"mt_MT",
"ISO8859-3"},
{
"nb_NO",
"ISO8859-1"},
{
"nl_BE",
"ISO8859-15"},
{
"nl_BE@euro",
"ISO8859-15"},
{
"nl_NL",
"ISO8859-15"},
{
"nl_NL@euro",
"ISO8859-15"},
{
"nn_NO",
"ISO8859-1"},
{
"oc_FR",
"ISO8859-1"},
{
"om_KE",
"ISO8859-1"},
{
"pl_PL",
"ISO8859-2"},
{
"pt_BR",
"ISO8859-1"},
{
"pt_PT",
"ISO8859-15"},
{
"pt_PT@euro",
"ISO8859-15"},
{
"ro_RO",
"ISO8859-2"},
{
"ru_RU.KOI8-R",
"KOI8-R"},
{
"ru_RU",
"ISO8859-5"},
{
"ru_UA",
"KOI8-U"},
{
"sk_SK",
"ISO8859-2"},
{
"sl_SI",
"ISO8859-2"},
{
"so_DJ",
"ISO8859-1"},
{
"so_KE",
"ISO8859-1"},
{
"so_SO",
"ISO8859-1"},
{
"sq_AL",
"ISO8859-1"},
{
"st_ZA",
"ISO8859-1"},
{
"sv_FI",
"ISO8859-15"},
{
"sv_FI@euro",
"ISO8859-15"},
{
"sv_SE",
"ISO8859-1"},
{
"tg_TJ",
"KOI8-T"},
{
"th_TH",
"TIS-620"},
{
"tl_PH",
"ISO8859-1"},
{
"tr_CY",
"ISO8859-9"},
{
"tr_TR",
"ISO8859-9"},
{
"uk_UA",
"KOI8-U"},
{
"uz_UZ",
"ISO8859-1"},
{
"wa_BE",
"ISO8859-15"},
{
"wa_BE@euro",
"ISO8859-15"},
{
"xh_ZA",
"ISO8859-1"},
{
"yi_US",
"CP1255"},
{
"zh_CN.GB18030",
"GB18030"},
{
"zh_CN.GBK",
"GBK"},
{
"zh_CN",
"GB2312"},
{
"zh_HK",
"BIG5-HKSCS"},
{
"zh_SG.GBK",
"GBK"},
{
"zh_SG",
"GB2312"},
{
"zh_TW.EUC-TW",
"EUC-TW"},
{
"zh_TW",
"BIG5"},
{
"zu_ZA",
"ISO8859-1"},
{
NULL,
NULL}
};
#define MOD_CHECK_INTERVAL 3000
static int doSave(WindowInfo *window, Boolean setEncAttr);
static void safeClose(WindowInfo *window);
static int doOpen(WindowInfo *window,
const char *name,
const char *path,
const char *encoding,
const char *filter_name,
int flags);
static void backupFileName(WindowInfo *window,
char *name,
size_t len);
static int writeBckVersion(WindowInfo *window);
static int bckError(WindowInfo *window,
const char *errString,
const char *file);
static int fileWasModifiedExternally(WindowInfo *window);
static const char *errorString(
void);
static void addWrapNewlines(WindowInfo *window);
static int cmpWinAgainstFile(WindowInfo *window,
const char *fileName);
static int min(
int i1,
int i2);
static void modifiedWindowDestroyedCB(Widget w, XtPointer clientData,
XtPointer callData);
static void forceShowLineNumbers(WindowInfo *window);
static char* getEncodingAttribute(
const char *path);
WindowInfo *EditNewFile(WindowInfo *inWindow,
char *geometry,
int iconic,
const char *languageMode,
const char *defaultPath)
{
char name[
MAXPATHLEN];
WindowInfo *window;
size_t pathlen;
char *path;
UniqueUntitledName(name);
if (inWindow)
window = CreateDocument(inWindow, name);
else
window = CreateWindow(name, geometry, iconic);
path = window->path;
strcpy(window->filename, name);
strcpy(path, (defaultPath && *defaultPath) ? defaultPath : GetCurrentDir());
pathlen = strlen(window->path);
if (
0 < pathlen && path[pathlen -
1] !=
'/' && pathlen <
MAXPATHLEN -
1) {
strcpy(&path[pathlen],
"/");
}
SetWindowModified(window,
FALSE);
CLEAR_ALL_LOCKS(window->lockReasons);
UpdateWindowReadOnly(window);
UpdateStatsLine(window);
UpdateWindowTitle(window);
RefreshTabState(window);
if (languageMode ==
NULL)
DetermineLanguageMode(window, True);
else
SetLanguageMode(window, FindLanguageMode(languageMode), True);
ShowTabBar(window, GetShowTabBar(window));
if (iconic && IsIconic(window))
RaiseDocument(window);
else
RaiseDocumentWindow(window);
SortTabBar(window);
return window;
}
static void ApplyEditorConfig(WindowInfo *window, EditorConfig ec) {
char *params[
1];
char numStr[
25];
if(ec.indent_size >
0 && ec.tab_width ==
0) {
ec.tab_width = ec.indent_size;
}
int indent_size_set =
0;
if(ec.indent_style ==
EC_TAB) {
params[
0] = numStr;
snprintf(numStr,
24,
"%d",
1);
XtCallActionProc(window->textArea,
"set_use_tabs",
NULL, params,
1);
}
else if(ec.indent_style ==
EC_SPACE) {
int emTabDist = ec.indent_size;
if(emTabDist ==
0) {
XtVaGetValues(window->textArea, textNemulateTabs, &emTabDist,
NULL);
if(emTabDist ==
0) {
emTabDist = BufGetTabDistance(window->buffer);
}
}
params[
0] = numStr;
snprintf(numStr,
24,
"%d",
0);
XtCallActionProc(window->textArea,
"set_use_tabs",
NULL, params,
1);
params[
0] = numStr;
snprintf(numStr,
24,
"%d", emTabDist);
XtCallActionProc(window->textArea,
"set_em_tab_dist",
NULL, params,
1);
indent_size_set =
1;
}
if(ec.tab_width >
0) {
params[
0] = numStr;
snprintf(numStr,
24,
"%d", ec.tab_width);
XtCallActionProc(window->textArea,
"set_tab_dist",
NULL, params,
1);
}
if(ec.indent_size >
0 && !indent_size_set) {
params[
0] = numStr;
snprintf(numStr,
24,
"%d", ec.indent_size);
XtCallActionProc(window->textArea,
"set_em_tab_dist",
NULL, params,
1);
}
switch(ec.end_of_line) {
default:
break;
case EC_LF: {
window->fileFormat =
UNIX_FILE_FORMAT;
break;
}
case EC_CR: {
window->fileFormat =
MAC_FILE_FORMAT;
break;
}
case EC_CRLF: {
window->fileFormat =
DOS_FILE_FORMAT;
break;
}
}
}
WindowInfo *EditExistingFile(WindowInfo *inWindow,
const char *name,
const char *path,
const char *encoding,
const char *filter,
int flags,
char *geometry,
int iconic,
const char *languageMode,
int tabbed,
int bgOpen)
{
WindowInfo *window;
char fullname[
MAXPATHLEN];
window = FindWindowWithFile(name, path);
if (window !=
NULL) {
if (!bgOpen) {
if (iconic)
RaiseDocument(window);
else
RaiseDocumentWindow(window);
}
return window;
}
if (inWindow ==
NULL) {
window = CreateWindow(name, geometry, iconic);
}
else if (inWindow->filenameSet || inWindow->fileChanged ||
inWindow->macroCmdData !=
NULL) {
if (tabbed) {
window = CreateDocument(inWindow, name);
}
else {
window = CreateWindow(name, geometry, iconic);
}
}
else {
window = inWindow;
strcpy(window->path, path);
strcpy(window->filename, name);
if(encoding) {
SetEncoding(window, encoding);
}
else {
window->encoding[
0] =
'\0';
}
if (!iconic && !bgOpen) {
RaiseDocumentWindow(window);
}
}
EditorConfig ec;
if(GetEditorConfig()) {
ec = EditorConfigGet(path, name);
}
else {
memset(&ec,
0,
sizeof(EditorConfig));
}
if(ec.charset && !encoding) {
encoding = ec.charset;
if(ec.bom !=
EC_BOM_UNSET) {
window->bom = True;
}
}
if (!doOpen(window, name, path, encoding, filter, flags)) {
safeClose(window);
if(ec.charset) {
free(ec.charset);
}
return NULL;
}
forceShowLineNumbers(window);
if(window->buffer->length <
DISABLE_LANG_THRESHOLD) {
if (languageMode ==
NULL) {
DetermineLanguageMode(window, True);
}
else {
SetLanguageMode(window, FindLanguageMode(languageMode), True);
}
}
else {
SetLanguageMode(window,
PLAIN_LANGUAGE_MODE, True);
}
window->wrapModeNoneForced = False;
RefreshTabState(window);
SortTabBar(window);
ShowTabBar(window, GetShowTabBar(window));
if (!bgOpen)
RaiseDocument(window);
UpdateWindowTitle(window);
UpdateWindowReadOnly(window);
UpdateStatsLine(window);
strcpy(fullname, path);
strcat(fullname, name);
if(GetPrefAlwaysCheckRelTagsSpecs())
AddRelTagsFile(GetPrefTagFile(), path,
TAG);
AddToPrevOpenMenu(fullname);
if(ec.found) {
ApplyEditorConfig(window, ec);
}
if(ec.charset) {
free(ec.charset);
}
return window;
}
void RevertToSaved(WindowInfo *window,
char *newEncoding)
{
char name[
MAXPATHLEN], path[
MAXPATHLEN];
char *encoding;
int i;
int insertPositions[
MAX_PANES], topLines[
MAX_PANES];
int horizOffsets[
MAX_PANES];
int openFlags =
0;
Widget text;
if (!window->filenameSet)
{
DialogF(
DF_WARN, window->shell,
1,
"Error",
"Window ''%s'' was never saved, can''t re-read",
"OK",
window->filename);
return;
}
for (i=
0; i<=window->nPanes; i++) {
text = i==
0 ? window->textArea : window->textPanes[i-
1];
insertPositions[i] = TextGetCursorPos(text);
TextGetScroll(text, &topLines[i], &horizOffsets[i]);
}
strcpy(name, window->filename);
strcpy(path, window->path);
if(newEncoding) {
encoding = newEncoding;
}
else if(window->encoding[
0] !=
'\0') {
encoding = window->encoding;
}
else {
encoding =
NULL;
}
RemoveBackupFile(window);
ClearUndoList(window);
openFlags |=
IS_USER_LOCKED(window->lockReasons) && !
IS_ENCODING_LOCKED(window->lockReasons) ?
PREF_READ_ONLY :
0;
if (!doOpen(window, name, path, encoding, window->filter, openFlags)) {
if (!window->fileMissing)
safeClose(window);
else {
window->lastModTime=
0;
window->fileMissing=
FALSE;
}
return;
}
forceShowLineNumbers(window);
UpdateWindowTitle(window);
UpdateWindowReadOnly(window);
for (i=
0; i<=window->nPanes; i++) {
text = i==
0 ? window->textArea : window->textPanes[i-
1];
TextSetCursorPos(text, insertPositions[i]);
TextSetScroll(text, topLines[i], horizOffsets[i]);
}
}
static void safeClose(WindowInfo *window)
{
WindowInfo* p = WindowList;
while(p) {
if (p == window) {
CloseWindow(window);
return;
}
p = p->next;
}
}
static char bom_utf8[
3] = { (
char)0xEF, (
char)0xBB, (
char)0xBF };
static char bom_utf16be[
2] = { (
char)0xFE, (
char)0xFF };
static char bom_utf16le[
2] = { (
char)0xFF, (
char)0xFE };
static char bom_utf32be[
4] = { (
char)
0, (
char)
0, (
char)0xFE, (
char)0xFF };
static char bom_utf32le[
4] = { (
char)0xFF, (
char)0xFE, (
char)
0, (
char)
0 };
static char bom_gb18030[
4] = { (
char)0x84, (
char)0x31, (
char)0x95, (
char)0x33 };
static char bom_utfebcdic[
4] = { (
char)0xDD, (
char)0x73, (
char)0x66, (
char)0x73 };
typedef size_t(*ConvertFunc)(
iconv_t,
char **,
size_t *,
char **,
size_t *);
size_t copyBytes(
iconv_t cd,
char **inbuf,
size_t *inbytesleft,
char **outbuf,
size_t *outbytesleft)
{
if(!inbuf || !outbuf) {
return 0;
}
size_t in = *inbytesleft;
size_t out = *outbytesleft;
size_t cp = in > out ? out : in;
memcpy(*outbuf, *inbuf, cp);
*inbuf = (*inbuf) + cp;
*outbuf = (*outbuf) + cp;
*inbytesleft = in - cp;
*outbytesleft = out - cp;
return 0;
}
#define IO_BUFSIZE 2048
static int doOpen(WindowInfo *window,
const char *name,
const char *path,
const char *encoding,
const char *filter_name,
int flags)
{
char fullname[
MAXPATHLEN];
int resp;
char encoding_buffer[
MAX_ENCODING_LENGTH];
if(encoding == window->encoding) {
size_t enclen = strlen(window->encoding);
if(enclen >
MAX_ENCODING_LENGTH) {
enclen =
MAX_ENCODING_LENGTH-
1;
}
memcpy(encoding_buffer, window->encoding, enclen);
encoding_buffer[enclen] =
0;
encoding = encoding_buffer;
}
CLEAR_ALL_LOCKS(window->lockReasons);
size_t name_len = strlen(name);
size_t path_len = strlen(path);
if(name_len + path_len >=
MAXPATHLEN-
1) {
window->filenameSet =
FALSE;
DialogF(
DF_ERR, window->shell,
1,
"Error opening File",
"Path too long",
"OK");
window->filenameSet =
TRUE;
return False;
}
memcpy(window->filename, name, name_len+
1);
memcpy(window->path, path, path_len+
1);
window->encoding[
0] =
'\0';
window->filenameSet =
TRUE;
window->fileMissing =
TRUE;
memcpy(fullname, path, path_len);
memcpy(fullname+path_len, name, name_len+
1);
FileContent content;
if(GetFileContent(window->shell, fullname, encoding, filter_name, &content)) {
if(content.err ==
ENOENT && flags &
CREATE) {
if (!(flags &
SUPPRESS_CREATE_WARN)) {
RaiseShellWindow(window->shell, False);
if (WindowList == window && window->next ==
NULL) {
resp = DialogF(
DF_WARN, window->shell,
3,
"New File",
"Can''t open %s:\n%s",
"New File",
"Cancel",
"Exit NEdit", fullname, strerror(content.err));
}
else {
resp = DialogF(
DF_WARN, window->shell,
2,
"New File",
"Can''t open %s:\n%s",
"New File",
"Cancel", fullname,
strerror(content.err));
}
if (resp ==
2) {
return FALSE;
}
else if (resp ==
3) {
exit(
EXIT_SUCCESS);
}
}
int fd;
if ((fd = creat(fullname,
0666)) == -
1) {
DialogF(
DF_ERR, window->shell,
1,
"Error creating File",
"Can''t create %s:\n%s",
"OK", fullname, errorString());
return FALSE;
}
else {
close(fd);
remove(fullname);
}
SetWindowModified(window,
FALSE);
if ((flags &
PREF_READ_ONLY) !=
0) {
SET_USER_LOCKED(window->lockReasons,
TRUE);
}
UpdateWindowReadOnly(window);
return TRUE;
}
else if(content.isdir) {
window->filenameSet =
FALSE;
DialogF(
DF_ERR, window->shell,
1,
"Error opening File",
"Can''t open directory %s",
"OK", name);
window->filenameSet =
TRUE;
}
else if(content.isblk) {
window->filenameSet =
FALSE;
DialogF(
DF_ERR, window->shell,
1,
"Error opening File",
"Can''t open block device %s",
"OK", name);
window->filenameSet =
TRUE;
}
else if(content.allocerror) {
window->filenameSet =
FALSE;
DialogF(
DF_ERR, window->shell,
1,
"Error while opening File",
"File is too large to edit",
"OK");
window->filenameSet =
TRUE;
}
else if(content.iconverror) {
window->filenameSet =
FALSE;
char *format =
"File cannot be converted from %s to UTF8";
size_t msglen = strlen(format) + strlen(encoding) +
4;
char *msgbuf = NEditMalloc(msglen);
snprintf(msgbuf, msglen, format, encoding);
DialogF(
DF_ERR, window->shell,
1,
"Error while opening File",
msgbuf,
"OK");
NEditFree(msgbuf);
window->filenameSet =
TRUE;
}
else {
DialogF(
DF_ERR, window->shell,
1,
"Error opening File",
"Could not open %s%s:\n%s",
"OK", path, name,
strerror(content.err));
return FALSE;
}
return 0;
}
SetEncoding(window, content.encoding);
SET_ENCODING_LOCKED(window->lockReasons,
FALSE);
if(content.readonly) {
SET_PERM_LOCKED(window->lockReasons,
TRUE);
}
int show_infobar =
FALSE;
int lock_enc_error =
FALSE;
if(content.skipped >
0) {
char *lockmsg =
"";
if(GetPrefLockEncodingError()) {
lockmsg =
": file locked to prevent accidental changes";
flags = flags |
PREF_READ_ONLY;
lock_enc_error =
TRUE;
}
char msgbuf[
256];
snprintf(msgbuf,
256,
"%d non-convertible characters skipped%s", content.skipped, lockmsg);
show_infobar =
TRUE;
SetEncodingInfoBarLabel(window, msgbuf);
SetEncErrors(window, content.enc_errors, content.num_enc_errors);
}
else {
SetEncodingInfoBarLabel(window,
"No conversion errors");
SetEncErrors(window,
NULL,
0);
NEditFree(content.enc_errors);
}
SetFilter(window, filter_name);
window->bom = content.hasBOM;
window->fileFormat = content.fileFormat;
window->fileMode = content.statbuf.st_mode;
window->fileUid = content.statbuf.st_uid;
window->fileGid = content.statbuf.st_gid;
window->lastModTime = content.statbuf.st_mtime;
window->device = content.statbuf.st_dev;
window->inode = content.statbuf.st_ino;
window->fileMissing =
FALSE;
if(content.length >
DISABLE_WRAPPING_THRESHOLD) {
SetAutoWrap(window,
NO_WRAP);
window->wrapModeNoneForced = True;
}
window->ignoreModify = True;
BufSetAll(window->buffer, content.content);
window->ignoreModify = False;
if (window->buffer->length != content.length) {
if (!BufSubstituteNullChars(content.content, content.length, window->buffer)) {
resp = DialogF(
DF_ERR, window->shell,
2,
"Error while opening File",
"Too much binary data in file. You may view\n"
"it, but not modify or re-save its contents.",
"View",
"Cancel");
if (resp ==
2) {
return FALSE;
}
SET_TMBD_LOCKED(window->lockReasons,
TRUE);
for (
char *c = content.content; c < &content.content[content.length]; c++) {
if (*c ==
'\0') {
*c = (
char) 0xfe;
}
}
window->buffer->nullSubsChar = (
char) 0xfe;
}
window->ignoreModify = True;
BufSetAll(window->buffer, content.content);
window->ignoreModify = False;
}
NEditFree(content.content);
if ((flags &
PREF_READ_ONLY) !=
0) {
SET_USER_LOCKED(window->lockReasons,
TRUE);
SET_ENCODING_LOCKED(window->lockReasons, lock_enc_error);
}
if (
IS_PERM_LOCKED(window->lockReasons)) {
window->fileChanged =
FALSE;
UpdateWindowTitle(window);
}
else {
SetWindowModified(window,
FALSE);
if (
IS_ANY_LOCKED(window->lockReasons)) {
UpdateWindowTitle(window);
}
}
UpdateWindowReadOnly(window);
if(show_infobar) {
ShowEncodingInfoBar(window,
1);
}
return TRUE;
}
int GetFileContent(Widget shell,
const char *path,
const char *encoding,
const char *filter_name, FileContent *content)
{
memset(content,
0,
sizeof(FileContent));
off_t fileLen, readLen;
char *fileString, *c;
char buf[
IO_BUFSIZE];
FILE *fp =
NULL;
FileStream *stream =
NULL;;
int err;
char encoding_buffer[
MAX_ENCODING_LENGTH];
if(encoding) {
size_t enclen = strlen(encoding);
if(enclen >=
MAX_ENCODING_LENGTH) {
enclen =
MAX_ENCODING_LENGTH-
1;
}
memcpy(encoding_buffer, encoding, enclen);
encoding_buffer[enclen] =
0;
encoding = encoding_buffer;
}
else {
encoding_buffer[
0] =
0;
}
encoding = encoding_buffer[
0] !=
'\0' ? encoding_buffer :
NULL;
fp = fopen(path,
"rb+");
if(!fp) {
fp = fopen(path,
"rb");
if(fp) {
content->readonly =
1;
}
else {
content->err = errno;
return 1;
}
}
if (fstat(fileno(fp), &content->statbuf) !=
0) {
fclose(fp);
content->err = errno;
return 1;
}
if (
S_ISDIR(content->statbuf.st_mode)) {
fclose(fp);
content->isdir =
1;
return 1;
}
#ifdef S_ISBLK
if (
S_ISBLK(content->statbuf.st_mode)) {
fclose(fp);
content->isblk =
1;
return 1;
}
#endif
fileLen = content->statbuf.st_size;
IOFilter* filter = GetFilterFromName(filter_name);
char *filter_cmd =
NULL;
if(filter && filter->cmdin && strlen(filter->cmdin) >
0) {
filter_cmd = filter->cmdin;
}
stream = filestream_open_r(shell, fp, filter_cmd);
if(!encoding) {
char *xattr_charset = getEncodingAttribute(path);
if(xattr_charset) {
size_t enclen = strlen(xattr_charset);
if(enclen >=
MAX_ENCODING_LENGTH) {
enclen =
MAX_ENCODING_LENGTH-
1;
}
memcpy(encoding_buffer, xattr_charset, enclen);
encoding_buffer[enclen] =
0;
encoding = encoding_buffer;
NEditFree(xattr_charset);
}
}
int checkBOM =
1;
int checkEncoding =
0;
if(encoding) {
if(strlen(encoding) <
3) {
checkBOM =
0;
}
else {
char encpre[
4];
encpre[
0] = encoding[
0];
encpre[
1] = encoding[
1];
encpre[
2] = encoding[
2];
encpre[
3] =
0;
if(strcasecmp(encpre,
"UTF")) {
checkBOM =
0;
}
}
if(!strcasecmp(encoding,
"GB18030")) {
checkBOM =
1;
}
}
else {
encoding = GetPrefDefaultCharset();
checkEncoding =
1;
}
char *setEncoding =
NULL;
int hasBOM =
0;
if(checkBOM) {
int bom =
0;
size_t r = filestream_read(buf,
4, stream);
do {
if(r >=
4) {
bom =
4;
if(!memcmp(buf, bom_utf32be,
4)) {
setEncoding =
"UTF-32BE";
hasBOM =
TRUE;
break;
}
else if(!memcmp(buf, bom_utf32le,
4)) {
setEncoding =
"UTF-32LE";
hasBOM =
TRUE;
break;
}
else if(!memcmp(buf, bom_gb18030,
4)) {
setEncoding =
"GB18030";
hasBOM =
TRUE;
break;
}
else if(!memcmp(buf, bom_utfebcdic,
4)) {
setEncoding =
"UTF-EBCDIC";
hasBOM =
TRUE;
break;
}
}
if(r >=
3) {
bom =
3;
if(!memcmp(buf, bom_utf8,
3)) {
setEncoding =
"UTF-8";
hasBOM =
TRUE;
break;
}
}
if(r >=
2) {
bom =
2;
if(!memcmp(buf, bom_utf16be,
2)) {
setEncoding =
"UTF-16BE";
hasBOM =
TRUE;
break;
}
else if(!memcmp(buf, bom_utf16le,
2)) {
setEncoding =
"UTF-16LE";
hasBOM =
TRUE;
break;
}
}
bom =
0;
}
while (
0);
filestream_reset(stream, bom);
}
if(setEncoding) {
encoding = setEncoding;
checkEncoding =
0;
}
if(checkEncoding) {
size_t r = filestream_read(buf,
IO_BUFSIZE, stream);
const char *newEnc = DetectEncoding(buf, r, encoding);
if(newEnc && newEnc != encoding) {
encoding = newEnc;
}
filestream_reset(stream,
0);
}
size_t strAlloc = fileLen;
fileString = malloc(strAlloc +
1);
if (fileString ==
NULL) {
filestream_close(stream);
content->allocerror =
1;
return 1;
}
iconv_t ic =
NULL;
ConvertFunc strconv = copyBytes;
if(encoding) {
ic = iconv_open(
"UTF-8", encoding);
if(ic == (
iconv_t) -
1) {
filestream_close(stream);
free(fileString);
return 1;
}
strconv = (ConvertFunc)iconv;
size_t len = strlen(encoding);
if(len+
1 >=
MAX_ENCODING_LENGTH) {
fprintf(stderr,
"Error: Encoding string too large\n");
len =
MAX_ENCODING_LENGTH-
1;
}
memcpy(content->encoding, encoding, len);
content->encoding[len] =
0;
}
EncError *encErrors = NEditCalloc(
ENC_ERROR_LIST_LEN,
sizeof(EncError));
size_t numEncErrors =
0;
size_t allocEncErrors =
ENC_ERROR_LIST_LEN;
err =
0;
int skipped =
0;
size_t r =
0;
readLen =
0;
char *outStr = fileString;
size_t prev =
0;
while((r = filestream_read(buf+prev,
IO_BUFSIZE-prev, stream)) >
0 && !err) {
char *str = buf;
size_t inleft = prev + r;
size_t outleft = strAlloc - readLen;
prev =
0;
while(inleft >
0) {
size_t w = outleft;
size_t rc = strconv(ic, &str, &inleft, &outStr, &outleft);
w = w - outleft;
readLen += w;
if(rc == (
size_t)-
1) {
int extendBuf =
0;
switch(errno) {
default: err =
1; content->iconverror =
1;
break;
case EILSEQ: {
if(inleft >
0) {
if(outleft <
3) {
extendBuf =
1;
break;
}
outStr[
0] = 0xEF;
outStr[
1] = 0xBF;
outStr[
2] = 0xBD;
if(numEncErrors >= allocEncErrors) {
allocEncErrors +=
16;
encErrors = NEditRealloc(encErrors, allocEncErrors *
sizeof(EncError));
}
encErrors[numEncErrors].c = (
unsigned char)*str;
encErrors[numEncErrors].pos = outStr - fileString;
numEncErrors++;
outStr +=
3;
outleft -=
3;
readLen +=
3;
str++;
inleft--;
skipped++;
}
break;
}
case EINVAL: {
memcpy(buf, str, inleft);
prev = inleft;
inleft =
0;
break;
}
case E2BIG: {
extendBuf =
1;
break;
}
}
if(extendBuf) {
strAlloc +=
512;
size_t outpos = outStr - fileString;
fileString = realloc(fileString, strAlloc +
1);
if(!fileString) {
err =
1;
break;
}
outStr = fileString + outpos;
outleft = strAlloc - readLen;
}
if(err) {
break;
}
}
}
}
if (filestream_close(stream) !=
0) {
content->closeerror =
1;
content->err = errno;
}
fileString[readLen] =
0;
if(ic) {
iconv_close(ic);
}
content->hasBOM = hasBOM;
content->skipped = skipped;
if (GetPrefForceOSConversion()) {
content->fileFormat = FormatOfFile(fileString);
int rLen = readLen;
if (content->fileFormat ==
DOS_FILE_FORMAT) {
ConvertFromDosFileString(fileString, &rLen,
NULL);
}
else if (content->fileFormat ==
MAC_FILE_FORMAT) {
ConvertFromMacFileString(fileString, rLen);
}
readLen = rLen;
}
if(err) {
free(fileString);
free(encErrors);
}
else {
content->content = fileString;
content->length = readLen;
content->enc_errors = encErrors;
content->num_enc_errors = numEncErrors;
}
return err;
}
int IncludeFile(WindowInfo *window,
const char *name,
const char *encoding,
const char *filter_name)
{
int err =
0;
FileContent content;
if(GetFileContent(window->shell, name, encoding, filter_name, &content)) {
int filenameSet = window->filenameSet;
if(content.isdir) {
window->filenameSet =
FALSE;
DialogF(
DF_ERR, window->shell,
1,
"Error opening File",
"Can''t open directory %s",
"OK", name);
window->filenameSet = filenameSet;
}
else if(content.isblk) {
window->filenameSet =
FALSE;
DialogF(
DF_ERR, window->shell,
1,
"Error opening File",
"Can''t open block device %s",
"OK", name);
window->filenameSet = filenameSet;
}
else if(content.allocerror) {
window->filenameSet =
FALSE;
DialogF(
DF_ERR, window->shell,
1,
"Error while opening File",
"File is too large to include",
"OK");
window->filenameSet = filenameSet;
}
else if(content.iconverror) {
window->filenameSet =
FALSE;
char *format =
"File cannot be converted from %s to UTF8";
size_t msglen = strlen(format) + strlen(encoding) +
4;
char *msgbuf = NEditMalloc(msglen);
snprintf(msgbuf, msglen, format, encoding);
DialogF(
DF_ERR, window->shell,
1,
"Error while opening File",
msgbuf,
"OK");
NEditFree(msgbuf);
window->filenameSet = filenameSet;
}
else {
window->filenameSet =
FALSE;
DialogF(
DF_ERR, window->shell,
1,
"Error while opening File",
"Unknown error",
"OK");
window->filenameSet = filenameSet;
}
return 0;
}
if(content.skipped >
0) {
int btn = DialogF(
DF_WARN, window->shell,
2,
"Encoding warning",
"%d non-convertible characters skipped\n"
"Include anyway?",
"NO",
"YES",
content.skipped);
if(btn ==
1) {
err =
TRUE;
}
}
if(!err) {
if (!BufSubstituteNullChars(content.content, content.length, window->buffer))
{
DialogF(
DF_ERR, window->shell,
1,
"Error opening File",
"Too much binary data in file",
"OK");
}
else {
if (window->buffer->primary.selected) {
BufReplaceSelected(window->buffer, content.content);
}
else {
BufInsert(window->buffer, TextGetCursorPos(window->lastFocus), content.content);
}
}
}
NEditFree(content.content);
NEditFree(content.enc_errors);
return TRUE;
}
int CloseAllFilesAndWindows(
void)
{
while (WindowList->next !=
NULL ||
WindowList->filenameSet || WindowList->fileChanged) {
if (WindowList == MacroRunWindow() && WindowList->next !=
NULL) {
if (!CloseAllDocumentInWindow(WindowList->next)) {
return False;
}
}
else {
if (!CloseAllDocumentInWindow(WindowList)) {
return False;
}
}
}
return TRUE;
}
int CloseFileAndWindow(WindowInfo *window,
int preResponse)
{
int response, stat;
if (window->fileChanged)
RaiseDocumentWindow(window);
if (!window->fileChanged &&
((!window->fileMissing && window->lastModTime >
0) ||
(window->fileMissing && window->lastModTime ==
0) ||
!GetPrefWarnFileMods()))
{
CloseWindow(window);
}
else
{
if (preResponse ==
PROMPT_SBC_DIALOG_RESPONSE)
{
response = DialogF(
DF_WARN, window->shell,
3,
"Save File",
"Save %s before closing?",
"Yes",
"No",
"Cancel", window->filename);
}
else
{
response = preResponse;
}
if (response ==
YES_SBC_DIALOG_RESPONSE)
{
stat = SaveWindow(window);
if (stat)
{
CloseWindow(window);
}
else
{
return FALSE;
}
}
else if (response ==
NO_SBC_DIALOG_RESPONSE)
{
RemoveBackupFile(window);
CloseWindow(window);
}
else
{
return FALSE;
}
}
return TRUE;
}
int SaveWindow(WindowInfo *window)
{
int stat;
CheckForChangesToFile(window);
if ( (!window->fileChanged && !window->fileMissing &&
window->lastModTime >
0) ||
IS_ANY_LOCKED_IGNORING_PERM(window->lockReasons))
return TRUE;
if (!window->filenameSet)
return SaveWindowAs(window,
NULL);
if (GetPrefWarnFileMods() && fileWasModifiedExternally(window))
{
stat = DialogF(
DF_WARN, window->shell,
2,
"Save File",
"%s has been modified by another program.\n\n"
"Continuing this operation will overwrite any external\n"
"modifications to the file since it was opened in NEdit,\n"
"and your work or someone else''s may potentially be lost.\n\n"
"To preserve the modified file, cancel this operation and\n"
"use Save As... to save this file under a different name,\n"
"or Revert to Saved to revert to the modified version.",
"Continue",
"Cancel", window->filename);
if (stat ==
2)
{
window->lastModTime =
0;
window->fileMissing =
FALSE;
return FALSE;
}
}
if (writeBckVersion(window))
return FALSE;
stat = doSave(window,
0);
if (stat)
RemoveBackupFile(window);
return stat;
}
int SaveWindowAs(WindowInfo *window, FileSelection *file)
{
int response, retVal, fileFormat;
char filename[
MAXPATHLEN], pathname[
MAXPATHLEN];
WindowInfo *otherWindow;
char fullname[
MAXPATHLEN];
FileSelection newFile;
if (!file) {
memset(&newFile,
0,
sizeof(FileSelection));
newFile.extraoptions = True;
newFile.encoding = strlen(window->encoding) >
0 ? window->encoding :
NULL;
newFile.format = window->fileFormat;
newFile.writebom = window->bom;
response = PromptForNewFile(window,
"Save File As", &newFile, &fileFormat);
if (response !=
GFN_OK)
return FALSE;
window->bom = newFile.writebom;
window->fileFormat = newFile.format;
SetFilter(window, newFile.filter);
size_t pathlen = strlen(newFile.path);
if(pathlen >=
MAXPATHLEN) {
fprintf(stderr,
"Error: Path too long\n");
NEditFree(newFile.path);
return FALSE;
}
memcpy(fullname, newFile.path, pathlen);
fullname[pathlen] =
'\0';
NEditFree(newFile.path);
if(newFile.encoding) {
if(!strcmp(newFile.encoding,
"UTF-8")) {
window->encoding[
0] =
'\0';
}
else {
SetEncoding(window, newFile.encoding);
}
}
file = &newFile;
}
else
{
strcpy(fullname, file->path);
if(!strcmp(file->encoding,
"UTF-8")) {
window->encoding[
0] =
'\0';
}
else {
SetEncoding(window, file->encoding);
}
window->bom = file->writebom;
window->fileFormat = file->format;
SetFilter(window, file->filter);
}
if (
1 == NormalizePathname(fullname))
{
return False;
}
if (file->addwrap)
addWrapNewlines(window);
if (ParseFilename(fullname, filename, pathname) !=
0) {
return FALSE;
}
if (!strcmp(window->filename, filename) &&
!strcmp(window->path, pathname)) {
if (writeBckVersion(window))
return FALSE;
return doSave(window, file->setxattr);
}
otherWindow = FindWindowWithFile(filename, pathname);
if (otherWindow !=
NULL)
{
response = DialogF(
DF_WARN, window->shell,
2,
"File open",
"%s is open in another XNEdit window",
"Cancel",
"Close Other Window", filename);
if (response ==
1)
{
return FALSE;
}
if (otherWindow == FindWindowWithFile(filename, pathname))
{
if (!CloseFileAndWindow(otherWindow,
PROMPT_SBC_DIALOG_RESPONSE))
{
return FALSE;
}
}
}
DeleteFileClosedProperty(window);
RemoveBackupFile(window);
strcpy(window->filename, filename);
strcpy(window->path, pathname);
window->fileMode =
0;
window->fileUid =
0;
window->fileGid =
0;
CLEAR_ALL_LOCKS(window->lockReasons);
retVal = doSave(window, file->setxattr);
UpdateWindowReadOnly(window);
RefreshTabState(window);
AddToPrevOpenMenu(fullname);
if (
PLAIN_LANGUAGE_MODE == window->languageMode || window->filenameSet) {
DetermineLanguageMode(window, False);
}
window->filenameSet = True;
UpdateWindowTitle(window);
UpdateStatsLine(window);
SortTabBar(window);
return retVal;
}
static int getBOM(
char *encoding,
char **bom)
{
int len =
0;
*bom =
NULL;
if(!encoding || strlen(encoding) ==
0 || !strcasecmp(encoding,
"UTF-8")) {
*bom = bom_utf8;
len =
3;
}
else if(!strcasecmp(encoding,
"UTF-16BE")) {
*bom = bom_utf16be;
len =
2;
}
else if(!strcasecmp(encoding,
"UTF-16LE")) {
*bom = bom_utf16le;
len =
2;
}
else if(!strcasecmp(encoding,
"UTF-32BE")) {
*bom = bom_utf32be;
len =
4;
}
else if(!strcasecmp(encoding,
"UTF-32LE")) {
*bom = bom_utf32le;
len =
4;
}
else if(!strcasecmp(encoding,
"GB18030")) {
*bom = bom_gb18030;
len =
4;
}
else if(!strcasecmp(encoding,
"UTF-EBCDIC")) {
*bom = bom_utfebcdic;
len =
4;
}
return len;
}
static int doSave(WindowInfo *window, Boolean setEncAttr)
{
char *fileString =
NULL;
char fullname[
MAXPATHLEN];
struct stat statbuf;
FILE *fp;
int fileLen, result;
iconv_t ic =
NULL;
ConvertFunc strconv = copyBytes;
if(strlen(window->encoding) >
0) {
ic = iconv_open(window->encoding,
"UTF-8");
if(ic == (
iconv_t) -
1) {
DialogF(
DF_ERR, window->shell,
1,
"Error saving File",
"The text cannot be converted to %s",
"OK", window->encoding);
return FALSE;
}
strconv = (ConvertFunc)iconv;
}
strcpy(fullname, window->path);
strcat(fullname, window->filename);
if ((
0 == getuid())
&& (
0 == stat(fullname, &statbuf))
&& !(statbuf.st_mode & (
S_IWUSR |
S_IWGRP |
S_IWOTH)))
{
result = DialogF(
DF_WARN, window->shell,
2,
"Writing Read-only File",
"File ''%s'' is marked as read-only.\n"
"Do you want to save anyway?",
"Save",
"Cancel", window->filename);
if (
1 != result)
{
return True;
}
}
if (BufGetCharacter(window->buffer, window->buffer->length -
1) !=
'\n'
&& window->buffer->length !=
0
&& GetPrefAppendLF())
{
BufInsert(window->buffer, window->buffer->length,
"\n");
}
fp = fopen(fullname,
"wb");
if (fp ==
NULL)
{
result = DialogF(
DF_WARN, window->shell,
2,
"Error saving File",
"Unable to save %s:\n%s\n\nSave as a new file?",
"Save As...",
"Cancel",
window->filename, errorString());
if (result ==
1)
{
return SaveWindowAs(window,
NULL);
}
return FALSE;
}
fileString = BufGetAll(window->buffer);
fileLen = window->buffer->length;
BufUnsubstituteNullChars(fileString, window->buffer);
if (window->fileFormat ==
DOS_FILE_FORMAT)
{
if (!ConvertToDosFileString(&fileString, &fileLen))
{
DialogF(
DF_ERR, window->shell,
1,
"Out of Memory",
"Out of memory! Try\nsaving in Unix format",
"OK");
fclose(fp);
return FALSE;
}
}
else if (window->fileFormat ==
MAC_FILE_FORMAT)
{
ConvertToMacFileString(fileString, fileLen);
}
IOFilter *filter = GetFilterFromName(window->filter);
char *filter_cmd =
NULL;
if(filter && filter->cmdout && strlen(filter->cmdout) >
0) {
filter_cmd = filter->cmdout;
}
FileStream *stream = filestream_open_w(window->shell, fp, filter_cmd);
if(window->bom) {
char *bom;
int bomLen = getBOM(window->encoding, &bom);
if(bomLen >
0) {
if(filestream_write(bom, bomLen, stream) != bomLen) {
fileLen =
0;
}
}
}
int skipped =
0;
int nonreversible =
0;
int unerr =
0;
char buf[
IO_BUFSIZE];
char *in = fileString;
size_t inleft = fileLen;
while(in) {
char *out = buf;
size_t outleft =
IO_BUFSIZE;
size_t w = outleft;
if (inleft ==
0) {
in =
NULL;
}
size_t rc = strconv(ic, &in, &inleft, &out, &outleft);
w -= outleft;
if(w >
0) {
filestream_write(buf, w, stream);
}
if(rc == (
size_t)-
1) {
size_t skip;
switch (errno) {
case EILSEQ:
case EINVAL:
skip = Utf8CharLen((
const unsigned char*)in);
++skipped;
in += skip;
if(inleft >= skip) {
inleft -= skip;
}
else {
inleft =
0;
}
break;
case E2BIG:
break;
default:
++unerr;
}
}
if (inleft ==
0) {
nonreversible += rc;
}
}
unsigned int eresp =
0;
if (skipped >
0 || nonreversible >
0 || unerr >
0) {
eresp = DialogF(
DF_WARN, window->shell,
2,
"Encoding warning",
"%d non-convertible characters skipped\n"
"%d non-reversible characters encountered\n"
"%d unknown errors occurred\n"
"Save anyway?",
"YES",
"NO",
skipped, nonreversible, unerr);
}
if(ic) {
iconv_close(ic);
}
if (ferror(fp))
{
DialogF(
DF_ERR, window->shell,
1,
"Error saving File",
"%s not saved:\n%s",
"OK", window->filename, errorString());
}
if (ferror(fp) || eresp ==
2) {
filestream_close(stream);
remove(fullname);
NEditFree(fileString);
return FALSE;
}
if(setEncAttr) {
size_t encLen = strlen(window->encoding);
char *encStr;
char *encCopy =
NULL;
if(encLen ==
0) {
encStr =
"utf-8";
encLen =
5;
}
else {
encCopy = NEditStrdup(window->encoding);
for(
int i=
0;i<encLen;i++) {
encCopy[i] = tolower(encCopy[i]);
}
encStr = encCopy;
}
if(xattr_set(fullname,
"charset", encStr, encLen)) {
perror(
"xattr_set failed");
}
if(encCopy) {
NEditFree(encCopy);
}
}
else {
ssize_t len =
0;
char *fileAttr = xattr_get(fullname,
"charset", &len);
if(fileAttr) {
size_t winEncLen = strlen(window->encoding);
size_t cmpLen = winEncLen > len ? len : winEncLen;
if(len != winEncLen || memcmp(fileAttr, window->encoding, cmpLen)) {
if(xattr_remove(fullname,
"charset")) {
DialogF(
DF_ERR, window->shell,
1,
"Error saving File",
"Cannot remove previous charset attribute");
}
}
free(fileAttr);
}
}
if (filestream_close(stream) !=
0)
{
DialogF(
DF_ERR, window->shell,
1,
"Error closing File",
"Error closing file:\n%s",
"OK", errorString());
NEditFree(fileString);
return FALSE;
}
NEditFree(fileString);
SetWindowModified(window,
FALSE);
if (stat(fullname, &statbuf) ==
0) {
window->lastModTime = statbuf.st_mtime;
window->fileMissing =
FALSE;
window->device = statbuf.st_dev;
window->inode = statbuf.st_ino;
}
else {
window->lastModTime =
0;
window->fileMissing =
TRUE;
window->device =
0;
window->inode =
0;
}
return TRUE;
}
int WriteBackupFile(WindowInfo *window)
{
char *fileString =
NULL;
char name[
MAXPATHLEN];
FILE *fp;
int fd, fileLen;
backupFileName(window, name,
sizeof(name));
remove(name);
if ((fd = open(name,
O_CREAT|
O_EXCL|
O_WRONLY,
S_IRUSR |
S_IWUSR)) <
0
|| (fp = fdopen(fd,
"w")) ==
NULL)
{
DialogF(
DF_WARN, window->shell,
1,
"Error writing Backup",
"Unable to save backup for %s:\n%s\n"
"Automatic backup is now off",
"OK", window->filename,
errorString());
window->autoSave =
FALSE;
SetToggleButtonState(window, window->autoSaveItem,
FALSE,
FALSE);
return FALSE;
}
fileString = BufGetAll(window->buffer);
fileLen = window->buffer->length;
BufUnsubstituteNullChars(fileString, window->buffer);
if (fileLen !=
0 && fileString[fileLen-
1] !=
'\n')
fileString[fileLen++] =
'\n';
fwrite(fileString,
sizeof(
char), fileLen, fp);
if (ferror(fp))
{
DialogF(
DF_ERR, window->shell,
1,
"Error saving Backup",
"Error while saving backup for %s:\n%s\n"
"Automatic backup is now off",
"OK", window->filename,
errorString());
fclose(fp);
remove(name);
NEditFree(fileString);
window->autoSave =
FALSE;
return FALSE;
}
if (fclose(fp) !=
0) {
NEditFree(fileString);
return FALSE;
}
NEditFree(fileString);
return TRUE;
}
void RemoveBackupFile(WindowInfo *window)
{
char name[
MAXPATHLEN];
if (window->autoSave ==
FALSE)
return;
backupFileName(window, name,
sizeof(name));
remove(name);
}
static void backupFileName(WindowInfo *window,
char *name,
size_t len)
{
char bckname[
MAXPATHLEN];
if (window->filenameSet)
{
sprintf(name,
"%s~%s", window->path, window->filename);
}
else
{
strcpy(bckname,
"~");
strncat(bckname, window->filename,
MAXPATHLEN -
1);
PrependHome(bckname, name, len);
}
}
static int writeBckVersion(WindowInfo *window)
{
char fullname[
MAXPATHLEN], bckname[
MAXPATHLEN];
struct stat statbuf;
int in_fd, out_fd;
char *io_buffer;
#define IO_BUFFER_SIZE ((
size_t)(
1024*
1024))
if (!window->saveOldVersion) {
return False;
}
strcpy(fullname, window->path);
strcat(fullname, window->filename);
if ((strlen(fullname) +
5) > (
size_t)
MAXPATHLEN) {
return bckError(window,
"file name too long", window->filename);
}
if(snprintf(bckname,
MAXPATHLEN,
"%s.bck", fullname) >=
MAXPATHLEN) {
return FALSE;
}
remove(bckname);
in_fd = open(fullname,
O_RDONLY);
if (in_fd<
0) {
return FALSE;
}
if (fstat(in_fd, &statbuf) !=
0) {
close(in_fd);
return FALSE;
}
out_fd = open(bckname,
O_CREAT|
O_EXCL|
O_TRUNC|
O_WRONLY,
S_IRUSR |
S_IWUSR);
if (out_fd <
0) {
close(in_fd);
return bckError(window,
"Error open backup file", bckname);
}
if (fchmod(out_fd, statbuf.st_mode) !=
0) {
close(in_fd);
close(out_fd);
remove(bckname);
return bckError(window,
"fchmod() failed", bckname);
}
io_buffer = (
char*) malloc(
IO_BUFFER_SIZE);
if (
NULL == io_buffer) {
close(in_fd);
close(out_fd);
remove(bckname);
return bckError(window,
"out of memory", bckname);
}
for(;;) {
ssize_t bytes_read;
ssize_t bytes_written;
bytes_read = read(in_fd, io_buffer,
IO_BUFFER_SIZE);
if (bytes_read <
0) {
close(in_fd);
close(out_fd);
remove(bckname);
free(io_buffer);
return bckError(window,
"read() error", window->filename);
}
if (
0 == bytes_read) {
break;
}
bytes_written = write(out_fd, io_buffer, (
size_t) bytes_read);
if (bytes_written != bytes_read) {
close(in_fd);
close(out_fd);
remove(bckname);
free(io_buffer);
return bckError(window, errorString(), bckname);
}
}
close(in_fd);
close(out_fd);
free(io_buffer);
return FALSE;
}
static int bckError(WindowInfo *window,
const char *errString,
const char *file)
{
int resp;
resp = DialogF(
DF_ERR, window->shell,
3,
"Error writing Backup",
"Couldn''t write .bck (last version) file.\n%s: %s",
"Cancel Save",
"Turn off Backups",
"Continue", file, errString);
if (resp ==
1)
return TRUE;
if (resp ==
2) {
window->saveOldVersion =
FALSE;
SetToggleButtonState(window, window->saveLastItem,
FALSE,
FALSE);
}
return FALSE;
}
void PrintWindow(WindowInfo *window,
int selectedOnly)
{
textBuffer *buf = window->buffer;
selection *sel = &buf->primary;
char *fileString =
NULL;
int fileLen;
if (selectedOnly) {
if (!sel->selected) {
XBell(TheDisplay,
0);
return;
}
if (sel->rectangular) {
fileString = BufGetSelectionText(buf);
fileLen = strlen(fileString);
}
else
fileString = TextGetWrapped(window->textArea, sel->start, sel->end,
&fileLen);
}
else
fileString = TextGetWrapped(window->textArea,
0, buf->length, &fileLen);
BufUnsubstituteNullChars(fileString, buf);
if (fileLen !=
0 && fileString[fileLen-
1] !=
'\n')
fileString[fileLen++] =
'\n';
PrintString(fileString, fileLen, window->shell, window->filename);
NEditFree(fileString);
}
void PrintString(
const char *string,
int length, Widget parent,
const char *jobName)
{
char tmpFileName[L_tmpnam];
FILE *fp;
int fd;
#ifdef __GLIBC__
mkstemp(tmpFileName);
#else
tmpnam(tmpFileName);
#endif
if ((fd = open(tmpFileName,
O_CREAT|
O_EXCL|
O_WRONLY,
S_IRUSR |
S_IWUSR)) <
0 || (fp = fdopen(fd,
"w")) ==
NULL)
{
DialogF(
DF_WARN, parent,
1,
"Error while Printing",
"Unable to write file for printing:\n%s",
"OK",
errorString());
return;
}
fwrite(string,
sizeof(
char), length, fp);
if (ferror(fp))
{
DialogF(
DF_ERR, parent,
1,
"Error while Printing",
"%s not printed:\n%s",
"OK", jobName, errorString());
fclose(fp);
remove(tmpFileName);
return;
}
if (fclose(fp) !=
0)
{
DialogF(
DF_ERR, parent,
1,
"Error while Printing",
"Error closing temp. print file:\n%s",
"OK",
errorString());
remove(tmpFileName);
return;
}
PrintFile(parent, tmpFileName, jobName);
remove(tmpFileName);
return;
}
int PromptForExistingFile(WindowInfo *window,
char *prompt, FileSelection *file)
{
char *savedDefaultDir;
int retVal;
savedDefaultDir = GetFileDialogDefaultDirectory();
if (*window->path !=
'\0')
SetFileDialogDefaultDirectory(window->path);
retVal = GetExistingFilename(window->shell, prompt, file);
if (retVal !=
GFN_OK)
SetFileDialogDefaultDirectory(savedDefaultDir);
NEditFree(savedDefaultDir);
return retVal;
}
int PromptForNewFile(WindowInfo *window,
char *prompt, FileSelection *file,
int *fileFormat)
{
int retVal;
char *savedDefaultDir;
*fileFormat = window->fileFormat;
savedDefaultDir = GetFileDialogDefaultDirectory();
if (*window->path !=
'\0')
SetFileDialogDefaultDirectory(window->path);
char *prevPath =
NULL;
if(window->path[
0] !=
'\0' && window->filename[
0] !=
'\0' && window->filenameSet) {
size_t plen = strlen(window->path);
size_t nlen = strlen(window->filename);
prevPath = NEditMalloc(plen + nlen +
2);
memcpy(prevPath, window->path, plen);
if(window->path[plen-
1] !=
'/') {
prevPath[plen] =
'/';
plen++;
}
memcpy(prevPath+plen, window->filename, nlen);
prevPath[plen+nlen] =
'\0';
}
file->path = prevPath;
retVal = GetNewFilename(window->shell, prompt, file,
"");
if(prevPath) {
NEditFree(prevPath);
}
if (retVal !=
GFN_OK)
SetFileDialogDefaultDirectory(savedDefaultDir);
NEditFree(savedDefaultDir);
return retVal;
}
void UniqueUntitledName(
char *name)
{
WindowInfo *w;
int i;
for (i=
0; i<
INT_MAX; i++) {
if (i ==
0)
sprintf(name,
"Untitled");
else
sprintf(name,
"Untitled_%d", i);
for (w=WindowList; w!=
NULL; w=w->next)
if (!strcmp(w->filename, name))
break;
if (w ==
NULL)
break;
}
}
static void modifiedWindowDestroyedCB(Widget w, XtPointer clientData,
XtPointer callData)
{
*(Bool*)clientData =
TRUE;
}
void CheckForChangesToFile(WindowInfo *window)
{
static WindowInfo* lastCheckWindow =
NULL;
static Time lastCheckTime =
0;
char fullname[
MAXPATHLEN];
struct stat statbuf;
Time timestamp;
FILE *fp;
int resp, silent =
0;
XWindowAttributes winAttr;
Boolean windowIsDestroyed = False;
if(!window->filenameSet)
return;
timestamp = XtLastTimestampProcessed(XtDisplay(window->shell));
if (window == lastCheckWindow &&
timestamp - lastCheckTime <
MOD_CHECK_INTERVAL)
return;
lastCheckWindow = window;
lastCheckTime = timestamp;
if (!IsTopDocument(window))
silent =
1;
else {
XGetWindowAttributes(XtDisplay(window->shell),
XtWindow(window->shell),
&winAttr);
if (winAttr.map_state != IsViewable)
silent =
1;
}
strcpy(fullname, window->path);
strcat(fullname, window->filename);
if (stat(fullname, &statbuf) !=
0) {
if (window->fileMissing || silent) {
return;
}
window->fileMissing =
TRUE;
window->lastModTime =
1;
window->device =
0;
window->inode =
0;
if (GetPrefWarnFileMods()) {
char* title;
char* body;
XUngrabPointer(XtDisplay(window->shell), timestamp);
XtAddCallback(window->shell, XmNdestroyCallback,
modifiedWindowDestroyedCB, &windowIsDestroyed);
switch (errno) {
case ENOENT:
title =
"File not Found";
body =
"File ''%s'' (or directory in its path)\n"
"no longer exists.\n"
"Another program may have deleted or moved it.";
resp = DialogF(
DF_ERR, window->shell,
2, title, body,
"Save",
"Cancel", window->filename);
break;
case EACCES:
title =
"Permission Denied";
body =
"You no longer have access to file ''%s''.\n"
"Another program may have changed the permissions of\n"
"one of its parent directories.";
resp =
1 + DialogF(
DF_ERR, window->shell,
1, title, body,
"Cancel", window->filename);
break;
default:
title =
"File not Accessible";
body =
"Error while checking the status of file ''%s'':\n"
" ''%s''\n"
"Please make sure that no data is lost before closing\n"
"this window.";
resp = DialogF(
DF_ERR, window->shell,
2, title, body,
"Save",
"Cancel", window->filename,
errorString());
break;
}
if (!windowIsDestroyed) {
XtRemoveCallback(window->shell, XmNdestroyCallback,
modifiedWindowDestroyedCB, &windowIsDestroyed);
}
switch (resp) {
case 1:
SaveWindow(window);
break;
}
}
if (!windowIsDestroyed) {
SET_PERM_LOCKED(window->lockReasons, False);
UpdateWindowTitle(window);
UpdateWindowReadOnly(window);
}
return;
}
if (window->fileMode != statbuf.st_mode ||
window->fileUid != statbuf.st_uid ||
window->fileGid != statbuf.st_gid) {
window->fileMode = statbuf.st_mode;
window->fileUid = statbuf.st_uid;
window->fileGid = statbuf.st_gid;
if ((fp = fopen(fullname,
"r")) !=
NULL) {
int readOnly;
fclose(fp);
#ifndef DONT_USE_ACCESS
readOnly = access(fullname,
W_OK) !=
0;
#else
if (((fp = fopen(fullname,
"r+")) !=
NULL)) {
readOnly =
FALSE;
fclose(fp);
}
else
readOnly =
TRUE;
#endif
if (
IS_PERM_LOCKED(window->lockReasons) != readOnly) {
SET_PERM_LOCKED(window->lockReasons, readOnly);
UpdateWindowTitle(window);
UpdateWindowReadOnly(window);
}
}
}
if (!silent &&
((window->lastModTime !=
0 &&
window->lastModTime != statbuf.st_mtime) ||
window->fileMissing) ){
window->lastModTime =
0;
window->fileMissing =
FALSE;
if (!GetPrefWarnFileMods())
return;
if (GetPrefWarnRealFileMods() &&
!cmpWinAgainstFile(window, fullname)) {
window->lastModTime = statbuf.st_mtime;
return;
}
XUngrabPointer(XtDisplay(window->shell), timestamp);
if (window->fileChanged)
resp = DialogF(
DF_WARN, window->shell,
2,
"File modified externally",
"%s has been modified by another program. Reload?\n\n"
"WARNING: Reloading will discard changes made in this\n"
"editing session!",
"Reload",
"Cancel", window->filename);
else
resp = DialogF(
DF_WARN, window->shell,
2,
"File modified externally",
"%s has been modified by another\nprogram. Reload?",
"Reload",
"Cancel", window->filename);
if (resp ==
1)
RevertToSaved(window,
NULL);
}
}
static int fileWasModifiedExternally(WindowInfo *window)
{
char fullname[
MAXPATHLEN];
struct stat statbuf;
if(!window->filenameSet)
return FALSE;
strcpy(fullname, window->path);
strcat(fullname, window->filename);
if (stat(fullname, &statbuf) !=
0)
return FALSE;
if (window->lastModTime == statbuf.st_mtime)
return FALSE;
if (GetPrefWarnRealFileMods() &&
!cmpWinAgainstFile(window, fullname)) {
return FALSE;
}
return TRUE;
}
int CheckReadOnly(WindowInfo *window)
{
if (
IS_ANY_LOCKED(window->lockReasons)) {
XBell(TheDisplay,
0);
return True;
}
return False;
}
static const char *errorString(
void)
{
return strerror(errno);
}
static void addWrapNewlines(WindowInfo *window)
{
int fileLen, i, insertPositions[
MAX_PANES], topLines[
MAX_PANES];
int horizOffset;
Widget text;
char *fileString;
for (i=
0; i<=window->nPanes; i++) {
text = i==
0 ? window->textArea : window->textPanes[i-
1];
insertPositions[i] = TextGetCursorPos(text);
TextGetScroll(text, &topLines[i], &horizOffset);
}
fileString = TextGetWrapped(window->textArea,
0,
window->buffer->length, &fileLen);
BufSetAll(window->buffer, fileString);
NEditFree(fileString);
for (i=
0; i<=window->nPanes; i++) {
text = i==
0 ? window->textArea : window->textPanes[i-
1];
TextSetCursorPos(text, insertPositions[i]);
TextSetScroll(text, topLines[i],
0);
}
SetToggleButtonState(window, window->continuousWrapItem, False, True);
}
#define PREFERRED_CMPBUF_LEN 32768
static int cmpWinAgainstFile(WindowInfo *window,
const char *fileName)
{
char fileString[
PREFERRED_CMPBUF_LEN +
2];
struct stat statbuf;
int fileLen, restLen, nRead, bufPos, rv, offset, filePos;
char pendingCR =
0;
int fileFormat = window->fileFormat;
char message[
MAXPATHLEN+
50];
textBuffer *buf = window->buffer;
FILE *fp;
fp = fopen(fileName,
"r");
if (!fp)
return (
1);
if (fstat(fileno(fp), &statbuf) !=
0) {
fclose(fp);
return (
1);
}
fileLen = statbuf.st_size;
if (fileFormat !=
DOS_FILE_FORMAT) {
if (fileLen != buf->length) {
fclose(fp);
return (
1);
}
}
else {
if (fileLen < buf->length) {
fclose(fp);
return (
1);
}
}
sprintf(message,
"Comparing externally modified %s ...", window->filename);
restLen = min(
PREFERRED_CMPBUF_LEN, fileLen);
bufPos =
0;
filePos =
0;
while (restLen >
0) {
AllWindowsBusy(message);
if (pendingCR) {
fileString[
0] = pendingCR;
offset =
1;
}
else {
offset =
0;
}
nRead = fread(fileString+offset,
sizeof(
char), restLen, fp);
if (nRead != restLen) {
fclose(fp);
AllWindowsUnbusy();
return (
1);
}
filePos += nRead;
nRead += offset;
if (bufPos ==
0 && fileFormat != FormatOfFile(fileString)) {
fclose(fp);
AllWindowsUnbusy();
return (
1);
}
if (fileFormat ==
MAC_FILE_FORMAT)
ConvertFromMacFileString(fileString, nRead);
else if (fileFormat ==
DOS_FILE_FORMAT)
ConvertFromDosFileString(fileString, &nRead, &pendingCR);
BufSubstituteNullChars(fileString, nRead, buf);
rv = BufCmp(buf, bufPos, nRead, fileString);
if (rv) {
fclose(fp);
AllWindowsUnbusy();
return (rv);
}
bufPos += nRead;
restLen = min(fileLen - filePos,
PREFERRED_CMPBUF_LEN);
}
AllWindowsUnbusy();
fclose(fp);
if (pendingCR) {
rv = BufCmp(buf, bufPos,
1, &pendingCR);
if (rv) {
return (rv);
}
bufPos +=
1;
}
if (bufPos != buf->length) {
return (
1);
}
return (
0);
}
static void forceShowLineNumbers(WindowInfo *window)
{
Boolean showLineNum = window->showLineNumbers;
if (showLineNum) {
window->showLineNumbers = False;
ShowLineNumbers(window, showLineNum);
}
}
static int min(
int i1,
int i2)
{
return i1 <= i2 ? i1 : i2;
}
static const char * GetDefaultEncoding(
void) {
const char *fallback = GetPrefFallbackCharset();
if(strcmp(fallback,
"locale")) {
return fallback;
}
char *lc = setlocale (
LC_ALL,
"");
char *d = strchr(lc,
'.');
if(d) {
*d =
0;
}
int i =
0;
while(locales[i].locale) {
if(!strcmp(locales[i].locale, lc)) {
return locales[i].encoding;
}
i++;
}
return "ISO8859-1";
}
const char * DetectEncoding(
const char *buf,
size_t len,
const char *def) {
int utf8Err =
0;
int utf8Mb =
0;
const unsigned char *u = (
const unsigned char*)buf;
int charLen =
0;
for(
size_t i=
0;i<len;i++) {
unsigned char c = u[i];
if(charLen ==
0) {
if(c >=
240) {
charLen =
3;
}
else if(c >=
224) {
charLen =
2;
}
else if(c >
192) {
charLen =
1;
}
}
else {
if((c &
192) ==
128) {
if(--charLen ==
0) {
utf8Mb++;
}
}
else {
utf8Err++;
charLen =
0;
}
}
}
if(utf8Err ==
0 || utf8Mb - utf8Err >
2) {
return "UTF-8";
}
char defbuf[
4];
memset(defbuf,
0,
4);
if(def && strlen(def) >
3) {
memcpy(defbuf, def,
3);
}
if(!strcasecmp(defbuf,
"utf")) {
return GetDefaultEncoding();
}
return def;
}
static char* getEncodingAttribute(
const char *path)
{
char *enc_attr =
NULL;
ssize_t attrlen =
0;
enc_attr = xattr_get(path,
"charset", &attrlen);
if(enc_attr) {
if(attrlen ==
0) {
free(enc_attr);
return NULL;
}
if(isspace(enc_attr[
0]) || isspace(enc_attr[attrlen-
1])) {
char *enc_trim = enc_attr;
size_t etlen = attrlen;
while(etlen >
0 && isspace(*enc_trim)) {
enc_trim++;
etlen--;
}
while(etlen >
0 && isspace(enc_trim[etlen-
1])) {
etlen--;
}
enc_trim[etlen] =
'\0';
if(etlen ==
0) {
free(enc_attr);
return NULL;
}
char *enc_str = malloc(attrlen +
1);
if(enc_str) {
memcpy(enc_str, enc_trim, etlen+
1);
}
free(enc_attr);
enc_attr = enc_str;
}
}
return enc_attr;
}