From 71b82656b1a1fcfa74d9734e5511e51946584ebc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= <capelle.mikael@gmail.com>
Date: Sun, 9 Jun 2024 13:47:43 +0200
Subject: [PATCH 1/4] Remove appveyor.yml.

---
 appveyor.yml | 40 ----------------------------------------
 1 file changed, 40 deletions(-)
 delete mode 100644 appveyor.yml

diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index ca6eec6..0000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,40 +0,0 @@
-version: 1.0.{build}
-skip_branch_with_pr: true
-image: Visual Studio 2019
-environment:
-  WEBHOOK_URL:
-    secure: gOKbXaZM9ImtMD5XrYITvdyZUW/az082G9OIN1EC1Vbg57wBaeLhi49uGjxPw5GVujHku6kxN6ab89zhbS5GVeluR76GM83IbKV4Sh7udXzoYZZdg6YudtYHzdhCgUeiedpswbuczTq9ceIkkfSEWZuh/lMAAVVwvcGsJAnoPFw=
-build_script:
-- pwsh: >-
-    $ErrorActionPreference = 'Stop'
-
-    git clone --depth=1 --no-single-branch https://github.com/ModOrganizer2/modorganizer-umbrella.git c:\projects\modorganizer-umbrella
-
-    New-Item -ItemType Directory -Path c:\projects\modorganizer-build
-
-    cd c:\projects\modorganizer-umbrella
-    
-    ($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH -eq $null) ? ($branch = $env:APPVEYOR_REPO_BRANCH) : ($branch = $env:APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH)
-
-    git checkout $(git show-ref --verify --quiet refs/remotes/origin/${branch} || echo '-b') ${branch}
-
-    C:\Python37-x64\python.exe unimake.py -d c:\projects\modorganizer-build -s Appveyor_Build=True ${env:APPVEYOR_PROJECT_NAME}
-    
-    if($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode )  }
-artifacts:
-- path: vsbuild\src\RelWithDebInfo\installer_bundle.dll
-  name: installer_bundle_dll
-- path: vsbuild\src\RelWithDebInfo\installer_bundle.pdb
-  name: installer_bundle_pdb
-- path: vsbuild\src\RelWithDebInfo\installer_bundle.lib
-  name: installer_bundle_lib
-on_success:
-  - ps: Set-Location -Path $env:APPVEYOR_BUILD_FOLDER
-  - ps: Invoke-RestMethod https://raw.githubusercontent.com/DiscordHooks/appveyor-discord-webhook/master/send.ps1 -o send.ps1
-  - ps: ./send.ps1 success $env:WEBHOOK_URL
-on_failure:
-  - ps: Set-Location -Path $env:APPVEYOR_BUILD_FOLDER
-  - ps: Push-AppveyorArtifact ${env:APPVEYOR_BUILD_FOLDER}\stdout.log
-  - ps: Push-AppveyorArtifact ${env:APPVEYOR_BUILD_FOLDER}\stderr.log
-  - ps: Invoke-RestMethod https://raw.githubusercontent.com/DiscordHooks/appveyor-discord-webhook/master/send.ps1 -o send.ps1
-  - ps: ./send.ps1 failure $env:WEBHOOK_URL
\ No newline at end of file

From 988523cac10641c510fd711d80d59c82d473f6a5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= <capelle.mikael@gmail.com>
Date: Sun, 9 Jun 2024 13:47:44 +0200
Subject: [PATCH 2/4] Format files and add .gitattributes and .clang-format.

---
 .clang-format              |  41 +++++++++
 .gitattributes             |   7 ++
 src/installerbundle.cpp    |  54 ++++++-----
 src/installerbundle.h      | 177 ++++++++++++++++++-------------------
 src/multiarchivedialog.cpp |  12 +--
 src/multiarchivedialog.h   |  39 ++++----
 6 files changed, 186 insertions(+), 144 deletions(-)
 create mode 100644 .clang-format
 create mode 100644 .gitattributes

diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..6098e1f
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,41 @@
+---
+# We'll use defaults from the LLVM style, but with 4 columns indentation.
+BasedOnStyle: LLVM
+IndentWidth: 2
+---
+Language: Cpp
+DeriveLineEnding: false
+UseCRLF: true
+DerivePointerAlignment: false
+PointerAlignment: Left
+AlignConsecutiveAssignments: true
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: Never
+AllowShortLambdasOnASingleLine: Empty
+AlwaysBreakTemplateDeclarations: Yes
+AccessModifierOffset: -2
+AlignTrailingComments: true
+SpacesBeforeTrailingComments: 2
+NamespaceIndentation: Inner
+MaxEmptyLinesToKeep: 1
+BreakBeforeBraces: Custom
+BraceWrapping:
+  AfterCaseLabel: false
+  AfterClass: true
+  AfterControlStatement: false
+  AfterEnum: true
+  AfterFunction: true
+  AfterNamespace: true
+  AfterStruct: true
+  AfterUnion: true
+  AfterExternBlock: true
+  BeforeCatch: false
+  BeforeElse: false
+  BeforeLambdaBody: false
+  BeforeWhile: false
+  IndentBraces: false
+  SplitEmptyFunction: false
+  SplitEmptyRecord: false
+  SplitEmptyNamespace: true
+ColumnLimit: 88
+ForEachMacros: ['Q_FOREACH', 'foreach']
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..f869712
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,7 @@
+# Set the default behavior, in case people don't have core.autocrlf set.
+* text=auto
+
+# Explicitly declare text files you want to always be normalized and converted
+# to native line endings on checkout.
+*.cpp text eol=crlf
+*.h text eol=crlf
diff --git a/src/installerbundle.cpp b/src/installerbundle.cpp
index e7b3da2..6fe205f 100644
--- a/src/installerbundle.cpp
+++ b/src/installerbundle.cpp
@@ -26,13 +26,9 @@ along with Mod Organizer.  If not, see <http://www.gnu.org/licenses/>.
 
 using namespace MOBase;
 
+InstallerBundle::InstallerBundle() {}
 
-InstallerBundle::InstallerBundle()
-{
-}
-
-
-bool InstallerBundle::init(IOrganizer *organizer)
+bool InstallerBundle::init(IOrganizer* organizer)
 {
   m_Organizer = organizer;
   return true;
@@ -65,9 +61,10 @@ VersionInfo InstallerBundle::version() const
 
 QList<PluginSetting> InstallerBundle::settings() const
 {
-  return {
-    PluginSetting("auto_reinstall", "when reinstalling from an archive containing multiple mods, automatically select the previously installed", true)
-  };
+  return {PluginSetting("auto_reinstall",
+                        "when reinstalling from an archive containing multiple mods, "
+                        "automatically select the previously installed",
+                        true)};
 }
 
 unsigned int InstallerBundle::priority() const
@@ -80,13 +77,14 @@ bool InstallerBundle::isManualInstaller() const
   return false;
 }
 
