Commit Graph

275 Commits

Author SHA1 Message Date
Benny Baumann c98d4577c9 Refactor code for reading process environment from procfs 2020-10-27 17:54:37 +01:00
Christian Göttsche a3bb7cbe64 Hold only a const version of Settings in ProcessList 2020-10-26 19:30:38 +01:00
Christian Göttsche 4eb443926f Hold only a const version of Settings in Process 2020-10-26 19:30:38 +01:00
Christian Göttsche 7109172431 Mark process parameter of Process_writeField consistently const 2020-10-26 19:30:38 +01:00
Christian Göttsche 72103e9613 Hold only a const version of the ProcessList in Meters 2020-10-26 19:30:38 +01:00
Christian Göttsche f757810f48 Improve handling of no data in Disk and Network IO Meters 2020-10-26 19:17:14 +01:00
Jan Palus 167adc0a2b Parse POWER_SUPPLY_CAPACITY
If POWER_SUPPLY_{CHARGE,ENERGY}_NOW is missing then try to use
POWER_SUPPLY_CAPACITY to determine current charge level.
2020-10-26 19:03:09 +01:00
Christian Göttsche e12d48a661 Fix wrong strncmp replacement
Fixes 4c66eb6d4c
2020-10-20 22:30:13 +02:00
Christian Göttsche 7429c22201 Drop unnecessary cast 2020-10-20 22:29:16 +02:00
Christian Göttsche 8a08a3209c IWYU update
- Add Settings forward declaration in Process.h
- Add assert.h include in XUitls.c
- Add conditional stdbool.h include in Object.h
- Drop unneeded stddef.h include in Richstring.c
- Drop unneeded unistd.h include in Process.h
- Drop unneeded string.h include in linux/Platform.c
- Use String_eq to avoid string.h include in Action.c
- Improve script to run custom iwyu version
2020-10-20 21:44:25 +02:00
Daniel Lange 0ea18a6edb Merge branch 'Xalloc_Cleanup' of cgzones/htop 2020-10-20 10:23:47 +02:00
Daniel Lange 9f1a9ab2c2 Merge branch 'header_pause' of cgzones/htop
Continue to update generic data in paused mode
2020-10-20 10:17:58 +02:00
Christian Göttsche 4c66eb6d4c XUtils string related updates
- allow count out-parameter of String_split() to be NULL
- introduce xStrndup()
- do not allow NULL pointers passed to String_eq()
  it is not used in any code
- implement String_startsWith(), String_contains_i() and String_eq()
  as inline header functions
- adjust several conversion issues
2020-10-19 15:38:45 +02:00
Christian Göttsche 96e2a4259e Continue to update generic data in paused mode
Generic data, as CPU and memory usage, are used by Meters.
In paused mode they would stop receiving updates and especially Graph
Meters would stop showing continuous data.

Improves: #214
Closes: #253
2020-10-19 14:45:39 +02:00
Christian Göttsche 361877454f Cache PAGE_SIZE
man:sysconf(3) states:
    The values obtained from these functions are system configuration constants.
    They do not change during the lifetime of a process.
2020-10-19 14:42:35 +02:00
Benny Baumann 0f5262917f Make all required includes explicit
Information as seen by IWYU 0.12 + clang 9 on Linux
2020-10-18 20:09:05 +02:00
Benny Baumann 81543253cf Fix misaligned access inside taskstats structure
Reported by UB sanitizer (alongside several other messages):
linux/LinuxProcessList.c:782:25: runtime error: member access within misaligned address 0x614000000264 for type 'struct taskstats', which requires 8 byte alignment
0x614000000264: note: pointer points here
  64 01 03 00 0a 00 00 00  00 00 00 00 02 00 00 00  00 00 00 00 4b c8 2e 00  00 00 00 00 3e 45 3c fd
              ^

