Cppcheck
librarydialog.cpp
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2024 Cppcheck team.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "librarydialog.h"
20 
21 #include "common.h"
23 #include "libraryeditargdialog.h"
24 #include "path.h"
25 #include "utils.h"
26 
27 #include "ui_librarydialog.h"
28 
29 #include <QCheckBox>
30 #include <QComboBox>
31 #include <QFile>
32 #include <QFileDialog>
33 #include <QFlags>
34 #include <QIODevice>
35 #include <QLineEdit>
36 #include <QList>
37 #include <QListWidget>
38 #include <QListWidgetItem>
39 #include <QMessageBox>
40 #include <QPlainTextEdit>
41 #include <QPushButton>
42 #include <QRegularExpression>
43 #include <QTextStream>
44 #include <Qt>
45 
46 class QWidget;
47 
48 // TODO: get/compare functions from header
49 
50 namespace {
51  class FunctionListItem : public QListWidgetItem {
52  public:
53  FunctionListItem(QListWidget *view,
55  bool selected)
56  : QListWidgetItem(view), function(function) {
57  setText(function->name);
58  setFlags(flags() | Qt::ItemIsEditable);
59  setSelected(selected);
60  }
62  };
63 }
64 
65 LibraryDialog::LibraryDialog(QWidget *parent) :
66  QDialog(parent),
67  mUi(new Ui::LibraryDialog)
68 {
69  mUi->setupUi(this);
70  mUi->buttonSave->setEnabled(false);
71  mUi->buttonSaveAs->setEnabled(false);
72  mUi->sortFunctions->setEnabled(false);
73  mUi->filter->setEnabled(false);
74  mUi->addFunction->setEnabled(false);
75 
76  //As no function selected, this disables function editing widgets
78 }
79 
81 {
82  delete mUi;
83 }
84 
86 {
87  QList<QListWidgetItem *> selitems = mUi->functions->selectedItems();
88  if (selitems.count() != 1)
89  return nullptr;
90  return static_cast<FunctionListItem *>(selitems.first())->function;
91 }
92 
94 {
95  const QString datadir = getDataDir();
96 
97  QString selectedFilter;
98  const QString filter(tr("Library files (*.cfg)"));
99  const QString selectedFile = QFileDialog::getOpenFileName(this,
100  tr("Open library file"),
101  datadir,
102  filter,
103  &selectedFilter);
104 
105  if (selectedFile.isEmpty())
106  return;
107 
108  QFile file(selectedFile);
109  if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
110  QMessageBox msg(QMessageBox::Critical,
111  tr("Cppcheck"),
112  tr("Cannot open file %1.").arg(selectedFile),
113  QMessageBox::Ok,
114  this);
115  msg.exec();
116  return;
117  }
118 
119  CppcheckLibraryData tempdata;
120  const QString errmsg = tempdata.open(file);
121  if (!errmsg.isNull()) {
122  QMessageBox msg(QMessageBox::Critical,
123  tr("Cppcheck"),
124  tr("Failed to load %1. %2.").arg(selectedFile).arg(errmsg),
125  QMessageBox::Ok,
126  this);
127  msg.exec();
128  return;
129  }
130 
131  mIgnoreChanges = true;
132  mData.swap(tempdata);
133  mFileName = selectedFile;
134  mUi->buttonSave->setEnabled(false);
135  mUi->buttonSaveAs->setEnabled(true);
136  mUi->filter->clear();
137  mUi->functions->clear();
138  for (CppcheckLibraryData::Function &function : mData.functions) {
139  mUi->functions->addItem(new FunctionListItem(mUi->functions,
140  &function,
141  false));
142  }
143  mUi->sortFunctions->setEnabled(!mData.functions.empty());
144  mUi->filter->setEnabled(!mData.functions.empty());
145  mUi->addFunction->setEnabled(true);
146  mIgnoreChanges = false;
147 }
148 
150 {
151  if (mFileName.isNull())
152  return;
153  QFile file(mFileName);
154  if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
155  QTextStream ts(&file);
156  ts << mData.toString() << '\n';
157  mUi->buttonSave->setEnabled(false);
158  } else {
159  QMessageBox msg(QMessageBox::Critical,
160  tr("Cppcheck"),
161  tr("Cannot save file %1.").arg(mFileName),
162  QMessageBox::Ok,
163  this);
164  msg.exec();
165  }
166 }
167 
169 {
170  const QString filter(tr("Library files (*.cfg)"));
171  const QString path = Path::getPathFromFilename(mFileName.toStdString()).c_str();
172  QString selectedFile = QFileDialog::getSaveFileName(this,
173  tr("Save the library as"),
174  path,
175  filter);
176  if (selectedFile.isEmpty())
177  return;
178 
179  if (!selectedFile.endsWith(".cfg", Qt::CaseInsensitive))
180  selectedFile += ".cfg";
181 
182  mFileName = selectedFile;
183  saveCfg();
184 }
185 
187 {
188  auto *d = new LibraryAddFunctionDialog;
189  if (d->exec() == QDialog::Accepted && !d->functionName().isEmpty()) {
190 
192  f.name = d->functionName();
193  const int args = d->numberOfArguments();
194 
195  for (int i = 1; i <= args; i++) {
197  arg.nr = i;
198  f.args.append(arg);
199  }
200  mData.functions.append(f);
201  mUi->functions->addItem(new FunctionListItem(mUi->functions, &mData.functions.back(), false));
202  mUi->buttonSave->setEnabled(true);
203  mUi->sortFunctions->setEnabled(!mData.functions.empty());
204  mUi->filter->setEnabled(!mData.functions.empty());
205  }
206  delete d;
207 }
208 
209 void LibraryDialog::editFunctionName(QListWidgetItem* item)
210 {
211  if (mIgnoreChanges)
212  return;
213  QString functionName = item->text();
214  CppcheckLibraryData::Function * const function = dynamic_cast<FunctionListItem*>(item)->function;
215  if (functionName != function->name) {
216  const QRegularExpressionMatch matchRes = QRegularExpression("^" NAMES "$").match(functionName);
217  if (matchRes.hasMatch()) {
218  function->name = functionName;
219  mUi->buttonSave->setEnabled(true);
220  } else {
221  mIgnoreChanges = true;
222  item->setText(function->name);
223  mIgnoreChanges = false;
224  }
225  }
226 }
227 
229 {
230  const CppcheckLibraryData::Function * const function = currentFunction();
231 
232  if (function == nullptr) {
233  mUi->comments->clear();
234  mUi->comments->setEnabled(false);
235 
236  mUi->noreturn->setCurrentIndex(0);
237  mUi->noreturn->setEnabled(false);
238 
239  mUi->useretval->setChecked(false);
240  mUi->useretval->setEnabled(false);
241 
242  mUi->leakignore->setChecked(false);
243  mUi->leakignore->setEnabled(false);
244 
245  mUi->arguments->clear();
246  mUi->arguments->setEnabled(false);
247 
248  mUi->editArgButton->setEnabled(false);
249  return;
250  }
251 
252  mIgnoreChanges = true;
253  mUi->comments->setPlainText(function->comments);
254  mUi->comments->setEnabled(true);
255 
256  mUi->noreturn->setCurrentIndex(function->noreturn);
257  mUi->noreturn->setEnabled(true);
258 
259  mUi->useretval->setChecked(function->useretval);
260  mUi->useretval->setEnabled(true);
261 
262  mUi->leakignore->setChecked(function->leakignore);
263  mUi->leakignore->setEnabled(true);
264 
265  updateArguments(*function);
266  mUi->arguments->setEnabled(true);
267 
268  mUi->editArgButton->setEnabled(true);
269  mIgnoreChanges = false;
270 }
271 
273 {
274  if (sort) {
275  mUi->functions->sortItems();
276  } else {
277  mIgnoreChanges = true;
278  const CppcheckLibraryData::Function* selfunction = currentFunction();
279  mUi->functions->clear();
280  for (CppcheckLibraryData::Function &function : mData.functions) {
281  mUi->functions->addItem(new FunctionListItem(mUi->functions,
282  &function,
283  selfunction == &function));
284  }
285  if (!mUi->filter->text().isEmpty())
286  filterFunctions(mUi->filter->text());
287  mIgnoreChanges = false;
288  }
289 }
290 
291 void LibraryDialog::filterFunctions(const QString& filter)
292 {
293  QList<QListWidgetItem *> allItems = mUi->functions->findItems(QString(), Qt::MatchContains);
294 
295  if (filter.isEmpty()) {
296  for (QListWidgetItem *item : allItems) {
297  item->setHidden(false);
298  }
299  } else {
300  for (QListWidgetItem *item : allItems) {
301  item->setHidden(!item->text().startsWith(filter));
302  }
303  }
304 }
305 
307 {
308  if (mIgnoreChanges)
309  return;
310 
312  if (!function)
313  return;
314 
315  function->comments = mUi->comments->toPlainText();
316  function->noreturn = (CppcheckLibraryData::Function::TrueFalseUnknown)mUi->noreturn->currentIndex();
317  function->useretval = mUi->useretval->isChecked();
318  function->leakignore = mUi->leakignore->isChecked();
319 
320  mUi->buttonSave->setEnabled(true);
321 }
322 
324 {
326  if (!function)
327  return;
328 
329  if (mUi->arguments->selectedItems().count() != 1)
330  return;
331  CppcheckLibraryData::Function::Arg &arg = function->args[mUi->arguments->row(mUi->arguments->selectedItems().first())];
332 
333  LibraryEditArgDialog d(nullptr, arg);
334  if (d.exec() == QDialog::Accepted) {
335  const unsigned number = arg.nr;
336  arg = d.getArg();
337  arg.nr = number;
338  mUi->arguments->selectedItems().first()->setText(getArgText(arg));
339  }
340  mUi->buttonSave->setEnabled(true);
341 }
342 
344 {
345  QString s("arg");
347  s += QString::number(arg.nr);
348 
349  s += "\n not bool: " + QString(bool_to_string(arg.notbool));
350  s += "\n not null: " + QString(bool_to_string(arg.notnull));
351  s += "\n not uninit: " + QString(bool_to_string(arg.notuninit));
352  s += "\n format string: " + QString(bool_to_string(arg.formatstr));
353  s += "\n strz: " + QString(bool_to_string(arg.strz));
354  s += "\n valid: " + QString(arg.valid.isEmpty() ? "any" : arg.valid);
355  for (const CppcheckLibraryData::Function::Arg::MinSize &minsize : arg.minsizes) {
356  s += "\n minsize: " + minsize.type + " " + minsize.arg + " " + minsize.arg2;
357  }
358  return s;
359 }
360 
362 {
363  mUi->arguments->clear();
364  for (const CppcheckLibraryData::Function::Arg &arg : function.args) {
365  mUi->arguments->addItem(getArgText(arg));
366  }
367 }
QString open(QIODevice &file)
QList< Function > functions
void swap(CppcheckLibraryData &other)
static QString getArgText(const CppcheckLibraryData::Function::Arg &arg)
~LibraryDialog() override
QString mFileName
Definition: librarydialog.h:58
CppcheckLibraryData::Function * currentFunction()
CppcheckLibraryData mData
Definition: librarydialog.h:57
void editFunctionName(QListWidgetItem *)
Ui::LibraryDialog * mUi
Definition: librarydialog.h:56
void sortFunctions(bool)
void updateArguments(const CppcheckLibraryData::Function &function)
LibraryDialog(QWidget *parent=nullptr)
void filterFunctions(const QString &)
CppcheckLibraryData::Function::Arg getArg() const
static std::string getPathFromFilename(const std::string &filename)
Lookup the path part from a filename (e.g., '/tmp/a.h' -> '/tmp/', 'a.h' -> '')
Definition: path.cpp:88
QString getDataDir()
Get configured data dir.
Definition: common.cpp:75
#define NAMES
Definition: aboutdialog.h:27
static const char * bool_to_string(bool b)
Definition: utils.h:345