-void InstallerBundle::onInstallationStart(QString const& archive, bool reinstallation, IModInterface* currentMod)
+void InstallerBundle::onInstallationStart(QString const& archive, bool reinstallation,
+                                          IModInterface* currentMod)
 {
   // We reset some field and fetch the previously installed file:
   m_InstallationFile = archive;
-  m_InstallerUsed = false;
-  m_SelectedFile = "";
-  m_PreviousFile = "";
+  m_InstallerUsed    = false;
+  m_SelectedFile     = "";
+  m_PreviousFile     = "";
 
   if (reinstallation && m_Organizer->pluginSetting(name(), "auto_reinstall").toBool()) {
     m_PreviousFile = currentMod->pluginSetting(name(), "archive", QString()).toString();
@@ -102,7 +100,8 @@ void InstallerBundle::onInstallationEnd(EInstallResult result, IModInterface* ne
   }
 }
 
-std::vector<std::shared_ptr<const MOBase::FileTreeEntry>> InstallerBundle::findObjects(std::shared_ptr<const IFileTree> tree) const
+std::vector<std::shared_ptr<const MOBase::FileTreeEntry>>
+InstallerBundle::findObjects(std::shared_ptr<const IFileTree> tree) const
 {
   std::vector<std::shared_ptr<const MOBase::FileTreeEntry>> entries;
   // Check if we have an archive:
@@ -112,11 +111,11 @@ std::vector<std::shared_ptr<const MOBase::FileTreeEntry>> InstallerBundle::findO
   int nDirs = 0;
   for (auto entry : *tree) {
     if (entry->isFile()) {
-      if (managerExtensions.contains(entry->suffix(), FileNameComparator::CaseSensitivity)) {
+      if (managerExtensions.contains(entry->suffix(),
+                                     FileNameComparator::CaseSensitivity)) {
         entries.push_back(entry);
       }
-    }
-    else {
+    } else {
       nDirs++;
     }
   }
@@ -140,8 +139,9 @@ bool InstallerBundle::isArchiveSupported(std::shared_ptr<const IFileTree> tree)
   return !findObjects(tree).empty();
 }
 
-IPluginInstaller::EInstallResult InstallerBundle::install(
-  GuessedValue<QString>& modName, std::shared_ptr<IFileTree>& tree, QString&, int &modId)
+IPluginInstaller::EInstallResult
+InstallerBundle::install(GuessedValue<QString>& modName,
+                         std::shared_ptr<IFileTree>& tree, QString&, int& modId)
 {
   // Find a valid "object":
   auto entries = findObjects(tree);
@@ -152,8 +152,7 @@ IPluginInstaller::EInstallResult InstallerBundle::install(
   std::shared_ptr<const FileTreeEntry> entry = nullptr;
   if (entries.size() == 1) {
     entry = entries[0];
-  }
-  else {
+  } else {
     if (!m_PreviousFile.isEmpty()) {
       entry = tree->find(m_PreviousFile);
     }
@@ -161,14 +160,12 @@ IPluginInstaller::EInstallResult InstallerBundle::install(
     if (entry == nullptr) {
       MultiArchiveDialog dialog(entries, parentWidget());
       if (dialog.exec() == QDialog::Accepted) {
-        entry = dialog.selectedEntry();
+        entry          = dialog.selectedEntry();
         m_SelectedFile = entry->pathFrom(tree);
-      }
-      else {
+      } else {
         if (dialog.manualRequested()) {
           return IPluginInstaller::RESULT_MANUALREQUESTED;
-        }
-        else {
+        } else {
           return IPluginInstaller::RESULT_CANCELED;
         }
       }
@@ -183,13 +180,14 @@ IPluginInstaller::EInstallResult InstallerBundle::install(
 
   // Extract it:
   QString tempFile = manager()->extractFile(entry);
-  IPluginInstaller::EInstallResult res = manager()->installArchive(modName, tempFile, modId);
+  IPluginInstaller::EInstallResult res =
+      manager()->installArchive(modName, tempFile, modId);
   if (res == IPluginInstaller::RESULT_SUCCESS) {
     res = IPluginInstaller::RESULT_SUCCESSCANCEL;
   }
   return res;
 }
 
-#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
+#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
 Q_EXPORT_PLUGIN2(installerBundle, InstallerBundle)
 #endif
diff --git a/src/installerbundle.h b/src/installerbundle.h
index bfd1466..885c6e9 100644
--- a/src/installerbundle.h
+++ b/src/installerbundle.h
@@ -1,89 +1,88 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer 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 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer 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 Mod Organizer.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef INSTALLERBUNDLE_H
-#define INSTALLERBUNDLE_H
-
-
-#include <iplugininstallersimple.h>
-
-class InstallerBundle : public MOBase::IPluginInstallerSimple
-{
-
-  Q_OBJECT
-  Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerSimple)
-#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
-  Q_PLUGIN_METADATA(IID "org.tannin.InstallerBundle" FILE "installerbundle.json")
-#endif
-
-public:
-
-  InstallerBundle();
-
-  virtual bool init(MOBase::IOrganizer* moInfo) override;
-  virtual QString name() const override;
-  virtual QString localizedName() const override;
-  virtual QString author() const override;
-  virtual QString description() const override;
-  virtual MOBase::VersionInfo version() const override;
-  virtual QList<MOBase::PluginSetting> settings() const override;
-
-  virtual unsigned int priority() const override;
-  virtual bool isManualInstaller() const override;
-
-  virtual void onInstallationStart(QString const& archive, bool reinstallation, MOBase::IModInterface* currentMod) override;
-  virtual void onInstallationEnd(EInstallResult result, MOBase::IModInterface* newMod) override;
-
-  virtual bool isArchiveSupported(std::shared_ptr<const MOBase::IFileTree> tree) const override;
-  virtual EInstallResult install(MOBase::GuessedValue<QString> &modName,
-                                 std::shared_ptr<MOBase::IFileTree> &tree,
-                                 QString &version, int &modID) override;
-
-private:
-
-  /**
-   * @brief Find the entries that can be extracted from this archive.
-   *
-   * @param tree The tree to look the entry in.
-   *
-   * @return the entry, if one was found, or a null pointer.
-   */
-  std::vector<std::shared_ptr<const MOBase::FileTreeEntry>> findObjects(std::shared_ptr<const MOBase::IFileTree> tree) const;
-
-private:
-
-  MOBase::IOrganizer* m_Organizer;
-
-  // The archive being installed:
-  QString m_InstallationFile;
-
-  // Indicates if this installer was used during the installation process:
-  bool m_InstallerUsed;
-
-  // Contains the file installed (when multiple archives are present), or an
-  // empty string:
-  QString m_SelectedFile;
-
-  // Contains the file previously installed (when multiple archives are present), or an
-  // empty string:
-  QString m_PreviousFile;
-};
-
-
-#endif // INSTALLERBUNDLE_H
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer 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 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer 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 Mod Organizer.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef INSTALLERBUNDLE_H
+#define INSTALLERBUNDLE_H
+
+#include <iplugininstallersimple.h>
+
+class InstallerBundle : public MOBase::IPluginInstallerSimple
+{
+
+  Q_OBJECT
+  Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerSimple)
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+  Q_PLUGIN_METADATA(IID "org.tannin.InstallerBundle" FILE "installerbundle.json")
+#endif
+
+public:
+  InstallerBundle();
+
+  virtual bool init(MOBase::IOrganizer* moInfo) override;
+  virtual QString name() const override;
+  virtual QString localizedName() const override;
+  virtual QString author() const override;
+  virtual QString description() const override;
+  virtual MOBase::VersionInfo version() const override;
+  virtual QList<MOBase::PluginSetting> settings() const override;
+
+  virtual unsigned int priority() const override;
+  virtual bool isManualInstaller() const override;
+
+  virtual void onInstallationStart(QString const& archive, bool reinstallation,
+                                   MOBase::IModInterface* currentMod) override;
+  virtual void onInstallationEnd(EInstallResult result,
+                                 MOBase::IModInterface* newMod) override;
+
+  virtual bool
+  isArchiveSupported(std::shared_ptr<const MOBase::IFileTree> tree) const override;
+  virtual EInstallResult install(MOBase::GuessedValue<QString>& modName,
+                                 std::shared_ptr<MOBase::IFileTree>& tree,
+                                 QString& version, int& modID) override;
+
+private:
+  /**
+   * @brief Find the entries that can be extracted from this archive.
+   *
+   * @param tree The tree to look the entry in.
+   *
+   * @return the entry, if one was found, or a null pointer.
+   */
+  std::vector<std::shared_ptr<const MOBase::FileTreeEntry>>
+  findObjects(std::shared_ptr<const MOBase::IFileTree> tree) const;
+
+private:
+  MOBase::IOrganizer* m_Organizer;
+
+  // The archive being installed:
+  QString m_InstallationFile;
+
+  // Indicates if this installer was used during the installation process:
+  bool m_InstallerUsed;
+
+  // Contains the file installed (when multiple archives are present), or an
+  // empty string:
+  QString m_SelectedFile;
+
+  // Contains the file previously installed (when multiple archives are present), or an
+  // empty string:
+  QString m_PreviousFile;
+};
+
+#endif  // INSTALLERBUNDLE_H
diff --git a/src/multiarchivedialog.cpp b/src/multiarchivedialog.cpp
index 485640f..03774f6 100644
--- a/src/multiarchivedialog.cpp
+++ b/src/multiarchivedialog.cpp
@@ -1,13 +1,12 @@
 #include "multiarchivedialog.h"
 #include "ui_multiarchivedialog.h"
 
-
 using namespace MOBase;
 
-
 MultiArchiveDialog::MultiArchiveDialog(
-  std::vector<std::shared_ptr<const MOBase::FileTreeEntry>> entries, QWidget* parent)
-  : QDialog(parent), ui(new Ui::MultiArchiveDialog), m_Manual(false), m_SelectedEntry(nullptr)
+    std::vector<std::shared_ptr<const MOBase::FileTreeEntry>> entries, QWidget* parent)
+    : QDialog(parent), ui(new Ui::MultiArchiveDialog), m_Manual(false),
+      m_SelectedEntry(nullptr)
 {
   ui->setupUi(this);
 
@@ -24,21 +23,18 @@ MultiArchiveDialog::MultiArchiveDialog(
   }
 }
 
-
 MultiArchiveDialog::~MultiArchiveDialog()
 {
   delete ui;
 }
 
-
 void MultiArchiveDialog::on_cancelBtn_clicked()
 {
   this->reject();
 }
 
-
 void MultiArchiveDialog::on_manualBtn_clicked()
 {
   m_Manual = true;
   this->reject();
-}
\ No newline at end of file
+}
diff --git a/src/multiarchivedialog.h b/src/multiarchivedialog.h
index 52ddbca..8995869 100644
--- a/src/multiarchivedialog.h
+++ b/src/multiarchivedialog.h
@@ -26,29 +26,32 @@ along with Mod Organizer.  If not, see <http://www.gnu.org/licenses/>.
 
 #include "ifiletree.h"
 
-namespace Ui {
-  class MultiArchiveDialog;
+namespace Ui
+{
+class MultiArchiveDialog;
 }
 
-
-
 /**
  *
  **/
-class MultiArchiveDialog: public QDialog
+class MultiArchiveDialog : public QDialog
 {
   Q_OBJECT
-  
+
 public:
- /**
-  * @brief Constructor
-  *
-  * @param tree the directory tree of the archive. The caller is resonsible to verify this actually qualifies as a bain installer
-  * @param modName proposed name for the mod. The dialog allows the user to change this
-  * @param packageTXT path to the extracted package.txt file or an empty string if there is none
-  * @param parent parent widget
-  **/
- explicit MultiArchiveDialog(std::vector<std::shared_ptr<const MOBase::FileTreeEntry>> entries,  QWidget *parent);
+  /**
+   * @brief Constructor
+   *
+   * @param tree the directory tree of the archive. The caller is resonsible to verify
+   *this actually qualifies as a bain installer
+   * @param modName proposed name for the mod. The dialog allows the user to change this
+   * @param packageTXT path to the extracted package.txt file or an empty string if
+   *there is none
+   * @param parent parent widget
+   **/
+  explicit MultiArchiveDialog(
+      std::vector<std::shared_ptr<const MOBase::FileTreeEntry>> entries,
+      QWidget* parent);
   ~MultiArchiveDialog();
 
   /**
@@ -67,11 +70,9 @@ private slots:
   void on_cancelBtn_clicked();
 
 private:
-
-  Ui::MultiArchiveDialog *ui;
+  Ui::MultiArchiveDialog* ui;
   bool m_Manual;
   std::shared_ptr<const MOBase::FileTreeEntry> m_SelectedEntry;
-
 };
 
-#endif // BAINCOMPLEXINSTALLERDIALOG_H
+#endif  // BAINCOMPLEXINSTALLERDIALOG_H

From 5995bce5e617f36f10e7ddfd01319516dda599f5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= <capelle.mikael@gmail.com>
Date: Sun, 9 Jun 2024 13:47:44 +0200
Subject: [PATCH 3/4] Add .git-blame-ignore-revs.

---
 .git-blame-ignore-revs | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 .git-blame-ignore-revs

diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 0000000..b2ed96a
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1 @@
+988523cac10641c510fd711d80d59c82d473f6a5

From 288e1b93c4237688e2ea1d5906a634c62f473c89 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= <capelle.mikael@gmail.com>
Date: Sun, 9 Jun 2024 13:47:44 +0200
Subject: [PATCH 4/4] Add github actions.

---
 .github/workflows/build.yml   | 16 ++++++++++++++++
 .github/workflows/linting.yml | 16 ++++++++++++++++
 2 files changed, 32 insertions(+)
 create mode 100644 .github/workflows/build.yml
 create mode 100644 .github/workflows/linting.yml

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..f4b1230
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,16 @@
+name: Build Installer Bundle Plugin
+
+on:
+  push:
+    branches: master
+  pull_request:
+    types: [opened, synchronize, reopened]
+
+jobs:
+  build:
+    runs-on: windows-2022
+    steps:
+      - name: Build Installer Bundle Plugin
+        uses: ModOrganizer2/build-with-mob-action@master
+        with:
+          mo2-dependencies: cmake_common uibase
diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml
new file mode 100644
index 0000000..31c504d
--- /dev/null
+++ b/.github/workflows/linting.yml
@@ -0,0 +1,16 @@
+name: Lint Installer Bundle Plugin
+
+on:
+  push:
+  pull_request:
+    types: [opened, synchronize, reopened]
+
+jobs:
+  lint:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+      - name: Check format
+        uses: ModOrganizer2/check-formatting-action@master
+        with:
+          check-path: "."