HAL  v4.5.0-83-g30c8f0afc
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
import_project_dialog.cpp
Go to the documentation of this file.
5 #include <QRegularExpression>
6 #include <QGridLayout>
7 #include <QVBoxLayout>
8 #include <QFileDialog>
9 #include <QLabel>
10 #include <QUuid>
11 #include <QMessageBox>
12 #include <JlCompress.h>
13 
14 namespace hal {
15  FileSelectWidget::FileSelectWidget(const QString &defaultEntry, bool existingDir, QWidget* parent)
16  : QFrame(parent), mExistingDir(existingDir), mValid(false)
17  {
18  setMaximumHeight(48);
20  setLineWidth(3);
22 
23  QGridLayout* layout = new QGridLayout(this);
24  mEditor = new QLineEdit(this);
26  mEditor->setMinimumWidth(320);
27  mEditor->setText(defaultEntry);
28  connect(mEditor,&QLineEdit::textChanged,this,&FileSelectWidget::handleTextChanged);
29  layout->addWidget(mEditor,0,0);
30 
31  mButton = new QPushButton(gui_utility::getStyledSvgIcon("all->#3192C5",":/icons/folder"),"",this);
33  connect(mButton,&QPushButton::clicked,this,&FileSelectWidget::handleActivateFileDialog);
34  layout->addWidget(mButton,0,1);
35  handleTextChanged(defaultEntry);
36  }
37 
38  void FileSelectWidget::handleActivateFileDialog()
39  {
40  QString entry = mExistingDir
41  ? QFileDialog::getExistingDirectory(this, "Select directory:", mEditor->text())
42  : QFileDialog::getOpenFileName(this, "Select file:", mEditor->text(), "Zipped files (*.zip)");
43  mEditor->setText(entry);
44  }
45 
46  void FileSelectWidget::handleTextChanged(const QString &txt)
47  {
48  bool val = !txt.isEmpty();
49  if (val) val = QFileInfo(txt).exists();
50  if (val == mValid) return;
51  mValid = val;
53  }
54 
56  {
57  mEditor->setText(entry);
58  mEditor->setReadOnly(true);
59  mButton->setDisabled(true);
60  mButton->hide();
61  }
62 
64  : QDialog(parent), mStatus(NoImport)
65  {
66  setWindowTitle("Import Project");
67  QVBoxLayout* layout = new QVBoxLayout(this);
68  layout->addWidget(new QLabel("Zipped HAL project file:",this));
69  mZippedFile = new FileSelectWidget("", false, this);
70  connect(mZippedFile,&FileSelectWidget::selectionStatusChanged, this, &ImportProjectDialog::handleSelectionStatusChanged);
71  layout->addWidget(mZippedFile);
72 
73  layout->addStretch();
74 
75  layout->addWidget(new QLabel("Decompress in directory:",this));
76  mTargetDirectory = new FileSelectWidget(QDir::currentPath(), true, this);
77  connect(mTargetDirectory,&FileSelectWidget::selectionStatusChanged, this, &ImportProjectDialog::handleSelectionStatusChanged);
78  layout->addWidget(mTargetDirectory);
79 
80  layout->addStretch();
81 
82  layout->addWidget(new QLabel("Decompressed HAL project name:"));
83  mExtractProjectEdit = new QLineEdit(this);
84  layout->addWidget(mExtractProjectEdit);
85 
86  layout->addStretch();
87 
91  layout->addWidget(mButtonBox);
92  handleSelectionStatusChanged();
93  }
94 
96  {
97  mZippedFile->setFixedEntry(filename);
98  handleSelectionStatusChanged();
99  disconnect(mZippedFile,&FileSelectWidget::selectionStatusChanged, this, &ImportProjectDialog::handleSelectionStatusChanged);
100  }
101 
102  void ImportProjectDialog::handleSelectionStatusChanged()
103  {
104  bool valid = mTargetDirectory->valid() && mZippedFile->valid();
105  if (valid)
106  {
107  QRegularExpression reProj("(.*)/\\.project.json$");
108  QStringList zipFlist = JlCompress::getFileList(mZippedFile->selection());
109  int inx = zipFlist.indexOf(reProj);
110  if (inx < 0)
111  {
112  QMessageBox::warning(this,"Invalid ZIP-file", "The zipped file " + mZippedFile->selection() + " does not seem to be a hal project.\n");
113  valid = false;
114  }
115  else
116  {
117  QRegularExpressionMatch match = reProj.match(zipFlist.at(inx));
118  mTargetProjectName = match.captured(1);
119  QString suggestedDir = suggestedProjectDir(QDir(mTargetDirectory->selection()).absoluteFilePath(mTargetProjectName));
120  mExtractProjectEdit->setText(QFileInfo(suggestedDir).fileName());
121  }
122  }
123  mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(valid);
124  }
125 
127  {
128  mStatus = NoFileSelected;
129  if (mZippedFile->selection().isEmpty()) return false;
130 
131  mExtractedProjectAbsolutePath = QDir(mTargetDirectory->selection()).absoluteFilePath(mExtractProjectEdit->text());
132  if (QFileInfo(mExtractedProjectAbsolutePath).exists())
133  {
134  QMessageBox::warning(this, "Cannot Create Project", "Direcotry " + mExtractedProjectAbsolutePath + " already exists");
135  return false;
136  }
137  QStringList extracted;
138  QString tempdir;
139  if (mExtractProjectEdit->text() != mTargetProjectName)
140  {
141  tempdir = QDir(mTargetDirectory->selection()).absoluteFilePath("hal_temp_" + QUuid::createUuid().toString(QUuid::WithoutBraces));
142  QDir().mkpath(tempdir);
143  extracted = JlCompress::extractDir(mZippedFile->selection(),tempdir);
144  }
145  else
146  {
147  extracted = JlCompress::extractDir(mZippedFile->selection(),mTargetDirectory->selection());
148  }
149  mStatus = ErrorDecompress;
150  if (extracted.isEmpty()) return false;
151 
152  QString extractedDir = extracted.at(0);
153  while (extractedDir.back().unicode() == '/' || extractedDir.back().unicode() == '\\')
154  extractedDir.chop(1);
155 
156  switch (FileManager::directoryStatus(extractedDir))
157  {
159  mStatus = Ok;
160  break;
162  log_warning("gui", "No hal project file found in '{}' archive.", mZippedFile->selection().toStdString());
163  mStatus = NotAHalProject;
164  break;
165  case FileManager::IsFile:
166  log_warning("gui", "Archive '{}' contains single file, not a hal project directory.", mZippedFile->selection().toStdString());
167  mStatus = NotAHalProject;
168  break;
170  log_warning("gui", "Failed to decompress archive '{}', extracted HAL directory not found.", mZippedFile->selection().toStdString());
171  mStatus = NotAHalProject;
172  break;
174  log_warning("gui", "HAL directory extracted from archive '{}' must not have an extension.", mZippedFile->selection().toStdString());
175  mStatus = NotAHalProject;
176  break;
178  log_warning("gui", "Failed to parse HAL project file from archive '{}'.", mZippedFile->selection().toStdString());
179  mStatus = NotAHalProject;
180  break;
182  log_warning("gui", "Failed to find netlist in archiv '{}'.", mZippedFile->selection().toStdString());
183  mStatus = NotAHalProject;
184  break;
186  log_warning("gui", "Failed to find gate library in archiv '{}'.", mZippedFile->selection().toStdString());
187  mStatus = NotAHalProject;
188  break;
190  log_warning("gui", "Failed to decompress archive '{}', result is neither a file nor a directory.", mZippedFile->selection().toStdString());
191  mStatus = NotAHalProject;
192  break;
193  }
194 
195  if (!tempdir.isEmpty())
196  {
197  if (!QFile::rename(extractedDir,mExtractedProjectAbsolutePath))
198  {
199  log_warning("gui", "Failed to move decompressed archive to new location '{}'.", mExtractedProjectAbsolutePath.toStdString());
200  mStatus = ErrorDecompress;
201  }
202  else
203  {
204  // delete everything left in tempdir after moving project to project dir
205  deleteFilesRecursion(tempdir);
206  }
207  }
208 
209  if (mStatus != Ok)
210  {
211  // Not a hal project, delete all extracted
212  deleteFilesList(extracted);
213  return false;
214  }
215 
216  return true;
217  }
218 
219  void ImportProjectDialog::deleteFilesRecursion(QString dir)
220  {
221  for (QFileInfo finfo : QDir(dir).entryInfoList(QDir::AllEntries | QDir::Hidden | QDir::NoDotAndDotDot))
222  {
223  if (finfo.isDir())
224  deleteFilesRecursion(finfo.absoluteFilePath());
225  else
226  QFile::remove(finfo.absoluteFilePath());
227  }
228  QDir().rmdir(dir);
229  }
230 
231  void ImportProjectDialog::deleteFilesList(QStringList files)
232  {
233  // Delete files, keep directories (might be not empty)
234  auto it = files.begin();
235  while (it != files.end())
236  if (QFileInfo(*it).isDir())
237  ++it;
238  else
239  {
240  QFile::remove(*it);
241  it = files.erase(it);
242  }
243 
244  // Delete directories in revers order
245  if (!files.isEmpty())
246  {
247  for (;;)
248  {
249  --it;
250  QFile::remove(*it);
251  if (it == files.begin())
252  break;
253  }
254  }
255  }
256 
258  {
259  QString retval = filename;
260  retval.remove(QRegularExpression("\\.\\w*$"));
261 
262  QString basedir = retval;
263  int count = 2;
264 
265  for (;;) // loop until non existing directory found
266  {
267  if (!QFileInfo(retval).exists())
268  return retval;
269  retval = QString("%1_%2").arg(basedir).arg(count++);
270  }
271  return retval;
272  }
273 }
static DirectoryStatus directoryStatus(const QString &pathname)
FileSelectWidget(const QString &defaultEntry, bool existingDir, QWidget *parent=nullptr)
void setFixedEntry(const QString &entry)
static QString suggestedProjectDir(const QString &filename)
ImportProjectDialog(QWidget *parent=nullptr)
void setZippedFile(const QString &filename)
#define log_warning(channel,...)
Definition: log.h:76
QIcon getStyledSvgIcon(const QString &from_to_colors_enabled, const QString &svg_path, QString from_to_colors_disabled=QString())
Definition: graphics.cpp:60
Definition: defines.h:45
void clicked(bool checked)
ushort unicode() const const
virtual void accept()
virtual void reject()
QPushButton * button(QDialogButtonBox::StandardButton which) const const
QString absoluteFilePath(const QString &fileName) const const
QString currentPath()
bool mkpath(const QString &dirPath) const const
bool rmdir(const QString &dirName) const const
bool remove()
bool rename(const QString &newName)
QString getExistingDirectory(QWidget *parent, const QString &caption, const QString &dir, QFileDialog::Options options)
bool exists() const const
bool isDir() const const
void setLineWidth(int)
void setFrameStyle(int style)
void addWidget(QWidget *w)
void setReadOnly(bool)
void setText(const QString &)
void textChanged(const QString &text)
const T & at(int i) const const
QList::iterator begin()
QList::iterator end()
QList::iterator erase(QList::iterator pos)
bool isEmpty() const const
QMessageBox::StandardButton warning(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
QString captured(int nth) const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QChar back() const const
void chop(int n)
bool isEmpty() const const
QString & remove(int position, int n)
std::string toStdString() const const
int indexOf(QStringView str, int from) const const
QUuid createUuid()
void setEnabled(bool)
void hide()
QLayout * layout() const const
void setMaximumHeight(int maxh)
void setMinimumWidth(int minw)
void setDisabled(bool disable)
void setSizePolicy(QSizePolicy)
void setWindowTitle(const QString &)