diff --git a/src/CharacterWidth.cpp b/src/CharacterWidth.cpp index 816e7307..b04076de 100644 --- a/src/CharacterWidth.cpp +++ b/src/CharacterWidth.cpp @@ -1,159 +1,161 @@ /* This file is part of Konsole, a terminal emulator for KDE. Copyright 2018 by Mariusz Glebocki This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // // THIS IS A GENERATED FILE. DO NOT EDIT. // // CharacterWidth.cpp file is automatically generated - do not edit it. // To change anything here, edit CharacterWidth.src.cpp and regenerate the file // using following command: // // uni2characterwidth -U "https://unicode.org/Public/11.0.0/ucd/UnicodeData.txt" -A "https://unicode.org/Public/11.0.0/ucd/EastAsianWidth.txt" -E "https://unicode.org/Public/emoji/11.0/emoji-data.txt" -W "tools/uni2characterwidth/overrides.txt" --ambiguous-width=1 --emoji=presentation -g "code:src/CharacterWidth.src.cpp" "src/CharacterWidth.cpp" // #include "CharacterWidth.h" #include "konsoledebug.h" #include "konsoleprivate_export.h" struct Range { uint first, last; }; struct RangeLut { int8_t width; const Range * const lut; int size; }; enum { InvalidWidth = INT8_MIN, }; static constexpr const int8_t DIRECT_LUT[] = { 0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }; static constexpr const Range LUT_NONPRINTABLE[] = { {0x00d800,0x00dfff}, }; static constexpr const Range LUT_2[] = { {0x001100,0x00115f},{0x00231a,0x00231b},{0x002329,0x00232a},{0x0023e9,0x0023ec},{0x0023f0,0x0023f0},{0x0023f3,0x0023f3},{0x0025fd,0x0025fe},{0x002614,0x002615}, {0x002648,0x002653},{0x00267f,0x00267f},{0x002693,0x002693},{0x0026a1,0x0026a1},{0x0026aa,0x0026ab},{0x0026bd,0x0026be},{0x0026c4,0x0026c5},{0x0026ce,0x0026ce}, {0x0026d4,0x0026d4},{0x0026ea,0x0026ea},{0x0026f2,0x0026f3},{0x0026f5,0x0026f5},{0x0026fa,0x0026fa},{0x0026fd,0x0026fd},{0x002705,0x002705},{0x00270a,0x00270b}, {0x002728,0x002728},{0x00274c,0x00274c},{0x00274e,0x00274e},{0x002753,0x002755},{0x002757,0x002757},{0x002795,0x002797},{0x0027b0,0x0027b0},{0x0027bf,0x0027bf}, {0x002b1b,0x002b1c},{0x002b50,0x002b50},{0x002b55,0x002b55},{0x002e80,0x002e99},{0x002e9b,0x002ef3},{0x002f00,0x002fd5},{0x002ff0,0x002ffb},{0x003000,0x003029}, {0x00302e,0x00303e},{0x003041,0x003096},{0x00309b,0x0030ff},{0x003105,0x00312f},{0x003131,0x00318e},{0x003190,0x0031ba},{0x0031c0,0x0031e3},{0x0031f0,0x00321e}, {0x003220,0x003247},{0x003250,0x0032fe},{0x003300,0x004dbf},{0x004e00,0x00a48c},{0x00a490,0x00a4c6},{0x00a960,0x00a97c},{0x00ac00,0x00d7a3},{0x00f900,0x00faff}, {0x00fe10,0x00fe19},{0x00fe30,0x00fe52},{0x00fe54,0x00fe66},{0x00fe68,0x00fe6b},{0x00ff01,0x00ff60},{0x00ffe0,0x00ffe6},{0x016fe0,0x016fe1},{0x017000,0x0187f1}, {0x018800,0x018af2},{0x01b000,0x01b11e},{0x01b170,0x01b2fb},{0x01f004,0x01f004},{0x01f0cf,0x01f0cf},{0x01f18e,0x01f18e},{0x01f191,0x01f19a},{0x01f1e6,0x01f202}, {0x01f210,0x01f23b},{0x01f240,0x01f248},{0x01f250,0x01f251},{0x01f260,0x01f265},{0x01f300,0x01f320},{0x01f32d,0x01f335},{0x01f337,0x01f37c},{0x01f37e,0x01f393}, {0x01f3a0,0x01f3ca},{0x01f3cf,0x01f3d3},{0x01f3e0,0x01f3f0},{0x01f3f4,0x01f3f4},{0x01f3f8,0x01f43e},{0x01f440,0x01f440},{0x01f442,0x01f4fc},{0x01f4ff,0x01f53d}, {0x01f54b,0x01f54e},{0x01f550,0x01f567},{0x01f57a,0x01f57a},{0x01f595,0x01f596},{0x01f5a4,0x01f5a4},{0x01f5fb,0x01f64f},{0x01f680,0x01f6c5},{0x01f6cc,0x01f6cc}, {0x01f6d0,0x01f6d2},{0x01f6eb,0x01f6ec},{0x01f6f4,0x01f6f9},{0x01f910,0x01f93e},{0x01f940,0x01f970},{0x01f973,0x01f976},{0x01f97a,0x01f97a},{0x01f97c,0x01f9a2}, {0x01f9b0,0x01f9b9},{0x01f9c0,0x01f9c2},{0x01f9d0,0x01f9ff},{0x020000,0x02fffd},{0x030000,0x03fffd}, }; static constexpr const Range LUT_0[] = { {0x000300,0x00036f},{0x000483,0x000489},{0x000591,0x0005bd},{0x0005bf,0x0005bf},{0x0005c1,0x0005c2},{0x0005c4,0x0005c5},{0x0005c7,0x0005c7},{0x000600,0x000605}, {0x000610,0x00061a},{0x00061c,0x00061c},{0x00064b,0x00065f},{0x000670,0x000670},{0x0006d6,0x0006dd},{0x0006df,0x0006e4},{0x0006e7,0x0006e8},{0x0006ea,0x0006ed}, {0x00070f,0x00070f},{0x000711,0x000711},{0x000730,0x00074a},{0x0007a6,0x0007b0},{0x0007eb,0x0007f3},{0x0007fd,0x0007fd},{0x000816,0x000819},{0x00081b,0x000823}, {0x000825,0x000827},{0x000829,0x00082d},{0x000859,0x00085b},{0x0008d3,0x000902},{0x00093a,0x00093a},{0x00093c,0x00093c},{0x000941,0x000948},{0x00094d,0x00094d}, {0x000951,0x000957},{0x000962,0x000963},{0x000981,0x000981},{0x0009bc,0x0009bc},{0x0009c1,0x0009c4},{0x0009cd,0x0009cd},{0x0009e2,0x0009e3},{0x0009fe,0x0009fe}, {0x000a01,0x000a02},{0x000a3c,0x000a3c},{0x000a41,0x000a42},{0x000a47,0x000a48},{0x000a4b,0x000a4d},{0x000a51,0x000a51},{0x000a70,0x000a71},{0x000a75,0x000a75}, {0x000a81,0x000a82},{0x000abc,0x000abc},{0x000ac1,0x000ac5},{0x000ac7,0x000ac8},{0x000acd,0x000acd},{0x000ae2,0x000ae3},{0x000afa,0x000aff},{0x000b01,0x000b01}, {0x000b3c,0x000b3c},{0x000b3f,0x000b3f},{0x000b41,0x000b44},{0x000b4d,0x000b4d},{0x000b56,0x000b56},{0x000b62,0x000b63},{0x000b82,0x000b82},{0x000bc0,0x000bc0}, {0x000bcd,0x000bcd},{0x000c00,0x000c00},{0x000c04,0x000c04},{0x000c3e,0x000c40},{0x000c46,0x000c48},{0x000c4a,0x000c4d},{0x000c55,0x000c56},{0x000c62,0x000c63}, {0x000c81,0x000c81},{0x000cbc,0x000cbc},{0x000cbf,0x000cbf},{0x000cc6,0x000cc6},{0x000ccc,0x000ccd},{0x000ce2,0x000ce3},{0x000d00,0x000d01},{0x000d3b,0x000d3c}, {0x000d41,0x000d44},{0x000d4d,0x000d4d},{0x000d62,0x000d63},{0x000dca,0x000dca},{0x000dd2,0x000dd4},{0x000dd6,0x000dd6},{0x000e31,0x000e31},{0x000e34,0x000e3a}, {0x000e47,0x000e4e},{0x000eb1,0x000eb1},{0x000eb4,0x000eb9},{0x000ebb,0x000ebc},{0x000ec8,0x000ecd},{0x000f18,0x000f19},{0x000f35,0x000f35},{0x000f37,0x000f37}, {0x000f39,0x000f39},{0x000f71,0x000f7e},{0x000f80,0x000f84},{0x000f86,0x000f87},{0x000f8d,0x000f97},{0x000f99,0x000fbc},{0x000fc6,0x000fc6},{0x00102d,0x001030}, {0x001032,0x001037},{0x001039,0x00103a},{0x00103d,0x00103e},{0x001058,0x001059},{0x00105e,0x001060},{0x001071,0x001074},{0x001082,0x001082},{0x001085,0x001086}, {0x00108d,0x00108d},{0x00109d,0x00109d},{0x001160,0x001160},{0x00135d,0x00135f},{0x001712,0x001714},{0x001732,0x001734},{0x001752,0x001753},{0x001772,0x001773}, {0x0017b4,0x0017b5},{0x0017b7,0x0017bd},{0x0017c6,0x0017c6},{0x0017c9,0x0017d3},{0x0017dd,0x0017dd},{0x00180b,0x00180e},{0x001885,0x001886},{0x0018a9,0x0018a9}, {0x001920,0x001922},{0x001927,0x001928},{0x001932,0x001932},{0x001939,0x00193b},{0x001a17,0x001a18},{0x001a1b,0x001a1b},{0x001a56,0x001a56},{0x001a58,0x001a5e}, {0x001a60,0x001a60},{0x001a62,0x001a62},{0x001a65,0x001a6c},{0x001a73,0x001a7c},{0x001a7f,0x001a7f},{0x001ab0,0x001abe},{0x001b00,0x001b03},{0x001b34,0x001b34}, {0x001b36,0x001b3a},{0x001b3c,0x001b3c},{0x001b42,0x001b42},{0x001b6b,0x001b73},{0x001b80,0x001b81},{0x001ba2,0x001ba5},{0x001ba8,0x001ba9},{0x001bab,0x001bad}, {0x001be6,0x001be6},{0x001be8,0x001be9},{0x001bed,0x001bed},{0x001bef,0x001bf1},{0x001c2c,0x001c33},{0x001c36,0x001c37},{0x001cd0,0x001cd2},{0x001cd4,0x001ce0}, {0x001ce2,0x001ce8},{0x001ced,0x001ced},{0x001cf4,0x001cf4},{0x001cf8,0x001cf9},{0x001dc0,0x001df9},{0x001dfb,0x001dff},{0x00200b,0x00200f},{0x00202a,0x00202e}, {0x002060,0x002064},{0x002066,0x00206f},{0x0020d0,0x0020f0},{0x002cef,0x002cf1},{0x002d7f,0x002d7f},{0x002de0,0x002dff},{0x00302a,0x00302d},{0x003099,0x00309a}, {0x00a66f,0x00a672},{0x00a674,0x00a67d},{0x00a69e,0x00a69f},{0x00a6f0,0x00a6f1},{0x00a802,0x00a802},{0x00a806,0x00a806},{0x00a80b,0x00a80b},{0x00a825,0x00a826}, {0x00a8c4,0x00a8c5},{0x00a8e0,0x00a8f1},{0x00a8ff,0x00a8ff},{0x00a926,0x00a92d},{0x00a947,0x00a951},{0x00a980,0x00a982},{0x00a9b3,0x00a9b3},{0x00a9b6,0x00a9b9}, {0x00a9bc,0x00a9bc},{0x00a9e5,0x00a9e5},{0x00aa29,0x00aa2e},{0x00aa31,0x00aa32},{0x00aa35,0x00aa36},{0x00aa43,0x00aa43},{0x00aa4c,0x00aa4c},{0x00aa7c,0x00aa7c}, {0x00aab0,0x00aab0},{0x00aab2,0x00aab4},{0x00aab7,0x00aab8},{0x00aabe,0x00aabf},{0x00aac1,0x00aac1},{0x00aaec,0x00aaed},{0x00aaf6,0x00aaf6},{0x00abe5,0x00abe5}, {0x00abe8,0x00abe8},{0x00abed,0x00abed},{0x00fb1e,0x00fb1e},{0x00fe00,0x00fe0f},{0x00fe20,0x00fe2f},{0x00feff,0x00feff},{0x00fff9,0x00fffb},{0x0101fd,0x0101fd}, {0x0102e0,0x0102e0},{0x010376,0x01037a},{0x010a01,0x010a03},{0x010a05,0x010a06},{0x010a0c,0x010a0f},{0x010a38,0x010a3a},{0x010a3f,0x010a3f},{0x010ae5,0x010ae6}, {0x010d24,0x010d27},{0x010f46,0x010f50},{0x011001,0x011001},{0x011038,0x011046},{0x01107f,0x011081},{0x0110b3,0x0110b6},{0x0110b9,0x0110ba},{0x0110bd,0x0110bd}, {0x0110cd,0x0110cd},{0x011100,0x011102},{0x011127,0x01112b},{0x01112d,0x011134},{0x011173,0x011173},{0x011180,0x011181},{0x0111b6,0x0111be},{0x0111c9,0x0111cc}, {0x01122f,0x011231},{0x011234,0x011234},{0x011236,0x011237},{0x01123e,0x01123e},{0x0112df,0x0112df},{0x0112e3,0x0112ea},{0x011300,0x011301},{0x01133b,0x01133c}, {0x011340,0x011340},{0x011366,0x01136c},{0x011370,0x011374},{0x011438,0x01143f},{0x011442,0x011444},{0x011446,0x011446},{0x01145e,0x01145e},{0x0114b3,0x0114b8}, {0x0114ba,0x0114ba},{0x0114bf,0x0114c0},{0x0114c2,0x0114c3},{0x0115b2,0x0115b5},{0x0115bc,0x0115bd},{0x0115bf,0x0115c0},{0x0115dc,0x0115dd},{0x011633,0x01163a}, {0x01163d,0x01163d},{0x01163f,0x011640},{0x0116ab,0x0116ab},{0x0116ad,0x0116ad},{0x0116b0,0x0116b5},{0x0116b7,0x0116b7},{0x01171d,0x01171f},{0x011722,0x011725}, {0x011727,0x01172b},{0x01182f,0x011837},{0x011839,0x01183a},{0x011a01,0x011a0a},{0x011a33,0x011a38},{0x011a3b,0x011a3e},{0x011a47,0x011a47},{0x011a51,0x011a56}, {0x011a59,0x011a5b},{0x011a8a,0x011a96},{0x011a98,0x011a99},{0x011c30,0x011c36},{0x011c38,0x011c3d},{0x011c3f,0x011c3f},{0x011c92,0x011ca7},{0x011caa,0x011cb0}, {0x011cb2,0x011cb3},{0x011cb5,0x011cb6},{0x011d31,0x011d36},{0x011d3a,0x011d3a},{0x011d3c,0x011d3d},{0x011d3f,0x011d45},{0x011d47,0x011d47},{0x011d90,0x011d91}, {0x011d95,0x011d95},{0x011d97,0x011d97},{0x011ef3,0x011ef4},{0x016af0,0x016af4},{0x016b30,0x016b36},{0x016f8f,0x016f92},{0x01bc9d,0x01bc9e},{0x01bca0,0x01bca3}, {0x01d167,0x01d169},{0x01d173,0x01d182},{0x01d185,0x01d18b},{0x01d1aa,0x01d1ad},{0x01d242,0x01d244},{0x01da00,0x01da36},{0x01da3b,0x01da6c},{0x01da75,0x01da75}, {0x01da84,0x01da84},{0x01da9b,0x01da9f},{0x01daa1,0x01daaf},{0x01e000,0x01e006},{0x01e008,0x01e018},{0x01e01b,0x01e021},{0x01e023,0x01e024},{0x01e026,0x01e02a}, {0x01e8d0,0x01e8d6},{0x01e944,0x01e94a},{0x0e0001,0x0e0001},{0x0e0020,0x0e007f},{0x0e0100,0x0e01ef}, }; static constexpr const RangeLut RANGE_LUT_LIST[] = { {-1, LUT_NONPRINTABLE, 1}, { 2, LUT_2 , 109}, { 0, LUT_0 , 325}, { 1, nullptr , 1}, }; static constexpr const int RANGE_LUT_LIST_SIZE = 4; int KONSOLEPRIVATE_EXPORT characterWidth(uint ucs4) { - if(Q_LIKELY(ucs4 < sizeof(DIRECT_LUT))) + if(Q_LIKELY(ucs4 < sizeof(DIRECT_LUT))) { return DIRECT_LUT[ucs4]; + } for(auto rl = RANGE_LUT_LIST; rl->lut != nullptr; ++rl) { int l = 0; int r = rl->size - 1; while(l <= r) { const int m = (l + r) / 2; - if(rl->lut[m].last < ucs4) + if(rl->lut[m].last < ucs4) { l = m + 1; - else if(rl->lut[m].first > ucs4) + } else if(rl->lut[m].first > ucs4) { r = m - 1; - else + } else { return rl->width; + } } } return RANGE_LUT_LIST[RANGE_LUT_LIST_SIZE - 1].width; } diff --git a/src/DetachableTabBar.cpp b/src/DetachableTabBar.cpp index ef2ca4f0..4508004c 100644 --- a/src/DetachableTabBar.cpp +++ b/src/DetachableTabBar.cpp @@ -1,109 +1,113 @@ /* Copyright 2018 by Tomaz Canabrava This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "DetachableTabBar.h" #include "ViewContainer.h" #include #include namespace Konsole { DetachableTabBar::DetachableTabBar(QWidget *parent) : QTabBar(parent), dragType(DragType::NONE), _originalCursor(cursor()) {} bool DetachableTabBar::droppedContainerIsNotThis(const QPoint& currentPos) const { for(const auto dropWidget : _containers) { if (dropWidget->rect().contains(dropWidget->mapFromGlobal(currentPos))) { if (dropWidget != parent()) { return true; } } } return false; } void DetachableTabBar::mousePressEvent(QMouseEvent *event) { QTabBar::mousePressEvent(event); _containers = window()->findChildren(); } void DetachableTabBar::mouseMoveEvent(QMouseEvent *event) { QTabBar::mouseMoveEvent(event); auto widgetAtPos = qApp->topLevelAt(event->globalPos()); if (widgetAtPos != nullptr) { if (window() == widgetAtPos->window()) { if (droppedContainerIsNotThis(event->globalPos())) { if (dragType != DragType::WINDOW) { dragType = DragType::WINDOW; setCursor(QCursor(Qt::DragMoveCursor)); } } else { if (dragType != DragType::NONE) { dragType = DragType::NONE; setCursor(_originalCursor); } } } else { if (dragType != DragType::WINDOW) { dragType = DragType::WINDOW; setCursor(QCursor(Qt::DragMoveCursor)); } } } else if (!contentsRect().adjusted(-30,-30,30,30).contains(event->pos())) { // Don't let it detach the last tab. - if (count() == 1) + if (count() == 1) { return; + } if (dragType != DragType::OUTSIDE) { dragType = DragType::OUTSIDE; setCursor(QCursor(Qt::DragCopyCursor)); } } } void DetachableTabBar::mouseReleaseEvent(QMouseEvent *event) { QTabBar::mouseReleaseEvent(event); setCursor(_originalCursor); if (contentsRect().adjusted(-30,-30,30,30).contains(event->pos())) { return; } auto widgetAtPos = qApp->topLevelAt(event->globalPos()); if (widgetAtPos == nullptr) { - if (count() != 1) + if (count() != 1) { emit detachTab(currentIndex()); + } } else if (window() != widgetAtPos->window()) { // splits have a tendency to break, forbid to detach if split and it's the last tab. - if (_containers.size() == 1 || count() > 1) + if (_containers.size() == 1 || count() > 1) { emit moveTabToWindow(currentIndex(), widgetAtPos); + } } else if (droppedContainerIsNotThis(event->globalPos())){ - if (count() != 1) + if (count() != 1) { emit moveTabToWindow(currentIndex(), widgetAtPos); + } } } } diff --git a/src/IncrementalSearchBar.cpp b/src/IncrementalSearchBar.cpp index a7f1ab56..5c4f9068 100644 --- a/src/IncrementalSearchBar.cpp +++ b/src/IncrementalSearchBar.cpp @@ -1,311 +1,312 @@ /* Copyright 2006-2008 by Robert Knight This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // Own #include "IncrementalSearchBar.h" // Qt #include #include #include #include #include #include #include #include #include // KDE #include #include #include #include "KonsoleSettings.h" using namespace Konsole; IncrementalSearchBar::IncrementalSearchBar(QWidget *aParent) : QWidget(aParent), _searchEdit(nullptr), _caseSensitive(nullptr), _regExpression(nullptr), _highlightMatches(nullptr), _reverseSearch(nullptr), _findNextButton(nullptr), _findPreviousButton(nullptr), _searchFromButton(nullptr), _searchTimer(nullptr) { setPalette(qApp->palette()); setAutoFillBackground(true); auto closeButton = new QToolButton(this); closeButton->setObjectName(QStringLiteral("close-button")); closeButton->setToolTip(i18nc("@info:tooltip", "Close the search bar")); closeButton->setAutoRaise(true); closeButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-close"))); connect(closeButton, &QToolButton::clicked, this, &Konsole::IncrementalSearchBar::closeClicked); _searchEdit = new QLineEdit(this); _searchEdit->setClearButtonEnabled(true); _searchEdit->installEventFilter(this); _searchEdit->setPlaceholderText(i18nc("@label:textbox", "Find...")); _searchEdit->setObjectName(QStringLiteral("search-edit")); _searchEdit->setToolTip(i18nc("@info:tooltip", "Enter the text to search for here")); _searchEdit->setCursor(Qt::IBeamCursor); _searchEdit->setStyleSheet(QString()); _searchEdit->setFont(QApplication::font()); setCursor(Qt::ArrowCursor); // text box may be a minimum of 6 characters wide and a maximum of 10 characters wide // (since the maxWidth metric is used here, more characters probably will fit in than 6 // and 10) QFontMetrics metrics(_searchEdit->font()); int maxWidth = metrics.maxWidth(); _searchEdit->setMinimumWidth(maxWidth * 6); _searchEdit->setMaximumWidth(maxWidth * 10); _searchTimer = new QTimer(this); _searchTimer->setInterval(250); _searchTimer->setSingleShot(true); connect(_searchTimer, &QTimer::timeout, this, &Konsole::IncrementalSearchBar::notifySearchChanged); connect(_searchEdit, &QLineEdit::textChanged, _searchTimer, static_cast(&QTimer::start)); _findNextButton = new QToolButton(this); _findNextButton->setObjectName(QStringLiteral("find-next-button")); _findNextButton->setText(i18nc("@action:button Go to the next phrase", "Next")); _findNextButton->setToolButtonStyle(Qt::ToolButtonIconOnly); _findNextButton->setAutoRaise(true); _findNextButton->setToolTip(i18nc("@info:tooltip", "Find the next match for the current search phrase")); connect(_findNextButton, &QToolButton::clicked, this, &Konsole::IncrementalSearchBar::findNextClicked); _findPreviousButton = new QToolButton(this); _findPreviousButton->setAutoRaise(true); _findPreviousButton->setObjectName(QStringLiteral("find-previous-button")); _findPreviousButton->setText(i18nc("@action:button Go to the previous phrase", "Previous")); _findPreviousButton->setToolButtonStyle(Qt::ToolButtonIconOnly); _findPreviousButton->setToolTip(i18nc("@info:tooltip", "Find the previous match for the current search phrase")); connect(_findPreviousButton, &QToolButton::clicked, this, &Konsole::IncrementalSearchBar::findPreviousClicked); _searchFromButton = new QToolButton(this); _searchFromButton->setAutoRaise(true); _searchFromButton->setObjectName(QStringLiteral("search-from-button")); connect(_searchFromButton, &QToolButton::clicked, this, &Konsole::IncrementalSearchBar::searchFromClicked); auto optionsButton = new QToolButton(this); optionsButton->setObjectName(QStringLiteral("find-options-button")); optionsButton->setCheckable(false); optionsButton->setPopupMode(QToolButton::InstantPopup); optionsButton->setToolButtonStyle(Qt::ToolButtonIconOnly); optionsButton->setToolTip(i18nc("@info:tooltip", "Display the options menu")); optionsButton->setIcon(QIcon::fromTheme(QStringLiteral("configure"))); optionsButton->setAutoRaise(true); // Fill the options menu auto optionsMenu = new QMenu(this); optionsButton->setMenu(optionsMenu); _caseSensitive = optionsMenu->addAction(i18nc("@item:inmenu", "Case sensitive")); _caseSensitive->setCheckable(true); _caseSensitive->setToolTip(i18nc("@info:tooltip", "Sets whether the search is case sensitive")); connect(_caseSensitive, &QAction::toggled, this, &Konsole::IncrementalSearchBar::matchCaseToggled); _regExpression = optionsMenu->addAction(i18nc("@item:inmenu", "Match regular expression")); _regExpression->setCheckable(true); connect(_regExpression, &QAction::toggled, this, &Konsole::IncrementalSearchBar::matchRegExpToggled); _highlightMatches = optionsMenu->addAction(i18nc("@item:inmenu", "Highlight all matches")); _highlightMatches->setCheckable(true); _highlightMatches->setToolTip(i18nc("@info:tooltip", "Sets whether matching text should be highlighted")); connect(_highlightMatches, &QAction::toggled, this, &Konsole::IncrementalSearchBar::highlightMatchesToggled); _reverseSearch = optionsMenu->addAction(i18nc("@item:inmenu", "Search backwards")); _reverseSearch->setCheckable(true); _reverseSearch->setToolTip(i18nc("@info:tooltip", "Sets whether search should start from the bottom")); connect(_reverseSearch, &QAction::toggled, this, &Konsole::IncrementalSearchBar::updateButtonsAccordingToReverseSearchSetting); updateButtonsAccordingToReverseSearchSetting(); setOptions(); auto barLayout = new QHBoxLayout(this); barLayout->addWidget(_searchEdit); barLayout->addWidget(_findNextButton); barLayout->addWidget(_findPreviousButton); barLayout->addWidget(_searchFromButton); barLayout->addWidget(optionsButton); barLayout->addWidget(closeButton); barLayout->setContentsMargins(4, 4, 4, 4); barLayout->setSpacing(0); setLayout(barLayout); adjustSize(); clearLineEdit(); } void IncrementalSearchBar::notifySearchChanged() { emit searchChanged(searchText()); } void IncrementalSearchBar::updateButtonsAccordingToReverseSearchSetting() { Q_ASSERT(_reverseSearch); if (_reverseSearch->isChecked()) { _searchFromButton->setToolTip(i18nc("@info:tooltip", "Search for the current search phrase from the bottom")); _searchFromButton->setIcon(QIcon::fromTheme(QStringLiteral("go-bottom"))); _findNextButton->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); _findPreviousButton->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); } else { _searchFromButton->setToolTip(i18nc("@info:tooltip", "Search for the current search phrase from the top")); _searchFromButton->setIcon(QIcon::fromTheme(QStringLiteral("go-top"))); _findNextButton->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); _findPreviousButton->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); } } QString IncrementalSearchBar::searchText() { return _searchEdit->text(); } void IncrementalSearchBar::setSearchText(const QString &text) { if (text != searchText()) { _searchEdit->setText(text); } } bool IncrementalSearchBar::eventFilter(QObject *watched, QEvent *event) { - if ((event->type() != QEvent::KeyPress) || watched != _searchEdit) + if ((event->type() != QEvent::KeyPress) || watched != _searchEdit) { return QWidget::eventFilter(watched, event); + } QKeyEvent *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Escape) { emit closeClicked(); return true; } if (keyEvent->key() == Qt::Key_Return && !keyEvent->modifiers()) { _findNextButton->click(); return true; } if ((keyEvent->key() == Qt::Key_Return) && (keyEvent->modifiers() == Qt::ShiftModifier)) { _findPreviousButton->click(); return true; } if ((keyEvent->key() == Qt::Key_Return) && (keyEvent->modifiers() == Qt::ControlModifier)) { _searchFromButton->click(); return true; } return QWidget::eventFilter(watched, event); } void IncrementalSearchBar::correctPosition(const QSize &parentSize) { const auto width = geometry().width(); const auto height = geometry().height(); const auto x = parentSize.width() - width; setGeometry(x, 0, width, height); } void IncrementalSearchBar::keyPressEvent(QKeyEvent *event) { static auto movementKeysToPassAlong = QSet{ Qt::Key_PageUp, Qt::Key_PageDown, Qt::Key_Up, Qt::Key_Down }; if (movementKeysToPassAlong.contains(event->key()) && (event->modifiers() == Qt::ShiftModifier)) { emit unhandledMovementKeyPressed(event); } } void IncrementalSearchBar::setVisible(bool visible) { QWidget::setVisible(visible); if (visible) { focusLineEdit(); } } void IncrementalSearchBar::setFoundMatch(bool match) { if (_searchEdit->text().isEmpty()) { clearLineEdit(); return; } const auto backgroundBrush = KStatefulBrush(KColorScheme::View, match ? KColorScheme::PositiveBackground : KColorScheme::NegativeBackground); const auto matchStyleSheet = QStringLiteral("QLineEdit{ background-color:%1 }") .arg(backgroundBrush.brush(_searchEdit).color().name()); _searchEdit->setStyleSheet(matchStyleSheet); } void IncrementalSearchBar::clearLineEdit() { _searchEdit->setStyleSheet(QString()); } void IncrementalSearchBar::focusLineEdit() { _searchEdit->setFocus(Qt::ActiveWindowFocusReason); _searchEdit->selectAll(); } const QBitArray IncrementalSearchBar::optionsChecked() { QBitArray options(4, false); options.setBit(MatchCase, _caseSensitive->isChecked()); options.setBit(RegExp, _regExpression->isChecked()); options.setBit(HighlightMatches, _highlightMatches->isChecked()); options.setBit(ReverseSearch, _reverseSearch->isChecked()); return options; } void IncrementalSearchBar::setOptions() { _caseSensitive->setChecked(KonsoleSettings::searchCaseSensitive()); _regExpression->setChecked(KonsoleSettings::searchRegExpression()); _highlightMatches->setChecked(KonsoleSettings::searchHighlightMatches()); _reverseSearch->setChecked(KonsoleSettings::searchReverseSearch()); } diff --git a/src/ProcessInfo.cpp b/src/ProcessInfo.cpp index 192e1cab..5fcf547e 100644 --- a/src/ProcessInfo.cpp +++ b/src/ProcessInfo.cpp @@ -1,1190 +1,1191 @@ /* Copyright 2007-2008 by Robert Knight This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // Config #include // Own #include "ProcessInfo.h" // Unix #include #include #include #include #include #include #include // Qt #include #include #include #include #include #include // KDE #include #include #include #if defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) || defined(Q_OS_MACOS) #include #endif #if defined(Q_OS_MACOS) #include #include #endif #if defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) #include #include #include # if defined(Q_OS_FREEBSD) # include # include # include # endif #endif using namespace Konsole; ProcessInfo::ProcessInfo(int pid) : _fields(ARGUMENTS) // arguments // are currently always valid, // they just return an empty // vector / map respectively // if no arguments // have been explicitly set , _pid(pid), _parentPid(0), _foregroundPid(0), _userId(0), _lastError(NoError), _name(QString()), _userName(QString()), _userHomeDir(QString()), _currentDir(QString()), _userNameRequired(true), _arguments(QVector()) { } ProcessInfo::Error ProcessInfo::error() const { return _lastError; } void ProcessInfo::setError(Error error) { _lastError = error; } void ProcessInfo::update() { readCurrentDir(_pid); } QString ProcessInfo::validCurrentDir() const { bool ok = false; // read current dir, if an error occurs try the parent as the next // best option int currentPid = parentPid(&ok); QString dir = currentDir(&ok); while (!ok && currentPid != 0) { ProcessInfo *current = ProcessInfo::newInstance(currentPid, QString()); current->update(); currentPid = current->parentPid(&ok); dir = current->currentDir(&ok); delete current; } return dir; } QSet ProcessInfo::_commonDirNames; QSet ProcessInfo::commonDirNames() { static bool forTheFirstTime = true; if (forTheFirstTime) { const KSharedConfigPtr &config = KSharedConfig::openConfig(); const KConfigGroup &configGroup = config->group("ProcessInfo"); _commonDirNames = QSet::fromList(configGroup.readEntry("CommonDirNames", QStringList())); forTheFirstTime = false; } return _commonDirNames; } QString ProcessInfo::formatShortDir(const QString &input) const { QString result; const QStringList &parts = input.split(QDir::separator()); QSet dirNamesToShorten = commonDirNames(); QListIterator iter(parts); iter.toBack(); // go backwards through the list of the path's parts // adding abbreviations of common directory names // and stopping when we reach a dir name which is not // in the commonDirNames set while (iter.hasPrevious()) { const QString &part = iter.previous(); if (dirNamesToShorten.contains(part)) { result.prepend(QDir::separator() + part[0]); } else { result.prepend(part); break; } } return result; } QVector ProcessInfo::arguments(bool *ok) const { *ok = _fields.testFlag(ARGUMENTS); return _arguments; } bool ProcessInfo::isValid() const { return _fields.testFlag(PROCESS_ID); } int ProcessInfo::pid(bool *ok) const { *ok = _fields.testFlag(PROCESS_ID); return _pid; } int ProcessInfo::parentPid(bool *ok) const { *ok = _fields.testFlag(PARENT_PID); return _parentPid; } int ProcessInfo::foregroundPid(bool *ok) const { *ok = _fields.testFlag(FOREGROUND_PID); return _foregroundPid; } QString ProcessInfo::name(bool *ok) const { *ok = _fields.testFlag(NAME); return _name; } int ProcessInfo::userId(bool *ok) const { *ok = _fields.testFlag(UID); return _userId; } QString ProcessInfo::userName() const { return _userName; } QString ProcessInfo::userHomeDir() const { return _userHomeDir; } QString ProcessInfo::localHost() { return QHostInfo::localHostName(); } void ProcessInfo::setPid(int pid) { _pid = pid; _fields |= PROCESS_ID; } void ProcessInfo::setUserId(int uid) { _userId = uid; _fields |= UID; } void ProcessInfo::setUserName(const QString &name) { _userName = name; setUserHomeDir(); } void ProcessInfo::setUserHomeDir() { const QString &usersName = userName(); if (!usersName.isEmpty()) { _userHomeDir = KUser(usersName).homeDir(); } else { _userHomeDir = QDir::homePath(); } } void ProcessInfo::setParentPid(int pid) { _parentPid = pid; _fields |= PARENT_PID; } void ProcessInfo::setForegroundPid(int pid) { _foregroundPid = pid; _fields |= FOREGROUND_PID; } void ProcessInfo::setUserNameRequired(bool need) { _userNameRequired = need; } bool ProcessInfo::userNameRequired() const { return _userNameRequired; } QString ProcessInfo::currentDir(bool *ok) const { if (ok != nullptr) { *ok = (_fields & CURRENT_DIR) != 0; } return _currentDir; } void ProcessInfo::setCurrentDir(const QString &dir) { _fields |= CURRENT_DIR; _currentDir = dir; } void ProcessInfo::setName(const QString &name) { _name = name; _fields |= NAME; } void ProcessInfo::addArgument(const QString &argument) { _arguments << argument; } void ProcessInfo::clearArguments() { _arguments.clear(); } void ProcessInfo::setFileError(QFile::FileError error) { switch (error) { case QFile::PermissionsError: setError(ProcessInfo::PermissionsError); break; case QFile::NoError: setError(ProcessInfo::NoError); break; default: setError(ProcessInfo::UnknownError); } } // // ProcessInfo::newInstance() is way at the bottom so it can see all of the // implementations of the UnixProcessInfo abstract class. // NullProcessInfo::NullProcessInfo(int pid, const QString & /*titleFormat*/) : ProcessInfo(pid) { } void NullProcessInfo::readProcessInfo(int /*pid*/) { } bool NullProcessInfo::readCurrentDir(int /*pid*/) { return false; } void NullProcessInfo::readUserName() { } #if !defined(Q_OS_WIN) UnixProcessInfo::UnixProcessInfo(int pid, const QString &titleFormat) : ProcessInfo(pid) { setUserNameRequired(titleFormat.contains(QLatin1String("%u"))); } void UnixProcessInfo::readProcessInfo(int pid) { // prevent _arguments from growing longer and longer each time this // method is called. clearArguments(); if (readProcInfo(pid)) { readArguments(pid); readCurrentDir(pid); bool ok = false; const QString &processNameString = name(&ok); if (ok && processNameString == QLatin1String("sudo")) { //Append process name along with sudo const QVector &args = arguments(&ok); - if (ok && args.size() > 1) + if (ok && args.size() > 1) { setName(processNameString + QStringLiteral(" ") + args[1]); + } } } } void UnixProcessInfo::readUserName() { bool ok = false; const int uid = userId(&ok); if (!ok) { return; } struct passwd passwdStruct; struct passwd *getpwResult; char *getpwBuffer; long getpwBufferSize; int getpwStatus; getpwBufferSize = sysconf(_SC_GETPW_R_SIZE_MAX); if (getpwBufferSize == -1) { getpwBufferSize = 16384; } getpwBuffer = new char[getpwBufferSize]; if (getpwBuffer == nullptr) { return; } getpwStatus = getpwuid_r(uid, &passwdStruct, getpwBuffer, getpwBufferSize, &getpwResult); if ((getpwStatus == 0) && (getpwResult != nullptr)) { setUserName(QLatin1String(passwdStruct.pw_name)); } else { setUserName(QString()); qWarning() << "getpwuid_r returned error : " << getpwStatus; } delete [] getpwBuffer; } #endif #if defined(Q_OS_LINUX) class LinuxProcessInfo : public UnixProcessInfo { public: LinuxProcessInfo(int pid, const QString &titleFormat) : UnixProcessInfo(pid, titleFormat) { } protected: bool readCurrentDir(int pid) Q_DECL_OVERRIDE { char path_buffer[MAXPATHLEN + 1]; path_buffer[MAXPATHLEN] = 0; QByteArray procCwd = QFile::encodeName(QStringLiteral("/proc/%1/cwd").arg(pid)); const int length = static_cast(readlink(procCwd.constData(), path_buffer, MAXPATHLEN)); if (length == -1) { setError(UnknownError); return false; } path_buffer[length] = '\0'; QString path = QFile::decodeName(path_buffer); setCurrentDir(path); return true; } private: bool readProcInfo(int pid) Q_DECL_OVERRIDE { // indicies of various fields within the process status file which // contain various information about the process const int PARENT_PID_FIELD = 3; const int PROCESS_NAME_FIELD = 1; const int GROUP_PROCESS_FIELD = 7; QString parentPidString; QString processNameString; QString foregroundPidString; QString uidLine; QString uidString; QStringList uidStrings; // For user id read process status file ( /proc//status ) // Can not use getuid() due to it does not work for 'su' QFile statusInfo(QStringLiteral("/proc/%1/status").arg(pid)); if (statusInfo.open(QIODevice::ReadOnly)) { QTextStream stream(&statusInfo); QString statusLine; do { statusLine = stream.readLine(); if (statusLine.startsWith(QLatin1String("Uid:"))) { uidLine = statusLine; } } while (!statusLine.isNull() && uidLine.isNull()); uidStrings << uidLine.split(QLatin1Char('\t'), QString::SkipEmptyParts); // Must be 5 entries: 'Uid: %d %d %d %d' and // uid string must be less than 5 chars (uint) if (uidStrings.size() == 5) { uidString = uidStrings[1]; } if (uidString.size() > 5) { uidString.clear(); } bool ok = false; const int uid = uidString.toInt(&ok); if (ok) { setUserId(uid); } // This will cause constant opening of /etc/passwd if (userNameRequired()) { readUserName(); } } else { setFileError(statusInfo.error()); return false; } // read process status file ( /proc//cmdline // the expected format is a list of strings delimited by null characters, // and ending in a double null character pair. QFile argumentsFile(QStringLiteral("/proc/%1/cmdline").arg(pid)); if (argumentsFile.open(QIODevice::ReadOnly)) { QTextStream stream(&argumentsFile); const QString &data = stream.readAll(); const QStringList &argList = data.split(QLatin1Char('\0')); foreach (const QString &entry, argList) { if (!entry.isEmpty()) { addArgument(entry); } } } else { setFileError(argumentsFile.error()); } return true; } }; #elif defined(Q_OS_FREEBSD) class FreeBSDProcessInfo : public UnixProcessInfo { public: FreeBSDProcessInfo(int pid, const QString &titleFormat) : UnixProcessInfo(pid, titleFormat) { } protected: bool readCurrentDir(int pid) Q_DECL_OVERRIDE { #if defined(HAVE_OS_DRAGONFLYBSD) char buf[PATH_MAX]; int managementInfoBase[4]; size_t len; managementInfoBase[0] = CTL_KERN; managementInfoBase[1] = KERN_PROC; managementInfoBase[2] = KERN_PROC_CWD; managementInfoBase[3] = pid; len = sizeof(buf); if (sysctl(managementInfoBase, 4, buf, &len, NULL, 0) == -1) { return false; } setCurrentDir(QString::fromUtf8(buf)); return true; #else int numrecords; struct kinfo_file *info = 0; info = kinfo_getfile(pid, &numrecords); if (!info) { return false; } for (int i = 0; i < numrecords; ++i) { if (info[i].kf_fd == KF_FD_TYPE_CWD) { setCurrentDir(QString::fromUtf8(info[i].kf_path)); free(info); return true; } } free(info); return false; #endif } private: bool readProcInfo(int pid) Q_DECL_OVERRIDE { int managementInfoBase[4]; size_t mibLength; struct kinfo_proc *kInfoProc; managementInfoBase[0] = CTL_KERN; managementInfoBase[1] = KERN_PROC; managementInfoBase[2] = KERN_PROC_PID; managementInfoBase[3] = pid; if (sysctl(managementInfoBase, 4, NULL, &mibLength, NULL, 0) == -1) { return false; } kInfoProc = new struct kinfo_proc [mibLength]; if (sysctl(managementInfoBase, 4, kInfoProc, &mibLength, NULL, 0) == -1) { delete [] kInfoProc; return false; } #if defined(HAVE_OS_DRAGONFLYBSD) setName(QString::fromUtf8(kInfoProc->kp_comm)); setPid(kInfoProc->kp_pid); setParentPid(kInfoProc->kp_ppid); setForegroundPid(kInfoProc->kp_pgid); setUserId(kInfoProc->kp_uid); #else setName(QString::fromUtf8(kInfoProc->ki_comm)); setPid(kInfoProc->ki_pid); setParentPid(kInfoProc->ki_ppid); setForegroundPid(kInfoProc->ki_pgid); setUserId(kInfoProc->ki_uid); #endif readUserName(); delete [] kInfoProc; return true; } bool readArguments(int pid) Q_DECL_OVERRIDE { char args[ARG_MAX]; int managementInfoBase[4]; size_t len; managementInfoBase[0] = CTL_KERN; managementInfoBase[1] = KERN_PROC; managementInfoBase[2] = KERN_PROC_ARGS; managementInfoBase[3] = pid; len = sizeof(args); if (sysctl(managementInfoBase, 4, args, &len, NULL, 0) == -1) { return false; } // len holds the length of the string QString qargs = QString::fromLocal8Bit(args, len); foreach (const QString &value, qargs.split(QLatin1Char('\u0000'))) { if (!value.isEmpty()) { addArgument(value); } } return true; } }; #elif defined(Q_OS_OPENBSD) class OpenBSDProcessInfo : public UnixProcessInfo { public: OpenBSDProcessInfo(int pid, const QString &titleFormat) : UnixProcessInfo(pid, titleFormat) { } protected: bool readCurrentDir(int pid) Q_DECL_OVERRIDE { char buf[PATH_MAX]; int managementInfoBase[3]; size_t len; managementInfoBase[0] = CTL_KERN; managementInfoBase[1] = KERN_PROC_CWD; managementInfoBase[2] = pid; len = sizeof(buf); if (::sysctl(managementInfoBase, 3, buf, &len, NULL, 0) == -1) { qWarning() << "sysctl() call failed with code" << errno; return false; } setCurrentDir(QString::fromUtf8(buf)); return true; } private: bool readProcInfo(int pid) Q_DECL_OVERRIDE { int managementInfoBase[6]; size_t mibLength; struct kinfo_proc *kInfoProc; managementInfoBase[0] = CTL_KERN; managementInfoBase[1] = KERN_PROC; managementInfoBase[2] = KERN_PROC_PID; managementInfoBase[3] = pid; managementInfoBase[4] = sizeof(struct kinfo_proc); managementInfoBase[5] = 1; if (::sysctl(managementInfoBase, 6, NULL, &mibLength, NULL, 0) == -1) { qWarning() << "first sysctl() call failed with code" << errno; return false; } kInfoProc = (struct kinfo_proc *)malloc(mibLength); if (::sysctl(managementInfoBase, 6, kInfoProc, &mibLength, NULL, 0) == -1) { qWarning() << "second sysctl() call failed with code" << errno; free(kInfoProc); return false; } setName(kInfoProc->p_comm); setPid(kInfoProc->p_pid); setParentPid(kInfoProc->p_ppid); setForegroundPid(kInfoProc->p_tpgid); setUserId(kInfoProc->p_uid); setUserName(kInfoProc->p_login); free(kInfoProc); return true; } char **readProcArgs(int pid, int whatMib) { void *buf = NULL; void *nbuf; size_t len = 4096; int rc = -1; int managementInfoBase[4]; managementInfoBase[0] = CTL_KERN; managementInfoBase[1] = KERN_PROC_ARGS; managementInfoBase[2] = pid; managementInfoBase[3] = whatMib; do { len *= 2; nbuf = realloc(buf, len); if (nbuf == NULL) { break; } buf = nbuf; rc = ::sysctl(managementInfoBase, 4, buf, &len, NULL, 0); qWarning() << "sysctl() call failed with code" << errno; } while (rc == -1 && errno == ENOMEM); if (nbuf == NULL || rc == -1) { free(buf); return NULL; } return (char **)buf; } bool readArguments(int pid) Q_DECL_OVERRIDE { char **argv; argv = readProcArgs(pid, KERN_PROC_ARGV); if (argv == NULL) { return false; } for (char **p = argv; *p != NULL; p++) { addArgument(QString(*p)); } free(argv); return true; } }; #elif defined(Q_OS_MACOS) class MacProcessInfo : public UnixProcessInfo { public: MacProcessInfo(int pid, const QString &titleFormat) : UnixProcessInfo(pid, titleFormat) { } protected: bool readCurrentDir(int pid) Q_DECL_OVERRIDE { struct proc_vnodepathinfo vpi; const int nb = proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, 0, &vpi, sizeof(vpi)); if (nb == sizeof(vpi)) { setCurrentDir(QString::fromUtf8(vpi.pvi_cdir.vip_path)); return true; } return false; } private: bool readProcInfo(int pid) Q_DECL_OVERRIDE { int managementInfoBase[4]; size_t mibLength; struct kinfo_proc *kInfoProc; QT_STATBUF statInfo; // Find the tty device of 'pid' (Example: /dev/ttys001) managementInfoBase[0] = CTL_KERN; managementInfoBase[1] = KERN_PROC; managementInfoBase[2] = KERN_PROC_PID; managementInfoBase[3] = pid; if (sysctl(managementInfoBase, 4, NULL, &mibLength, NULL, 0) == -1) { return false; } else { kInfoProc = new struct kinfo_proc [mibLength]; if (sysctl(managementInfoBase, 4, kInfoProc, &mibLength, NULL, 0) == -1) { delete [] kInfoProc; return false; } else { const QString deviceNumber = QString::fromUtf8(devname(((&kInfoProc->kp_eproc)->e_tdev), S_IFCHR)); const QString fullDeviceName = QStringLiteral("/dev/") + deviceNumber.rightJustified(3, QLatin1Char('0')); delete [] kInfoProc; const QByteArray deviceName = fullDeviceName.toLatin1(); const char *ttyName = deviceName.data(); if (QT_STAT(ttyName, &statInfo) != 0) { return false; } // Find all processes attached to ttyName managementInfoBase[0] = CTL_KERN; managementInfoBase[1] = KERN_PROC; managementInfoBase[2] = KERN_PROC_TTY; managementInfoBase[3] = statInfo.st_rdev; mibLength = 0; if (sysctl(managementInfoBase, sizeof(managementInfoBase) / sizeof(int), NULL, &mibLength, NULL, 0) == -1) { return false; } kInfoProc = new struct kinfo_proc [mibLength]; if (sysctl(managementInfoBase, sizeof(managementInfoBase) / sizeof(int), kInfoProc, &mibLength, NULL, 0) == -1) { return false; } // The foreground program is the first one setName(QString::fromUtf8(kInfoProc->kp_proc.p_comm)); delete [] kInfoProc; } setPid(pid); } return true; } bool readArguments(int pid) Q_DECL_OVERRIDE { Q_UNUSED(pid); return false; } }; #elif defined(Q_OS_SOLARIS) // The procfs structure definition requires off_t to be // 32 bits, which only applies if FILE_OFFSET_BITS=32. // Futz around here to get it to compile regardless, // although some of the structure sizes might be wrong. // Fortunately, the structures we actually use don't use // off_t, and we're safe. #if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64) #undef _FILE_OFFSET_BITS #endif #include class SolarisProcessInfo : public UnixProcessInfo { public: SolarisProcessInfo(int pid, const QString &titleFormat) : UnixProcessInfo(pid, titleFormat) { } protected: // FIXME: This will have the same issues as BKO 251351; the Linux // version uses readlink. bool readCurrentDir(int pid) Q_DECL_OVERRIDE { QFileInfo info(QString("/proc/%1/path/cwd").arg(pid)); const bool readable = info.isReadable(); if (readable && info.isSymLink()) { setCurrentDir(info.symLinkTarget()); return true; } else { if (!readable) { setError(PermissionsError); } else { setError(UnknownError); } return false; } } private: bool readProcInfo(int pid) Q_DECL_OVERRIDE { QFile psinfo(QString("/proc/%1/psinfo").arg(pid)); if (psinfo.open(QIODevice::ReadOnly)) { struct psinfo info; if (psinfo.read((char *)&info, sizeof(info)) != sizeof(info)) { return false; } setParentPid(info.pr_ppid); setForegroundPid(info.pr_pgid); setName(info.pr_fname); setPid(pid); // Bogus, because we're treating the arguments as one single string info.pr_psargs[PRARGSZ - 1] = 0; addArgument(info.pr_psargs); } return true; } bool readArguments(int /*pid*/) Q_DECL_OVERRIDE { // Handled in readProcInfo() return false; } }; #endif SSHProcessInfo::SSHProcessInfo(const ProcessInfo &process) : _process(process), _user(QString()), _host(QString()), _port(QString()), _command(QString()) { bool ok = false; // check that this is a SSH process const QString &name = _process.name(&ok); if (!ok || name != QLatin1String("ssh")) { if (!ok) { qWarning() << "Could not read process info"; } else { qWarning() << "Process is not a SSH process"; } return; } // read arguments const QVector &args = _process.arguments(&ok); // SSH options // these are taken from the SSH manual ( accessed via 'man ssh' ) // options which take no arguments static const QString noArgumentOptions(QStringLiteral("1246AaCfgKkMNnqsTtVvXxYy")); // options which take one argument static const QString singleArgumentOptions(QStringLiteral("bcDeFIiLlmOopRSWw")); if (ok) { // find the username, host and command arguments // // the username/host is assumed to be the first argument // which is not an option // ( ie. does not start with a dash '-' character ) // or an argument to a previous option. // // the command, if specified, is assumed to be the argument following // the username and host // // note that we skip the argument at index 0 because that is the // program name ( expected to be 'ssh' in this case ) for (int i = 1; i < args.count(); i++) { // If this one is an option ... // Most options together with its argument will be skipped. if (args[i].startsWith(QLatin1Char('-'))) { const QChar optionChar = (args[i].length() > 1) ? args[i][1] : QLatin1Char('\0'); // for example: -p2222 or -p 2222 ? const bool optionArgumentCombined = args[i].length() > 2; if (noArgumentOptions.contains(optionChar)) { continue; } else if (singleArgumentOptions.contains(optionChar)) { QString argument; if (optionArgumentCombined) { argument = args[i].mid(2); } else { // Verify correct # arguments are given if ((i + 1) < args.count()) { argument = args[i + 1]; } i++; } // support using `-l user` to specify username. if (optionChar == QLatin1Char('l')) { _user = argument; } // support using `-p port` to specify port. else if (optionChar == QLatin1Char('p')) { _port = argument; } continue; } } // check whether the host has been found yet // if not, this must be the username/host argument if (_host.isEmpty()) { // check to see if only a hostname is specified, or whether // both a username and host are specified ( in which case they // are separated by an '@' character: username@host ) const int separatorPosition = args[i].indexOf(QLatin1Char('@')); if (separatorPosition != -1) { // username and host specified _user = args[i].left(separatorPosition); _host = args[i].mid(separatorPosition + 1); } else { // just the host specified _host = args[i]; } } else { // host has already been found, this must be part of the // command arguments. // Note this is not 100% correct. If any of the above // noArgumentOptions or singleArgumentOptions are found, this // will not be correct (example ssh server top -i 50) // Suggest putting ssh command in quotes if (_command.isEmpty()) { _command = args[i]; } else { _command = _command + QLatin1Char(' ') + args[i]; } } } } else { qWarning() << "Could not read arguments"; return; } } QString SSHProcessInfo::userName() const { return _user; } QString SSHProcessInfo::host() const { return _host; } QString SSHProcessInfo::port() const { return _port; } QString SSHProcessInfo::command() const { return _command; } QString SSHProcessInfo::format(const QString &input) const { QString output(input); // search for and replace known markers output.replace(QLatin1String("%u"), _user); // provide 'user@' if user is defined -- this makes nicer // remote tabs possible: "%U%h %c" => User@Host Command // => Host Command // Depending on whether -l was passed to ssh (which is mostly not the // case due to ~/.ssh/config). if (_user.isEmpty()) { output.replace(QLatin1String("%U"), QString()); } else { output.replace(QLatin1String("%U"), _user + QLatin1Char('@')); } // test whether host is an ip address // in which case 'short host' and 'full host' // markers in the input string are replaced with // the full address struct in_addr address; const bool isIpAddress = inet_aton(_host.toLocal8Bit().constData(), &address) != 0; if (isIpAddress) { output.replace(QLatin1String("%h"), _host); } else { output.replace(QLatin1String("%h"), _host.left(_host.indexOf(QLatin1Char('.')))); } output.replace(QLatin1String("%H"), _host); output.replace(QLatin1String("%c"), _command); return output; } ProcessInfo *ProcessInfo::newInstance(int pid, const QString &titleFormat) { ProcessInfo *info; #if defined(Q_OS_LINUX) info = new LinuxProcessInfo(pid, titleFormat); #elif defined(Q_OS_SOLARIS) info = new SolarisProcessInfo(pid, titleFormat); #elif defined(Q_OS_MACOS) info = new MacProcessInfo(pid, titleFormat); #elif defined(Q_OS_FREEBSD) info = new FreeBSDProcessInfo(pid, titleFormat); #elif defined(Q_OS_OPENBSD) info = new OpenBSDProcessInfo(pid, titleFormat); #else info = new NullProcessInfo(pid, titleFormat); #endif info->readProcessInfo(pid); return info; }