aboutsummaryrefslogtreecommitdiffstats
path: root/idatui
diff options
context:
space:
mode:
Diffstat (limited to 'idatui')
-rw-r--r--idatui/launch.py16
-rw-r--r--idatui/project.py26
2 files changed, 36 insertions, 6 deletions
diff --git a/idatui/launch.py b/idatui/launch.py
index 0ae63a1..9618668 100644
--- a/idatui/launch.py
+++ b/idatui/launch.py
@@ -67,10 +67,18 @@ def main(argv: list[str] | None = None) -> int:
try:
if os.path.isfile(ppath):
project = Project.load(ppath)
- for b in args.binary: # extend an existing project
- project.add(b)
- if args.binary:
- project.save()
+ if args.binary: # extend an existing project, skipping repeats
+ before = len(project.refs)
+ for b in args.binary:
+ project.add(b)
+ added = len(project.refs) - before
+ dupes = len(args.binary) - added
+ if added:
+ project.save()
+ _log(f"added {added} binary(ies) to {ppath}")
+ if dupes:
+ _log(f"{dupes} already in the project (matched by path) "
+ f"— left alone")
elif args.binary:
project = Project.create(ppath, args.binary)
_log(f"created project {ppath} with {len(project.refs)} binaries")
diff --git a/idatui/project.py b/idatui/project.py
index 2d417df..ac4f033 100644
--- a/idatui/project.py
+++ b/idatui/project.py
@@ -123,8 +123,14 @@ class Project:
"""Write a new project file listing ``binaries`` (an ad-hoc project)."""
if not binaries:
raise ProjectError("a project needs at least one binary")
- entries = [{"path": os.path.abspath(os.path.expanduser(b))}
- for b in binaries]
+ entries, seen = [], set()
+ for b in binaries: # the same file twice on one command line is a typo
+ p = os.path.abspath(os.path.expanduser(b))
+ key = os.path.realpath(p)
+ if key in seen:
+ continue
+ seen.add(key)
+ entries.append({"path": p})
path = os.path.abspath(os.path.expanduser(path))
proj = cls(path, name or os.path.splitext(os.path.basename(path))[0],
entries, memory_pct)
@@ -188,7 +194,23 @@ class Project:
def by_label(self, label: str) -> BinaryRef | None:
return next((r for r in self._refs if r.label == label), None)
+ def by_source(self, binary: str) -> BinaryRef | None:
+ """The entry for ``binary``, matched by resolved path.
+
+ Identity is the real path, not the file name: a project can legitimately
+ hold two different ``foo.elf`` from different directories (the labels
+ disambiguate them), but the same file must not be listed twice — and
+ ``./a.elf``, ``/abs/a.elf`` and a symlink to it are all the same file.
+ """
+ key = os.path.realpath(os.path.abspath(os.path.expanduser(binary)))
+ return next((r for r in self._refs
+ if os.path.realpath(r.source) == key), None)
+
def add(self, binary: str, label: str | None = None) -> BinaryRef:
+ """Add a binary, or return the existing entry if it's already here."""
+ existing = self.by_source(binary)
+ if existing is not None:
+ return existing
entry = {"path": os.path.abspath(os.path.expanduser(binary))}
if label:
entry["label"] = label