The issue doesn't cause trouble on x86, but any architecture with stricter memory alignment requirements may inadvertedly break.
2020-10-18 17:20:34 +02:00
Benny Baumann 872e542f4e Rename StringUtils.[ch] to XUtils.[ch] 2020-10-16 20:30:21 +02:00
Benny Baumann c6f04a9c5d Move xAsprintf, xSnprintf and xStrdup to StringUtils.h 2020-10-16 20:29:45 +02:00
Christian Göttsche 7cd093ce95 Add NetworkIOMeter 2020-10-16 20:00:14 +02:00
Christian Göttsche a63cfc8b7c Refactor generating starttime string into Process class 2020-10-16 19:23:40 +02:00
Christian Göttsche 783be7711d Do not use extra starttime process field on Linux 2020-10-16 19:23:40 +02:00
Christian Göttsche d744dac7ee Add SELinuxMeter 2020-10-16 19:20:07 +02:00
Christian Göttsche 7af06659e2 Mark remaining classes const 2020-10-13 14:56:01 +02:00
Christian Göttsche 25022c219d Read CPU count every cycle to avoid issues when HT/SMT mode changes 2020-10-12 13:15:23 +02:00
Benny Baumann 601480003f Centralise fault handling
This should be done as all platforms essentially did the same anyway and there was nothing platform specific.
2020-10-12 13:06:12 +02:00
Christian Göttsche 79ad39c718 Mark Object pointer to _display function const 2020-10-10 11:25:19 +02:00
Christian Göttsche 41eea8a355 Mark process argument of Process_isThread const 2020-10-09 10:18:40 +02:00
Benny Baumann 2970cae543 Handle parsing envID & VPid from process status file
Fixes #55
Fixes #192
2020-10-07 13:14:39 +02:00
Christian Göttsche ba282cfe19 Mark Object instances const 2020-10-07 13:01:53 +02:00
Christian Göttsche 08d85e6143 Mark Object classes and Object class fields const 2020-10-07 13:01:53 +02:00
Christian Göttsche 954d6c12f5 Simplify statm parsing and document unused fields 2020-10-06 18:59:02 +02:00
Christian Göttsche ad3acfc847 Handle Panel_getSelected() returning NULL
Found by compiling with LTO:

    ColumnsPanel.c: In function ‘ColumnsPanel_eventHandler’:
    ColumnsPanel.c:46:59: error: potential null pointer dereference [-Werror=null-dereference]
       46 |             ((ListItem*)Panel_getSelected(super))->moving = this->moving;
          |                                                           ^
    AvailableColumnsPanel.c: In function ‘AvailableColumnsPanel_eventHandler’:
    AvailableColumnsPanel.c:31:8: error: potential null pointer dereference [-Werror=null-dereference]
       31 |    int key = ((ListItem*) Panel_getSelected(super))->key;
          |        ^
    AvailableMetersPanel.c: In function ‘AvailableMetersPanel_eventHandler’:
    AvailableMetersPanel.c:40:24: error: potential null pointer dereference [-Werror=null-dereference]
       40 |    int param = selected->key & 0xff;
          |                        ^
    linux/IOPriorityPanel.c: In function ‘IOPriorityPanel_getIOPriority’:
    linux/IOPriorityPanel.c:37:11: error: potential null pointer dereference [-Werror=null-dereference]
       37 |    return (IOPriority) ( ((ListItem*) Panel_getSelected(this))->key );
          |           ^
