From d9fde3de2b9a294a8b423eda2b596aa45a4ffca5 Mon Sep 17 00:00:00 2001 From: cxl Date: Sun, 16 Feb 2014 13:51:30 +0000 Subject: [PATCH] ide: In Posix, ide now handles non-owned files and symlinks differently #230 git-svn-id: svn://ultimatepp.org/upp/trunk@6922 f0d560ea-af0d-0410-9eb7-867de7ffcac7 --- uppsrc/Core/BlockStream.cpp | 1 + uppsrc/Core/Path.cpp | 11 ++ uppsrc/Core/Path.h | 10 +- uppsrc/Core/Stream.cpp | 1 + uppsrc/Core/src.tpp/FindFile$en-us.tpp | 28 +++++ uppsrc/Core/src.tpp/Stream$en-us.tpp | 3 +- uppsrc/CtrlLib/LineEdit.cpp | 5 +- uppsrc/CtrlLib/TextEdit.h | 4 + uppsrc/ide/ide.h | 1 + uppsrc/ide/idefile.cpp | 145 +++++++++++++++---------- 10 files changed, 148 insertions(+), 61 deletions(-) diff --git a/uppsrc/Core/BlockStream.cpp b/uppsrc/Core/BlockStream.cpp index 93cc8be30..26a78e574 100644 --- a/uppsrc/Core/BlockStream.cpp +++ b/uppsrc/Core/BlockStream.cpp @@ -257,6 +257,7 @@ void BlockStream::OpenInit(dword mode, int64 _filesize) { if(!buffer) SetBufferSize(4096); if(mode == APPEND) SeekEnd(); + ClearError(); } // ---------------------------- File stream ----------------------------- diff --git a/uppsrc/Core/Path.cpp b/uppsrc/Core/Path.cpp index d04bd4fc5..20f240105 100644 --- a/uppsrc/Core/Path.cpp +++ b/uppsrc/Core/Path.cpp @@ -653,6 +653,17 @@ struct stat& FindFile::Stat() const { return statf; } +bool FindFile::CanMode(dword usr, dword grp, dword oth) const +{ + const struct stat& s = Stat(); + dword mode = GetMode(); + static uid_t uid = getuid(); + static gid_t gid = getgid(); + return (mode & oth) || + (mode & grp) && gid == s.st_gid || + (mode & usr) && uid == s.st_uid; +} + bool FindFile::IsSymLink() const { if(file) { diff --git a/uppsrc/Core/Path.h b/uppsrc/Core/Path.h index f5b5b1d83..3802889ef 100644 --- a/uppsrc/Core/Path.h +++ b/uppsrc/Core/Path.h @@ -119,6 +119,7 @@ class FindFile : NoCopy { String pattern; struct stat &Stat() const; + bool CanMode(dword usr, dword grp, dword oth) const; public: bool Search(const char *name); @@ -133,7 +134,14 @@ public: FileTime GetLastAccessTime() const { return Stat().st_atime; } FileTime GetLastWriteTime() const { return Stat().st_mtime; } - bool IsReadOnly() const { return !(GetMode() & (S_IWUSR|S_IWGRP|S_IWOTH)); } + uid_t GetUid() { return Stat().st_uid; } + gid_t GetGid() { return Stat().st_gid; } + + bool CanRead() const { return CanMode(S_IRUSR, S_IRGRP, S_IROTH); } + bool CanWrite() const { return CanMode(S_IWUSR, S_IWGRP, S_IWOTH); } + bool CanExecute() const { return CanMode(S_IXUSR, S_IXGRP, S_IXOTH); } + + bool IsReadOnly() const { return CanRead() && !CanWrite(); } bool IsHidden() const { return *name == '.'; } bool IsDirectory() const { return S_ISDIR(GetMode()); } diff --git a/uppsrc/Core/Stream.cpp b/uppsrc/Core/Stream.cpp index c55b8ac92..d585399c3 100644 --- a/uppsrc/Core/Stream.cpp +++ b/uppsrc/Core/Stream.cpp @@ -1089,6 +1089,7 @@ void CompareStream::Open(Stream& astream) { wrlim = buffer + 128; ptr = buffer; equal = true; + ClearError(); } bool CompareStream::IsOpen() const { diff --git a/uppsrc/Core/src.tpp/FindFile$en-us.tpp b/uppsrc/Core/src.tpp/FindFile$en-us.tpp index bc315faec..24fd2a692 100644 --- a/uppsrc/Core/src.tpp/FindFile$en-us.tpp +++ b/uppsrc/Core/src.tpp/FindFile$en-us.tpp @@ -66,6 +66,34 @@ with all filesystems.&] _[@(0.0.255) const]&] [s2;%% Returns the last time the entry was modified. Always works.&] [s3;%% &] +[s4; &] +[s5;:FindFile`:`:GetLastChangeTime`(`)const: uid`_t_[* GetUid]()&] +[s6;%% `[POSIX`]&] +[s2;%% Returns user ID of file.&] +[s3; &] +[s4; &] +[s5;:FindFile`:`:GetLastChangeTime`(`)const: gid`_t_[* GetGid]()&] +[s6;%% `[POSIX`]&] +[s2;%% Returns group ID of file.&] +[s3; &] +[s4; &] +[s5;:FindFile`:`:GetLastChangeTime`(`)const: [@(0.0.255) bool]_[* CanRead]()_[@(0.0.255) co +nst]&] +[s6;%% `[POSIX`]&] +[s2;%% Returns true if current user can read the file.&] +[s3; &] +[s4; &] +[s5;:FindFile`:`:GetLastChangeTime`(`)const: [@(0.0.255) bool]_[* CanWrite]()_[@(0.0.255) c +onst]&] +[s6;%% `[POSIX`]&] +[s2;%% Returns true if current user can write the file.&] +[s3; &] +[s4; &] +[s5;:FindFile`:`:GetLastChangeTime`(`)const: [@(0.0.255) bool]_[* CanExecute]()_[@(0.0.255) c +onst]&] +[s6;%% `[POSIX`]&] +[s2;%% Returns true if current user can execute the file.&] +[s3; &] [s4;%% &] [s5;:FindFile`:`:GetLastChangeTime`(`)const: [_^FileTime^ FileTime]_[* GetLastChangeTime]( )_[@(0.0.255) const]&] diff --git a/uppsrc/Core/src.tpp/Stream$en-us.tpp b/uppsrc/Core/src.tpp/Stream$en-us.tpp index b39033eb7..c95c11e01 100644 --- a/uppsrc/Core/src.tpp/Stream$en-us.tpp +++ b/uppsrc/Core/src.tpp/Stream$en-us.tpp @@ -284,7 +284,8 @@ onst]&] [s7; [* Return value]-|Size of stream.&] [s3; &] [s4;%- &] -[s5;K%- [@(0.0.255) virtual] [@(0.0.255) void]_[* SetSize]([_^int64^ int64]_[*@3 size])&] +[s5;:Stream`:`:SetSize`(int64`):%- [@(0.0.255) virtual] [@(0.0.255) void]_[* SetSize]([_^int64^ i +nt64]_[*@3 size])&] [s2; Alters the size of the stream.&] [s7; [%-*C@3 size]-|New size.&] [s3; &] diff --git a/uppsrc/CtrlLib/LineEdit.cpp b/uppsrc/CtrlLib/LineEdit.cpp index bfb3a033d..a2279765b 100644 --- a/uppsrc/CtrlLib/LineEdit.cpp +++ b/uppsrc/CtrlLib/LineEdit.cpp @@ -21,6 +21,7 @@ LineEdit::LineEdit() { filter = NULL; showspaces = false; showlines = false; + showreadonly = true; } LineEdit::~LineEdit() {} @@ -100,7 +101,7 @@ void LineEdit::Paint0(Draw& w) { if(w.IsPainting(0, y, sz.cx, fsz.cy)) { Highlight ih; ih.ink = color[IsShowEnabled() ? INK_NORMAL : INK_DISABLED]; - ih.paper = color[IsReadOnly() || !IsShowEnabled() ? PAPER_READONLY : PAPER_NORMAL]; + ih.paper = color[IsReadOnly() && showreadonly || !IsShowEnabled() ? PAPER_READONLY : PAPER_NORMAL]; if(nobg) ih.paper = Null; ih.font = font; @@ -221,7 +222,7 @@ void LineEdit::Paint0(Draw& w) { selh -= len + 1; pos += len + 1; } - w.DrawRect(0, y, sz.cx, sz.cy - y, color[IsReadOnly() || !IsShowEnabled() ? PAPER_READONLY : PAPER_NORMAL]); + w.DrawRect(0, y, sz.cx, sz.cy - y, color[IsReadOnly() && showreadonly || !IsShowEnabled() ? PAPER_READONLY : PAPER_NORMAL]); DrawTiles(w, DropCaret(), CtrlImg::checkers()); } diff --git a/uppsrc/CtrlLib/TextEdit.h b/uppsrc/CtrlLib/TextEdit.h index c76c3c8c7..30c55f09a 100644 --- a/uppsrc/CtrlLib/TextEdit.h +++ b/uppsrc/CtrlLib/TextEdit.h @@ -269,6 +269,7 @@ protected: bool overwrite; bool showspaces; bool showlines; + bool showreadonly; void Paint0(Draw& w); @@ -362,6 +363,9 @@ public: LineEdit& NoCutLine() { return WithCutLine(false); } bool IsWithCutLine() const { return cutline; } LineEdit& SetFilter(int (*f)(int c)) { filter = f; return *this; } + LineEdit& ShowReadOnly(bool b) { showreadonly = b; Refresh(); return *this; } + LineEdit& NoShowReadOnly() { return ShowReadOnly(false); } + bool IsShowReadOnly() { return showreadonly; } LineEdit& SetScrollBarStyle(const ScrollBar::Style& s) { sb.SetStyle(s); return *this; } diff --git a/uppsrc/ide/ide.h b/uppsrc/ide/ide.h index e88d4cf64..279286c99 100644 --- a/uppsrc/ide/ide.h +++ b/uppsrc/ide/ide.h @@ -834,6 +834,7 @@ public: void EditWorkspace(); void EditAnyFile(); bool IsProjectFile(const String& f) const; + void SaveEditorFile(Stream& out); void SaveFile0(bool always); void SaveFile(bool always = false); void DoSaveFile() { SaveFile(); } diff --git a/uppsrc/ide/idefile.cpp b/uppsrc/ide/idefile.cpp index 8069d9f4a..ae18cc9d1 100644 --- a/uppsrc/ide/idefile.cpp +++ b/uppsrc/ide/idefile.cpp @@ -273,6 +273,24 @@ void Ide::SaveFile(bool always) issaving--; } +void Ide::SaveEditorFile(Stream& out) +{ + if(GetFileExt(editfile) == ".t") { + for(int i = 0; i < editor.GetLineCount(); i++) { + if(i) out.PutCrLf(); + out.Put(ConvertTLine(editor.GetUtf8Line(i), ASCSTRING_OCTALHI)); + } + } + else { + int le = line_endings; + if(le == DETECT_CRLF) + le = Nvl(editfile_line_endings, CRLF); + if(le == DETECT_LF) + le = Nvl(editfile_line_endings, LF); + editor.Save(out, editor.GetCharset(), le == LF ? TextCtrl::LE_LF : TextCtrl::LE_CRLF); + } +} + void Ide::SaveFile0(bool always) { if(designer) { @@ -297,62 +315,71 @@ void Ide::SaveFile0(bool always) fd.filetime = edittime; if(!editor.IsDirty() && !always) return; -#ifdef PLATFORM_POSIX - struct stat statbuf; - int statcode = stat(editfile, &statbuf); -#endif TouchFile(editfile); - String tmpfile = editfile + ".$tmp", outfile = editfile; - { - FileOut out(tmpfile); - if(!out.IsOpen()) { - if(IsDeactivationSave()) - return; - Exclamation(NFormat("Error creating temporary file [* \1%s\1].", tmpfile)); - return; - } - if(designer) - designer->Save(); - else - if(GetFileExt(editfile) == ".t") { - for(int i = 0; i < editor.GetLineCount(); i++) { - if(i) out.PutCrLf(); - out.Put(ConvertTLine(editor.GetUtf8Line(i), ASCSTRING_OCTALHI)); - } +#ifdef PLATFORM_POSIX + FindFile ff(editfile); + if(ff && !designer && (ff.GetUid() != getuid() || ff.IsSymLink())) { + for(;;) { + FileStream out; + if(out.Open(editfile, FileStream::READWRITE)) { + SaveEditorFile(out); + out.SetSize(out.GetPos()); + if(!out.IsError()) + break; } - else { - int le = line_endings; - if(le == DETECT_CRLF) - le = Nvl(editfile_line_endings, CRLF); - if(le == DETECT_LF) - le = Nvl(editfile_line_endings, LF); - editor.Save(out, editor.GetCharset(), le == LF ? TextCtrl::LE_LF : TextCtrl::LE_CRLF); + console.Flush(); + Sleep(200); + int art = Prompt(Ctrl::GetAppName(), CtrlImg::exclamation(), + "Unable to save current file.&" + "Retry save, ignore it or save file to another location?", + "Save as...", "Retry", "Ignore"); + if(art < 0) + break; + if(art && AnySourceFs().ExecuteSaveAs()) { + editfile = AnySourceFs(); + MakeTitle(); + PosSync(); + break; } - out.Close(); - if(out.IsError()) { - if(IsDeactivationSave()) - return; - Exclamation(NFormat("Error writing temporary file [* \1%s\1].", tmpfile)); - return; } - } - - Progress progress; - int time = msecs(); - for(;;) { - progress.SetTotal(10000); - progress.SetText(NFormat("Saving '%s'", GetFileName(outfile))); - if(editfile != outfile || !FileExists(tmpfile)) - return; - FileDelete(outfile); - if(FileMove(tmpfile, outfile)) - break; if(IsDeactivationSave()) return; - console.Flush(); - Sleep(200); - if(progress.SetPosCanceled(msecs(time) % progress.GetTotal())) + } + else +#endif + { + String tmpfile = editfile + ".$tmp", outfile = editfile; { + FileOut out(tmpfile); + if(!out.IsOpen()) { + if(IsDeactivationSave()) + return; + Exclamation(NFormat("Error creating temporary file [* \1%s\1].", tmpfile)); + return; + } + if(designer) + designer->Save(); + else + SaveEditorFile(out); + out.Close(); + if(out.IsError()) { + if(IsDeactivationSave()) + return; + Exclamation(NFormat("Error writing temporary file [* \1%s\1].", tmpfile)); + return; + } + } + + Progress progress; + for(;;) { + if(editfile != outfile || !FileExists(tmpfile)) + return; + FileDelete(outfile); + if(FileMove(tmpfile, outfile)) + break; + if(IsDeactivationSave()) + return; + console.Flush(); int art = Prompt(Ctrl::GetAppName(), CtrlImg::exclamation(), "Unable to save current file.&" "Retry save, ignore it or save file to another location?", @@ -365,15 +392,14 @@ void Ide::SaveFile0(bool always) PosSync(); break; } - progress.Reset(); } + + #ifdef PLATFORM_POSIX + if(ff) + chmod(editfile, ff.GetMode()); + #endif } -#ifdef PLATFORM_POSIX - if(statcode == 0) - chmod(editfile, statbuf.st_mode); -#endif - if(!designer) { FindFile ff(editfile); fd.filetime = edittime = ff.GetLastWriteTime(); @@ -484,7 +510,7 @@ void Ide::EditFile0(const String& path, byte charset, bool astext, const String& if(edittime != fd.filetime || IsNull(fd.filetime)) fd.undodata.Clear(); FileIn in(editfile); - if(in) + if(in) { if(tfile && editastext.Find(editfile) < 0) { String f; String ln; @@ -509,14 +535,19 @@ void Ide::EditFile0(const String& path, byte charset, bool astext, const String& int le = editor.Load(in, charset); editfile_line_endings = le == TextCtrl::LE_CRLF ? CRLF : le == TextCtrl::LE_LF ? LF : (int)Null; } + } + else + Exclamation("Failed to read the file."); editor.SetEditPos(fd.editpos); if(!IsNull(fd.columnline) && fd.columnline.y >= 0 && fd.columnline.y < editor.GetLineCount()) editor.SetCursor(editor.GetColumnLinePos(fd.columnline)); editor.SetPickUndoData(fd.undodata); editor.SetLineInfo(fd.lineinfo); editor.SetLineInfoRem(fd.lineinforem); - if(ff.IsReadOnly() || IsNestReadOnly(editfile)) + if(ff.IsReadOnly() || IsNestReadOnly(editfile)) { editor.SetReadOnly(); + editor.NoShowReadOnly(); + } fd.undodata.Clear(); PosSync(); }