summaryrefslogtreecommitdiff
path: root/goo/gbasename.cc
blob: e84276e0e130bb6e8e8cc64995f06694b96ada12 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//========================================================================
//
// gbasename.cc
//
// Wrapper for libgen's basename() call which returns a std::string.
// This is a convenience method working around questionable behavior
// in the copy of basename() provided by libgen.h.
//
// According to man 3 basename:
//
//    Both dirname() and basename() may modify the contents of path, so it
//    may be desirable to pass a copy when calling one of these functions.
//
//    ...
//
//    These functions may return pointers to statically allocated memory
//    which may be overwritten by subsequent calls.  Alternatively, they
//    may return a pointer to some part of path, so that the string
//    referred to by path should not be modified or freed until the pointer
//    returned by the function is no longer required.
//
// Because basename can modify filename (for some reason), we have to
// duplicate our input into a mutable buffer before we can call it.
// The return value might be part of this mutable temporary, but not
// generally the front, so 'char *' cannot be used as our return value.
// The return value might also be a statically allocated string,
// rendering basename (and thus gbasename) non-thread-safe. Because
// we don't know how basename()'s return value is lifecycled, we need
// to duplicate it again into something whose lifecycle we can predict.
//
// This is how a method that should amount to finding the last slash
// in a string ends up requiring two memory allocations while managing
// not to be thread-safe. In a way, it's kind of impressive.
//
// This file is licensed under the GPLv2 or later
//
// Copyright (C) 2018, 2019 Greg Knight <lyngvi@gmail.com>
// Copyright (C) 2019 Albert Astals Cid <aacid@kde.org>
//
//========================================================================

#include "gbasename.h"
#ifndef _MSC_VER
#  include <libgen.h>
#endif
#include <cstdlib>
#include <cstring>

std::string gbasename(const char* filename)
{
#ifdef _MSC_VER
  char fname[_MAX_FNAME] = {}, fext[_MAX_EXT] = {};
  errno_t z = _splitpath_s(filename, NULL, 0, NULL, 0, fname, _countof(fname), fext, _countof(fext));
  return std::string(fname) + std::string(fext);
#else
  char* mutabl = strdup(filename);
  std::string retu = basename(mutabl);
  free(mutabl);
  return retu;
#endif
}