From b317fad973b19bf7bc4566c39648f7a1e5a257dd Mon Sep 17 00:00:00 2001 From: Michal Ambroz Date: Mar 22 2021 21:43:05 +0000 Subject: Initial r2cutter package for Fedora. --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c03546b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/cutter-translations-8e1d24b4040474c681d8db39cb75c0ed66bb5bda.tar.gz +/r2cutter-0.1.1.tar.gz diff --git a/r2cutter-00-unhandled-write.patch b/r2cutter-00-unhandled-write.patch new file mode 100644 index 0000000..634b78f --- /dev/null +++ b/r2cutter-00-unhandled-write.patch @@ -0,0 +1,33 @@ +From 3e34672e7e2cb2bdba3541f391121e0cf52d508c Mon Sep 17 00:00:00 2001 +From: pancake +Date: Fri, 19 Mar 2021 10:58:02 +0100 +Subject: [PATCH] Fix #10 - check return value of write API call + +--- + src/widgets/ConsoleWidget.cpp | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/src/widgets/ConsoleWidget.cpp b/src/widgets/ConsoleWidget.cpp +index 8730f953..cb920e29 100644 +--- a/src/widgets/ConsoleWidget.cpp ++++ b/src/widgets/ConsoleWidget.cpp +@@ -247,10 +247,15 @@ void ConsoleWidget::executeCommand(const QString &command) + + void ConsoleWidget::sendToStdin(const QString &input) + { +-#ifndef Q_OS_WIN +- write(stdinFile, (input + "\n").toStdString().c_str(), input.size() + 1); +- fsync(stdinFile); +- addOutput("Sent input: '" + input + "'"); ++#if __UNIX__ ++ ssize_t input_size = input.size() + 1; ++ ssize_t res = write(stdinFile, (input + "\n").toStdString().c_str(), input_size); ++ if (res == input_size) { ++ fsync(stdinFile); ++ addOutput("Sent input: '" + input + "'"); ++ } else { ++ addOutput("Couldn't write to stdin."); ++ } + #else + // Stdin redirection isn't currently available in windows because console applications + // with stdin already get their own console window with stdin when they are launched diff --git a/r2cutter-01-unused-iod.patch b/r2cutter-01-unused-iod.patch new file mode 100644 index 0000000..2bcd02d --- /dev/null +++ b/r2cutter-01-unused-iod.patch @@ -0,0 +1,23 @@ +From 19435220bfa377a503a32aa4b0bb660cfd8a274a Mon Sep 17 00:00:00 2001 +From: pancake +Date: Fri, 19 Mar 2021 10:50:52 +0100 +Subject: [PATCH] Fix #9 - move unused variable into commented code block + +--- + src/core/Cutter.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp +index af4a2c33..b8f082c4 100644 +--- a/src/core/Cutter.cpp ++++ b/src/core/Cutter.cpp +@@ -612,8 +612,8 @@ bool CutterCore::loadFile(QString path, ut64 baddr, ut64 mapaddr, int perms, int + // Not loading RBin info coz va = false + } + +- auto iod = core->io ? core->io->desc : NULL; + /* ++ auto iod = core->io ? core->io->desc : NULL; + auto debug = core->file && iod && (core->file->fd == iod->fd) && iod->plugin && \ + iod->plugin->isdbg; + */ diff --git a/r2cutter-02-doubled-enum.patch b/r2cutter-02-doubled-enum.patch new file mode 100644 index 0000000..9fdd9ad --- /dev/null +++ b/r2cutter-02-doubled-enum.patch @@ -0,0 +1,51 @@ +From 7d9729bbffe18a87c6039b583c30ea84887bdff1 Mon Sep 17 00:00:00 2001 +From: pancake +Date: Fri, 19 Mar 2021 10:54:23 +0100 +Subject: [PATCH] Fix #8 - do not define the same enum twice + +--- + src/widgets/ProcessesWidget.cpp | 10 ++++------ + src/widgets/ThreadsWidget.cpp | 9 +++------ + 2 files changed, 7 insertions(+), 12 deletions(-) + +diff --git a/src/widgets/ProcessesWidget.cpp b/src/widgets/ProcessesWidget.cpp +index 75e95489..946d607c 100644 +--- a/src/widgets/ProcessesWidget.cpp ++++ b/src/widgets/ProcessesWidget.cpp +@@ -9,12 +9,10 @@ + + #define DEBUGGED_PID (-1) + +-enum ColumnIndex { +- COLUMN_PID = 0, +- COLUMN_UID, +- COLUMN_STATUS, +- COLUMN_PATH, +-}; ++#define COLUMN_PID 0 ++#define COLUMN_UID 1 ++#define COLUMN_STATUS 2 ++#define COLUMN_PATH 3 + + ProcessesWidget::ProcessesWidget(MainWindow *main) : + CutterDockWidget(main), +diff --git a/src/widgets/ThreadsWidget.cpp b/src/widgets/ThreadsWidget.cpp +index 75f9c726..36508dc1 100644 +--- a/src/widgets/ThreadsWidget.cpp ++++ b/src/widgets/ThreadsWidget.cpp +@@ -8,12 +8,9 @@ + #include "core/MainWindow.h" + + #define DEBUGGED_PID (-1) +- +-enum ColumnIndex { +- COLUMN_PID = 0, +- COLUMN_STATUS, +- COLUMN_PATH, +-}; ++#define COLUMN_PID 0 ++#define COLUMN_STATUS 1 ++#define COLUMN_PATH 2 + + ThreadsWidget::ThreadsWidget(MainWindow *main) : + CutterDockWidget(main), diff --git a/r2cutter-03-unhandled-pipe.patch b/r2cutter-03-unhandled-pipe.patch new file mode 100644 index 0000000..1d495d4 --- /dev/null +++ b/r2cutter-03-unhandled-pipe.patch @@ -0,0 +1,38 @@ +From 955d6278363474a3e91aaff4b2ef846b094422ca Mon Sep 17 00:00:00 2001 +From: xambroz <723625+xambroz@users.noreply.github.com> +Date: Sat, 20 Mar 2021 21:26:59 +0100 +Subject: [PATCH] Unhandled result of the pipe function + +Added handling of the failed call to pipe function. + +Related to https://github.com/radareorg/r2cutter/issues/10 + +[ 58%] Building CXX object CMakeFiles/r2cutter.dir/widgets/ProcessesWidget.cpp.o +/usr/lib64/ccache/g++ -DCUTTER_ENABLE_GRAPHVIZ -DCUTTER_ENABLE_KSYNTAXHIGHLIGHTING -DCUTTER_SOURCE_BUILD -DQT_CORE_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_NO_DEBUG -DQT_SVG_LIB -DQT_WIDGETS_LIB -Dr2cutter_EXPORTS -I/home/mambroz/rpmbuild/BUILD/r2cutter-0.1.1/x86_64-redhat-linux-gnu -I/home/mambroz/rpmbuild/BUILD/r2cutter-0.1.1/src -I/home/mambroz/rpmbuild/BUILD/r2cutter-0.1.1/x86_64-redhat-linux-gnu/r2cutter_autogen/include -I/home/mambroz/rpmbuild/BUILD/r2cutter-0.1.1/src/core -I/home/mambroz/rpmbuild/BUILD/r2cutter-0.1.1/src/widgets -I/home/mambroz/rpmbuild/BUILD/r2cutter-0.1.1/src/common -I/home/mambroz/rpmbuild/BUILD/r2cutter-0.1.1/src/plugins -I/home/mambroz/rpmbuild/BUILD/r2cutter-0.1.1/src/menus -I/home/mambroz/rpmbuild/BUILD/r2cutter-0.1.1/src/. -isystem /usr/include/graphviz -isystem /usr/include/qt5 -isystem /usr/include/qt5/QtCore -isystem /usr/lib64/qt5/mkspecs/linux-g++ -isystem /usr/include/qt5/QtWidgets -isystem /usr/include/qt5/QtGui -isystem /usr/include/qt5/QtSvg -isystem /usr/include/qt5/QtNetwork -isystem /usr/include/libr -isystem /usr/include/libr/sdb -isystem /usr/include/capstone -isystem /usr/include/KF5/KSyntaxHighlighting -isystem /usr/include/KF5 -O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fvisibility=hidden -Wall -Wextra -fPIC -std=gnu++11 -o CMakeFiles/r2cutter.dir/widgets/ProcessesWidget.cpp.o -c /home/mambroz/rpmbuild/BUILD/r2cutter-0.1.1/src/widgets/ProcessesWidget.cpp +/home/mambroz/rpmbuild/BUILD/r2cutter-0.1.1/src/widgets/ConsoleWidget.cpp: In member function 'void ConsoleWidget::redirectOutput()': +/home/mambroz/rpmbuild/BUILD/r2cutter-0.1.1/src/widgets/ConsoleWidget.cpp:468:9: warning: ignoring return value of 'int pipe(int*)' declared with attribute 'warn_unused_result' [-Wunused-result] + 468 | pipe(redirectPipeFds); + | ~~~~^~~~~~~~~~~~~~~~~ +[ 60%] Building CXX object CMakeFiles/r2cutter.dir/dialogs/MapFileDialog.cpp.o +[ 60%] Building CXX object CMakeFiles/r2cutter.dir/widgets/BacktraceWidget.cpp.o +[ 60%] Building CXX object CMakeFiles/r2cutter.dir/common/CommandTask.cpp.o +--- + src/widgets/ConsoleWidget.cpp | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/widgets/ConsoleWidget.cpp b/src/widgets/ConsoleWidget.cpp +index cb920e29..544a02bb 100644 +--- a/src/widgets/ConsoleWidget.cpp ++++ b/src/widgets/ConsoleWidget.cpp +@@ -465,7 +465,10 @@ void ConsoleWidget::redirectOutput() + + pipeSocket->connectToServer(pipeName, QIODevice::ReadOnly); + #else +- pipe(redirectPipeFds); ++ if (0 > pipe(redirectPipeFds)) { ++ addOutput("Failed to create pipe."); ++ return; ++ } + stdinFifoPath = QString(STDIN_PIPE_NAME).arg(QDir::tempPath(), QUuid::createUuid().toString()); + mkfifo(stdinFifoPath.toStdString().c_str(), (mode_t) 0777); + stdinFile = open(stdinFifoPath.toStdString().c_str(), O_RDWR | O_ASYNC); diff --git a/r2cutter-04-uninitialized-menu.patch b/r2cutter-04-uninitialized-menu.patch new file mode 100644 index 0000000..517da06 --- /dev/null +++ b/r2cutter-04-uninitialized-menu.patch @@ -0,0 +1,31 @@ +From f9acd9e53ff7bd936a731bfc446461946c6b57a9 Mon Sep 17 00:00:00 2001 +From: xambroz <723625+xambroz@users.noreply.github.com> +Date: Sat, 20 Mar 2021 21:52:02 +0100 +Subject: [PATCH] Avoid warning about uninitialized menu + +Avoid warning message about potentially uninitialized variable "menu". +It is initialized in if/else so just nulling it on the beginning should do the trick. + +[ 36%] Building CXX object CMakeFiles/r2cutter.dir/widgets/DisassemblyWidget.cpp.o +/usr/lib64/ccache/g++ -DCUTTER_ENABLE_GRAPHVIZ -DCUTTER_ENABLE_KSYNTAXHIGHLIGHTING -DCUTTER_SOURCE_BUILD -DQT_CORE_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_NO_DEBUG -DQT_SVG_LIB -DQT_WIDGETS_LIB -Dr2cutter_EXPORTS -IBUILD/r2cutter-0.1.1/x86_64-redhat-linux-gnu -IBUILD/r2cutter-0.1.1/src -IBUILD/r2cutter-0.1.1/x86_64-redhat-linux-gnu/r2cutter_autogen/include -IBUILD/r2cutter-0.1.1/src/core -IBUILD/r2cutter-0.1.1/src/widgets -IBUILD/r2cutter-0.1.1/src/common -IBUILD/r2cutter-0.1.1/src/plugins -IBUILD/r2cutter-0.1.1/src/menus -IBUILD/r2cutter-0.1.1/src/. -isystem /usr/include/graphviz -isystem /usr/include/qt5 -isystem /usr/include/qt5/QtCore -isystem /usr/lib64/qt5/mkspecs/linux-g++ -isystem /usr/include/qt5/QtWidgets -isystem /usr/include/qt5/QtGui -isystem /usr/include/qt5/QtSvg -isystem /usr/include/qt5/QtNetwork -isystem /usr/include/libr -isystem /usr/include/libr/sdb -isystem /usr/include/capstone -isystem /usr/include/KF5/KSyntaxHighlighting -isystem /usr/include/KF5 -O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fvisibility=hidden -Wall -Wextra -fPIC -std=gnu++11 -o CMakeFiles/r2cutter.dir/widgets/DisassemblyWidget.cpp.o -c BUILD/r2cutter-0.1.1/src/widgets/DisassemblyWidget.cpp +BUILD/r2cutter-0.1.1/src/menus/DecompilerContextMenu.cpp: In member function 'DecompilerContextMenu::updateTargetMenuActions()': +BUILD/r2cutter-0.1.1/src/menus/DecompilerContextMenu.cpp:575:24: warning: 'menu' may be used uninitialized in this function [-Wmaybe-uninitialized] + 575 | action->setMenu(menu); + | ~~~~~~~~~~~~~~~^~~~~~ +--- + src/menus/DecompilerContextMenu.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/menus/DecompilerContextMenu.cpp b/src/menus/DecompilerContextMenu.cpp +index 206ca4b2..7044b520 100644 +--- a/src/menus/DecompilerContextMenu.cpp ++++ b/src/menus/DecompilerContextMenu.cpp +@@ -552,7 +552,7 @@ void DecompilerContextMenu::updateTargetMenuActions() + RCoreLocked core = Core()->core(); + if (isReference()) { + QString name; +- QMenu *menu; ++ QMenu *menu = NULL; + if (annotationHere->type == R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE + || annotationHere->type == R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE) { + menu = mainWindow->createShowInMenu(this, annotationHere->reference.offset, diff --git a/r2cutter.spec b/r2cutter.spec new file mode 100644 index 0000000..ae14c36 --- /dev/null +++ b/r2cutter.spec @@ -0,0 +1,184 @@ +Name: r2cutter +Version: 0.1.1 +Release: 3%{?dist} +Summary: GUI for radare2 reverse engineering framework + +%global cutter_translations_commit 8e1d24b4040474c681d8db39cb75c0ed66bb5bda + + +# CC-BY-SA: src/img/icons/ +# CC0: src/fonts/Anonymous Pro.ttf +License: GPLv3 and CC-BY-SA and CC0 + +URL: https://github.com/radareorg/r2cutter/ +Source0: https://github.com/radareorg/r2cutter/archive/%{version}/r2cutter-%{version}.tar.gz +Source1: https://github.com/radareorg/cutter-translations/archive/%{cutter_translations_commit}.tar.gz#/cutter-translations-%{cutter_translations_commit}.tar.gz + +# Cosmetics - GCC10 compilation warnings - Fix unhandled pipe return code +# reported to upstream https://github.com/radareorg/r2cutter/issues/10 +Patch0: https://github.com/radareorg/r2cutter/commit/3e34672e7e2cb2bdba3541f391121e0cf52d508c.patch#/r2cutter-00-unhandled-write.patch + + +# Cosmetics - GCC10 compilation warnings - get rid of unused iod variable +# reported to upstream https://github.com/radareorg/r2cutter/issues/9 +Patch1: https://github.com/radareorg/r2cutter/commit/19435220bfa377a503a32aa4b0bb660cfd8a274a.patch#/r2cutter-01-unused-iod.patch + +# Cosmetics - GCC10 compilation warnings - Two definitions of the ColumnIndex +# reported to upstream https://github.com/radareorg/r2cutter/issues/8 +Patch2: https://github.com/radareorg/r2cutter/commit/7d9729bbffe18a87c6039b583c30ea84887bdff1.patch#/r2cutter-02-doubled-enum.patch + +# Cosmetics - GCC10 compilation warnings - Fix unhandled pipe return code +# reported to upstream https://github.com/radareorg/r2cutter/pull/11 +Patch3: https://github.com/radareorg/r2cutter/commit/955d6278363474a3e91aaff4b2ef846b094422ca.patch#/r2cutter-03-unhandled-pipe.patch + +# Cosmetics - GCC10 compilation warnings - Avoid warning about uninitialized menu +# reported to upstream https://github.com/radareorg/r2cutter/pull/12 +Patch4: https://github.com/radareorg/r2cutter/commit/f9acd9e53ff7bd936a731bfc446461946c6b57a9.patch#/r2cutter-04-uninitialized-menu.patch + + +BuildRequires: radare2-devel >= 4.5.0 +BuildRequires: cmake +BuildRequires: gcc-c++ +BuildRequires: make +BuildRequires: kf5-syntax-highlighting-devel +BuildRequires: python3-devel +BuildRequires: qt5-qtsvg-devel +BuildRequires: file-devel +BuildRequires: desktop-file-utils +BuildRequires: libappstream-glib +BuildRequires: graphviz-devel +BuildRequires: qt5-linguist +%ifarch %{qt5_qtwebengine_arches} +BuildRequires: qt5-qtwebengine-devel +%endif +Requires: python3-jupyter-client +Requires: python3-notebook +Requires: hicolor-icon-theme + +%description +R2Cutter is a Qt and C++ GUI for radare2. +It is the continuation of Cutter before the fork to keep radare2 as backend. +Its goal is making an advanced, customizable and FOSS reverse-engineering +platform while keeping the user experience at mind. +The r2cutter is created by reverse engineers for reverse engineers. +Focus on supporting latest version of radare2. +Recommend the use of system installed libraries/radare2. +Closer integration between r2 and the UI. + +%package devel +Summary: Development files for the r2cutter package +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description devel +Development files for the r2cutter package. See r2cutter package for more +information. + + +%prep +%autosetup -p1 -n r2cutter-%{version} +tar --strip-component=1 -xvf %{SOURCE1} -C src/translations + + +%build +%cmake src +%cmake_build + + +%install +%cmake_install + +mkdir -p %{buildroot}%{_metainfodir} +install -pm644 src/org.radare.r2cutter.appdata.xml %{buildroot}%{_metainfodir} + + +%check +appstream-util validate-relax --nonet %{buildroot}%{_metainfodir}/*.appdata.xml +desktop-file-validate %{buildroot}/%{_datadir}/applications/*.desktop + + +%files +%{_bindir}/r2cutter +%{_datadir}/applications/*.desktop +%{_datadir}/RadareOrg/ +%{_metainfodir}/*.appdata.xml +%{_datadir}/icons/hicolor/scalable/apps/*.svg +%license COPYING src/img/icons/Iconic-LICENSE +%doc README.md + + +%files devel +%{_includedir}/r2cutter +%{_libdir}/r2cutter/*.cmake +%dir %{_libdir}/r2cutter + + +%changelog +* Fri Mar 19 2021 Michal Ambroz - 0.1.1-3 +- switch from cutter to r2cutter +- cosmetic patches to fix gcc10+ warnings (reported upstream) + +* Tue Jan 26 2021 Fedora Release Engineering - 0.0.1.11.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Mon Jul 27 2020 Fedora Release Engineering - 0.0.1.11.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Mon Jul 27 2020 Riccardo Schirone - 0.0.1.11.0-1 +- Bump to upstream version 1.11.0-1 (Thanks to Michal Ambroz, changes mostly + taken from https://src.fedoraproject.org/rpms/cutter-re/pull-request/2#request_diff) +- Add cutter translations +- Provide -devel sub package to allow compilation of cutter plugins + +* Fri May 8 2020 Riccardo Schirone - 0.0.1.10.2-2 +- Just re-build + +* Tue May 5 2020 Riccardo Schirone - 0.0.1.10.2-1 +- Rebase to upstream version 1.10.2 + +* Tue May 5 2020 Riccardo Schirone - 0.0.1.10.1-5 +- Re-build for new radare2 release + +* Wed Feb 5 2020 Riccardo Schirone - 0.0.1.10.1-4 +- Just use the right desktop file name and app metadata instead of messing with cutter source code + +* Wed Feb 5 2020 Riccardo Schirone - 0.0.1.10.1-3 +- Rebuild with new radare2 + +* Wed Feb 5 2020 Riccardo Schirone - 0.0.1.10.1-2 +- Fix the main window icon + +* Mon Feb 3 2020 Riccardo Schirone - 0.0.1.10.1-1 +- Rebase to cutter 1.10.1 + +* Tue Jan 28 2020 Fedora Release Engineering - 0.0.1.9.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Fri Oct 11 2019 Riccardo Schirone - 0.0.1.9.0-2 +- Rebuilt for radare2-3.9.0-3 + +* Mon Sep 30 2019 Riccardo Schirone - 0.0.1.9.0-1 +- rebase to cutter 1.9.0 + +* Wed Jul 24 2019 Fedora Release Engineering - 0.0.1.8.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Mon Jul 15 2019 Riccardo Schirone - 0.0.1.8.3-1 +- rebase to cutter 1.8.3 + +* Wed Jun 26 2019 Riccardo Schirone - 0.0.1.8.0-4 +- recompile for radare2 3.6.0 + +* Mon Apr 15 2019 Riccardo Schirone - 0.0.1.8.0-3 +- recompile for radare2 3.4.1 + +* Tue Apr 09 2019 Lubomir Rintel - 0.0.1.8.0-2 +- Update to radare2 3.4.1 + +* Thu Mar 21 2019 Lubomir Rintel - 0.0.1.8.0-1 +- Update to 1.8.0 +- Require hicolor-icon-theme +- Move appdata to a correct location +- Fix license field (Robert-André Mauchin, #1690050) + +* Thu Mar 14 2019 Lubomir Rintel - 0.0.1.7.4-1 +- Cutter - Initial packaging diff --git a/sources b/sources new file mode 100644 index 0000000..4021728 --- /dev/null +++ b/sources @@ -0,0 +1,2 @@ +SHA512 (cutter-translations-8e1d24b4040474c681d8db39cb75c0ed66bb5bda.tar.gz) = f82ba586ff2061e2e19041bcb1c6a5ebfefc72b86fdf412712ccb6613e52931b5c568941c2b9845bd52a6cac688556f892d1e4543eb918357900ce0283918d49 +SHA512 (r2cutter-0.1.1.tar.gz) = f1059af8076774ec249701b8407933e0e52a2672dc97dae5450c73ff72a563390f46cf44e78914a52481bb409ded751a9b079963ecd8f04a29ee2783a4f3cd90