2020-10-06 11:17:23 +02:00
Nathan Scott e9fa290019 Merge branch 'update-license-and-copyright-info' 2020-10-06 10:27:38 +11:00
Michael F. Schönitzer d93cac12be
Add a date and datetime meter (#159)
Add a date meter and sort header and source files in Makefile

Change the lists of header and source files sorted alphabetical and one
file per line. This way diffs become better readable and merges easier.
2020-10-05 13:52:58 +02:00
Daniel Lange 079c2abf8e Update License consistently to GPLv2 as per COPYING file 2020-10-05 10:13:12 +02:00
Christian Göttsche 42946ec113 Introduce ARRAYSIZE 2020-10-03 19:05:40 +02:00
Christian Göttsche d69585b82a Resolve DEBUG compilation issues
Use NDEBUG conditional instead of DEBUG.

Do not call static functions in extern inline ones.
    Vector.c:67:11: error: static function 'Vector_isConsistent' is used in an inline function with external linkage [-Werror,-Wstatic-in-inline]
2020-10-03 19:04:27 +02:00
Christian Göttsche e518459981 Add DiskIOMeter for IO read/write usage 2020-10-03 19:01:38 +02:00
Christian Göttsche 6f387008cb Add security attribute process column 2020-10-03 18:51:17 +02:00
Christian Göttsche 241e4b3dbf Drop redundant declarations
- `CRT_fatalError()` is declared twice in CRT.h
- `Process_pidFormat`, `Process_writeField()` and `Process_compare` are
  declared twice in Process.h
- `btime` is defined in LinuxProcess.c and also declared in
  LinuxProcess.h, so drop in LinuxProcessList.h
2020-09-29 10:44:42 +02:00
Christian Göttsche 7ae967a04b Drop redundant return statements 2020-09-29 10:44:42 +02:00
Christian Göttsche 843949131a Drop redundant casts to the same type 2020-09-29 10:44:42 +02:00
multi 9ee72568dc CPUMeter: add octuple-column CPU meters.
This is a straightforward extension of the existing multi-column CPU meter
code, which now allows for up CPU meters to be displayed in up to 16 columns.

This also adds the meter declarations to all the platform-specific code.
2020-09-28 14:35:35 +02:00
Nathan Scott 400178a89b Merge branch 'arc-is-not-cache' of https://github.com/multiplexd/htop into multiplexd-arc-is-not-cache 2020-09-28 14:44:12 +10:00
multi dfa40ad0eb Linux: consider the ZFS ARC to be cache.
This commit is based on a patch originally by @edef1c. The ZFS ARC is a cache
(it's in the name), which will be evicted by the kernel if memory pressure so
requires. Hence, the ARC should not be counted towards a system's total used
memory, and should instead be grouped with the other caches in the system.

Signed-off-by: edef <edef@edef.eu>
2020-09-24 23:27:27 +01:00
Christian Göttsche 18b1e9fba9 Do not drop qualifier in cast
ListItem.c:73:33: warning: cast from 'const void *' to 'struct ListItem_ *' drops const qualifier [-Wcast-qual]
   ListItem* obj1 = (ListItem*) cast1;
                                ^
ListItem.c:74:33: warning: cast from 'const void *' to 'struct ListItem_ *' drops const qualifier [-Wcast-qual]
   ListItem* obj2 = (ListItem*) cast2;
                                ^

Process.c:434:28: warning: cast from 'const void *' to 'struct Process_ *' drops const qualifier [-Wcast-qual]
   Process* p1 = (Process*)v1;
                           ^
Process.c:435:28: warning: cast from 'const void *' to 'struct Process_ *' drops const qualifier [-Wcast-qual]
   Process* p2 = (Process*)v2;
                           ^
Process.c:441:36: warning: cast from 'const void *' to 'struct Process_ *' drops const qualifier [-Wcast-qual]
   Settings *settings = ((Process*)v1)->settings;
                                   ^
Process.c:443:22: warning: cast from 'const void *' to 'struct Process_ *' drops const qualifier [-Wcast-qual]
      p1 = (Process*)v1;
                     ^
Process.c:444:22: warning: cast from 'const void *' to 'struct Process_ *' drops const qualifier [-Wcast-qual]
      p2 = (Process*)v2;
                     ^
Process.c:446:22: warning: cast from 'const void *' to 'struct Process_ *' drops const qualifier [-Wcast-qual]
      p2 = (Process*)v1;
                     ^
Process.c:447:22: warning: cast from 'const void *' to 'struct Process_ *' drops const qualifier [-Wcast-qual]
      p1 = (Process*)v2;
                     ^

AffinityPanel.c:37:16: warning: cast from 'const char *' to 'void *' drops const qualifier [-Wcast-qual]
   free((void*)this->text);
               ^
AffinityPanel.c:39:19: warning: cast from 'const char *' to 'void *' drops const qualifier [-Wcast-qual]
      free((void*)this->indent);
                  ^

linux/LinuxProcess.c:294:36: warning: cast from 'const void *' to 'struct Process_ *' drops const qualifier [-Wcast-qual]
   Settings *settings = ((Process*)v1)->settings;
                                   ^
linux/LinuxProcess.c:296:27: warning: cast from 'const void *' to 'struct LinuxProcess_ *' drops const qualifier [-Wcast-qual]
      p1 = (LinuxProcess*)v1;
                          ^
linux/LinuxProcess.c:297:27: warning: cast from 'const void *' to 'struct LinuxProcess_ *' drops const qualifier [-Wcast-qual]
      p2 = (LinuxProcess*)v2;
                          ^
linux/LinuxProcess.c:299:27: warning: cast from 'const void *' to 'struct LinuxProcess_ *' drops const qualifier [-Wcast-qual]
      p2 = (LinuxProcess*)v1;
                          ^
linux/LinuxProcess.c:300:27: warning: cast from 'const void *' to 'struct LinuxProcess_ *' drops const qualifier [-Wcast-qual]
      p1 = (LinuxProcess*)v2;
                          ^

linux/LinuxProcessList.c:62:32: warning: cast from 'const void *' to 'struct TtyDriver_ *' drops const qualifier [-Wcast-qual]
   TtyDriver* a = (TtyDriver*) va;
                               ^
linux/LinuxProcessList.c:63:32: warning: cast from 'const void *' to 'struct TtyDriver_ *' drops const qualifier [-Wcast-qual]
   TtyDriver* b = (TtyDriver*) vb;
                               ^

linux/Battery.c:130:21: warning: cast from 'const char *' to 'char *' drops const qualifier [-Wcast-qual]
      free((char *) isOnline);
                    ^
linux/Battery.c:197:26: warning: cast from 'const char *' to 'char *' drops const qualifier [-Wcast-qual]
      xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/type", entryName);
                         ^
linux/Battery.c:209:29: warning: cast from 'const char *' to 'char *' drops const qualifier [-Wcast-qual]
         xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/uevent", entryName);
                            ^
linux/Battery.c:262:29: warning: cast from 'const char *' to 'char *' drops const qualifier [-Wcast-qual]
         xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/online", entryName);
                            ^
2020-09-24 20:14:17 +02:00
Christian Göttsche cd1ba1422b Avoid bad function cast warning
linux/Platform.c:142:17: warning: cast from function call of type 'double' to non-matching type 'int' [-Wbad-function-cast]
   return (int) floor(uptime);
                ^~~~~~~~~~~~~
2020-09-24 20:14:17 +02:00
Christian Göttsche 4296e74ada Include prototype in Battery implementation
linux/Battery.c:291:6: warning: no previous prototype for function 'Battery_getData' [-Wmissing-prototypes]
void Battery_getData(double* level, ACPresence* isOnAC) {
     ^
2020-09-24 20:14:17 +02:00
Christian Göttsche edf1b10d2c Read CPU frequency from sysfs by default
Use the more portable sysfs node /sys/devices/system/cpu/cpuX/cpufreq/scaling_cur_freq
to get the CPU frequency.
In case of an error fall back to /proc/cpuinfo .

Also use a fixed width of 4 for the frequency to avoid position jumps
in case the frequency moves in the range 900-1100 MHz.
2020-09-24 20:11:28 +02:00
Christian Göttsche 594409f299 Add DeepCode inline suppression
commsize is bounded by the allocated length passed in by commLen, saved
into commLenIn
2020-09-24 19:56:30 +02:00
Benny Baumann 7ecea3d485 Use return value of CLAMP function 2020-09-24 18:06:36 +02:00
Benny Baumann 321960bd96 Update delay accounting to use NAN on error 2020-09-24 18:06:36 +02:00
Benny Baumann 3c65d78d77 Update CPU freq display to use NAN on error 2020-09-24 18:06:36 +02:00
Benny Baumann d0d3deb73c Properly query sysconf settting and use NAN if unavailable
This also fixes an issue with time returned negative if sysconf(_SC_CLK_TCK) returned an error.
2020-09-24 18:06:36 +02:00
Benny Baumann 29ec115143 Update IO rate display to use NAN on error 2020-09-24 18:06:36 +02:00
Benny Baumann 47e2cefe02 Update battery API to use NAN on error 2020-09-24 18:06:36 +02:00
Christian Göttsche f9966b5be3 Use checked allocation wrappers 2020-09-23 17:50:21 +02:00
Christian Göttsche eb260af6bf Fix memory leak on cgroup read failure 2020-09-21 13:55:29 +02:00
Christian Göttsche e719a85994 Mark noreturn functions 2020-09-18 12:28:40 +02:00
Christian Göttsche c3952e7c20 Use strict function prototypes
int foo(); declares a function taking any number of arguments.
2020-09-18 12:28:40 +02:00
Christian Göttsche 5d4061732f Allow third party sigsegv handler
For example from sanitizers.
2020-09-17 21:54:21 +02:00
Christian Göttsche 00665e2a2b Avoid unsigned integer overflow
unsigned overflow is well defined, but creates noise when using
sanitizers. unsigned overflow can be a symptom of logic issues of
counter, so its reasonable to use.

linux/LinuxProcessList.c:64:50: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'unsigned int'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior linux/LinuxProcessList.c:64:50 in
linux/LinuxProcessList.c:64:11: runtime error: implicit conversion from type 'unsigned int' of value 4294967295 (32-bit, unsigned) to type 'int' changed the value to -1 (32-bit, signed)
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior linux/LinuxProcessList.c:64:11 in
linux/LinuxProcessList.c:64:78: runtime error: unsigned integer overflow: 4 - 136 cannot be represented in type 'unsigned int'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior linux/LinuxProcessList.c:64:78 in
2020-09-17 21:53:31 +02:00
Christian Göttsche 98ee833932 Add Linux process column for context switches
Displays the incremental sum of voluntary_ctxt_switches and nonvoluntary_ctxt_switches.
2020-09-17 21:53:15 +02:00
Christian Göttsche 37921382f4 Use PROCDIR throughout instead of /proc on Linux 2020-09-17 21:44:27 +02:00
Christian Göttsche b096fdbfc0 Avoid potential buffer overflow in LinuxProcessList_readStatFile
Pass size of allocated command buffer and limit write.
2020-09-17 21:43:53 +02:00
Christian Göttsche d5eb72e64d Drop always true condition
`env` is allocated by checked allocation functions and can not be NULL.

This checks confuses clang analyzer and causes a null-dereference
warning on `env[size-1]`.
2020-09-12 18:14:39 +02:00
Nathan Scott 4597332959 Switch variable/field naming from WhiteList to MatchList 2020-09-09 19:38:15 +10:00
Nathan Scott c5808c56db Consolidate repeated macro definitions into one header
The MIN, MAX, CLAMP, MINIMUM, and MAXIMUM macros appear
throughout the codebase with many re-definitions.  Make
a single copy of each in a common header file, and use
the BSD variants of MINIMUM/MAXIMUM due to conflicts in
the system <sys/param.h> headers.
2020-09-09 16:56:04 +10:00
Nathan Scott 8ec5d4a3a0 Further, minor cleanups to headers post-MakeHeaders
Remove leftover empty ifdef/endif pairs, whitespace.
The generated htop.h file was also unused - removed.
2020-09-08 17:33:50 +10:00
Zev Weiss 7b7822b896 Remove superfluous 'extern's from function declarations.
Applied via:

  $ find * -name '*.h' -exec sed -i -r 's/^extern (.+\()/\1/;' {} +

Suggested-by: Bert Wesarg <bert.wesarg@googlemail.com>
2020-09-03 11:59:26 -05:00
Zev Weiss a1a027b9bd Axe automated header generation.
Reasoning:
 - implementation was unsound -- broke down when I added a fairly
   basic macro definition expanding to a struct initializer in a *.c
   file.

 - made it way too easy (e.g. via otherwise totally innocuous git
   commands) to end up with timestamps such that it always ran
   MakeHeader.py but never used its output, leading to overbuild noise
   when running what should be a null 'make'.

 - but mostly: it's just an awkward way of dealing with C code.
2020-09-03 11:58:58 -05:00
Christian Göttsche e7f6d1ce5f Reduce oom cast from long to int
Oom values should never be greater then INT_MAX, they should be in the
range 0 - 1000.

Improves: d9a5dd4b91
2020-08-31 11:55:53 +02:00
Nathan Scott 47a7d0bd74 Merge branch 'configure' of https://github.com/cgzones/htop into cgzones-configure 2020-08-31 17:13:37 +10:00
Nathan Scott b321177b08 Merge branch 'master' of github.com:htop-dev/htop 2020-08-31 16:57:46 +10:00
Christian Göttsche 5c99c6e942 Check btime sscanf parse from /proc/stat
Found by Coverity
2020-08-28 16:46:50 +02:00
Christian Göttsche a850d81bf5 Avoid use of uninitialized variables
Found by Coverity
2020-08-28 16:46:50 +02:00
Christian Göttsche 05a5fdc47f Ignore sscanf return value of /proc/stat
Found by Coverity
2020-08-28 16:46:50 +02:00
Christian Göttsche af84d3dfa9 Fail on out-of-range CPU number
Found by Coverity
2020-08-28 16:46:50 +02:00
Christian Göttsche d9a5dd4b91 Improve OOM output
* Fix sort by adding cast
* Shrink column size to 4
* Drop unnecessary maximum field width specifier in sscanf
2020-08-28 14:24:59 +02:00
Christian Göttsche a48ce9d103 Really tell gcc to ignore return value of fscanf 2020-08-28 13:10:41 +02:00
Nathan Scott b992d52bcf Increae the size of sysfs power supply path buffers
Resolves https://github.com/htop-dev/htop/issues/15
2020-08-28 16:57:21 +10:00
Christian Göttsche 4e2b9f0965 Avoid shadowing warnings 2020-08-25 12:01:56 +02:00
Christian Göttsche 6b11769448 Avoid conversion warning
linux/Platform.c:47:90: error: implicit conversion from ‘enum LinuxProcessFields’ to ‘enum ProcessFields’ [-Werror=enum-conversion]
       47 | ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
          |
2020-08-25 11:59:53 +02:00
Nathan Scott 6900e57efd Updates to project URLs in docs and embedded in source code 2020-08-22 15:47:11 +10:00
Nathan Scott 5dad65ac2a Update header files to match whitespace changes in source files 2020-08-22 15:46:31 +10:00
Christian Göttsche b92f62f912 Remove trailing whitespaces 2020-08-21 10:37:33 +02:00
Christian Göttsche 3856bf574b Introduce xAsprintf as checked version of asprintf 2020-08-21 10:37:29 +02:00
Nathan Scott 9a55efc8b5 Merge branch 'hishamhm-pull-920' 2020-08-20 18:24:35 +10:00
Nathan Scott 31391b206c Merge branch 'hishamhm-pull-1012' 2020-08-20 15:51:21 +10:00
Nathan Scott fed14a584d Merge branch 'hishamhm-pull-970' 2020-08-20 15:33:30 +10:00
Nathan Scott 6b443c5da9 Merge branch 'hishamhm-pull-932' 2020-08-20 14:47:07 +10:00
Nathan Scott a82fd262d7 Merge branch 'hishamhm-pull-960' 2020-08-20 14:19:53 +10:00
Nathan Scott cdff8aea2a Merge branch 'hishamhm-pull-946' 2020-08-20 14:13:07 +10:00
Nathan Scott 0f76359e4e Merge branch 'hishamhm-pull-890' 2020-08-20 14:01:47 +10:00
Nathan Scott 2be0992700 Merge branch 'hishamhm-pull-890' 2020-08-20 14:00:13 +10:00
Nathan Scott 45ae6191c1 Merge branch 'hishamhm-pull-866' 2020-08-20 12:29:25 +10:00
Nathan Scott e7d2f9383a Merge branch 'hishamhm-pull-857' 2020-08-20 09:47:13 +10:00
Nathan Scott 500fb283e9 Resolve compiler warnings and errors relating to the Arg union
Promote the Arg union to a core data type in Object.c such
that it is visible everywhere (many source files need it),
and correct declarations of several functions that use it.

The Process_sendSignal function is also corrected to have
the expected return type (bool, not void) - an error being
masked by ignoring this not-quite-harmless warning.  I've
also added error checking to the kill(2) call here, which
was previously overlooked / missing (?).
2020-08-20 09:35:33 +10:00
Nathan Scott eef6bc447d Correction to smaps buffer size passed to smaps path snprintf 2020-08-19 17:50:43 +10:00